FrameWork中哪些模块用到了Handler机制?
在FrameWork中很多重要的模块都可以看到Handler机制的身影,通过理解这些使用场景中Handler的作用,可以让我们更加深刻的理解Handler机制。
Activity.runOnUiThread()
在Activity中,使用runOnUiThread()
可以很轻松的实现到UI线程的切换,其内部实现也是Handler机制。
framework/base/core/java/android/app/Activity.java
final Handler mHandler = new Handler();
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
从代码中我们可以看到,Activity中其实是存在一个Handler成员变量的,但是由于访问权限是default
,所以我们无法直接使用。而且runOnUiThread()
不支持延时Runnable,所以如果想使用Handler的其他功能的话,我们还是需要自己单独定义Handler。
由于Handler的初始化发生在Activity被实例化的过程中,所以Handler绑定的线程与Activity被实例化的线程是相同的,而Activity被实例化发生在UI线程,所以我们的Runnable总是可以在UI线程执行。关于Activity被实例化的详细过程,将在下面的文章里进行介绍。
AsyncTask的异步处理
AsyncTask是Android提供给我们的一个实现异步的类,AsyncTask.doInBackground()
会在线程池中运行,而AsyncTask.onPostExecute()
和AsyncTask.onProgressUpdate()
则在UI线程中调用。在这个场景中需要进行工作线程与UI线程的切换,内部的实现机制就是Handler。
为了保证Handler在UI线程中被实例化,在ActivityThread.main()
中就进行了这步操作。
public static void main(String[] args) {
Looper.prepareMainLooper();
AsyncTask.init();
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
private static final InternalHandler sHandler = new InternalHandler();
//强制静态变量sHandler被初始化
public static void init() {
sHandler.getLooper();
}
private static class InternalHandler extends Handler {
@Override
public void handleMessage(Message msg) {
AsyncTaskResult result = (AsyncTaskResult) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
由上面代码我们很容易就可以看出,应用内所有AsyncTask的UI回调都是通过同一个静态变量sHandler实现的,而sHandler在UI线程中进行的实例化,即与UI线程进行了绑定,从而保证其handleMessage()是在UI线程完成。
ActivityThread与AMS的通信
ActivityThread与AMS的通信涉及到Binder机制,当要开启一个Activity时,Instrumentation会通过ActivityManagerProxy将数据通过Binder传递给AMS,当AMS协调工作完毕后,会通过ApplicationThreadProxy通知ActivityThread开启具体的Activity,但是由于这部分通信是在Binder线程池中,因此ActivityThread需要将线程切换至UI线程,这里就用到了Handler机制。
下面,将重点介绍AMS通过ApplicationThreadProxy通知ActivityThread开启新的Activity这个过程。
因为ApplicationThreadProxy代理的是ApplicationThread,所以具体的实现是在ApplicationThread。
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
IVoiceInteractor voiceInteractor, int procState, Bundle state,
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
ProfilerInfo profilerInfo) {
updateProcessState(procState, false);
//ActivityClientRecord的数据准备操作
ActivityClientRecord r = new ActivityClientRecord();
r.token = token;
r.ident = ident;
r.intent = intent;
r.voiceInteractor = voiceInteractor;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;
r.persistentState = persistentState;
r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;
r.startsNotResumed = notResumed;
r.isForward = isForward;
r.profilerInfo = profilerInfo;
updatePendingConfiguration(curConfig);
//发送Message
sendMessage(H.LAUNCH_ACTIVITY, r);
}
因为ApplicationThread是ActivityThread的内部类,所以具体的发送操作是在ActivityThread完成。
public final class ActivityThread {
final H mH = new H();
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj) {
sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
Message msg = Message.obtain();
msg.what = what;
msg.obj = obj;
msg.arg1 = arg1;
msg.arg2 = arg2;
if (async) {
msg.setAsynchronous(true);
}
mH.sendMessage(msg);
}
private class H extends Handler {
public static final int LAUNCH_ACTIVITY = 100;
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
} break;
...
}
由于mH是在UI线程初始化,因此H.handleMessage()
的执行线程为UI线程,从而完成了从Binder线程到UI线程的切换。
Toast的显示与隐藏
如果想在子线程中显示Toast应该怎么做呢?必须在Thread中创建消息循环,否则就会报错Can't create handler inside thread that has not called Looper.prepare()
。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(context,message).show();
Looper.loop();
}
}).start();
为什么会这样呢?因为Toast的显示也需要Handler机制来完成线程的切换。
public class Toast {
final TN mTN;
public Toast(Context context) {
mContext = context;
//实例化TN,TN负责与NotificationManager进行Binder通信
mTN = new TN();
}
private static class TN extends ITransientNotification.Stub {
final Runnable mShow = new Runnable() {
@Override
public void run() {
//最终的Toast的显示和隐藏,是在handleShow()中通过WindowManager完成
handleShow();
}
};
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
handleShow()
mNextView = null;
}
};
final Handler mHandler = new Handler();
@Override
public void show() {
//当Toast确实需要显示的时候,NotificationManager会通过Binder调用show()
//为了保证View的操作在UI线程,需要用Handler进行线程的切换
mHandler.post(mShow);
}
@Override
public void hide() {
mHandler.post(mHide);
}
}
}
从上面的代码可以很容易看出,当Toast完成实例化的时候,就会完成TN的实例化,而在TN中的成员变量mHandler的实例化就是在Toast实例化的线程完成的,所以如果当前Toast所在线程没有绑定Looper的时候,就会抛出RuntimeException,这也是为什么在子线程中使用Toast时比如手动开启Looper循环的根本原因。
界面刷新
Android的单线程编程模式,决定了对View的界面操作都需要在UI线程执行,当我们调用View.invalidate()
通知系统刷新界面时,最终会将这个请求传递到ViewRootImpl,具体流程我们会在后面的View机制中详细介绍,现在重点关注一下传递到ViewRootImpl之后的操作。
Last updated
Was this helpful?