Android開(kāi)發(fā)App啟動(dòng)流程與消息機(jī)制詳解
引言
相信很多人對(duì)這個(gè)問(wèn)題不陌生,但是大家回答的都比較簡(jiǎn)單,如談到app啟動(dòng)流程有人就會(huì)是app的生命周期去了,談到消息機(jī)制有人就會(huì)說(shuō)looper循環(huán)消息進(jìn)行分發(fā),如果是面試可能面試官不會(huì)滿(mǎn)意,今天我們搞一篇完善的源碼解析來(lái)進(jìn)行闡述上面的問(wèn)題
1、第一步了解 ThreadLocal
什么是ThreadLocal呢,專(zhuān)業(yè)的來(lái)講,ThreadLocal 是一個(gè)線程內(nèi)部的數(shù)據(jù)存儲(chǔ)類(lèi),通過(guò)它可以在指定的線程中存儲(chǔ)數(shù)據(jù),數(shù)據(jù)存儲(chǔ)以后,只有再指定線程中可以獲取到存儲(chǔ)的數(shù)據(jù),對(duì)于其他線程來(lái)說(shuō)則無(wú)法獲取到數(shù)據(jù),是共享數(shù)據(jù)變量存儲(chǔ),通俗的來(lái)講,就是保存每個(gè)線程的數(shù)據(jù),肯定大家都沒(méi)聽(tīng)懂,沒(méi)事的,接下來(lái)我們通過(guò)代碼來(lái)解釋ThreadLocal的具體作用
首先看一個(gè)例子
public static void main(String[] args) throws InterruptedException { ThreadLocal<String> threadLocal = new ThreadLocal<String>(){ @Override protected String initialValue() { return "data--1"; } }; System.out.println("1主線程--> "+threadLocal.get()); Thread t1 = new Thread(()->{ }); t1.start(); threadLocal.set("data--2"); System.out.println("2主線程--> "+threadLocal.get()); Thread t2 = new Thread(()->{ System.out.println("線程2---> "+threadLocal.get()); }); t2.start(); Thread t3 = new Thread(()->{ threadLocal.set("data-->3"); System.out.println("線程3---> "+threadLocal.get()); }); t3.start(); System.out.println("3主線程--> "+threadLocal.get()); Thread.sleep(1000); }
打印結(jié)果
1主線程--> data--1
2主線程--> data--2
線程2---> data--1
3主線程--> data--2
線程3---> data-->3
從上面的例子我們可以看到,ThreadLocal保存一個(gè)String這個(gè)變量,這個(gè)變量初始化會(huì)有一個(gè)值,在接下來(lái)的線程種,每個(gè)線程都會(huì)擁有一個(gè)初始值,這個(gè)初始值在主線程中,一旦這個(gè)初始值發(fā)生改變,如果是在主線程種改變?nèi)邕M(jìn)行set,則后面的子線程獲取的都是這個(gè)改變后的值,但是如果子線程種也改變了這個(gè)值,則只在當(dāng)前子線程種有此值 沒(méi)其子線程還是獲取的主線程種那個(gè)值,我們來(lái)簡(jiǎn)單畫(huà)個(gè)圖給大家
ThreadLocal種的三個(gè)重要方法
//默認(rèn)情況下initialValue是返回為空的 protected T initialValue() { return null; } //在get的時(shí)候如果沒(méi)有調(diào)用set方法 getMap(t);是返回為空的,所以,返回的是setInitialValue(),這些方法請(qǐng)看后面的介紹,而setInitialValue方法返回的其實(shí)就是初始值 public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); } //這個(gè)方法在在調(diào)用的時(shí)候?qū)嶋H上getMap(t)是為空的,所以就會(huì)調(diào)用createMap,這個(gè)方法會(huì)把當(dāng)前的線程作為值,保證getMap再調(diào)用就不會(huì)為空 public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
簡(jiǎn)單來(lái)講,就是自己有的,用自己的,自己沒(méi)有就用初始化的,初始化改變了,后面的也改變,但是自己設(shè)置的,還是用自己的,就這么簡(jiǎn)單,好了,接下來(lái)進(jìn)行下一步
2、App的啟動(dòng)流程
我們看下Android的源碼
//這是main函數(shù)的入口 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"); }
我們重點(diǎn)看下 Looper.prepareMainLooper();
這個(gè)方法
/** * Initialize the current thread as a looper, marking it as an * application's main looper. The main looper for your application * is created by the Android environment, so you should never need * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
我們?cè)冱c(diǎn)擊去看,myLooper
/** * 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(); }
很驚訝的看見(jiàn)了 sThreadLocal
,這里是調(diào)用get方法
// sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
這里我們可以看到ThreadLocal保存的是Looper這個(gè)對(duì)象
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)); }
這里調(diào)用了set方法,創(chuàng)建了一個(gè)全局唯一的Looper
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
創(chuàng)建了一個(gè)全局唯一的主線程消息隊(duì)列
3、Activity中創(chuàng)建Handler
- 創(chuàng)建一個(gè)handler,重寫(xiě)handleMessage方法
private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); } };
- 發(fā)送消息
Message message = new Message(); handler.sendMessage(message); //點(diǎn)擊去 public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } //點(diǎn)擊去 public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
//繼續(xù)看sendMessageAtTime 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); }
//我們看到了enqueueMessage,我們看這個(gè)queue在哪里獲取的 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; }
到這里我們明白了,也就是再app啟動(dòng)后那個(gè)唯一的Queue,好了我們整理下Handler的消息機(jī)制
hander發(fā)送消息的時(shí)候,調(diào)用sendMessage方法,handler種會(huì)講消息放到全局的消息隊(duì)列中queue.enqueueMessage(msg, uptimeMillis)
接著就會(huì)在MessageQueue種賦值全局消息
消息處理
消息消費(fèi)
以上就是Android開(kāi)發(fā)App啟動(dòng)流程與消息機(jī)制詳解的詳細(xì)內(nèi)容,更多關(guān)于Android App啟動(dòng)流程消息機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android自定義view實(shí)現(xiàn)列表內(nèi)左滑刪除Item
這篇文章主要介紹了微信小程序列表中item左滑刪除功能,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02Android側(cè)滑導(dǎo)航欄的實(shí)例代碼
這篇文章主要介紹了Android側(cè)滑導(dǎo)航欄的實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下2017-01-01基于Android MarginLeft與MarginStart的區(qū)別(詳解)
下面小編就為大家分享一篇基于Android MarginLeft與MarginStart的區(qū)別(詳解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12Android防止點(diǎn)擊過(guò)快造成多次響應(yīng)事件的解決方法
btn點(diǎn)擊用戶(hù)可能只點(diǎn)擊了一次但是后臺(tái)響應(yīng)了多次,像一些表單的提交出現(xiàn)這種問(wèn)題比較棘手,本篇文章主要介紹Android防止點(diǎn)擊過(guò)快造成多次響應(yīng)事件的解決方法,有興趣的可以了解一下。2016-12-12Android Shader應(yīng)用開(kāi)發(fā)之雷達(dá)掃描效果
這篇文章主要為大家詳細(xì)介紹了Android Shader應(yīng)用開(kāi)發(fā)之雷達(dá)掃描效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-07-07Flutter 自定義Drawer 滑出位置的大小實(shí)例代碼詳解
這篇文章主要介紹了Flutter 自定義Drawer 滑出位置的大小,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04