Berikut adalah contoh lain dari AsyncTask yang menggunakan a Fragment
untuk menangani perubahan konfigurasi runtime (seperti ketika pengguna memutar layar) setRetainInstance(true)
. Bilah kemajuan yang ditentukan (diperbarui secara berkala) juga ditunjukkan.
Contohnya sebagian didasarkan pada dokumen resmi, Mempertahankan Objek Selama Perubahan Konfigurasi .
Dalam contoh ini pekerjaan yang membutuhkan utas latar belakang adalah hanya memuat gambar dari internet ke UI.
Alex Lockwood tampaknya benar bahwa ketika menangani perubahan konfigurasi runtime dengan AsyncTasks menggunakan "Retained Fragment" adalah praktik terbaik. onRetainNonConfigurationInstance()
akan ditinggalkan di Lint, di Android Studio. Dokumen resmi memperingatkan kami untuk tidak menggunakan android:configChanges
, dari Menangani Konfigurasi, Ubah Diri , ...
Menangani perubahan konfigurasi sendiri dapat membuatnya jauh lebih sulit untuk menggunakan sumber daya alternatif, karena sistem tidak secara otomatis menerapkannya untuk Anda. Teknik ini harus dianggap sebagai upaya terakhir ketika Anda harus menghindari restart karena perubahan konfigurasi dan tidak dianjurkan untuk sebagian besar aplikasi.
Lalu ada masalah apakah seseorang harus menggunakan AsyncTask sama sekali untuk utas latar belakang.
The referensi resmi untuk AsyncTask memperingatkan ...
AsyncTasks idealnya harus digunakan untuk operasi pendek (paling banyak beberapa detik.) Jika Anda perlu menjaga agar thread tetap berjalan untuk jangka waktu yang lama, sangat disarankan Anda menggunakan berbagai API yang disediakan oleh pacakge java.util.concurrent seperti Pelaksana, ThreadPoolExecutor dan FutureTask.
Atau orang dapat menggunakan layanan, loader (menggunakan CursorLoader atau AsyncTaskLoader), atau penyedia konten untuk melakukan operasi asinkron.
Saya memecah sisa posting menjadi:
- Prosedur; dan
- Semua kode untuk prosedur di atas.
Prosedur
Mulailah dengan AsyncTask dasar sebagai kelas dalam dari suatu kegiatan (tidak perlu menjadi kelas dalam tetapi mungkin akan nyaman untuk dilakukan). Pada tahap ini AsyncTask tidak menangani perubahan konfigurasi runtime.
public class ThreadsActivity extends ActionBarActivity {
private ImageView mPictureImageView;
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
return loadImageFromNetwork(urls[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
mPictureImageView.setImageBitmap(bitmap);
}
}
/**
* Requires in AndroidManifext.xml
* <uses-permission android:name="android.permission.INTERNET" />
*/
private Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream)
new URL(url).getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
}
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask()
.execute("http://i.imgur.com/SikTbWe.jpg");
}
}
Tambahkan kelas bersarang RetainedFragment yang memperluas kelas Fragement dan tidak memiliki UI sendiri. Tambahkan setRetainInstance (true) ke acara onCreate dari fragmen ini. Berikan prosedur untuk mengatur dan mendapatkan data Anda.
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
...
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive
// runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Integer,Bitmap> {
....
Di onCreate kelas aktivitas terluar () menangani RetainedFragment: Referensi itu jika sudah ada (dalam hal aktivitas me-restart); buat dan tambahkan jika tidak ada; Kemudian, jika sudah ada, dapatkan data dari RetainedFragment dan atur UI Anda dengan data itu.
public class ThreadsActivity extends Activity {
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView =
(ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar =
(ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must
// reference it with a tag.
mRetainedFragment =
(RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction()
.add(mRetainedFragment, retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView
.setImageBitmap(mRetainedFragment.getData());
}
}
Memulai AsyncTask dari UI
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
Tambahkan dan beri kode bilah kemajuan yang ditentukan:
- Tambahkan bilah kemajuan ke tata letak UI;
- Dapatkan referensi untuknya di Activity oncreate ();
- Jadikan itu terlihat dan tidak terlihat pada awal dan akhir proses;
- Tetapkan progres untuk melaporkan ke UI di onProgressUpdate.
- Ubah parameter Generik AsyncTask ke-2 dari Void ke tipe yang dapat menangani pembaruan kemajuan (mis. Integer).
- publikasikan Kemajuan pada titik reguler di doInBackground ().
Semua kode untuk prosedur di atas
Tata Letak Aktivitas.
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.mysecondapp.ThreadsActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<ImageView
android:id="@+id/imageView_picture"
android:layout_width="300dp"
android:layout_height="300dp"
android:background="@android:color/black" />
<Button
android:id="@+id/button_get_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@id/imageView_picture"
android:onClick="getPicture"
android:text="Get Picture" />
<Button
android:id="@+id/button_clear_picture"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/button_get_picture"
android:layout_toEndOf="@id/button_get_picture"
android:layout_toRightOf="@id/button_get_picture"
android:onClick="clearPicture"
android:text="Clear Picture" />
<ProgressBar
android:id="@+id/progressBar_loading"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/button_get_picture"
android:progress="0"
android:indeterminateOnly="false"
android:visibility="invisible" />
</RelativeLayout>
</ScrollView>
Aktivitas dengan: subclassed AsyncTask inner class; kelas dalam RetainedFragment subclass yang menangani perubahan konfigurasi runtime (misalnya ketika pengguna memutar layar); dan pembaruan progress bar yang ditentukan secara berkala. ...
public class ThreadsActivity extends Activity {
private ImageView mPictureImageView;
private RetainedFragment mRetainedFragment = null;
private ProgressBar mLoadingProgressBar;
public static class RetainedFragment extends Fragment {
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// The key to making data survive runtime configuration changes.
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask extends AsyncTask<String,
Integer, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
// Simulate a burdensome load.
int sleepSeconds = 4;
for (int i = 1; i <= sleepSeconds; i++) {
SystemClock.sleep(1000); // milliseconds
publishProgress(i * 20); // Adjust for a scale to 100
}
return com.example.standardapplibrary.android.Network
.loadImageFromNetwork(
urls[0]);
}
@Override
protected void onProgressUpdate(Integer... progress) {
mLoadingProgressBar.setProgress(progress[0]);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
publishProgress(100);
mRetainedFragment.setData(bitmap);
mPictureImageView.setImageBitmap(bitmap);
mLoadingProgressBar.setVisibility(View.INVISIBLE);
publishProgress(0);
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_threads);
final String retainedFragmentTag = "RetainedFragmentTag";
mPictureImageView = (ImageView) findViewById(R.id.imageView_picture);
mLoadingProgressBar = (ProgressBar) findViewById(R.id.progressBar_loading);
// Find the RetainedFragment on Activity restarts
FragmentManager fm = getFragmentManager();
// The RetainedFragment has no UI so we must reference it with a tag.
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
retainedFragmentTag);
// if Retained Fragment doesn't exist create and add it.
if (mRetainedFragment == null) {
// Add the fragment
mRetainedFragment = new RetainedFragment();
fm.beginTransaction().add(mRetainedFragment,
retainedFragmentTag).commit();
// The Retained Fragment exists
} else {
mPictureImageView.setImageBitmap(mRetainedFragment.getData());
}
}
public void getPicture(View view) {
mLoadingProgressBar.setVisibility(View.VISIBLE);
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
public void clearPicture(View view) {
mRetainedFragment.setData(null);
mPictureImageView.setImageBitmap(null);
}
}
Dalam contoh ini fungsi perpustakaan (direferensikan di atas dengan awalan paket eksplisit com.example.standardapplibrary.android.Network) yang melakukan pekerjaan nyata ...
public static Bitmap loadImageFromNetwork(String url) {
Bitmap bitmap = null;
try {
bitmap = BitmapFactory.decodeStream((InputStream) new URL(url)
.getContent());
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
Tambahkan izin apa pun yang diperlukan tugas latar belakang Anda ke AndroidManifest.xml ...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
Tambahkan aktivitas Anda ke AndroidManifest.xml ...
<manifest>
...
<application>
<activity
android:name=".ThreadsActivity"
android:label="@string/title_activity_threads"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.mysecondapp.MainActivity" />
</activity>