1. 背景
背景是在实际开发中,有一个Message消息在主线程接收到了后,将Message切换到子 线程去处理,但是出现了Message.obj被串改或者被置空,导致出现NullPointException 或者ClassCastException ,此时正好从源码分析下出现的原因和优化方法。
2. Message几个关键点
2.1 recycle什么时候会被调用
recycle被调用也即意味着这个消息对象会清空,并重新加入到消息池中等待调用。这就意味着某个Message对象在回收之后再使用此消息会出现空指针异常或者其他未知的异常。
- 当Handler指定删除单条消息,或所有消息的时候
void removeMessages(Handler h, int what, Object object)
void removeMessages(Handler h, Runnable r, Object object)
void removeCallbacksAndMessages(Handler h, Object object)
- 当android.os.Looper#loop() 循环处理方法最后也会调用
msg.recycleUnchecked() , 这也即意味着Message 不要去切换线程处理,有可能会导致多线程安全
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next();
msg.recycleUnchecked();
}
}
3. Message源码解析
Message 在Handler机制中是用于传递信息的,位于android.os.Message
package android.os;
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public static final int UID_NONE = -1;
public int sendingUid = UID_NONE;
public int workSourceUid = UID_NONE;
static final int FLAG_IN_USE = 1 << 0;
static final int FLAG_ASYNCHRONOUS = 1 << 1;
static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
@UnsupportedAppUsage
int flags;
@UnsupportedAppUsage
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public long when;
Bundle data;
@UnsupportedAppUsage
Handler target;
@UnsupportedAppUsage
Runnable callback;
@UnsupportedAppUsage
Message next;
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0;
sPoolSize--;
return m;
}
}
return new Message();
}
public static Message obtain(Message orig) {
Message m = obtain();
m.what = orig.what;
m.arg1 = orig.arg1;
m.arg2 = orig.arg2;
m.obj = orig.obj;
m.replyTo = orig.replyTo;
m.sendingUid = orig.sendingUid;
m.workSourceUid = orig.workSourceUid;
if (orig.data != null) {
m.data = new Bundle(orig.data);
}
m.target = orig.target;
m.callback = orig.callback;
return m;
}
public static Message obtain(Handler h) {
Message m = obtain();
m.target = h;
return m;
}
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
public static Message obtain(Handler h, int what) {
Message m = obtain();
m.target = h;
m.what = what;
return m;
}
public static Message obtain(Handler h, int what, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.obj = obj;
return m;
}
public static Message obtain(Handler h, int what, int arg1, int arg2) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
return m;
}
public static Message obtain(Handler h, int what,
int arg1, int arg2, Object obj) {
Message m = obtain();
m.target = h;
m.what = what;
m.arg1 = arg1;
m.arg2 = arg2;
m.obj = obj;
return m;
}
public static void updateCheckRecycle(int targetSdkVersion) {
if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
gCheckRecycle = false;
}
}
public void recycle() {
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
recycleUnchecked();
}
@UnsupportedAppUsage
void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = UID_NONE;
workSourceUid = UID_NONE;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
public void copyFrom(Message o) {
this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
this.what = o.what;
this.arg1 = o.arg1;
this.arg2 = o.arg2;
this.obj = o.obj;
this.replyTo = o.replyTo;
this.sendingUid = o.sendingUid;
this.workSourceUid = o.workSourceUid;
if (o.data != null) {
this.data = (Bundle) o.data.clone();
} else {
this.data = null;
}
}
public long getWhen() {
return when;
}
public void setTarget(Handler target) {
this.target = target;
}
public Handler getTarget() {
return target;
}
public Runnable getCallback() {
return callback;
}
@UnsupportedAppUsage
public Message setCallback(Runnable r) {
callback = r;
return this;
}
public Bundle getData() {
if (data == null) {
data = new Bundle();
}
return data;
}
public Bundle peekData() {
return data;
}
public void setData(Bundle data) {
this.data = data;
}
public Message setWhat(int what) {
this.what = what;
return this;
}
public void sendToTarget() {
target.sendMessage(this);
}
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}
boolean isInUse() {
return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
@UnsupportedAppUsage
void markInUse() {
flags |= FLAG_IN_USE;
}
public Message() {
}
@Override
public String toString() {
return toString(SystemClock.uptimeMillis());
}
@UnsupportedAppUsage
String toString(long now) {
StringBuilder b = new StringBuilder();
b.append("{ when=");
TimeUtils.formatDuration(when - now, b);
if (target != null) {
if (callback != null) {
b.append(" callback=");
b.append(callback.getClass().getName());
} else {
b.append(" what=");
b.append(what);
}
if (arg1 != 0) {
b.append(" arg1=");
b.append(arg1);
}
if (arg2 != 0) {
b.append(" arg2=");
b.append(arg2);
}
if (obj != null) {
b.append(" obj=");
b.append(obj);
}
b.append(" target=");
b.append(target.getClass().getName());
} else {
b.append(" barrier=");
b.append(arg1);
}
b.append(" }");
return b.toString();
}
}
参考资料
|