1、Android 線程間通信有哪幾種方式(重要)
共享內存(變量);文件,數據庫、Handler;Java 里的 wait(),notify(),notifyAll()
2、請介紹下 AsyncTask的內部實現,適用的場景是
AsyncTask 內部也是 Handler 機制來完成的,只不過 Android 提供了執行框架來提供線程池來
執行相應地任務,因為線程池的大小問題,所以 AsyncTask 只應該用來執行耗時時間較短的任務,
比如 HTTP 請求,大規模的下載和數據庫的更改不適用于 AsyncTask,因為會導致線程池堵塞,沒有
3、Activity生命周期
-啟動Activity: onCreate()—>onStart()—>onResume(),Activity進入運行狀態。
-Activity退居后臺: 當前Activity轉到新的Activity界面或按Home鍵回到主屏: onPause()—>onStop(),進入停滯狀態。
-Activity返回前臺: onRestart()—>onStart()—>onResume(),再次回到運行狀態。
-Activity退居后臺,且系統內存不足, 系統會殺死這個后臺狀態的Activity(此時這個Activity引用仍然處在任務棧中,只是這個時候引用指向的對象已經為null),若再次回到這個Activity,則會走onCreate()–>onStart()—>onResume()(將重新走一次Activity的初始化生命周期)
-鎖屏:onPause()->onStop()
-解鎖:onStart()->onResume()
4、通過Acitivty的xml標簽來改變任務棧的默認行為
-使用android:launchMode="standard|singleInstance|singleTask|singleTop"來控制Acivity任務棧。
任務棧是一種后進先出的結構。位于棧頂的Activity處于焦點狀態,當按下back按鈕的時候,棧內的Activity會一個一個的出棧,并且調用其onDestory()方法。如果棧內沒有Activity,那么系統就會回收這個棧,每個APP默認只有一個棧,以APP的包名來命名.
-standard : 標準模式,每次啟動Activity都會創建一個新的Activity實例,并且將其壓入任務棧棧頂,而不管這個Activity是否已經存在。Activity的啟動三回調(onCreate()->onStart()->onResume())都會執行。standard : 標準模式,每次啟動Activity都會創建一個新的Activity實例,并且將其壓入任務棧棧頂,而不管這個Activity是否已經存在。Activity的啟動三回調(onCreate()->onStart()->onResume())都會執行。
-singleTop : 棧頂復用模式.這種模式下,如果新Activity已經位于任務棧的棧頂,那么此Activity不會被重新創建,所以它的啟動三回調就不會執行,同時Activity的onNewIntent()方法會被回調.如果Activity已經存在但是不在棧頂,那么作用與standard模式一樣.
-singleTask: 棧內復用模式.創建這樣的Activity的時候,系統會先確認它所需任務棧已經創建,否則先創建任務棧.然后放入Activity,如果棧中已經有一個Activity實例,那么這個Activity就會被調到棧頂,onNewIntent(),并且singleTask會清理在當前Activity上面的所有Activity.(clear top)
-singleInstance : 加強版的singleTask模式,這種模式的Activity只能單獨位于一個任務棧內,由于棧內復用的特性,后續請求均不會創建新的Activity,除非這個獨特的任務棧被系統銷毀了
Activity的堆棧管理以ActivityRecord為單位,所有的ActivityRecord都放在一個List里面.可以認為一個ActivityRecord就是一個Activity棧
5、Fragment的生命周期和activity如何的一個關系。
6、為什么在Service中創建子線程而不是Activity中
-Activity很難對Thread進行控制,當Activity被銷毀之后,就沒有任何其它的辦法可以再重新獲取到之前創建的子線程的實例
-在一個Activity中創建的子線程,另一個Activity無法對其進行操作
-Service就不同了,所有的Activity都可以與Service進行關聯,然后可以很方便地操作其中的方法,即使Activity被銷毀了,之后只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的實例
7、Service的兩種啟動方法,有什么區別
-.在Context中通過public boolean bindService(Intent service,ServiceConnection conn,int flags) 方法來進行Service與Context的關聯并啟動,并且Service的生命周期依附于Context(不求同時同分同秒生!但求同時同分同秒屎!!)。
-通過public ComponentName startService(Intent service)方法去啟動一個Service,此時Service的生命周期與啟動它的Context無關。
-.要注意的是,whatever,都需要在xml里注冊你的Service,就像這樣:
android:name=".packnameName.youServiceName"
android:enabled="true" />
8、廣播(Broadcast Receiver)的兩種動態注冊和靜態注冊有什么區別。
-靜態注冊:在AndroidManifest.xml文件中進行注冊,當App退出后,Receiver仍然可以接收到廣播并且進行相應的處理
-動態注冊:在代碼中動態注冊,當App退出后,也就沒辦法再接受廣播了
9、ContentProvider使用方法
http://blog.csdn.net/juetion/article/details/17481039
10、目前能否保證service不被殺死
-Service設置成START_STICKY
-提升service優先級
-在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時適用于廣播。
-【結論】目前看來,priority這個屬性貌似只適用于broadcast,對于Service來說可能無效
-提升service進程優先級
Android中的進程是托管的,當系統進程空間緊張的時候,會依照優先級自動進行進程的回收
當service運行在低內存的環境時,將會kill掉一些存在的進程。因此進程的優先級將會很重要,可以在startForeground()使用startForeground()將service放到前臺狀態。這樣在低內存時被kill的幾率會低一些。
【結論】如果在極度極度低內存的壓力下,該service還是會被kill掉,并且不一定會restart()
-onDestroy方法里重啟service
11、Android 內存泄漏總結
Java 內存分配策略
java 程序運行時的內存分配策略有三種,分別是靜態分配,棧式分配,和堆式分配,對應的,三種存儲策略使用的內存空間主要分別是靜態存儲區(也稱方法區)、棧區和堆區。
-靜態存儲區(方法區):主要存放靜態數據、全局 static 數據和常量。這塊內存在程序編譯時就已經分配好,并且在程序整個運行期間都存在。
-棧區 :當方法被執行時,方法體內的局部變量(其中包括基礎數據類型、對象的引用)都在棧上創建,并在方法執行結束時這些局部變量所持有的內存將會自動被釋放。因為棧內存分配運算內置于處理器的指令集中,效率很高,但是分配的內存容量有限。
-堆區 : 又稱動態內存分配,通常就是指在程序運行時直接 new 出來的內存,也就是對象的實例。這部分內存在不使用時將會由 Java 垃圾回收器來負責回收。
棧與堆的區別:
在方法體內定義的(局部變量)一些基本類型的變量和對象的引用變量都是在方法的棧內存中分配的。當在一段方法塊中定義一個變量時,Java 就會在棧中為該變量分配內存空間,當超過該變量的作用域后,該變量也就無效了,分配給它的內存空間也將被釋放掉,該內存空間可以被重新使用。
堆內存用來存放所有由 new 創建的對象(包括該對象其中的所有成員變量)和數組。在堆中分配的內存,將由 Java 垃圾回收器來自動管理。在堆中產生了一個數組或者對象后,還可以在棧中定義一個特殊的變量,這個變量的取值等于數組或者對象在堆內存中的首地址,這個特殊的變量就是我們上面說的引用變量。我們可以通過這個引用變量來訪問堆中的對象或者數組。
局部變量的基本數據類型和引用存儲于棧中,引用的對象實體存儲于堆中。—— 因為它們屬于方法中的變量,生命周期隨方法而結束。
成員變量全部存儲與堆中(包括基本數據類型,引用和引用的對象實體)—— 因為它們屬于類,類對象終究是要被new出來使用的。
12線程通信基礎流程分析
Looper(先分析這個是因為能夠引出四者的關系) 在Looper中,維持一個Thread對象以及MessageQueue,通過Looper的構造函數我們可以知道:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//傳入的參數代表這個Queue是否能夠被退出
mThread = Thread.currentThread();
}
Looper在構造函數里干了兩件事情:
-將線程對象指向了創建Looper的線程
-創建了一個新的MessageQueue
分析完構造函數之后,接下來我們主要分析兩個方法:
-looper.loop()
-looper.prepare()
looper.loop()(在當前線程啟動一個Message loop機制,此段代碼將直接分析出Looper、Handler、Message、MessageQueue的關系)
public static void loop() {
final Looper me = myLooper();//獲得當前線程綁定的Looper
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//獲得與Looper綁定的MessageQueue
// 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();
//進入死循環,不斷地去取對象,分發對象到Handler中消費
for (;;) {
Message msg = queue.next(); // 不斷的取下一個Message對象,在這里可能會造成堵塞。
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);
}
//在這里,開始分發Message了
//至于這個target是神馬?什么時候被賦值的?
//我們一會分析Handler的時候就會講到
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);
}
//當分發完Message之后,當然要標記將該Message標記為 正在使用 啦
msg.recycleUnchecked();
}
}
分析了上面的源代碼,我們可以意識到,最重要的方法是:
-queue.next()
-msg.target.dispatchMessage(msg)
-msg.recycleUnchecked()
其實Looper中最重要的部分都是由Message、MessageQueue組成的有木有!這段最重要的代碼中涉及到了四個對象,他們與彼此的關系如下:
-MessageQueue:裝食物的容器
-Message:被裝的食物
-Handler(msg.target實際上就是Handler):食物的消費者
-Looper:負責分發食物的人
looper.prepare()(在當前線程關聯一個Looper對象) ####\
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//在當前線程綁定一個Looper
sThreadLocal.set(new Looper(quitAllowed));
}
以上代碼只做了兩件事情:
-判斷當前線程有木有Looper,如果有則拋出異常(在這里我們就可以知道,Android規定一個線程只能夠擁有一個與自己關聯的Looper)。
-如果沒有的話,那么就設置一個新的Looper到當前線程。
Handler 由于我們使用Handler的通常性的第一步是:
Handler handler = new Handler(){
//你們有沒有很好奇這個方法是在哪里被回調的?
//我也是!所以接下來會分析到喲!
@Override
public void handleMessage(Message msg) {
//Handler your Message
}
};
/空參數的構造方法與之對應,這里只給出主要的代碼,具體大家可以到源碼中查看
public Handler(Callback callback, boolean async) {
//打印內存泄露提醒log
....
//獲取與創建Handler線程綁定的Looper
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
//獲取與Looper綁定的MessageQueue
//因為一個Looper就只有一個MessageQueue,也就是與當前線程綁定的MessageQueue
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
帶上問題:
-Looper.loop()死循環中的msg.target是什么時候被賦值的?
-handler.handleMessage(msg)在什么時候被回調的?
A1:Looper.loop()死循環中的msg.target是什么時候被賦值的? 要分析這個問題,很自然的我們想到從發送消息開始,無論是handler.sendMessage(msg)還是handler.sendEmptyMessage(what),我們最終都可以追溯到以下方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//引用Handler中的MessageQueue
//這個MessageQueue就是創建Looper時被創建的MessageQueue
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
//將新來的Message加入到MessageQueue中
return enqueueMessage(queue, msg, uptimeMillis);
}
我們接下來分析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);
}
A2:handler.handleMessage(msg)在什么時候被回調的? 通過以上的分析,我們很明確的知道Message中的target是在什么時候被賦值的了,我們先來分析在Looper.loop()中出現過的過的dispatchMessage(msg)方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
//看到這個大寫加粗的方法調用沒!
handleMessage(msg);
}
}
加上以上分析,我們將之前分析結果串起來,就可以知道了某些東西: Looper.loop()不斷地獲取MessageQueue中的Message,然后調用與Message綁定的Handler對象的dispatchMessage方法,最后,我們看到了handleMessage就在dispatchMessage方法里被調用的。
通過以上的分析,我們可以很清晰的知道Handler、Looper、Message、MessageQueue這四者的關系以及如何合作的了。
總結: 當我們調用handler.sendMessage(msg)方法發送一個Message時,實際上這個Message是發送到與當前線程綁定的一個MessageQueue中,然后與當前線程綁定的Looper將會不斷的從MessageQueue中取出新的Message,調用msg.target.dispathMessage(msg)方法將消息分發到與Message綁定的handler.handleMessage()方法中。
一個Thread對應多個Handler 一個Thread對應一個Looper和MessageQueue,Handler與Thread共享Looper和MessageQueue。 Message只是消息的載體,將會被發送到與線程綁定的唯一的MessageQueue中,并且被與線程綁定的唯一的Looper分發,被與其自身綁定的Handler消費。
13 HandlerThread面試
一、handlerThread是什么,有哪些特點
handler+thread+looper
-HandlerThread本質上就是一個線程類,繼承thread
-HandlerThread有自己的內部Looper對象,可以進行looper循環;
-通過獲取HandlerThread的looper對象傳遞給Handler對象。可以在handlerMessage執行異步任務
-優點是不會有堵塞,減少對性能的消耗,缺點是不能進行多任務處理,需要等待,處理效率較低。
-與線程池注重并發不同,HandlerThrad是一個串行隊列,它只有一個線程。
14 Webview面試詳解
Webview常見的一些坑
1、webview 安全漏洞,在android api 16及以前沒有正確的使用webView.addJavaScriptIterface方法,遠程攻擊者可以通過使用java 的反射機制利用該漏洞執行java對象的方法
2、webView在內存中使用造成內存泄漏,要先在w外部容器中移除webView再調用webView的destroy
3、jsbridge
4、webViewClient.onPageFinished--->WebViewChromClient.onProgressChanged方法
5、后臺耗電:開啟進程沒有銷毀
5、硬件加速不要開啟
15 AsyncTask面試
1、什么是AyncTask?
它本質上就是一個封裝了線程池和handler的異步框架,執行異步任務,因為內部封裝了handler可以在主線程和任務線程進行切換
2、AysncTask三個參數五個方法?
3、AnsycTask的機制原理
-AysncTask的本質是一個靜態的線程池,AyncTask派生出的子類可以實現不同的異步任務,這些異步任務都是提交到靜態的線程池中執行
-線程池的工作線程執行doinbackBackground(mParams)方法執行異步任務
-當任務狀態改變后,工作線程會向UI線程發送消息,ASyncTask的內部的InternalHandler響應這些消息,并調用相關的回調函數
4、AsyncTask注意事件
16 LocalBroadcastManager詳解
1、localbradcastManager高效的原因是因為內部通過Handler實現,她的sendBroadcast()方法含義并非和我們平時所用的一樣,它其實是通過handler發送一個message實現的。
2、他是通過handler實現廣播發送,那么相比系統廣播通過Binder更高效,同時使用Handler來實現,別的應用也無法像我們的應用發送該廣播。我們的廣播也不會離開我們的應用。
3、LoacalBradcastManager內部協作主要靠兩個Map集合:mReceives和mActions,還有一個List集合mPendingBroadcasts,這個是存儲待接受的廣播對象
17、 Android中進程間通信的幾種方式
broadcast:用于發送和接收廣播!實現信息的發送和接收! 該方式只是接收到一個廣播消息,主要的業務邏輯代碼是在自己的onReceive()方法里面實現的。
那么它的優點是:注冊了這個廣播接收器的應用都能夠收到廣播,范圍廣。缺點是:速度慢點,而且必須在一定時間內把事情處理完(onReceive執行必須在幾秒之內),否則的話系統給出ANR。
aidl:用于不同程序將服務的相互調用!實現了一個程序為另一個程序服務的功能!業務邏輯代碼在遠程的服務中,通過aidl來調用。
優點是:速度快(系統底層直接是共享內存),性能穩,效率高,一般進程間通信就用它。
Content Provider:用于將程序的數據庫人為地暴露出來!實現一個程序可以對另個程序的數據庫進行相對用的操作!主要是用來將自己的數據庫共享出來供別的程序調用或者修改。
因為只是把自己的數據庫暴露出去,其他程序都可以來獲取數據,數據本身不是實時的,不像前兩者,只是啟個數據供應作用。一般是某個成熟的應用來暴露自己的數據用的。 你要是為了進程間通信,還是別用這個了,這個又不是實時數據。