likes
comments
collection
share

Android 面试| Android的Handle消息机制

作者站长头像
站长
· 阅读数 45

Android消息机制介绍

Android消息机制中的四大概念:

  • ThreadLocal:当前线程存储的数据仅能从当前线程取出。
  • MessageQueue:具有时间优先级的消息队列。
  • Looper:轮询消息队列,看是否有新的消息到来。
  • Handler:具体处理逻辑的地方。

过程:

  1. 准备工作:创建Handler,如果是在子线程中创建,还需要调用Looper#prepare(),在Handler的构造函数中,会绑定其中的LooperMessageQueue
  2. 发送消息:创建消息,使用Handler发送。
  3. 进入MessageQueue:因为Handler中绑定着消息队列,所以Message很自然的被放进消息队列。
  4. Looper轮询消息队列:Looper是一个死循环,一直观察有没有新的消息到来,之后从Message取出绑定的Handler,最后调用Handler中的处理逻辑,这一切都发生在Looper循环的线程,这也是Handler能够在指定线程处理任务的原因。

伪代码描述的Android主线程消息处理流程,这样会看得更清晰易懂:

// 1. 创建Handler实例,关联主线程的Looper
Handler mainHandler = new Handler(Looper.getMainLooper());

// 2. 定义消息处理逻辑
void handleMessage(Message msg) {
    int what = msg.what; // 获取消息的what属性,表示消息类型
    Object obj = msg.obj; // 获取消息的obj属性,携带的额外数据
    // 根据消息类型执行相应的操作
    switch (what) {
        case MESSAGE_TYPE_ONE:
            // 处理第一种类型的消息
            break;
        case MESSAGE_TYPE_TWO:
            // 处理第二种类型的消息
            break;
        // ...其他case处理不同的消息类型
        default:
            // 默认处理逻辑
            break;
    }
}

// 3. 发送消息到消息队列
Message msg = obtainMessage(MESSAGE_TYPE_ONE); // 创建一个新消息,并设置类型
mainHandler.sendMessage(msg); // 发送消息到主线程的消息队列

// 4. Handler在主线程的Looper循环中处理消息
while (true) { // Looper的循环体
    Message msg = mainHandler.getLooper().getMessageQueue().next(); // 从消息队列中取出一个消息
    if (msg == null) {
        break; // 如果消息队列为空,退出循环,而且App进程会结束
    }
    handleMessage(msg); // 调用handleMessage方法处理消息
}

// 5. 消息处理完成
// handleMessage方法中的代码会根据消息类型执行相应的逻辑

Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

Android中为什么主线程不会因为Looper.loop()里的死循环卡死? - Gityuan的回答 - 知乎 www.zhihu.com/question/34…

简单的总结来说:主线程也不过是运行在应用进程里面的,但实际上对于CPU来说这些进程,线程,都是一片片代码块,按照指令去执行,死循环的设计为了保证主线程能够一直存活,不会在运行一段时间后自动退出,这样App就不会无故结束掉了,而真正导致主线程卡死的操作是在回调方法如onCreateonStartonResume等中执行时间过长。这些长时间的操作会导致界面掉帧,甚至触发应用程序无响应 ANR。然而,Looper.loop()本身并不会导致应用卡死。当没有消息需要处理时,主线程会休眠,等待新消息的到来,不会过多占用CPU资源。


子线程一定不能更新ui么?什么时候可以?什么时候不可以。

子线程更新UI的规则:

不可以

  • 直接从子线程调用UI组件的方法,如TextView.setText()Button.setEnabled()等,这些都是不允许的。
  • 在子线程中直接操作布局或者视图层次结构也是不允许的。

可以

  • 通过Handler将消息发送到主线程的消息队列中,然后在主线程的Handler中处理消息并更新UI。
  • 使用AsyncTask类,它允许在后台线程中执行耗时操作,然后在完成时将结果发布到主线程以更新UI。
  • 使用LiveDataViewModel,这些是Android架构组件的一部分,它们可以在后台线程中进行数据加载,然后在主线程中观察和更新UI。
  • 使用runOnUiThread()方法,这是Activity类的一个方法,它允许你将一个Runnable对象发布到主线程的消息队列中,并在主线程中执行。

