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

假设这样一种产品需求:
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消息机制浅析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bjxiaxueliang

您的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值