1虏束、Android 線程間通信有哪幾種方式(重要)
共享內存(變量);文件桶癣,數(shù)據(jù)庫、Handler;Java 里的 wait()求橄,notify()今野,notifyAll()
2、請介紹下 AsyncTask的內部實現(xiàn)罐农,適用的場景是
AsyncTask 內部也是 Handler 機制來完成的条霜,只不過 Android 提供了執(zhí)行框架來提供線程池來
執(zhí)行相應地任務,因為線程池的大小問題涵亏,所以 AsyncTask 只應該用來執(zhí)行耗時時間較短的任務宰睡,
比如 HTTP 請求,大規(guī)模的下載和數(shù)據(jù)庫的更改不適用于 AsyncTask气筋,因為會導致線程池堵塞拆内,沒有
3、Activity生命周期
-啟動Activity: onCreate()—>onStart()—>onResume()宠默,Activity進入運行狀態(tài)麸恍。
-Activity退居后臺: 當前Activity轉到新的Activity界面或按Home鍵回到主屏: onPause()—>onStop(),進入停滯狀態(tài)搀矫。
-Activity返回前臺: onRestart()—>onStart()—>onResume()抹沪,再次回到運行狀態(tài)刻肄。
-Activity退居后臺,且系統(tǒng)內存不足采够, 系統(tǒng)會殺死這個后臺狀態(tài)的Activity(此時這個Activity引用仍然處在任務棧中肄方,只是這個時候引用指向的對象已經為null)冰垄,若再次回到這個Activity,則會走onCreate()–>onStart()—>onResume()(將重新走一次Activity的初始化生命周期)
-鎖屏:onPause()->onStop()
-解鎖:onStart()->onResume()
4蹬癌、通過Acitivty的xml標簽來改變任務棧的默認行為
-使用android:launchMode="standard|singleInstance|singleTask|singleTop"來控制Acivity任務棧。
任務棧是一種后進先出的結構虹茶。位于棧頂?shù)腁ctivity處于焦點狀態(tài),當按下back按鈕的時候,棧內的Activity會一個一個的出棧,并且調用其onDestory()方法逝薪。如果棧內沒有Activity,那么系統(tǒng)就會回收這個棧,每個APP默認只有一個棧,以APP的包名來命名.
-standard : 標準模式,每次啟動Activity都會創(chuàng)建一個新的Activity實例,并且將其壓入任務棧棧頂,而不管這個Activity是否已經存在。Activity的啟動三回調(onCreate()->onStart()->onResume())都會執(zhí)行蝴罪。standard : 標準模式,每次啟動Activity都會創(chuàng)建一個新的Activity實例,并且將其壓入任務棧棧頂,而不管這個Activity是否已經存在董济。Activity的啟動三回調(onCreate()->onStart()->onResume())都會執(zhí)行。
-singleTop : 棧頂復用模式.這種模式下,如果新Activity已經位于任務棧的棧頂,那么此Activity不會被重新創(chuàng)建,所以它的啟動三回調就不會執(zhí)行,同時Activity的onNewIntent()方法會被回調.如果Activity已經存在但是不在棧頂,那么作用與standard模式一樣.
-singleTask: 棧內復用模式.創(chuàng)建這樣的Activity的時候,系統(tǒng)會先確認它所需任務棧已經創(chuàng)建,否則先創(chuàng)建任務棧.然后放入Activity,如果棧中已經有一個Activity實例,那么這個Activity就會被調到棧頂,onNewIntent(),并且singleTask會清理在當前Activity上面的所有Activity.(clear top)
-singleInstance : 加強版的singleTask模式,這種模式的Activity只能單獨位于一個任務棧內,由于棧內復用的特性,后續(xù)請求均不會創(chuàng)建新的Activity,除非這個獨特的任務棧被系統(tǒng)銷毀了
Activity的堆棧管理以ActivityRecord為單位,所有的ActivityRecord都放在一個List里面.可以認為一個ActivityRecord就是一個Activity棧
5要门、Fragment的生命周期和activity如何的一個關系虏肾。
6、為什么在Service中創(chuàng)建子線程而不是Activity中
-Activity很難對Thread進行控制欢搜,當Activity被銷毀之后封豪,就沒有任何其它的辦法可以再重新獲取到之前創(chuàng)建的子線程的實例
-在一個Activity中創(chuàng)建的子線程,另一個Activity無法對其進行操作
-Service就不同了炒瘟,所有的Activity都可以與Service進行關聯(lián)吹埠,然后可以很方便地操作其中的方法,即使Activity被銷毀了疮装,之后只要重新與Service建立關聯(lián)缘琅,就又能夠獲取到原有的Service中Binder的實例
7、Service的兩種啟動方法廓推,有什么區(qū)別
-.在Context中通過public boolean bindService(Intent service,ServiceConnection conn,int flags) 方法來進行Service與Context的關聯(lián)并啟動刷袍,并且Service的生命周期依附于Context(不求同時同分同秒生!但求同時同分同秒屎7埂做个!)。
-通過public ComponentName startService(Intent service)方法去啟動一個Service滚局,此時Service的生命周期與啟動它的Context無關居暖。
-.要注意的是,whatever藤肢,都需要在xml里注冊你的Service太闺,就像這樣:
android:name=".packnameName.youServiceName"
android:enabled="true" />
8、廣播(Broadcast Receiver)的兩種動態(tài)注冊和靜態(tài)注冊有什么區(qū)別嘁圈。
-靜態(tài)注冊:在AndroidManifest.xml文件中進行注冊省骂,當App退出后蟀淮,Receiver仍然可以接收到廣播并且進行相應的處理
-動態(tài)注冊:在代碼中動態(tài)注冊,當App退出后钞澳,也就沒辦法再接受廣播了
9怠惶、ContentProvider使用方法
http://blog.csdn.net/juetion/article/details/17481039
10、目前能否保證service不被殺死
-Service設置成START_STICKY
-提升service優(yōu)先級
-在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設置最高優(yōu)先級轧粟,1000是最高值策治,如果數(shù)字越小則優(yōu)先級越低,同時適用于廣播兰吟。
-【結論】目前看來通惫,priority這個屬性貌似只適用于broadcast,對于Service來說可能無效
-提升service進程優(yōu)先級
Android中的進程是托管的混蔼,當系統(tǒng)進程空間緊張的時候履腋,會依照優(yōu)先級自動進行進程的回收
當service運行在低內存的環(huán)境時,將會kill掉一些存在的進程惭嚣。因此進程的優(yōu)先級將會很重要遵湖,可以在startForeground()使用startForeground()將service放到前臺狀態(tài)。這樣在低內存時被kill的幾率會低一些晚吞。
【結論】如果在極度極度低內存的壓力下延旧,該service還是會被kill掉,并且不一定會restart()
-onDestroy方法里重啟service
11载矿、Android 內存泄漏總結
Java 內存分配策略
java 程序運行時的內存分配策略有三種,分別是靜態(tài)分配,棧式分配,和堆式分配垄潮,對應的,三種存儲策略使用的內存空間主要分別是靜態(tài)存儲區(qū)(也稱方法區(qū))闷盔、棧區(qū)和堆區(qū)弯洗。
-靜態(tài)存儲區(qū)(方法區(qū)):主要存放靜態(tài)數(shù)據(jù)、全局 static 數(shù)據(jù)和常量逢勾。這塊內存在程序編譯時就已經分配好牡整,并且在程序整個運行期間都存在。
-棧區(qū) :當方法被執(zhí)行時溺拱,方法體內的局部變量(其中包括基礎數(shù)據(jù)類型逃贝、對象的引用)都在棧上創(chuàng)建,并在方法執(zhí)行結束時這些局部變量所持有的內存將會自動被釋放迫摔。因為棧內存分配運算內置于處理器的指令集中沐扳,效率很高,但是分配的內存容量有限句占。
-堆區(qū) : 又稱動態(tài)內存分配沪摄,通常就是指在程序運行時直接 new 出來的內存,也就是對象的實例。這部分內存在不使用時將會由 Java 垃圾回收器來負責回收杨拐。
棧與堆的區(qū)別:
在方法體內定義的(局部變量)一些基本類型的變量和對象的引用變量都是在方法的棧內存中分配的祈餐。當在一段方法塊中定義一個變量時,Java 就會在棧中為該變量分配內存空間哄陶,當超過該變量的作用域后帆阳,該變量也就無效了,分配給它的內存空間也將被釋放掉屋吨,該內存空間可以被重新使用蜒谤。
堆內存用來存放所有由 new 創(chuàng)建的對象(包括該對象其中的所有成員變量)和數(shù)組。在堆中分配的內存离赫,將由 Java 垃圾回收器來自動管理敲长。在堆中產生了一個數(shù)組或者對象后禾乘,還可以在棧中定義一個特殊的變量,這個變量的取值等于數(shù)組或者對象在堆內存中的首地址而钞,這個特殊的變量就是我們上面說的引用變量台妆。我們可以通過這個引用變量來訪問堆中的對象或者數(shù)組翎猛。
局部變量的基本數(shù)據(jù)類型和引用存儲于棧中,引用的對象實體存儲于堆中接剩∏欣澹—— 因為它們屬于方法中的變量,生命周期隨方法而結束懊缺。
成員變量全部存儲與堆中(包括基本數(shù)據(jù)類型疫稿,引用和引用的對象實體)—— 因為它們屬于類,類對象終究是要被new出來使用的鹃两。
12線程通信基礎流程分析
Looper(先分析這個是因為能夠引出四者的關系) 在Looper中遗座,維持一個Thread對象以及MessageQueue,通過Looper的構造函數(shù)我們可以知道:
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//傳入的參數(shù)代表這個Queue是否能夠被退出
mThread = Thread.currentThread();
}
Looper在構造函數(shù)里干了兩件事情:
-將線程對象指向了創(chuàng)建Looper的線程
-創(chuàng)建了一個新的MessageQueue
分析完構造函數(shù)之后俊扳,接下來我們主要分析兩個方法:
-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();
//進入死循環(huán),不斷地去取對象梯醒,分發(fā)對象到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);
}
//在這里茸习,開始分發(fā)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);
}
//當分發(fā)完Message之后,當然要標記將該Message標記為 正在使用 啦
msg.recycleUnchecked();
}
}
分析了上面的源代碼代箭,我們可以意識到墩划,最重要的方法是:
-queue.next()
-msg.target.dispatchMessage(msg)
-msg.recycleUnchecked()
其實Looper中最重要的部分都是由Message、MessageQueue組成的有木有嗡综!這段最重要的代碼中涉及到了四個對象,他們與彼此的關系如下:
-MessageQueue:裝食物的容器
-Message:被裝的食物
-Handler(msg.target實際上就是Handler):食物的消費者
-Looper:負責分發(fā)食物的人
looper.prepare()(在當前線程關聯(lián)一個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規(guī)定一個線程只能夠擁有一個與自己關聯(lián)的Looper)极景。
-如果沒有的話察净,那么就設置一個新的Looper到當前線程。
Handler 由于我們使用Handler的通常性的第一步是:
Handler handler = new Handler(){
//你們有沒有很好奇這個方法是在哪里被回調的盼樟?
//我也是氢卡!所以接下來會分析到喲!
@Override
public void handleMessage(Message msg) {
//Handler your Message
}
};
/空參數(shù)的構造方法與之對應晨缴,這里只給出主要的代碼译秦,具體大家可以到源碼中查看
public Handler(Callback callback, boolean async) {
//打印內存泄露提醒log
....
//獲取與創(chuàng)建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()死循環(huán)中的msg.target是什么時候被賦值的击碗?
-handler.handleMessage(msg)在什么時候被回調的筑悴?
A1:Looper.loop()死循環(huán)中的msg.target是什么時候被賦值的? 要分析這個問題稍途,很自然的我們想到從發(fā)送消息開始阁吝,無論是handler.sendMessage(msg)還是handler.sendEmptyMessage(what),我們最終都可以追溯到以下方法
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
//引用Handler中的MessageQueue
//這個MessageQueue就是創(chuàng)建Looper時被創(chuàng)建的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()中出現(xiàn)過的過的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)方法發(fā)送一個Message時,實際上這個Message是發(fā)送到與當前線程綁定的一個MessageQueue中哼凯,然后與當前線程綁定的Looper將會不斷的從MessageQueue中取出新的Message欲间,調用msg.target.dispathMessage(msg)方法將消息分發(fā)到與Message綁定的handler.handleMessage()方法中。
一個Thread對應多個Handler 一個Thread對應一個Looper和MessageQueue断部,Handler與Thread共享Looper和MessageQueue猎贴。 Message只是消息的載體,將會被發(fā)送到與線程綁定的唯一的MessageQueue中蝴光,并且被與線程綁定的唯一的Looper分發(fā)她渴,被與其自身綁定的Handler消費。
13 HandlerThread面試
一蔑祟、handlerThread是什么,有哪些特點
handler+thread+looper
-HandlerThread本質上就是一個線程類趁耗,繼承thread
-HandlerThread有自己的內部Looper對象,可以進行l(wèi)ooper循環(huán)疆虚;
-通過獲取HandlerThread的looper對象傳遞給Handler對象苛败。可以在handlerMessage執(zhí)行異步任務
-優(yōu)點是不會有堵塞装蓬,減少對性能的消耗著拭,缺點是不能進行多任務處理纱扭,需要等待牍帚,處理效率較低。
-與線程池注重并發(fā)不同乳蛾,HandlerThrad是一個串行隊列暗赶,它只有一個線程。
14 Webview面試詳解
Webview常見的一些坑
1肃叶、webview 安全漏洞蹂随,在android api 16及以前沒有正確的使用webView.addJavaScriptIterface方法,遠程攻擊者可以通過使用java 的反射機制利用該漏洞執(zhí)行java對象的方法
2因惭、webView在內存中使用造成內存泄漏岳锁,要先在w外部容器中移除webView再調用webView的destroy
3、jsbridge
4蹦魔、webViewClient.onPageFinished--->WebViewChromClient.onProgressChanged方法
5激率、后臺耗電:開啟進程沒有銷毀
5、硬件加速不要開啟
15 AsyncTask面試
1勿决、什么是AyncTask?
它本質上就是一個封裝了線程池和handler的異步框架乒躺,執(zhí)行異步任務,因為內部封裝了handler可以在主線程和任務線程進行切換
2低缩、AysncTask三個參數(shù)五個方法嘉冒?
3、AnsycTask的機制原理
-AysncTask的本質是一個靜態(tài)的線程池,AyncTask派生出的子類可以實現(xiàn)不同的異步任務讳推,這些異步任務都是提交到靜態(tài)的線程池中執(zhí)行
-線程池的工作線程執(zhí)行doinbackBackground(mParams)方法執(zhí)行異步任務
-當任務狀態(tài)改變后顶籽,工作線程會向UI線程發(fā)送消息,ASyncTask的內部的InternalHandler響應這些消息银觅,并調用相關的回調函數(shù)
4蜕衡、AsyncTask注意事件
16 LocalBroadcastManager詳解
1、localbradcastManager高效的原因是因為內部通過Handler實現(xiàn)设拟,她的sendBroadcast()方法含義并非和我們平時所用的一樣慨仿,它其實是通過handler發(fā)送一個message實現(xiàn)的。
2纳胧、他是通過handler實現(xiàn)廣播發(fā)送镰吆,那么相比系統(tǒng)廣播通過Binder更高效,同時使用Handler來實現(xiàn)跑慕,別的應用也無法像我們的應用發(fā)送該廣播万皿。我們的廣播也不會離開我們的應用。
3核行、LoacalBradcastManager內部協(xié)作主要靠兩個Map集合:mReceives和mActions,還有一個List集合mPendingBroadcasts,這個是存儲待接受的廣播對象
17牢硅、 Android中進程間通信的幾種方式
broadcast:用于發(fā)送和接收廣播!實現(xiàn)信息的發(fā)送和接收芝雪! 該方式只是接收到一個廣播消息减余,主要的業(yè)務邏輯代碼是在自己的onReceive()方法里面實現(xiàn)的。
那么它的優(yōu)點是:注冊了這個廣播接收器的應用都能夠收到廣播惩系,范圍廣位岔。缺點是:速度慢點,而且必須在一定時間內把事情處理完(onReceive執(zhí)行必須在幾秒之內)堡牡,否則的話系統(tǒng)給出ANR抒抬。
aidl:用于不同程序將服務的相互調用!實現(xiàn)了一個程序為另一個程序服務的功能晤柄!業(yè)務邏輯代碼在遠程的服務中擦剑,通過aidl來調用。
優(yōu)點是:速度快(系統(tǒng)底層直接是共享內存)芥颈,性能穩(wěn)惠勒,效率高,一般進程間通信就用它浇借。
Content Provider:用于將程序的數(shù)據(jù)庫人為地暴露出來捉撮!實現(xiàn)一個程序可以對另個程序的數(shù)據(jù)庫進行相對用的操作!主要是用來將自己的數(shù)據(jù)庫共享出來供別的程序調用或者修改妇垢。
因為只是把自己的數(shù)據(jù)庫暴露出去巾遭,其他程序都可以來獲取數(shù)據(jù)肉康,數(shù)據(jù)本身不是實時的,不像前兩者,只是啟個數(shù)據(jù)供應作用灼舍。一般是某個成熟的應用來暴露自己的數(shù)據(jù)用的吼和。 你要是為了進程間通信,還是別用這個了骑素,這個又不是實時數(shù)據(jù)炫乓。