在任何程序开发中,异步操作的处理都是一个麻烦事,而在 Android 中更繁杂一些,这是由于 Android 基于组件的设计对异步操作不够友好。所以,如果你在 Android 中开发界面,不妥善处理全部的异步回调,崩溃、内存泄露、状态错乱,就都接踵而至了。

而在 Android 中如何处理好异步请求,则是一个非常宽泛的话题,从这篇开始的若干篇,都会围绕这个来聊一聊。而这篇要讲的,就是看看界面中的异步回调,经常会引发什么问题,要注意些什么。

要在异步回调中判断界面状态

最典型的问题,就是回调时触碰界面而引发崩溃,一个简单示例如下:

// 发起一个异步请求,执行完成后更新界面状态requestManager.execute(aRequest, new Callback() {  protected void completed() {    textView.setText("Completed!");  }});
如果回调时,用户已经离开了界面,textView 可能会由于状态不对抛出异常而引发程序崩溃。因此,只要在页面上做了异步回调,需仔细考虑回调时的页面状态,可能需要调用  Activity.isFinishing  来检查界面是否销毁避免触发状态异常的崩溃;如果在 Fragment 中,就要调用  Fragment.isAdded  进行判断,避免 Activity 解绑后触发空指针;以及,在调用各种 Fragment 的 APIs 时,都需要确定页面 是否 Save State  了,否则很多 Fragment 的调用都会出错。当然,简单粗暴的,还能赤果果把回调操作中全部  Runtime 级别开始的全部异常都吃掉 ,不优雅,但至少比崩溃好...

但其实,解决回调崩溃的问题是知易行难,真实的项目中,情况会非常隐匿,线上崩溃总是由于 "不小心"、"没想到" 而引发的。所以,很多时候把保护放在服务内而不是调用者那里,是更安全可靠的方案。

要尽可能的取消异步操作

除了崩溃,和异步形影不离的还有内存泄露,它的隐蔽性很强,总是藏匿在 Activity 等具有 Context 的对象中。比如:

// 执行一个时间很长的操作...requestManager.execute(aRequest(activity) {  protected void run() {    // wait long long time...  }});

这个异步操作拿了 Activity 对象,如果它需要执行很长时间,那在这个阶段 Activity 和它上面的各种元素都是无法释放的,在用户离开页面很长时间后,内存依旧在占用,并且反复进出会持续累加直到 Out Of Memory(OOM),这其实也就是引发了内存泄露。而这样长时间的等待,有可能是定时引发、有可能是队列排队引发、有可能卡在了某个操作上(比如网络、IO ...),很多时候,甚至都难于提前判断。

因此,如果我们假设,大部分的异步操作都可能是慢的,需要执行很长时间,那如何来处理呢?使用弱引用是一个办法,把等待回调的界面对象(一般都持有 Context)用 WeakReference 包裹起来,如果用户离开页面,很快也会把它给收了,以此来避免内存泄露。但弱引用本身使用上有很多禁忌,稍有不慎就从一个坑到了另一个坑。比如,弱引用最好不要放到服务内弄成黑盒,而是要调用者自行使用,如果放在服务内调用者很可能会出现傻等回调等不来的状况;而一旦完全交由调用者,又难以保证实现是统一可靠的,依旧会由于 "不小心" 引发问题。

除此之外呢?支持干净的取消可以算是最漂亮的方案之一了,在异步回调未返回但界面要退出之前,把操作给回收了,而这个回收不只是设定一个标志位,而是要将回调中潜在泄露的对象彻底移除,这样才能是干净的、可以避免内存泄露的取消方案。


使用LeakCanary检测内存泄漏

        内存泄漏可能是一个难以检测和解决的难题,小的内存泄漏隐藏的比较深,并且在应用程序长时间使用后可能会出现,所以寻找内存泄漏并不是一项简单的任务。在本教程中,我们将创建一个泄漏的应用程序,我们将使用LeakCanary库来检测内存泄漏。
第1步:将LeakCanary依赖项添加到应用程序    
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1' }
第2步:扩展和配置应用程序类         我们需要在onCreate方法中调用LeakCanary.install:
import android.content.Context;import com.squareup.leakcanary.LeakCanary;import com.squareup.leakcanary.RefWatcher;public class MyApplicationextends Application {    private RefWatcher refWatcher;    public static RefWatcher getRefWatcher(Context context) {        MyApplication application = (MyApplication) context.getApplicationContext();        return application.refWatcher;    }    @Override    public void onCreate() {        super.onCreate();        refWatcher = LeakCanary.install(this);    }}
第3步:创建一个泄漏的活动         为此,我们将创建一个保存上下文的单例类:
import android.content.Context;public class SingletonSavesContext {    private Context context;    private static SingletonSavesContext instance;    public Context getContext() {        return context;    }    public void setContext(Contextcontext) {        this.context = context;    }    public    static SingletonSavesContext    getInstance() {        if (instance == null) {            instance = new SingletonSavesContext();        }        return instance;    }}
        然后,在主Acitivty中,添加如下代码测试:
