Aqui está outro exemplo de AsyncTask que usa a Fragment
para manipular alterações na configuração de tempo de execução (como quando o usuário gira a tela) com setRetainInstance(true)
. Uma barra de progresso determinada (atualizada regularmente) também é demonstrada.
O exemplo é parcialmente baseado nos documentos oficiais, Reter um objeto durante uma alteração na configuração .
Neste exemplo, o trabalho que requer um encadeamento em segundo plano é o mero carregamento de uma imagem da Internet na interface do usuário.
Alex Lockwood parece estar certo de que, quando se trata de lidar com alterações na configuração de tempo de execução com AsyncTasks, usar um "Fragmento Retido" é uma prática recomendada. onRetainNonConfigurationInstance()
é preterido no Lint, no Android Studio. Os documentos oficiais nos alertam sobre o uso android:configChanges
, de Manipulação da mudança de configuração , ...
O controle da alteração da configuração pode tornar muito mais difícil o uso de recursos alternativos, porque o sistema não os aplica automaticamente a você. Essa técnica deve ser considerada um último recurso quando você deve evitar reinicializações devido a uma alteração na configuração e não é recomendado para a maioria dos aplicativos.
Há a questão de saber se alguém deve usar um AsyncTask para o encadeamento em segundo plano.
A referência oficial do AsyncTask adverte ...
Idealmente, as AsyncTasks devem ser usadas para operações curtas (no máximo, alguns segundos). Se você precisar manter os threads em execução por longos períodos, é altamente recomendável usar as várias APIs fornecidas pelo pacakge java.util.concurrent, como Executor, ThreadPoolExecutor e FutureTask.
Como alternativa, pode-se usar um serviço, carregador (usando um CursorLoader ou AsyncTaskLoader) ou provedor de conteúdo para executar operações assíncronas.
Eu divido o resto do post em:
- O procedimento; e
- Todo o código para o procedimento acima.
O procedimento
Comece com um AsyncTask básico como uma classe interna de uma atividade (ela não precisa ser uma classe interna, mas provavelmente será conveniente). Nesta fase, o AsyncTask não lida com alterações na configuração de tempo de execução.
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");
}
}
Adicione uma classe aninhada RetainedFragment que estenda a classe Fragement e não tenha sua própria interface do usuário. Adicione setRetainInstance (true) ao evento onCreate deste fragmento. Forneça procedimentos para definir e obter seus dados.
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> {
....
No onCreate () da classe Activity mais externa, manipule RetainedFragment: referencie-o se ele já existir (no caso de a Atividade estar sendo reiniciada); crie e adicione-o se não existir; Em seguida, se ele já existir, obtenha dados do RetainedFragment e defina sua interface do usuário com esses dados.
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());
}
}
Iniciar o AsyncTask a partir da interface do usuário
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
Adicione e codifique uma barra de progresso determinada:
- Adicione uma barra de progresso ao layout da interface do usuário;
- Obtenha uma referência a ele na Activity oncreate ();
- Torne-o visível e invisível no início e no final do processo;
- Defina o progresso para relatar à interface do usuário no onProgressUpdate.
- Altere o parâmetro AsyncTask 2nd Generic de Void para um tipo que possa lidar com atualizações de progresso (por exemplo, Inteiro).
- publishProgress em pontos regulares em doInBackground ().
Todo o código para o procedimento acima
Layout da atividade.
<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>
A atividade com: classe interna AsyncTask subclasse; classe interna RetainedFragment subclassed que lida com alterações na configuração de tempo de execução (por exemplo, quando o usuário gira a tela); e uma barra de progresso determinada que atualiza em intervalos regulares. ...
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);
}
}
Neste exemplo, a função de biblioteca (mencionada acima com o prefixo explícito do pacote com.example.standardapplibrary.android.Network) que funciona de verdade ...
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;
}
Adicione as permissões necessárias à sua tarefa em segundo plano ao AndroidManifest.xml ...
<manifest>
...
<uses-permission android:name="android.permission.INTERNET" />
Adicione sua atividade ao 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>