Voici un autre exemple d'une AsyncTask qui utilise un Fragment
pour gérer la configuration de l'exécution des changements (comme lorsque l'utilisateur fait pivoter l'écran) avec setRetainInstance(true)
. Une période déterminée (régulièrement mis à jour) de la barre de progression est également démontrée.
L'exemple est basé en partie sur les docs officielles, la conservation d'un Objet Lors d'un Changement de Configuration.
Dans cet exemple, le travail exigeant un thread d'arrière-plan est le simple chargement d'une image à partir de l'internet dans l'INTERFACE utilisateur.
Alex Lockwood semble être droit que quand il s'agit de la manipulation de la configuration de l'exécution des changements avec AsyncTasks à l'aide d'un "Fragment Conservé" est la meilleure pratique. onRetainNonConfigurationInstance()
devient obsolète en Charpie, dans Android Studio. L'officiel docs de nous en avertir en utilisant de android:configChanges
, à partir de la Manipulation de la Configuration de Modifier Vous-même, ...
La manipulation de la modification de la configuration-vous pouvez le rendre beaucoup plus difficile l'utilisation de ressources alternatives, parce que le système ne s'applique pas automatiquement pour vous. Cette technique doit être considérée comme un dernier recours lorsque vous devez éviter les redémarrages en raison d'un changement de configuration n'est pas recommandée pour la plupart des applications.
Il y a ensuite la question de savoir si l'on doit utiliser un AsyncTask pour le thread d'arrière-plan.
La référence officielle pour AsyncTask avertit ...
AsyncTasks devrait idéalement être utilisé pour les opérations de courte durée (quelques secondes tout au plus.) Si vous avez besoin de garder les threads en cours d'exécution pour de longues périodes de temps, il est fortement recommandé que vous utilisez les différentes Api fournies par java.util.simultanées pacakge comme Exécuteur testamentaire, ThreadPoolExecutor et FutureTask.
Alternativement, on peut utiliser un service, chargeur (à l'aide d'un CursorLoader ou AsyncTaskLoader), ou de fournisseur de contenu pour exécuter des opérations asynchrones.
Je me casse le reste de la poste à:
- La Procédure; et
- Tout le code de la procédure ci-dessus.
La Procédure
-
Commencez avec une base AsyncTask intérieur de la classe d'une activité (il n'a pas besoin d'être un à l'intérieur de la classe, mais il sera probablement facile d'être). À ce stade, l'AsyncTask ne gère pas la configuration de l'exécution des changements.
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");
}
}
-
Ajouter une classe imbriquée RetainedFragment qui s'étend le Fragement de la classe et ne dispose pas de sa propre INTERFACE utilisateur. Ajouter setRetainInstance(true) pour l'événement onCreate de ce Fragment. Fournir des procédures pour définir et obtenir vos données.
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> {
....
-
Dans le ultrapériphériques de l'Activité de la classe onCreate() de la poignée de la RetainedFragment: Référence s'il existe déjà (dans le cas où l'Activité est de redémarrer); créer et ajouter si elle n'existe pas; Puis, si elle existait déjà, d'obtenir des données de la RetainedFragment et configurer votre INTERFACE utilisateur avec les données.
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());
}
}
-
Initier les AsyncTask à partir de l'INTERFACE utilisateur
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
-
Ajouter et code déterminée d'une barre de progression:
- Ajouter une barre de progression à la disposition de l'INTERFACE utilisateur;
- Obtenir une référence à elle dans l'Activité oncreate();
- Rendre visible et de l'invisible au début et à la fin du processus;
- Définir le progrès de l'INTERFACE utilisateur dans onProgressUpdate.
- Modifier les AsyncTask 2ème paramètre Générique de Void à un type qui peut gérer les mises à jour du progrès (p. ex. Entier).
- publishProgress à des points réguliers dans doInBackground().
Tout le code de la procédure ci-dessus
L'Activité De Mise En Page.
<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>
L'Activité: sous-classé AsyncTask intérieur de la classe; sous-classé RetainedFragment intérieur de la classe qui gère la configuration de l'exécution des modifications (par exemple, lorsque l'utilisateur fait pivoter l'écran); et déterminée d'une barre de progression de la mise à jour à intervalles réguliers. ...
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);
}
}
Dans cet exemple, la fonction de la bibliothèque (référencé ci-dessus avec l'explicite préfixe package com.exemple.standardapplibrary.android.Réseau) qui est la vrai travail ...
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;
}
Ajouter toutes les autorisations que votre arrière-plan la tâche nécessite de la AndroidManifest.xml ...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
Ajouter votre activité 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>