public class MainActivity extends AppCompatActivity {
private final String TAG = MainActivity.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
UiHandler uiHandler = new UiHandler(Looper.getMainLooper());
Message msg = uiHandler.obtainMessage();
msg.obj = 1;
uiHandler.sendMessage(msg);
}
final class UiHandler extends Handler{
public UiHandler(@NonNull Looper looper) {
super(looper);
}
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
Log.d(TAG, "handleMessage obj : "+msg.obj);
}
@Override
public void dispatchMessage(@NonNull Message msg) {
super.dispatchMessage(msg);
Log.d(TAG, "dispatchMessage obj: "+msg.obj);
}
}
}
**思考题1:**我们先来讲解一下这些代码,这里通过Message msg = uiHandler.obtainMessage();
来获取Message,那为什么要这么获取呢,而不是直接 Message msg = new Message();
或者Message msg = Message.obtain();
呢?先简单的看一下源码
uiHandler.obtainMessage()
使用该方法的优势是可以确保返回的Message对象不为null。在这个方法中,使用了@NonNull注解来标记返回的Message对象不为null,这样可以避免在使用返回的Message对象时出现NullPointerException
其实这方法里可以携带what参数,更为方便,其次是因为可以提前给Message赋值what,因为传递的数据都需要存放到Message中,上源码
@NonNull
public final Message obtainMessage(int what)
{
return Message.obtain(this, what);
}
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
以上三个方法很清楚吧,也是调用了Message.obtain(this, what);
去创建的Message,然后调用了obtain();
去new Message();
,最关键的是m.target = h;
和m.what = what;
,也就是在创建Message的过程中就已经赋值了what的值,并且Message和handler提前关联了(tartget就是handler实例),因为m.target
在后续sendMessage的时候会和handler实例进行关联,而这里提前关联了,obtain()
的作用就是复用Message对象,节约内存开销
Message msg = new Message()
直接new出来也可以,但是没有其他两种方法效率高,上面这种方法可以让Message和handler提前关联,也可以提前和方便赋值what。而下面这种方法,还可以复用Message,节约对象产生的内存开销。所以这种方法是不建议的
Message msg = Message.*obtain*();
frameworks/base/core/java/android/os/Message.java
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Message.obtain()
是一个静态方法,用于获取一个可复用的Message
对象。它的作用是避免在频繁的消息发送和处理过程中频繁地创建和销毁Message对象,从而提高性能。
通过Message.obtain()
,可以从一个对象池中获取一个空闲的Message
对象,而不是每次都通过new Message()
来创建一个新的对象。这样可以减少内存的分配和垃圾回收的开销,提高性能和效率。
**思考题2:**为什么以上代码没有提示会出现内存泄漏的警告?
因为它在创建UiHandler实例时使用了主线程的Looper对象。Looper对象会与主线程的生命周期保持一致,当主线程销毁时,Looper对象也会被销毁。因此,UiHandler实例持有的Looper对象会在主线程销毁时自动释放,不会造成内存泄漏。当然也可以使用:
uiHandler.removeCallbacksAndMessages(null);
这段代码来清理和释放对象
这里有必要说明几个关键角色:
handleMessage
和dispatchMessage
,用于接收发来的消息(Message)Message.target.dispatchMessage
来回调了Handler的方法,传递了数据,可以携带数据进行跨线程传递数据,可以传输Bundle
对象(八种基本类型以及序列化后的对象,ArrayList也可以,因为序列化了,但是ArrayList<对象>对象没有序列化,也是不行的)public Handler(@Nullable 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());
}
}
// 获取当前线程的looper,如果在主线程创建的handler,则这里就是获取主线程的looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
// 获取Looper中的MessageQueue对象
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
再来看看sendMessage
方法
public final boolean sendMessage(@NonNull Message msg) {
// 默认没有延迟,0ms
return sendMessageDelayed(msg, 0);
}
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
// 如果发送的延迟小于0,则默认没有延迟
if (delayMillis < 0) {
delayMillis = 0;
}
// 获取当前时间+延迟时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
// 将handler和message进行关联
// target就是handler,后续message会调用target,发送消息到handler中
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
// MessageQueue的方法
return queue.enqueueMessage(msg, uptimeMillis);
}
sendMessage→sendMessageDelayed→sendMessageAtTime→enqueueMessage→queue.enqueueMessage(msg, uptimeMillis);
所以注意如果发送延迟消息,不要用sendMessageAtTime,因为需要获取当前时间+要延迟的时间才能正确使用,为了更简便,所以请使用sendMessageDelayed方法来发送延迟消息.
所以这个调用链作用:Message和handler进行绑定,处理延迟消息,将消息存入消息队列中(MessageQueue)
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 同步锁,所以线程安全,存放消息只能排队
synchronized (this) {
...
// mQuitting代表是否退出了,简单的说,调用了quit,这个值就为真
// 如果调用了quit,则这里会被拦截,不再执行存入消息的步骤
if (mQuitting) {
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}
...
if (p == null || when == 0 || when < p.when) {
...
} else {
...
// 将消息存入队列
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
以上代码比较简单,就是链表形式,将Message存到队列中
再来看一下mLooper = Looper.myLooper();
,以下属于Looper.java
public static @Nullable Looper myLooper() {
// looper线程安全
return sThreadLocal.get();
}
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
private static void prepare(boolean quitAllowed) {
// 如果发现sThreadLocal中已经存在了Looper对象,则抛出异常
// 一个线程只能有一个looper
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 创建Looper并保存到sThreadLocal中
sThreadLocal.set(new Looper(quitAllowed));
}
public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
if (me.mInLoop) {
Slog.w(TAG, "Loop again would have the queued messages be executed"
+ " before this one completed.");
}
me.mInLoop = true;
// 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();
// Allow overriding a threshold with a system prop. e.g.
// adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
final int thresholdOverride =
SystemProperties.getInt("log.looper."
+ Process.myUid() + "."
+ Thread.currentThread().getName()
+ ".slow", 0);
me.mSlowDeliveryDetected = false;
for (;;) {
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
可以看到Looper创建的时候就创建了消息队列。调用prepare可以创建一个Looper对象,然后调用loop启动无限循环,然后才会处理MessageQueue队列里的消息
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
// 获取message消息
Message msg = me.mQueue.next(); // might block
// 消息为空 退出
if (msg == null) {
// No message indicates that the message queue is quitting.
return false;
}
...
try {
// 这里就是调用了handler的dispatchMessage方法,将消息发送到handler中
// 到这里任务基本上就完成了
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
...
} catch (Exception exception) {
...
} finally {
...
}
...
msg.recycleUnchecked();
return true;
}
以上代码就是获取队列中的message,然后调用msg.target.dispatchMessage(msg);
来对handler发送消息,看一下handler.java的这个方法
public void dispatchMessage(@NonNull Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
很简单,就是调用了dispatchMessage和handleMessage,所以建议handler中接收消息还是用handleMessage比较合适,如果重写了dispatchMessage来接受消息,那么callback可能就无法受到消息了
msg.target.dispatchMessage(msg);
将消息发送给Handler,至此任务完成