Android 代码优化
2015-06-01 21:34
369 查看
Android 代码优化
有这样一个话题在 StackOverflow: 上提起过:What is the best way to retain active objects—such as running Threads, Sockets, and AsyncTasks—across device configuration changes?
简单的说: AnsyncTask & Socket & Thread 当Configuration改变的时候该如何处理?
回答这个问题前,先来讨论一下Android开发如何在Activity生命周期中处理长时间后台任务,然后再提出两种处理方法的缺陷,最后给出简单的推荐处理方式。
假定,Activity启动一个后台AnsyncTask,当用户旋转屏幕以后,Activity会销毁和重建,当 AsyncTask 最终完成其工作时,它将错误地将结果返回到旧的Activity实例,完全没有意识到已创建一个新的Activity。好像这并不是神马问题,因为新的Activity可以从新发起AsyncTask,即使这被认为是浪费资源,因为新的Activity完全没有意识到已经有AsyncTask实例在运行。
基于以上原因,当Configuration改变的时候,正确有效的保存Activity实例可以节约必要的资源。
保存Activity实例-不推荐
通过修改Android manifest的configChanges 属性可以禁止销毁和重建Activity当Configuration改变的时候,这个是解决这类问题最普遍的做法。这个方法看似相当简单,所以大部分Android开发人员特别热衷这么做。然而Google的工程师们,并不推荐这么做。主要有以下原因:
Configuration改变以后,UI需要和Device的Configuration同步,如果你一个不小心,那么UI就会出现问题。因为此时Device的Configuration改变了,而你的UI却尚未同步改变。
一些Android开发人员错误认为设置android:configChanges=”orientation” 会确保禁用销毁和重建Activity,而实际上并非如此,因为到时Configuration改变可能有许多原因,不仅仅局限在旋转屏幕,还有可能是当你改变程序的默认语言,或者将设备和其他显示设备相连的时候,这些都可能引起Configuration的改变。
基于以上原因,在Android manifest设置configChanges 属性并非明智之举。
重写 onRetainNonConfigurationInstance()-不推荐
在Activity实例之间传递对象比较推荐的方法是通过重写onRetainNonConfigurationInstance() 和 getLastNonConfigurationInstance() 方法,然而这在API13以后废弃了,因为有更好的方法可以替代上面的重写方法。那么该如何处理上面的问题呢?保持Activity内部的Fragment实例-推荐
自从Android 3.0引入Fragment以后,在Activity实例之间传递对象的推荐方法变成了保持Activity内部的Fragment实例,因为默认情况下,Fragment的生命周期是和它依附的Activity生命周期同步的。通过调用* Fragment#setRetainInstance(true)* 可以帮助我们绕过销毁-重建实例的周期,直接得到例如AnsyncTask & Socket & Thread等的引用。下面是个简单的例子:
MainActivity.java
[code]/** * This Activity displays the screen's UI, creates a TaskFragment * to manage the task, and receives progress updates and results * from the TaskFragment when they occur. */ public class MainActivity extends Activity implements TaskFragment.TaskCallbacks { private static final String TAG_TASK_FRAGMENT = "task_fragment"; private TaskFragment mTaskFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); FragmentManager fm = getFragmentManager(); mTaskFragment = (TaskFragment) fm.findFragmentByTag(TAG_TASK_FRAGMENT); // If the Fragment is non-null, then it is currently being // retained across a configuration change. if (mTaskFragment == null) { mTaskFragment = new TaskFragment(); fm.beginTransaction().add(mTaskFragment, TAG_TASK_FRAGMENT).commit(); } // TODO: initialize views, restore saved state, etc. } // The four methods below are called by the TaskFragment when new // progress updates or results are available. The MainActivity // should respond by updating its UI to indicate the change. @Override public void onPreExecute() { ... } @Override public void onProgressUpdate(int percent) { ... } @Override public void onCancelled() { ... } @Override public void onPostExecute() { ... } }
TaskFragment.java
[code]/** * This Fragment manages a single background task and retains * itself across configuration changes. */ public class TaskFragment extends Fragment { /** * Callback interface through which the fragment will report the * task's progress and results back to the Activity. */ interface TaskCallbacks { void onPreExecute(); void onProgressUpdate(int percent); void onCancelled(); void onPostExecute(); } private TaskCallbacks mCallbacks; private DummyTask mTask; /** * Hold a reference to the parent Activity so we can report the * task's current progress and results. The Android framework * will pass us a reference to the newly created Activity after * each configuration change. */ @Override public void onAttach(Activity activity) { super.onAttach(activity); mCallbacks = (TaskCallbacks) activity; } /** * This method will only be called once when the retained * Fragment is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Retain this fragment across configuration changes. setRetainInstance(true); // Create and execute the background task. mTask = new DummyTask(); mTask.execute(); } /** * Set the callback to null so we don't accidentally leak the * Activity instance. */ @Override public void onDetach() { super.onDetach(); mCallbacks = null; } /** * A dummy task that performs some (dumb) background work and * proxies progress updates and results back to the Activity. * * Note that we need to check if the callbacks are null in each * method in case they are invoked after the Activity's and * Fragment's onDestroy() method have been called. */ private class DummyTask extends AsyncTask<Void, Integer, Void> { @Override protected void onPreExecute() { if (mCallbacks != null) { mCallbacks.onPreExecute(); } } /** * Note that we do NOT call the callback object's methods * directly from the background thread, as this could result * in a race condition. */ @Override protected Void doInBackground(Void... ignore) { for (int i = 0; !isCancelled() && i < 100; i++) { SystemClock.sleep(100); publishProgress(i); } return null; } @Override protected void onProgressUpdate(Integer... percent) { if (mCallbacks != null) { mCallbacks.onProgressUpdate(percent[0]); } } @Override protected void onCancelled() { if (mCallbacks != null) { mCallbacks.onCancelled(); } } @Override protected void onPostExecute(Void ignore) { if (mCallbacks != null) { mCallbacks.onPostExecute(); } } } }
对上面的简单Demo说明一下:
当MainActivity第一次创建时,将一并创建Fragment实例,并加入到MainActivity状态中去,Fragment创建AsyncTask,并通过CallBack回调将结果通知到MainActivity中去,而当COnfiguration改变以后,MainActivity经过正常的生命周期,而新的MainActivity实例会在OnCreate()方法中得到Fragment之前的引用,而在Fragment中,onAttach(Activity activity)方法会得到最新的Activity引用,二者之间都同步更新,有效避免了资源浪费和解决了因Configuration改变带来的问题。
这个简单的Demo是可靠的,从此不用再担心无法预料的Configuration改变带来的问题。
相关文章推荐
- Java、Android中的多线程异步下载
- Android BroadCast (一)
- Android之DDMS无法查看/data/data目录
- Android BroadCast (二)
- Android 序列化比对
- Android 进程间通信IPC_AIDL
- android开发(42) 使用andorid操作蓝牙打印机
- EventBus for Android 源码分析
- Android 启动模式
- Android手机屏幕之asm.jar
- Android View 分析(上)
- Android View 分析(中)
- Android View 分析(下)
- Android AsyncTask详解
- Android Service
- Android Socket
- 如何指定让Android的NDK工具编译出Release版本或Debug版本的Native程序
- Android中的设计模式
- Android通过webservice操作数据库1(查询数据库)
- 关于android移动动画和缩放动画的构造函数简介