Android 使用 Handle 处理异步消息

在学习Android的路上,大家肯定会遇到异步消息处理,Android提供给我们一个类来处理相关的问题,那就是Handler。相信大家大多都用过Handler了,下面我们就来看看Handler最简单的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class FirstActivity extends AppCompatActivity {

public static final String TAG = "FirstActivity";
private static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case 0:
Log.i(TAG, "handler receive msg.what = " + msg.what);
break;
default:
break;
}
}
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
new Thread(new Runnable() {
@Override
public void run() {
//这里做相关操作
handler.sendEmptyMessage(0);
}
}).start();
}

}

上面代码实现了在子线程中发出一个消息,然后在主线程中接收消息。Handler其他类似的用法在这就不过多叙述了。下面我们来看看Handler到底是怎么实现异步消息处理的吧!

先来看看我们new一个Handler的对象到底发生了什么(只截取了关键源码):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public Handler() {
this(null, false);
}

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;
}

可以看到我们平常写的 new Handler();其实是调用了另外一个构造方法,并且判断了mLooper是不是为空,为空则抛出一个异常**”Can’t create handler inside thread that has not called Looper.prepare()”**,mLooper其实是一个Looper类的成员变量,官方文档上对Looper类的解释是 **Class used to run a message loop for a thread.**也就是说Looper用于在一个线程中传递message的。 然后我们根据异常的提示知道要在new一个Handler的对象之前必须
先调用Looper.prepare()。那接下来就只能先去看看Looper.prepare()方法了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper; // guarded by Looper.class

final MessageQueue mQueue;
final Thread mThread;

private Printer mLogging;

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
prepare(true);
}

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));
}

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

