Android 子线程创建消息队列更新UI

来源:互联网 发布:win10笔记本修改Mac 编辑:程序博客网 时间:2024/06/05 15:23

假设这样一种产品需求:
Android主线程崩溃后,向用户弹出一个UI提醒(一个dialog或者一个toast),告知用户APP异常崩溃。

主线程崩溃后,给用户弹出一个UI提醒

一般我们的做法是这样:

CrashHandler.java

import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.os.Looper;public class CrashHandler implements Thread.UncaughtExceptionHandler {    public static final String TAG = "CrashHandler";    //--------------单例begin-------------    private static CrashHandler INSTANCE = new CrashHandler();    private CrashHandler() {    }    public static CrashHandler getInstance() {        return INSTANCE;    }    //-------------单例end-------------    private Context mContext;    public void init(Context ctx) {        this.mContext = ctx;        Thread.setDefaultUncaughtExceptionHandler(this);    }    @Override    public void uncaughtException(Thread thread, Throwable ex) {        System.out.println("uncaughtException");        new Thread() {            @Override            public void run() {                Looper.prepare();                //                new AlertDialog.Builder(mContext).setTitle("提示").setCancelable(false)                        .setMessage("居然崩溃了,呜呜呜...").setNeutralButton("哈哈终于崩溃了...", new DialogInterface.OnClickListener() {                    @Override                    public void onClick(DialogInterface dialog, int which) {                        System.exit(0);                    }                }).create().show();                //                Looper.loop();            }        }.start();    }}

MainActivity.java

import android.app.Activity;import android.os.Bundle;public class MainActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //传入参数必须为Activity,否则AlertDialog将不显示。        CrashHandler crashHandler = CrashHandler.getInstance();        crashHandler.init(this);    }}

这里,我们注意到,异步线程居然在操作UI,为什么异步线程可以操作UI呢?

new Thread() {    @Override    public void run() {        Looper.prepare();        //        new AlertDialog.Builder(mContext).setTitle("提示").setCancelable(false)                .setMessage("居然崩溃了,呜呜呜...").setNeutralButton("哈哈终于崩溃了...", new DialogInterface.OnClickListener() {            @Override            public void onClick(DialogInterface dialog, int which) {                System.exit(0);            }        }).create().show();        //        Looper.loop();    }}.start();

为什么异步线程可以操作UI呢?

在android.view.ViewRootImpl中有一个checkThread方法:
了解ViewRootImpl,推荐 从ViewRootImpl类分析View绘制的流程
android.view.ViewRootImpl

void checkThread() {    if (mThread != Thread.currentThread()) {        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");    }}

checkThread方法在requestLayout、焦点变化时,被调用;
用来检查,当前线程与View的创建线程是否在同一线程;
并未进行是否为主线程的判断。

所以其实我们在子线程中也是可以更新UI的,只要将操作UI对象的代码写在Looper.prepare()和Looper.loop()之间
当然前提还是操作的UI对象必须得是子线程自己创建的。因此即使子线程可以操作自己的UI对象,比如弹出一个Toast(使用getApplicationContext),但是子线程仍然不能直接操作主线程的UI。同理主线程也不能操作子线程创建的UI对象,想要操作也必须引用子线程中的Handler发送消息来更新。

参考:
Android使用UncaughtExceptionHandler捕获全局异常
Android消息机制浅析

0 0
原创粉丝点击