Android主线程消息循环
16lz
2021-01-23
Android主线程简介
当Android应用程序启动后,系统会创建一个叫做“main”的线程。它就是主线程,也叫UI线程,非常重要。 在Android系统中,主线程主要负责执行四大组件的执行。负责分发事件给构建,包括绘制事件。Android中规定访问UI只能在主线程进行,如果在子线程中访问UI,那么程序就会抛出异常。ViewRootImpl中对UI的操作进行了验证,由它的checkThread()方法来完成的。代码如下:
void checkThread() { if (mThread != Thread.currentThread()) { throw new CalledFromWrongThreadException( "Only the original thread that created a view hierarchy can touch its views."); } }
那么为什么安卓系统不允许在子线程中访问UI呢?这是因为Android的UI控件不是线程安全的,如果在多线程中并发访问可能会导致UI控件处于不可预期的状态。如果在UI控件的访问加上锁机制的话,由于锁机制会阻塞线程的执行, 会降低UI访问的效率。 主线程的主要责任:
1、快速的处理UI事件。Android希望UI线程能快速响应用户操作,如果UI线程花太多时间处理后台的工作,会让用户有非常糟糕的体验。当UI事件发生时,让用户等待时间超过5秒而未处理,Android系统就会给用户显示ANR提示信息。 2、快速的处理Broadcast消息。在BroadcastReceiver的onReceive()函数中,不宜占用太长的时间,否则会导致主线程无法处理其它的Broadcast消息或UI事件。如果占用时间超过10秒, Android系统就会给用户显示ANR提示信息。
所以当有比较耗时的任务时,我们需要在子线程执行,当有需要操作UI界面的才通过Handler,回调到主线程中修改UI界面。关于Handler的知识点,可以看一下我的另一篇博客:Handler消息机制。
Android主线程的消息机制
Android的主线程就是ActivityThread,主线程的入口方法和Java类一样,是main方法。下面我们来看一下: /frameworks/base/core/java/android/app/ActivityThread.java public static void main(String[] args) { ... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } // End of event ActivityThreadMain. Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
首先Looper.prepareMainLooper()方法创建主线程的Looper,而MessageQueue在Looper的构造函数中创建。而最后通过Looper.loop()来开启消息循环。通过Handler消息机制我们了解到,loop中是一个无限循环。而thread.getHandler()获取主线程中H类的对象mH。H继承于Handler。我们来看看H类的定义。 /frameworks/base/core/java/android/app/ActivityThread.java private class H extends Handler { public static final int LAUNCH_ACTIVITY = 100; public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; public static final int STOP_ACTIVITY_HIDE = 104; public static final int SHOW_WINDOW = 105; public static final int HIDE_WINDOW = 106; public static final int RESUME_ACTIVITY = 107; ... public void handleMessage(Message msg) { if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what)); switch (msg.what) { case LAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart"); final ActivityClientRecord r = (ActivityClientRecord) msg.obj; r.packageInfo = getPackageInfoNoCheck( r.activityInfo.applicationInfo, r.compatInfo); handleLaunchActivity(r, null); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case RELAUNCH_ACTIVITY: { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart"); ActivityClientRecord r = (ActivityClientRecord)msg.obj; handleRelaunchActivity(r); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } break; case PAUSE_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2, (msg.arg1&2) != 0); maybeSnapshot(); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case PAUSE_ACTIVITY_FINISHING: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause"); handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2, (msg.arg1&1) != 0); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case STOP_ACTIVITY_SHOW: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop"); handleStopActivity((IBinder)msg.obj, true, msg.arg2); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case STOP_ACTIVITY_HIDE: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStop"); handleStopActivity((IBinder)msg.obj, false, msg.arg2); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case SHOW_WINDOW: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityShowWindow"); handleWindowVisibility((IBinder)msg.obj, true); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case HIDE_WINDOW: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityHideWindow"); handleWindowVisibility((IBinder)msg.obj, false); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case RESUME_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityResume"); handleResumeActivity((IBinder) msg.obj, true, msg.arg1 != 0, true); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case SEND_RESULT: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDeliverResult"); handleSendResult((ResultData)msg.obj); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; case DESTROY_ACTIVITY: Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy"); handleDestroyActivity((IBinder)msg.obj, msg.arg1 != 0, msg.arg2, false); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); break; ... } if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what)); } ... }
通过H的定义,我们可以看到四大组件的启动和停止过程。 在之前思考过一个问题,UI线程中使用了Looper,那么它为什么没有阻塞。 现在我们可以知道答案了,UI主线程同样是以消息驱动执行的,所有的四大组件的生命周期都是通过消息来驱动执行的。当MessageQueue中没有消息的时候,next()就会阻塞在那里,导致主线程阻塞,只是这个时候用户没有任何操作界面静止,我们感觉不到而已。当一个新的消息到来,next立即将这个消息返回给loop。我们再通过mH回调到主线程中处理。若这个时候消息处理的时间过长,就会影响UI线程的刷新速率,造成卡顿的现象。
更多相关文章
- Android异步机制一:使用Thread+Handler实现非UI线程更新UI界面
- android 多任务多线程断点下载
- [Android]Thread线程入门3--多线程
- Android消息通知-Notification
- 【Android】Android的消息机制
- android的消息处理机制