/**
* 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.recycleUnchecked();
}
}

/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

prepare()方法就是将一个sThreadLocal和新建的Looper对象相绑定,同时mQueue成员变量也创建了新的MessageQueue对象,MessageQueue这个类就是用于存储Message的队列。在prepare()方法的注释上写着在调用prepare()方法之后还要调用loop()方法,我们再看loop方法,可以看到方法里写了一个for的死循环,主要用于在MessageQueue里不断地去取Message,如果msg为空,则阻塞;不然会调用msg.target.dispatchMessage(msg)这个方法。dispatchMessage()这个方法我会在后面讲解,先暂时放一边不管。

好了,捋一捋思路,当你在新建一个Handler对象时,要先确保调用了Looper.prepare()方法,然后调用Looper.loop()方法让MessageQueue这个队列“动”起来。这样你就成功地创建了一个Handler的对象。然后我们再使用Handler的sendMessage系列方法来发送一个消息。下面我们就来看看sendMessage系列方法里到底干了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

为什么我就贴出一个方法呢?这是因为Handler一系列的sendMessage方法基本上最后都是调用了sendMessageAtTime这个方法。从源码中我们看到主要就是干了把Message加入队列这个事,并把当前的Handler对象赋给了msg的target。再联系上面的Looper.loop方法,我们大概就懂了。好了,我们回过头来看看上面的msg.target.dispatchMessage(msg)主要的功能。其实就是调用了Handler的dispatchMessage方法:

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

我们看到了一行熟悉的代码:handleMessage(msg),这不正是我们再创建Handler对象时重写的那个方法么!好了,这一切的逻辑我们似乎已经搞清了:首先调用Looper.prepare()创建一个Looper对象,然后handler发送消息后把消息加入到MessageQueue里,因为之前调用了Looper.loop(),所以MessageQueue在不断地做出队的操作,然后再根据message的target变量分发消息,回到handler的handleMessage()方法。

也许有人会有疑问了,为什么在主线程中创建Handler对象可以直接使用而不需要调用Looper.prepare()和Looper.loop()两个方法呢?这是因为在ActivityThread里面已经调用了,下面附上ActivityThread的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/** 
* This manages the execution of the main thread in an
* application process, scheduling and executing activities,
* broadcasts, and other operations on it as the activity
* manager requests.
*
* {@hide}
*/
public final class ActivityThread {

static ContextImpl mSystemContext = null;

static IPackageManager sPackageManager;

// 创建ApplicationThread实例,以接收AMS指令并执行
final ApplicationThread mAppThread = new ApplicationThread();

final Looper mLooper = Looper.myLooper();

final H mH = new H();

final HashMap<IBinder, ActivityClientRecord> mActivities
= new HashMap<IBinder, ActivityClientRecord>();

// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
ActivityClientRecord mNewActivities = null;

// Number of activities that are currently visible on-screen.
int mNumVisibleActivities = 0;

final HashMap<IBinder, Service> mServices
= new HashMap<IBinder, Service>();

Application mInitialApplication;

final ArrayList<Application> mAllApplications
= new ArrayList<Application>();

static final ThreadLocal<ActivityThread> sThreadLocal = new ThreadLocal<ActivityThread>();
Instrumentation mInstrumentation;

static Handler sMainThreadHandler; // set once in main()

static final class ActivityClientRecord {
IBinder token;
int ident;
Intent intent;
Bundle state;
Activity activity;
Window window;
Activity parent;
String embeddedID;
Activity.NonConfigurationInstances lastNonConfigurationInstances;
boolean paused;
boolean stopped;
boolean hideForNow;
Configuration newConfig;
Configuration createdConfig;
ActivityClientRecord nextIdle;

String profileFile;
ParcelFileDescriptor profileFd;
boolean autoStopProfiler;

ActivityInfo activityInfo;
CompatibilityInfo compatInfo;
LoadedApk packageInfo; //包信息,通过调用ActivityThread.getPapckageInfo而获得

List<ResultInfo> pendingResults;
List<Intent> pendingIntents;

boolean startsNotResumed;
boolean isForward;
int pendingConfigChanges;
boolean onlyLocalRequest;

View mPendingRemoveWindow;
WindowManager mPendingRemoveWindowManager;

...
}


private class ApplicationThread extends ApplicationThreadNative {

private void updatePendingConfiguration(Configuration config) {
synchronized (mPackages) {
if (mPendingConfiguration == null ||
mPendingConfiguration.isOtherSeqNewer(config)) {
mPendingConfiguration = config;
}
}
}

public final void schedulePauseActivity(IBinder token, boolean finished,
boolean userLeaving, int configChanges) {
queueOrSendMessage(
finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
token,
(userLeaving ? 1 : 0),
configChanges);
}

// we use token to identify this activity without having to send the
// activity itself back to the activity manager. (matters more with ipc)
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
Bundle state, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
ActivityClientRecord r = new ActivityClientRecord();

r.token = token;
r.ident = ident;
r.intent = intent;
r.activityInfo = info;
r.compatInfo = compatInfo;
r.state = state;

r.pendingResults = pendingResults;
r.pendingIntents = pendingNewIntents;

r.startsNotResumed = notResumed;
r.isForward = isForward;

r.profileFile = profileName;
r.profileFd = profileFd;
r.autoStopProfiler = autoStopProfiler;

updatePendingConfiguration(curConfig);

queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}

...
}

private class H extends Handler {

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");
ActivityClientRecord r = (ActivityClientRecord)msg.obj;

r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
...
}
if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
}

...
}

public static ActivityThread currentActivityThread() {
return sThreadLocal.get();
}


public static void main(String[] args) {
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());

Process.setArgV0("<pre-initialized>");

Looper.prepareMainLooper();

// 创建ActivityThread实例
ActivityThread thread = new ActivityThread();
thread.attach(false);

if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}

AsyncTask.init();

if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}

Looper.loop();

throw new RuntimeException("Main thread loop unexpectedly exited");
}
}

可以看到上面的main方法里的181行和198行已经调用了prepare和loop的方法。因此在主线程中使用Handler不需要再调用prepare和loop方法了。