Android源碼解析–享元設(shè)計(jì)模式,handler消息傳遞機(jī)制(基于Android API 33 SDK分析)
一. 定義
使用共享對(duì)象可有效地支持大量的細(xì)粒度的對(duì)象
核心:對(duì)象復(fù)用。
1.1 享元模式Demo
火車票購票Demo
//火車票
public class Ticket {
private String from;
private String to;
public Ticket(String from, String to) {
this.from = from;
this.to = to;
}
public int getPrice() {
return new Random().nextInt(100) + 20;
}
}
緩存對(duì)象在一個(gè)Map中。下面我們還會(huì)分析
//火車票查詢工廠
public class TicketFactory {
public static Map<String, Ticket> sTicketMap = new HashMap<>();
public static Ticket getTicket(String from, String to) {
String key = from + "-" + to + "";
Ticket ticket = sTicketMap.get(key);
if (ticket != null) {
return ticket;
}
ticket = new Ticket(from, to);
sTicketMap.put(key, ticket);
return ticket;
}
}
二. Android中源碼實(shí)例分析Message
用法
val obtain = Message.obtain()
跟進(jìn)去
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
//防止多線程并發(fā)
synchronized (sPoolSync) {
//從線程池取對(duì)象
if (sPool != null) {
//鏈表取出每個(gè)Message對(duì)象
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
這就是最明顯的一個(gè)享元設(shè)計(jì)模式。
三. Message的關(guān)聯(lián)者Handler
Android 開發(fā)一個(gè)知識(shí)點(diǎn):UI 不能夠在子線程中更新。
class DebugActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
private var handler: Handler = Handler(Looper.getMainLooper())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun doSomething(){
thread {
//耗時(shí)操作,得到結(jié)果,不能在這個(gè)線程更新 UI
// Handler 將結(jié)果傳遞到主線程中,更新UI
handler.post {
//更新UI
}
}
}
}
我們跟進(jìn)post函數(shù)
public final boolean post(@NonNull Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
Handler 傳遞了一個(gè) Runnable給UI線程,裝到一個(gè) Message 對(duì)象中。
跟進(jìn)sendMessageDelayed函數(shù)
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
//當(dāng)前 Handler 所在的消息隊(duì)列
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//將消息添加到消息隊(duì)列中
return enqueueMessage(queue, msg, uptimeMillis);
}
sendMessageDelayed 函數(shù)調(diào)用了 sendMessageAtTime函數(shù),不手動(dòng)傳遞 Looper 那么 Handler 持有的 Looper 就是當(dāng)前線程的 Looper,也就是說在哪個(gè)線程創(chuàng)建的 Handler,就是哪個(gè)線程的 Looper。
在 getPostMessage 中的 Message 對(duì)象是Message.obtain()函數(shù)
Message m = Message.obtain();
分析下這段代碼,
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
public static Message obtain() {
//防止多線程并發(fā)
synchronized (sPoolSync) {
//從線程池取對(duì)象
if (sPool != null) {
//鏈表取出每個(gè)Message對(duì)象
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
Message消息池沒有使用 map 這樣的容器,使用的是鏈表。
在這里插入圖片描述
如何放到這個(gè)消息池里面呢?
我們看
Message 對(duì)象回收到消息池中
public void recycle() {
//該消息還在使用
if (isInUse()) {
if (gCheckRecycle) {
throw new IllegalStateException("This message cannot be recycled because it "
+ "is still in use.");
}
return;
}
//消息添加到消息池中
recycleUnchecked();
}
跟進(jìn)recycleUnchecked()
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
//清空消息狀態(tài)
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++;
}
}
}
這里用鏈表當(dāng)作了一個(gè)緩存池,存消息對(duì)象。每生成一條消息就會(huì)加入到鏈表在。
四. Android 的消息機(jī)制
Android應(yīng)用程序的入口實(shí)際上是ActivityThread,跟進(jìn)去
public static void main(String[] args) {
......
//創(chuàng)建Looper,UI線程的消息隊(duì)列
Looper.prepareMainLooper();
......
//啟動(dòng)應(yīng)用程序
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
//循環(huán)消息
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper 從消息隊(duì)列中取消息,處理消息。Handler不斷地往消息隊(duì)列中添加消息,消息不斷地被處理。
那么Handler是如何關(guān)聯(lián)消息隊(duì)列
Handler 的構(gòu)造函數(shù)
public Handler(@Nullable Callback callback, boolean async) {
......
mLooper = Looper.myLooper();//獲取 Looper
......
mQueue = mLooper.mQueue;//獲取消息隊(duì)列
mCallback = callback;
mAsynchronous = async;
}
Handler 通過myLooper()來獲取 Looper 對(duì)象,
跟進(jìn)myLooper()
public static @Nullable Looper myLooper() {
//myLooper通過sThreadLocal.get()獲取
return sThreadLocal.get();
}
Looper對(duì)象存儲(chǔ)在sThreadLocal中的,
@Deprecated
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
//prepare()方法中創(chuàng)建了一個(gè) Looper 對(duì)象
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//將該對(duì)象設(shè)置給了sThreadLocal,這樣線程和隊(duì)列就關(guān)聯(lián)上了
sThreadLocal.set(new Looper(quitAllowed));
}
Handler和線程、線程的消息隊(duì)列關(guān)聯(lián),Handler 發(fā)送的消息就會(huì)被執(zhí)行在這個(gè)線程上。
調(diào)用 Looper 的 loop 函數(shù),不斷地從消息隊(duì)列中取出、處理消息
public static void loop() {
......
//死循環(huán)
for (;;) {
//取消息
if (!loopOnce(me, ident, thresholdOverride)) {
return;
}
}
}
跟進(jìn)loopOnce
private static boolean loopOnce(final Looper me,
final long ident, final int thresholdOverride) {
//獲取消息 (might block )
Message msg = me.mQueue.next(); // might block
......
try {
//處理消息
msg.target.dispatchMessage(msg);
if (observer != null) {
observer.messageDispatched(token, msg);
}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
}
......
//回收消息,也就是我們分析享元模式時(shí)提到的將 Message 添加到消息池的操作
msg.recycleUnchecked();
return true;
}
看看next()核心代碼
Message next() {
......
//native層的事件
nativePollOnce(ptr, nextPollTimeoutMillis);
......
if (msg != null) {
//消息延遲,
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
......
}
}
next 函數(shù)從消息隊(duì)列中依次取出消息,如果這個(gè)消息到了執(zhí)行時(shí)間,那么就將這條消息返回給 Looper,隊(duì)列鏈表的指針后移。
五. 子線程中創(chuàng)建Handler拋出異常
class DebugActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
thread {
val handler = Handler()
}
}
}
分析:Looper 對(duì)象是 ThreadLocal,每個(gè)線程都有自己的Looper,要在子線程中創(chuàng)建 Handler 對(duì)象時(shí),如果 Looper 為空,那么就會(huì)拋出異常。跟進(jìn)Handler的構(gòu)造方法看看文章來源:http://www.zghlxwxcb.cn/news/detail-674075.html
public Handler(@Nullable Callback callback, boolean async) {
......
//獲取looper
mLooper = Looper.myLooper();
if (mLooper == null) {
//拋出異常
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
......
}
mLooper 對(duì)象為空,拋出異常。該線程中的Looper 對(duì)象還沒有創(chuàng)建,在子線程中沒有手動(dòng)調(diào)用 Looper.prepare之前該線程的 Looper為空,解決方法就是在構(gòu)造 Handler 之前為當(dāng)前線程設(shè)置 Looper 對(duì)象。文章來源地址http://www.zghlxwxcb.cn/news/detail-674075.html
class DebugActivity : AppCompatActivity() {
private val TAG = javaClass.simpleName
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
thread {
//綁定到 ThreadLocal中
Looper.prepare()
val handler = Handler()
//啟動(dòng)消息循環(huán)
Looper.loop()
}
}
}
這樣子線程的Looper對(duì)象就不會(huì)為null了,有了自己的消息隊(duì)列。
到了這里,關(guān)于Android源碼解析--享元設(shè)計(jì)模式,handler消息傳遞機(jī)制(基于Android API 33 SDK分析)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!