import android.content.Context;import android.content.Intent;import android.os.AsyncTask;import com.squareup.leakcanary.RefWatcher;import com.youshixiu.kuplaywawa.R;public class LeakedActivity extends AppCompatActivity {    @Override    protected void onCreate(BundlesavedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        new MyAsyncTask().execute(this);    }    @Override    public void onDestroy() {        super.onDestroy();        RefWatcher refWatcher = MyApplication.getRefWatcher(this);        refWatcher.watch(this);    }    public class MyAsyncTask extends AsyncTask {        private Context context;        @Override        protected String doInBackground(Object... params) {            context = (Context) params[0];// Invoke the leak!            SingletonSavesContext.getInstance().setContext(context);// Simulate long running task            try {                Thread.sleep(3000);            } catch (InterruptedException e) {            }            return "result";        }        @Override        protected void onPostExecute(Strings) {            super.onPostExecute(s);            Intent newActivity = new Intent(context, AnotherActivity.class);            startActivity(newActivity);        }    }}
 然后,在新的活动中,我们将调用System.gc强制垃圾收集器来加速分析。
第4步:检索分析结果         通知栏会显示泄漏信息:
        结果也可以从logcat中获取:
 14211-16753/anaware.leakcanarysample D/LeakCanary: In anaware.leakcanarysample:1.0:1. 14211-16753/anaware.leakcanarysample D/LeakCanary: * anaware.leakcanarysample.LeakedActivity has leaked: 14211-16753/anaware.leakcanarysample D/LeakCanary: * GC ROOT static anaware.leakcanarysample.SingletonSavesContext.instance 14211-16753/anaware.leakcanarysample D/LeakCanary: * references anaware.leakcanarysample.SingletonSavesContext.context 14211-16753/anaware.leakcanarysample D/LeakCanary: * leaks anaware.leakcanarysample.LeakedActivity instance 14211-16753/anaware.leakcanarysample D/LeakCanary: * Reference Key: 226ffd5e-902c-4202-8508-2e268616e2ae 14211-16753/anaware.leakcanarysample D/LeakCanary: * Device: samsung samsung GT-I9305 m3xx 14211-16753/anaware.leakcanarysample D/LeakCanary: * Android Version: 4.4.4 API: 19 LeakCanary: 1.3.1 14211-16753/anaware.leakcanarysample D/LeakCanary: * Durations: watch=5007ms, gc=120ms, heap dump=617ms, analysis=13070ms 14211-16753/anaware.leakcanarysample D/LeakCanary: * Details: 14211-16753/anaware.leakcanarysample D/LeakCanary: * Class anaware.leakcanarysample.SingletonSavesContext 14211-16753/anaware.leakcanarysample D/LeakCanary: | static $staticOverhead = byte[] [id=0x42548e79;length=24;size=40] 14211-16753/anaware.leakcanarysample D/LeakCanary: | static instance = anaware.leakcanarysample.SingletonSavesContext [id=0x42548fb8] 14211-16753/anaware.leakcanarysample D/LeakCanary: * Instance of anaware.leakcanarysample.SingletonSavesContext 14211-16753/anaware.leakcanarysample D/LeakCanary: | static $staticOverhead = byte[] [id=0x42548e79;length=24;size=40] 14211-16753/anaware.leakcanarysample D/LeakCanary: | static instance = anaware.leakcanarysample.SingletonSavesContext [id=0x42548fb8] 14211-16753/anaware.leakcanarysample D/LeakCanary: | context = anaware.leakcanarysample.Leake

声明

欢迎转载,但请保留文章原始出处
作者:Jaiky_杰哥 
出处:http://blog.csdn.net/jaikydota163/article/details/78915024

更多相关文章

  1. Android小项目之十二 设置中心的界面
  2. Android内存管理机制和内存泄漏分析及优化
  3. 从零开始--系统深入学习android(实践-让我们开始写代码-Android框
  4. android could not find adb.exe 如何解决
  5. Android的界面设计工具——DroidDraw
  6. 查看Android内存的8中方法
  7. Android界面开发问题总结
  8. Android(安卓)实现欢迎界面
  9. Android单个进程内存分配策略

随机推荐

  1. android中tabhost的使用
  2. android点击系统通知Notification,进入指
  3. 简单android Service 创建与启动示例
  4. Android(安卓)ApiDemos示例解析(108):Vie
  5. Android设置角标提示
  6. 学习android网址
  7. Android(安卓)Bitmap 圆角
  8. Android(安卓)sdk 接入时遇到的错误解决
  9. 转:Android(安卓)listview 实现分页
  10. android slidingview