这是另一个使用
Fragment
处理运行时配置更改(例如用户旋转屏幕)的AsyncTask示例,使用
setRetainInstance(true)
。还演示了确定(定期更新)进度条。
该示例部分基于官方文档
“在配置更改期间保留对象”。
在此示例中,需要在后台线程中加载互联网图像到UI。
Alex Lockwood认为,在使用AsyncTask处理运行时配置更改时,“保留片段”是最佳实践。在Android Studio的Lint中,
onRetainNonConfigurationInstance()
已被弃用。官方文档警告我们不要使用
android:configChanges
,来自
“自己处理配置更改”...
自己处理配置更改可能会使使用替代资源变得更加困难,因为系统不会自动应用它们。当您必须避免由于配置更改而重新启动时,应将此技术视为最后一种选择,并且不建议大多数应用程序使用。
然后就有了是否应该在后台线程中使用AsyncTask的问题。
AsyncTask
的
官方参考文档警告...
异步任务应该理想地用于短时间操作(最多几秒钟)。如果您需要长时间运行线程,强烈建议使用java.util.concurrent包提供的各种API,例如Executor、ThreadPoolExecutor和FutureTask。
或者,可以使用服务、加载器(使用CursorLoader或AsyncTaskLoader)或内容提供程序执行异步操作。
我将本文其余部分分为:
过程
Start with a basic AsyncTask as an inner class of an activity (it doesn't need to be an inner class but it will probably be convenient to be). At this stage the AsyncTask does not handle runtime configuration changes.
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);
}
}
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");
}
}
Add a nested class RetainedFragment that extends the Fragement class and doesn't have it's own UI. Add setRetainInstance(true) to the onCreate event of this Fragment. Provide procedures to set and get your data.
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);
setRetainInstance(true);
}
public Bitmap getData() {
return this.mBitmap;
}
public void setData(Bitmap bitmapToRetain) {
this.mBitmap = bitmapToRetain;
}
}
private class LoadImageFromNetworkAsyncTask
extends AsyncTask<String, Integer,Bitmap> {
....
In the outermost Activity class's onCreate() handle the RetainedFragment: Reference it if it already exists (in case the Activity is restarting); create and add it if it doesn't exist; Then, if it already existed, get data from the RetainedFragment and set your UI with that data.
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);
FragmentManager fm = getFragmentManager();
mRetainedFragment =
(RetainedFragment) fm.findFragmentByTag(retainedFragmentTag);
if (mRetainedFragment == null) {
mRetainedFragment = new RetainedFragment();
fm.beginTransaction()
.add(mRetainedFragment, retainedFragmentTag).commit();
} else {
mPictureImageView
.setImageBitmap(mRetainedFragment.getData());
}
}
Initiate the AsyncTask from the UI
public void getPicture(View view) {
new LoadImageFromNetworkAsyncTask().execute(
"http://i.imgur.com/SikTbWe.jpg");
}
Add and code a determinate progress bar:
- Add a progress bar to the UI layout;
- Get a reference to it in the Activity oncreate();
- Make it visible and invisble at the start and end of the process;
- Define the progress to report to UI in onProgressUpdate.
- Change the AsyncTask 2nd Generic parameter from Void to a type that can handle progress updates (e.g. Integer).
- publishProgress at regular points in 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内部类;子类化的RetainedFragment内部类,用于处理运行时配置更改(例如,当用户旋转屏幕时);还有一个定期更新确定进度条。
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);
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) {
int sleepSeconds = 4;
for (int i = 1; i <= sleepSeconds; i++) {
SystemClock.sleep(1000);
publishProgress(i * 20);
}
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);
FragmentManager fm = getFragmentManager();
mRetainedFragment = (RetainedFragment) fm.findFragmentByTag(
retainedFragmentTag);
if (mRetainedFragment == null) {
mRetainedFragment = new RetainedFragment();
fm.beginTransaction().add(mRetainedFragment,
retainedFragmentTag).commit();
} 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>