Activity的生命周期是怎么实现在死循环体外能够执行起来的?

实际上Activity的生命周期还是是通过Android的消息机制和Handler来实现的,依靠主线程的Looper.loop,当收到不同Message时则采用相应措施;比如收到msg=H.LAUNCH_ACTIVITY,则调用ActivityThread.handleLaunchActivity()方法,最终会通过反射机制,创建Activity实例,然后再执行Activity.onCreate()等方法;再比如收到msg=H.PAUSE_ACTIVITY,则调用ActivityThread.handlePauseActivity()方法,最终会执行Activity.onPause()等方法。

结合简单流程图和看以下的伪代码,你就更清晰的知道,Activity生命周期和Handle之间的一些细微关系,还有AMS在其中所做的一些处理:

Android 面试| Android的Handle消息机制

// 伪代码表示的Android系统类和方法
class ActivityThread {
    private Looper mainLooper;
    private Handler mainHandler;

    // 初始化主线程的Looper和Handler
    function initLooperAndHandler() {
        mainLooper = Looper.prepareMainLooper();
        mainHandler = new Handler(mainLooper) {
            override function handleMessage(Message msg) {
                // 根据消息的内容执行不同的生命周期操作
                switch (msg.what) {
                    case MSG_START_ACTIVITY:
                        startActivity(msg.obj); // msg.obj包含Activity的相关信息
                        break;
                    case MSG_RESUME_ACTIVITY:
                        resumeActivity(id); // id是Activity的标识
                        break;
                    case MSG_PAUSE_ACTIVITY:
                        pauseActivity(id);
                        break;
                    case MSG_STOP_ACTIVITY:
                        stopActivity(id);
                        break;
                    case MSG_DESTROY_ACTIVITY:
                        destroyActivity(id);
                        break;
                    // 其他消息处理...
                }
            }
        }
    }

    // 启动Activity
    function startActivity(activityInfo) {
        // 创建Activity实例并调用onCreate等生命周期方法
        activity = createActivityInstance(activityInfo)
        activity.onCreate()
        // ...
    }

    // 恢复Activity
    function resumeActivity(id) {
        activity = findActivityById(id)
        activity.onResume()
        // ...
    }

    // 暂停Activity
    function pauseActivity(id) {
        activity = findActivityById(id)
        activity.onPause()
        // ...
    }

    // 停止Activity
    function stopActivity(id) {
        activity = findActivityById(id)
        activity.onStop()
        // ...
    }

    // 销毁Activity
    function destroyActivity(id) {
        activity = findActivityById(id)
        activity.onDestroy()
        // ...
    }
}

// 在Application的onCreate方法中初始化ActivityThread
ActivityThread.mainThread.initLooperAndHandler();

// 伪代码表示的Activity类
class Activity {
    // 重写生命周期回调方法
    function onCreate() {
        // 初始化UI和数据
    }

    function onStart() {
        // Activity开始变得可见
    }

    function onResume() {
        // Activity准备交互
    }

    function onPause() {
        // Activity暂停,不再交互
    }

    function onStop() {
        // Activity不再可见
    }

    function onDestroy() {
        // 清理资源,Activity即将被销毁
    }
}

// 伪代码表示的AMS( Activity Manager Service )发送消息给ActivityThread
class AMS {
    function sendMessageToActivityThread(msg) {
        // 通过Binder IPC机制发送消息到ActivityThread的主线程Handler
        ActivityThread.mainHandler.sendMessage(msg);
    }
}

// AMS根据不同的事件向ActivityThread发送消息
AMS.sendMessageToActivityThread(MSG_START_ACTIVITY);
AMS.sendMessageToActivityThread(MSG_RESUME_ACTIVITY);
AMS.sendMessageToActivityThread(MSG_PAUSE_ACTIVITY);
AMS.sendMessageToActivityThread(MSG_STOP_ACTIVITY);
AMS.sendMessageToActivityThread(MSG_DESTROY_ACTIVITY);

以后每两天之内更新一道Android常见的面试题,以自己的角度去通俗简单易懂形式去说,不深入源码但讲基本原理,以伪代码为核心;如果你比较想问什么问题,可以评论。

转载自:https://juejin.cn/post/7352891079296073763
评论
请登录