在我们进行Android项目开发时,经常会遇到一个问题,如何保持程序的一些运行状态。比如当前的Activity正在下载一张图片,突然设备横竖屏切换(Activity会重新create),之前的下载线程可能并没有完成,这时如何来复用之前的下载线程呢,这是就需要把之前的下载线程保存起来
Android SDK对这种功能提供了支持,API 13之前可以通过
getLastNonConfigurationInstance()
获得保存的对象实例通
过
onRetainNonConfigurationInstance
()
回调方法来保存一个对象实例,但这种机制在API 13被废弃了,下面是一个demo
package de.vogella.android.threadslifecycle;
import java.io.IOException;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;
public class ThreadsLifecycleActivity extends Activity {
// Static so that the thread access the latest attribute
private static ProgressDialog dialog;
private static Bitmap downloadBitmap;
private static Handler handler;
private ImageView imageView;
private Thread downloadThread;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Create a handler to update the UI
handler = new Handler() {
@Override
public void handleMessage(Message msg) {
imageView.setImageBitmap(downloadBitmap);
dialog.dismiss();
}
};
// get the latest imageView after restart of the application
imageView = (ImageView) findViewById(R.id.imageView1);
Context context = imageView.getContext();
System.out.println(context);
// Did we already download the image?
if (downloadBitmap != null) {
imageView.setImageBitmap(downloadBitmap);
}
// Check if the thread is already running
downloadThread = (Thread) getLastNonConfigurationInstance();
if (downloadThread != null && downloadThread.isAlive()) {
dialog = ProgressDialog.show(this, "Download", "downloading");
}
}
public void resetPicture(View view) {
if (downloadBitmap != null) {
downloadBitmap = null;
}
imageView.setImageResource(R.drawable.icon);
}
public void downloadPicture(View view) {
dialog = ProgressDialog.show(this, "Download", "downloading");
downloadThread = new MyThread();
downloadThread.start();
}
// Save the thread
@Override
public Object onRetainNonConfigurationInstance() {
return downloadThread;
}
// dismiss dialog if activity is destroyed
@Override
protected void onDestroy() {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
dialog = null;
}
super.onDestroy();
}
// Utiliy method to download image from the internet
static private Bitmap downloadBitmap(String url) throws IOException {
HttpUriRequest request = new HttpGet(url);
HttpClient httpClient = new DefaultHttpClient();
HttpResponse response = httpClient.execute(request);
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) {
HttpEntity entity = response.getEntity();
byte[] bytes = EntityUtils.toByteArray(entity);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0,
bytes.length);
return bitmap;
} else {
throw new IOException("Download failed, HTTP response code "
+ statusCode + " - " + statusLine.getReasonPhrase());
}
}
static public class MyThread extends Thread {
@Override
public void run() {
try {
// Simulate a slow network
try {
new Thread().sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
downloadBitmap = downloadBitmap("http://www.devoxx.com/download/attachments/4751369/DV11");
// Updates the user interface
handler.sendEmptyMessage(0);
} catch (IOException e) {
e.printStackTrace();
} finally {
}
}
}
}
在模拟器中可以通过ctrl+11来看效果,以上代码可以在
http://www.vogella.com/articles/AndroidBackgroundProcessing/article.html#fragmentsandbackgroundprocessing
上找到
API 13 推荐使用Fragment来替代,通过设置
setRetainInstance(
true
);
使Fragment在设备配置改变后(比如横竖屏切换)Activity重新创建时还能够获取到,具体介绍可以去看下Fragment的说明 ,下面是一个简单的实现(代码提取自BitmapFun项目)
package com.activity.lifecycle.threads;
import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.TextView;
public class TestActivity extends Activity {
private static final String TAG = "TestActivity" ;
@TargetApi (Build.VERSION_CODES. HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
TextView tv = new TextView( this);
tv.setText( "this is the second activity" );
setContentView(tv);
// Search for, or create an instance of the non-UI RetainFragment
final RetainFragment mRetainFragment = findOrCreateRetainFragment(getFragmentManager());
// See if we already have an ImageCache stored in RetainFragment
Thread thread = (Thread) mRetainFragment.getObject();
// No existing ImageCache, create one and store it in RetainFragment
if (thread == null) {
thread = new Thread() ;
mRetainFragment.setObject(thread);
}
// TODO
}
/**
* Locate an existing instance of this Fragment or if not found, create and
* add it using FragmentManager.
*
* @param fm The FragmentManager manager to use.
* @return The existing instance of the Fragment or the new instance if just
* created.
*/
@TargetApi (Build.VERSION_CODES. HONEYCOMB)
private static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
// Check to see if we have retained the worker fragment.
RetainFragment mRetainFragment = (RetainFragment)fm.findFragmentByTag(TAG );
// If not retained (or first time running), we need to create and add it.
if (mRetainFragment == null) {
mRetainFragment = new RetainFragment();
fm.beginTransaction().add(mRetainFragment, TAG ).commitAllowingStateLoss();
}
return mRetainFragment;
}
/**
* A simple non - UI Fragment that stores a single Object and is retained over configuration
* changes. It will be used to retain the ImageCache object.
*/
public static class RetainFragment extends Fragment {
private Object mObject ;
/**
* Empty constructor as per the Fragment documentation
*/
public RetainFragment() {}
@TargetApi (Build.VERSION_CODES. HONEYCOMB)
@Override
public void onCreate(Bundle savedInstanceState) {
super .onCreate(savedInstanceState);
// Make sure this Fragment is retained over a configuration change
setRetainInstance( true );
}
/**
* Store a single object in this Fragment.
*
* @param object The object to store
*/
public void setObject(Object object) {
mObject = object;
}
/**
* Get the stored object.
*
* @return The stored object
*/
public Object getObject() {
return mObject ;
}
}
}
因为API 13才开始提供,所以之前的版本可以使用Android提供的兼容包来支持