Handler我们开发的时候可以说是必用,相信大家对Handler的用法也都很了解,不过Handler还是可以引出很多有趣的知识,所以从用法出发,让我们来多了解一下Handler吧。
Handler的主要作用就是在子线程执行完耗时任务之后,发送消息通知到主线程执行UI更新。
第一个思考:为什么要规定不能在子线程中更新UI?
我们知道子线程更新UI会报下面这个错误(在onCreate()方法中采用子线程更新不报错的情况就不管啦)。
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
百度翻译一下。
只有创建View层次的原始线程才能更改它,为什么不写只有主线程才能修改,我也不知道。子线程不能更新UI几乎是所有的GUI系统的做法,比如Qt,Swing等,其实就是不能多线程更新UI,一般做法就是将View的创建和更新控制在一个线程中,可能这也是上面那句英文的由来吧。知乎上有一个关于子线程不能更新UI的问题,可以看看https://www.zhihu.com/question/37334646,其中一个回答还提到了Sun副总裁关于多线程更新UI的一篇Blog https://community.oracle.com/blogs/kgh/2004/10/19/multithreaded-toolkits-failed-dream,感兴趣的都可以看看。这些文章和讨论都说明了多线程更新UI是一个复杂,容易出现各种问题且意义不大的事,可以认为它是前人踩过无数坑之后的经验总结。
下面我们来总结一下Handler的用法,大致分为两类。
1、sendXXX()方法。
private Handler handler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
//处理逻辑1
int arg1 = msg.arg1;
int arg2 = msg.arg2;
Object obj = msg.obj;
break;
case 2:
System.out.println("收到消息2");
break;
default:
break;
}
};
};
handler.sendEmptyMessage(1); //发送msg.what为1的消息
handler.sendEmptyMessageAtTime(1, SystemClock.uptimeMillis()+1000);//指定时间发送msg.what为 1的消息,较少用
handler.sendEmptyMessageDelayed(1, 1000);//延时发送msg.what为1的消息
Message msg = new Message();
msg.what = 1;
msg.arg1 = 12;
msg.arg2 = 13;
msg.obj = new Person("name",25);
handler.sendMessage(msg);//发送Message
handler.sendMessageAtTime(msg, SystemClock.uptimeMillis()+1000);//指定时间发送Message,较少用
handler.sendMessageAtFrontOfQueue(msg);//将Message插入列表的头部,不建议使用
handler.sendMessageDelayed(msg, 2000);//延时发送Message
一般常用的就是sendMessage(),sendMessageDelayed()方法。Message的创建除了new之外,还可以通过Message.obtain(handler)和handler.obtainMessage()获取,这两种方法一般配合Message的sendToTarget()方法。
Message obtain = Message.obtain(handler);
obtain.what = 1;
obtain.arg1 = 12;
obtain.arg2 = 13;
obtain.obj = new Person("name",25);
obtain.sendToTarget();
Message obtainMessage = handler.obtainMessage();
obtainMessage.what = 1;
obtainMessage.arg1 = 12;
obtainMessage.arg2 = 13;
obtainMessage.obj = new Person("name",25);
obtainMessage.sendToTarget();
这两种方式获取Message会重复利用已经创建的对象,可以节约资源。也是谷歌官方比较推荐的做法。
While the constructor of Message is public, the best way to get one of these is
to call Message.obtain() or one of the Handler.obtainMessage() methods,
which will pull them from a pool of recycled objects.
2、postXXX()方法。
handler.post(r);
handler.postAtFrontOfQueue(r);
handler.postAtTime(r, SystemClock.uptimeMillis() + delayMillis);
handler.postAtTime(r, token, SystemClock.uptimeMillis() + delayMillis);
handler.postDelayed(r, delayMillis);
post方法本质上也是通过sendMessage()方式实现的,其实就是将Runable对象保存到message的callback变量中,在handlerMessage之前判断callback是否存在,如果存在,则执行它的run方法。
Message发送出去之后,处理顺序是Message的CallBack,然后是我们在创建Handler是可能传入的CallBack,有的话执行它的handleMesssage()方法,最后才是我们熟悉的handleMessage()方法。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
CallBack是Handler的一个内部接口类,它的定义如下。
/**
* Callback interface you can use when instantiating a Handler to avoid
* having to implement your own subclass of Handler.
*/
public interface Callback {
public boolean handleMessage(Message msg);
}
用法我们点到为止,下面我们思考第二个问题:Activity的onCreate(),onResume()等方法是主线程,handleMessage()方法是主线程,Service的onCreate()等方法是主线程,BroadcastReceiver的onReceive()是主线程,这几个主线程是同个东西吗?Activity的主线程与Service的主线程或者BroadcastReceiver的主线程能做到并发吗,难道是串行工作的?
先说答案,这几个主线程确实是同一个东西,并且它们是串行工作的,一个运行结束后下一个才能运行。所以特别注意不要在这些方法中做耗时操作。
来看看下面这一个例子。
public class MainActivity extends Activity {
private Handler handler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 1:
System.out.println("收到消息1");
System.out.println("处理消息1花费3s");
SystemClock.sleep(3000);
System.out.println("消息1处理结束");
break;
case 2:
System.out.println("收到消息2");
break;
default:
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TimerTaskUtil.startTimer("check", 0, 100, new TimerTask() {
@Override
public void run() {
System.out.println("has " + handler.hasMessages(1) + " : " + handler.hasMessages(2));
}
});
System.out.println("发送消息1");
handler.sendEmptyMessage(1);
System.out.println("消息1发送完成,睡2s");
SystemClock.sleep(2000);
System.out.println("睡眠结束!");
System.out.println("发送消息2");
handler.sendEmptyMessage(2);
}
}
TimerTaskUtil是用来开定时器的,它的作用(不要管它的实现)是定时进行hasMessages的检测(每100ms检测一次),判断MessageQueue中有没有对应的message消息,没有其他作用。以下是输出。
01-01 00:06:36.240 I/System.out( 4690): 发送消息1
01-01 00:06:36.240 I/System.out( 4690): has false : false
01-01 00:06:36.240 I/System.out( 4690): 消息1发送完成,睡2s
01-01 00:06:36.340 I/System.out( 4690): has true : false
01-01 00:06:38.240 I/System.out( 4690): 睡眠结束!
01-01 00:06:38.240 I/System.out( 4690): 发送消息2
01-01 00:06:38.240 I/System.out( 4690): has true : true
01-01 00:06:38.260 I/System.out( 4690): 收到消息1
01-01 00:06:38.260 I/System.out( 4690): 处理消息1花费3s
01-01 00:06:38.340 I/System.out( 4690): has false : true
01-01 00:06:41.260 I/System.out( 4690): 消息1处理结束
01-01 00:06:41.260 I/System.out( 4690): 收到消息2
01-01 00:06:41.550 I/System.out( 4690): has false : false
1、onCreate()方法中发送了消息1后,睡了2s,然后发送了消息2。hanldeMessage()中并没有在发送了消息1之后得到执行,而是在onCreate()执行完成之后才执行,即便onCreate()中消息发送后还睡了2s,也是等到它睡完handleMessage()才开始工作。可见onCreate()和handleMessage()是串行的。
2、handleMessage()开始运行时,消息1,2都已经发送出去了,消息2在消息1之后。handleMessage()处理消息1的时候睡了3s,这个期间消息2并没有得到运行,而是等待消息1处理完成之后消息2才得到执行。所以,handleMessage()处理消息也是串行的,一条处理完成才处理下一条。
3、hasMessage()在sendMessage()之后就为true了,执行完成之后就为false。
为什么是这些看起来并不相关的方法是串行执行的,下面来说说Android的消息处理机制。
Android的消息机制主要有下面几个角色,Handler,Looper,MessageQueue,Message。其中重要的是Looper和MessageQueue。把Handler和Message去掉来看消息机制,其实就是Looper.loop一直死循环从MessageQueue队列中取消息,MessageQueue没有消息的时候就阻塞,MessageQueue有消息的时候就取出来处理。请看loop源码,看for(;;)这一段,queue.next() 就是死循环中一直从MessageQueue中取消息。
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycle();
}
}
加上Handler和Message来看的话,Message就是发送消息时被添加到队列并且在处理时被取出来的对象,Handler相当于给客户端用的一个发送消息的接口,同时消息的处理方法也安排在Handler中,意思就是让客户端自己安排消息的处理。所有的Handler在发送数据的时候都会被保存到Message的一个成员变量target中,比如sendEmptyMessage()最终会调到enqueueMessage()方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
msg.target = this;就是将Handler对象保存到target变量中。
loop()方法中,queue.next() 从MessageQueue中取出Message,记住取出来的Message是持有Handler对象的。消息处理过程就是得到这个Message关联的Handler对象,然后调用它的dispatchMessage()方法,就是上面代码的msg.target.dispatchMessage(msg)这句,它将Message作为参数传入。dispatchMessage()的源码我们前面有提到了,它的处理流程如下。
/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
处理顺序我们也说过了,先Message的CallBack,然后是我们在创建Handler是可能传入的CallBack,如果有执行它的handleMessage()方法,最后我们熟悉的Handler的handleMessage()方法。
根据以上,我们可以理解到消息机制架构中,Looper只有一个,MessageQueue也只有一个,它们一个死循环一个取数据。而Message可以不断的产生,每个Message都带有一个Handler,这些Handler可以是不同的对象,但一个消息只能在一个Handler中执行,即Message的target是哪个,该消息就在哪个Handler中执行。
第三个思考:Android的主线程到底是什么?不同的Handler为什么对应一个主线程,比如Service中创建的Handler和Activity创建的Handler为什么他们的handleMessage()方法都是主线程?
我们先从系统启动流程说起——以下内容参考了柯元旦《Android内核剖析》,感兴趣可以看看这本书——Android系统启动时运行的第一个Dalvik虚拟机程序叫zygote。zygote进程主要包含两个主要模块,一个是Socket服务端,它主要用于接收启动新的Dalvik进程的命令,从而启动一系列后续的Dalvik进程。另一个是Framework的共享类和共享资源,zygote孵化后续Dalvik进程是通过folk方式,这一部分的共享类和共享资源是可以让后续的Dalvik进程使用的,避免了重复装载。zygote是通过init.rc配置启动的,在手机的根目录就可以看到它。zygote启动后主动的孵化出SystemService,SystemService是AmS,PmS,WmS等重要的服务所在的进程。等以上的服务启动完成之后,AmS服务就会启动第一个HomeActivity即Launcher应用,启动完成后,系统启动就完成了。
讲完大概的系统启动流程,我们再来看看Activity的启动流程。Android中的主线程其实就是ActivityThread,ActivityThread的main()方法就是主线程运行的开始,它调用了Looper的loop方法,死循环从MessageQueue中取消息,等待消息处理完成又再一次循环等待,如此往复。一下是ActivityThread的main()方法和Looper.loop()方法。
//ActivityThread.java
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
SamplingProfilerIntegration.start();
// CloseGuard defaults to true and can be quite spammy. We
// disable it here, but selectively enable it later (via
// StrictMode) on debug builds, but using DropBox, not logs.
CloseGuard.setEnabled(false);
Environment.initForCurrentUser();
// Set the reporter for event logging in libcore
EventLogger.setReporter(new EventLoggingReporter());
// Make sure TrustedCertificateStore looks in the right place for CA certificates
final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
TrustedCertificateStore.setDefaultUserDirectory(configDir);
Process.setArgV0("<pre-initialized>");
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.java
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
那ActivityThread是如何启动的呢,即Android应用的主线程是如何起来的。让我们从Activity的启动流程说起。以下内容最后打开源码对着看,会有比较具体的认识,以下源码是基于Android 7.1.1的。
首先打开Activity.java的源码,当我们调用startActivity()启动一个Activity,可以看到是调用了startActivityForResult()方法
跟踪进去可以看到执行了Instrumentation的execStartActivity()
接下来是调用ActivityManagerNative.getDefault().startActivity()
点击getDefault()进去看看,它返回的是IActivityManager对象,这是一个接口,它的实现类是ActivityManagerProxy,所以其实就是调用了ActivityManagerProxy的startActivity()方法,该方法调用了transact()方法,
熟悉Binder调用流程的就知道(不是很了解的可以看一下上篇文章《了解Binder》哦^-^),该方法会引起onTransact()方法的回调,它对应的onTransact()方法就是ActivityManagerNative的onTransact()方法,这段代码其实已经是运行在ActivityManagerService即AmS中。该方法调用了startActivity()方法。
对应的startActivity方法在ActivityManagerService中,接下来是ActivityManager的startActivityAsUser()
再下来是ActivityStarter的startActivityMayWait()
接下来是startActivityLocked()方法
接下里是doPendingActivityLaunchesLocked()方法
然后是startActivityUnchecked()
接下来是ActivityStackSupervisor的resumeFocusedStackTopActivityLocked()
再来是ActivityStack的resumeTopActivityUncheckedLocked()
再然后是resumeTopActivityInnerLocked()
再来就是ActivityStackSuperVisor的startSpecificActivityLocked()了
到该方法我们可以停止了,来看看这个方法
如果app为空,即应用还没启动,那么AmS会执行mService.startProcessLocked()方法
startProcessLocked()又调用了重载的startProcessLocked()方法,
后来这个startProcessLocked()方法执行了如下代码
这是ActivityThread启动的开始,其内部调用了startViaZygote()方法
AmS与Zygote的通信是通过Socket,跟踪进去startViaZygote我们可以看到它配置了很多的参数,接着调用了zygoteSendArgsAndGetResult()方法
zygoteSendArgsAndGetResult()方法用到了Zygote中的BufferedWriter,这正是往Socket里写入数据
以上过程结束,Zygote进程会folk出一个新的应用进程,并且该进程会从ActivityThread类的main()方法开始执行。启动过程结束后,ActivityThread的main()方法调用了attach方法,该方法IPC调用AmS的attachApplication()方法,接下来的一系列工作就是初始化Application,加载应用的HomeActivity等等,这些动作指令经过AmS后又会回到ActivityThread中做具体的实现,ActivityThread负责创建对象以及回调onCreate(),onResume()等方法。比如在ActivityThread在调用了attach()方法后,Binder调用了AmS的attachApplication(),而AmS的attachApplication()又调用了thread.bindApplication()方法回到ApplicationThread的bindApplication()方法(Application继承了ApplicationThreadNative类,它在ActivityThread.java中,是一个内部类)。ApplicationThread正是采用sendMessage()方法将回调方法都运行在主线程中的。
//ActivityThread.java attach()方法
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
//ActivityManagerService.java attachApplicationLocked()方法
thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
app.instrumentationUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.persistent,
new Configuration(mConfiguration), app.compat,
getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked());
//ApplicationThread bindApplication()方法
public final void bindApplication(String processName, ApplicationInfo appInfo,
List<ProviderInfo> providers, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map<String, IBinder> services, Bundle coreSettings) {
if (services != null) {
// Setup the service cache in the ServiceManager
ServiceManager.initServiceCache(services);
}
setCoreSettings(coreSettings);
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providers;
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
sendMessage(H.BIND_APPLICATION, data);
}
看这一部分代码时记住ActivityThread通过ActivityManagerProxy这个类Binder调用ActivityManagerService的方法,而ActivityManagerService则是通过ApplicationThreadProxy这个类调用的ActivityThread的方法,就会比较容易看了。Acitivity主线程做的事都是运行在ActivityThread中的。
看到这里可能会疑惑,既然AmS不负责启动和回调生命周期方法,那AmS到底有什么用。应该是,AmS是统筹,它具有运行中的所有Activity的信息,前一个Activity是什么,下一个要启动的Activity的信息等,比如你要start一个新的Activity,发送了一个请求消息后,AmS会收集所以该Activity的消息,同时AmS也会负责通知原来的Activity暂停,虽然它不管具体的暂停操作,然后在Activity暂停完成后,AmS又收到通知,它调出收集到的要启动的Activity的信息,发给ActivityThread,让它自己负责该Activity的创建,比如上面bindApplication方法中的大量参数都是来自AmS。还有Back键应该返回到哪个Activity,让哪个Activity暂停,都是AmS来统筹。
让我们回到一开始的问题,Android的主线程是由Zygote进程通过folk创建的一个新进程,它具体运行的代码就是ActivityThread。前面我们知道,其实handler发送消息,就是往MessageQueue中插入新的Message消息。在主线程内,所有的Handler对应的MessageQueue都是同一个,它的创建在Looper的构造方法中,主线程对应的Looper也是只有一个,它的创建在ActivityThread的main()方法中
//ActivityThread.java
public static void main(String[] args) {
……
Looper.prepareMainLooper();
……
Looper.loop();
}
//Looper.java
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//Looper.java
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//Looper.java
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
//Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
//Looper.java
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
}
// Make sure that during the course of dispatching the
// identity of the thread wasn't corrupted.
final long newIdent = Binder.clearCallingIdentity();
if (ident != newIdent) {
Log.wtf(TAG, "Thread identity changed from 0x"
+ Long.toHexString(ident) + " to 0x"
+ Long.toHexString(newIdent) + " while dispatching to "
+ msg.target.getClass().getName() + " "
+ msg.callback + " what=" + msg.what);
}
msg.recycleUnchecked();
}
}
ThreadLocal是根据线程的不同,在每个线程中保存自己线程的对象,其内部原理是:每个线程即Thread对象都有一个ThreadLocalMap对象,每个ThreadLocalMap对象的key是ThreadLocal,value是设置的值,每次get之所以get不同,是因为Thread不同,所以得到的ThreadLocalMap不同,虽然他们的ThreadLocal是同一个,但是ThreadLocalMap本身就不是同一个,所以得到的value也不是同一个。能听懂不,这是我看了源码自己瞎总结的。简单一点可以看成把当前线程当成key,把对象看成Value的Map集合,虽然它里面的ThreadLocalMap并不是以线程作为key,每个线程得到的对象都是自己线程设置进去的,别的线程设置进去的你获取不到。就这样理解就可以。
当我们在主线程中创建Handler,无论是Activity还是Service,本质上它们的创建都运行在由ActivityThread开启的loop()方法中,Handler的构造方法都是获取对应线程的Looper以及MessageQueue,所以只要是主线程创建的Handler,都是在同一个loop()循环中处理,这就是为什么创建了多个Handler,但是这些Handler的handleMessage()方法都是在主线程的原因。
// Handler.java Handler的构造最终会走到这里
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//Looper.java
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
下面我们来谈谈与Handler机制密切相关的HandlerThread和IntentService。
我们知道在子线程中直接创建Handler会报错:Can't create handler inside thread that has not called Looper.prepare(),我们知道这是因为该Handler没有looper导致,主线程不报错是因为ActivityThread的main方法已经初始化了looper,并且保存到了ThreadLocal中。如何给子线程的Handler设置Looper呢,一般有两种方式,在子线程创建Handler之前,采用Looper.prepare()生成looper,它会创建looper并且保存到ThreadLocal中,这样在Handler在构造方法运行的时候就会从ThreadLocal中取得looper,这样就不会报错。
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("Study", "msg.what : " + msg.what);
}
};
Looper.loop()
}
}).start();
SystemClock.sleep(1000);
mHandler.sendEmptyMessage(1);
当然Looper.prepare只是生成了looper,handleMessage()能够工作,还需要在Handler创建后调用Looper.loop()启动MessageQueue的读取。至于为什么需要在创建Handler之后才loop,能不能之前,答案是不能,因为loop是一个死循环,创建Handler放在之后的话永远不会运行到。这样handlerMessage()运行是在子线程。
由于Handle是在子线程中创建的。Java中每个线程运行的先后顺序是不确定的,那就会有个问题,其他线程拿这个Handler来用的时候,它不一定创建完成了,那怎么办,尽管可以判断它是不是为null,但是判断之后就过去了,难道要while循环判断?当然不是,上面的例子采用sleep睡了1s,保证了子线程先运行,不过这也不是解决之道。
官方有一种更为标准的方式是采用HandlerThread生成looper,然后通过Handler的构造方法传入looper,Handler的创建在主线程,不过由于Looper是创建在子线程,handleMessage自然的运行在了子线程。如下。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main3);
HandlerThread ht = new HandlerThread("handler_thread_1");
ht.start();
mHandler = new Handler(ht.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("Study", "msg.what : " + msg.what + " " + Thread.currentThread().getName());
}
};
mHandler.sendEmptyMessage(1);
}
log输出是:01-01 00:42:18.980 D/Study ( 8968): msg.what : 1 handler_thread_1
这个方式Handler的创建和使用在同一个线程,可以保证了mHandler在使用时已经初始化。跟踪进去HandlerThread,它继承了Thread,它的Looper是在run()方法中生成并且开启loop循环的,loop循环运行在子线程,这也是为什么handleMessage()运行在子线程。
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
带Looper参数的Handler的构造方法是这样的。
public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
简单的保存了looper,并且提取了looper的MessageQueue。这是为sendMessage()等方法做了准备。
Looper的创建在HandlerThread的run()方法,那么HandlerThread的getLooper()方法又是如何保证得到的Looper对象不为空的。我们来看看getLooper()方法,同时再贴一遍HandlerThread的run()方法
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
看到getLooper()方法内注释的那句英文了吗,如果HandlerThread已经start了,且mLooper还没初始化,那么wait等待mLooper初始化。即getLooper()是一个阻塞的方法,当我们调用了HandlerThread的start()方法,由于线程运行顺序的不确定性,可能它的run()方法先运行,也可能主线程继续运行。如果run()方法先运行,那么没有问题,mLooper会得到初始化,等到getLooper()执行,它判断mLooper不为空,直接返回。如果主线程继续运行,此时mLooper为空,getLooper()的while判断成立,主线程由于wai()t方法暂停了,此时run()方法得到运行权限,生成Looper并保存到mLooper中,然后notifyAll唤醒getLooper()方法,当getLooper()再一次抢到运行权的时候,此时mLooper已经初始化完成,while循环判断不成立,返回mLooper。这样主线程中Handler的就可以自然创建并且拿来使用了。
这种方式虽然getLooper()有可能导致主线程阻塞,但是本质mLooper的创建不是一个耗时操作,所以getLooper()是可以很快执行完成的。以后我们在一个线程需要等到另一个线程先执行完才能继续执行的情况下,也可以采用这种方式,用同步代码块包裹起需要等待的部分,采用wait和notify处理两者关系。
了解了HandlerThread再来看看IntentService。
了解IntentService的知道,IntentService是可以处理耗时操作的,并且在处理完成后主动结束自己,属于用完即走的。比起普通线程,它的优先级更高,不易被系统杀死,比起普通Service,它又不用自己开线程处理耗时任务。
我们用Android Studio创建IntentService时自动生成的类作为例子看看IntentService的使用。
public class CustomIntentService extends IntentService {
private static final String ACTION_FOO = "com.example.administrator.study.action.FOO";
private static final String ACTION_BAZ = "com.example.administrator.study.action.BAZ";
private static final String EXTRA_PARAM1 = "com.example.administrator.study.extra.PARAM1";
private static final String EXTRA_PARAM2 = "com.example.administrator.study.extra.PARAM2";
public CustomIntentService() {
super("CustomIntentService");
}
public static void startActionFoo(Context context, String param1, String param2) {
Intent intent = new Intent(context, CustomIntentService.class);
intent.setAction(ACTION_FOO);
intent.putExtra(EXTRA_PARAM1, param1);
intent.putExtra(EXTRA_PARAM2, param2);
context.startService(intent);
}
public static void startActionBaz(Context context, String param1, String param2) {
Intent intent = new Intent(context, CustomIntentService.class);
intent.setAction(ACTION_BAZ);
intent.putExtra(EXTRA_PARAM1, param1);
intent.putExtra(EXTRA_PARAM2, param2);
context.startService(intent);
}
@Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
final String action = intent.getAction();
if (ACTION_FOO.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_PARAM1);
final String param2 = intent.getStringExtra(EXTRA_PARAM2);
handleActionFoo(param1, param2);
} else if (ACTION_BAZ.equals(action)) {
final String param1 = intent.getStringExtra(EXTRA_PARAM1);
final String param2 = intent.getStringExtra(EXTRA_PARAM2);
handleActionBaz(param1, param2);
}
}
}
private void handleActionFoo(String param1, String param2) {
Log.d("Study", "param1: " + param1 + " param2: " + param2 + " thread: " + Thread.currentThread().getName());
}
private void handleActionBaz(String param1, String param2) {
SystemClock.sleep(5000);
Log.d("Study", "param1: " + param1 + " param2: " + param2 + " thread: " + Thread.currentThread().getName());
}
@Override
public void onCreate() {
super.onCreate();
Log.d("Study","onCreate");
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
Log.d("Study","onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("Study","onDestroy");
}
}
继承IntentService需要一个构造函数,在构造函数中传入一个名字,还有重点是实现onHandleIntent()方法,这个方法是运行在子线程的,你可以通过这个方法提取在startService时传入的参数,然后针对不同参数做自己想做的事,当处理完成之后,Service会结束自己,onDestroy方法会得到调用。
这里的两个静态方法startActionFoo(),startActionBaz()是方便启动Service的,简单的我们可以在Activity中调用
CustomIntentService.startActionBaz(this, "study", "baz");
得到的输出是
01-01 00:39:02.660 D/Study (25267): onCreate
01-01 00:39:02.660 D/Study (25267): onStartCommand
01-01 00:39:07.680 D/Study (25267): param1: study param2: baz thread: IntentService[CustomIntentService]
01-01 00:39:07.680 D/Study (25267): onDestroy
IntentService的使用就是这么简单,下面我们来看看onHandleIntent()方法是怎么运行在子线程的。
其实非常简单,而且代码也很简短,其内部是使用了HandlerThread来实现onHandleIntent()在子线程运行的。
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
stopSelf(msg.arg1);
}
}
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public IntentService(String name) {
super();
mName = name;
}
/**
* Sets intent redelivery preferences. Usually called from the constructor
* with your preferred semantics.
*
* <p>If enabled is true,
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_REDELIVER_INTENT}, so if this process dies before
* {@link #onHandleIntent(Intent)} returns, the process will be restarted
* and the intent redelivered. If multiple Intents have been sent, only
* the most recent one is guaranteed to be redelivered.
*
* <p>If enabled is false (the default),
* {@link #onStartCommand(Intent, int, int)} will return
* {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
* dies along with it.
*/
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
/**
* You should not override this method for your IntentService. Instead,
* override {@link #onHandleIntent}, which the system calls when the IntentService
* receives a start request.
* @see android.app.Service#onStartCommand
*/
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
/**
* Unless you provide binding for your service, you don't need to implement this
* method, because the default implementation returns null.
* @see android.app.Service#onBind
*/
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
/**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
* This may be null if the service is being restarted after
* its process has gone away; see
* {@link android.app.Service#onStartCommand}
* for details.
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
IntentService在onCreate()方法中采用HandlerThread创建了一个Handler,名字叫ServiceHandler,在onStart()的时候通过sendMessage()发送Message消息,带的参数就是intent。接着ServiceHandler的handleMessage()得到回调,而它的处理就是调用onHandleIntent()将Intent传入,所以我们在onHandleIntent()中处理的Intent就是onStart()方法中的Intent,处理完成之后stopSelf()停止自己。就是这么简单。
另外需要注意,HandlerThread在程序退出之后,记得调用quit()方法停止loop进程,释放资源。就像IntentService的onDestroy()方法所做的那样。
其实做了这么多的介绍,是不是觉得还是少了什么,就是子线程在sendMessage()发送的消息,为什么在loop()中取出来之后就是在另外一个线程中运行了,Handler到底是如何切换线程的。感觉这是问题也不是问题,因为Java中本来两个线程就是可以使用同一个对象的,你在类中申明的变量,本来就是每个线程都可以使用的。这个本质就是Java内存模型中的共享变量,Handler机制中MessageQueue是共享变量。内存模型设计到可见性,指令重排序等知识,有兴趣的可以继续了解咯。
PS:文章把跟Handler相关的知识做了整理,不懂不确定的地方为了保证它的正确性细致的查了很多的资料,这个过程学到了很多,这也是写博客的意义,给有需要的人同时也督促自己去进步。欢迎对文中知识进行讨论。