Ось ще один приклад AsyncTask, який використовує a Fragmentдля обробки змін конфігурації виконання (як коли користувач обертає екран) за допомогоюsetRetainInstance(true) . Також показана визначена (регулярно оновлювана) смужка прогресу.
Приклад частково базується на офіційних документах, що зберігають об’єкт під час зміни конфігурації .
У цьому прикладі робота, що вимагає фонової нитки, - це просто завантаження зображення з Інтернету в інтерфейс користувача.
Алекс Локвуд, здається, має рацію, що, якщо мова йде про обробку змін конфігурації виконання за допомогою AsyncTasks, використовуючи "Збережений фрагмент", це найкраща практика. onRetainNonConfigurationInstance()стає застарілим у Lint, в Android Studio. Офіційні документи попереджають нас від використання android:configChanges, від Handling зміни конфігурації Самостійно ...
Самі поводження із зміною конфігурації може значно ускладнити використання альтернативних ресурсів, оскільки система автоматично не застосовує їх до вас. Цю методику слід вважати крайнім засобом, коли потрібно уникати перезапуску через зміну конфігурації і не рекомендується для більшості програм.
Тоді виникає питання, чи варто взагалі використовувати AsyncTask для фонового потоку.
Офіційна посилання на AsyncTask попереджає ...
AsyncTasks в ідеалі слід використовувати для коротких операцій (максимум декілька секунд.) Якщо вам потрібно тримати потоки протягом тривалих періодів часу, настійно рекомендується використовувати різні API, надані java.util.concurrent pacakge, наприклад Виконавець, ThreadPoolExecutor та FutureTask.
Можна також скористатися сервісом, завантажувачем (за допомогою CursorLoader або AsyncTaskLoader) або постачальником вмісту для виконання асинхронних операцій.
Я розбиваю решту публікації на:
- Порядок; і
- Весь код вищевказаної процедури.
Порядок
Почніть з базового AsyncTask як внутрішнього класу діяльності (він не повинен бути внутрішнім класом, але це, мабуть, буде зручно). На цьому етапі AsyncTask не обробляє зміни конфігурації часу виконання.
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");
}
}
Додайте вкладений клас RetainFragment, який розширює клас Fragement і не має власного інтерфейсу. Додайте setRetainInstance (true) до події onCreate цього фрагмента. Забезпечте процедури встановлення та отримання ваших даних.
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> {
....
У найвіддаленішому класі активності onCreate () обробляти RetainFragment: посилайтеся на нього, якщо він вже існує (у випадку, якщо активність перезапускається); створити та додати його, якщо його не існує; Потім, якщо він вже існував, дістаньте дані з ReservedFragment і встановіть свій інтерфейс з тими даними.
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());
}
}
Запустіть AsyncTask з інтерфейсу користувача
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
Додайте і кодуйте визначений рядок ходу:
- Додайте до макета інтерфейсу панель прогресу;
- Отримайте посилання на нього у Activity oncreate ();
- Зробити це видимим і невидимим на початку та в кінці процесу;
- Визначте хід звітування до інтерфейсу користувача у onProgressUpdate.
- Змініть параметр AsyncTask 2nd Generic з Void на тип, який може обробляти оновлення прогресу (наприклад, Integer).
- опублікувати прогрес у регулярних точках у doInBackground ().
Весь код вищевказаної процедури
Макет діяльності
<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>
Активність із: підкласовим внутрішнім класом AsyncTask; підклас внутрішнього класу RetainFragment, який обробляє зміни конфігурації виконання (наприклад, коли користувач повертає екран); і визначальне оновлення смуги прогресу, що оновлюється через рівні проміжки часу. ...
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);
}
}
У цьому прикладі функція бібліотеки (на яку посилалося вище з явним префіксом пакета com.example.standardapplibrary.android.Network), яка виконує справжню роботу ...
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;
}
Додайте до AndroidManifest.xml будь-які дозволи, необхідні для вашого фонового завдання ...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
Додайте свою активність до 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>