配置项发生改变时保持程序状态
在我们进行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提供的兼容包来支持