定位項(xiàng)目中银觅,如何選取定位方案,如何平衡耗電與實(shí)時(shí)位置的精度作郭?
重要
開(kāi)始定位,Application 持有一個(gè)全局的公共位置對(duì)象弦疮,然后隔一定時(shí)間自動(dòng)刷新位置夹攒,每次刷新成功都把新的位置信息賦值到全局的位置對(duì)象, 然后每個(gè)需要使用位置請(qǐng)求的地方都使用全局的位置信息進(jìn)行請(qǐng)求挂捅。
該方案好處:請(qǐng)求的時(shí)候無(wú)需再反復(fù)定位芹助,每次請(qǐng)求都使用全局的位置對(duì)象,節(jié)省時(shí)間闲先。
該方案弊端:耗電,每隔一定時(shí)間自動(dòng)刷新位置无蜂,對(duì)電量的消耗比較大伺糠。復(fù)制代碼
按需定位,每次請(qǐng)求前都進(jìn)行定位斥季。這樣做的好處是比較省電训桶,而且節(jié)省資源累驮,但是請(qǐng)求時(shí)間會(huì)變得相對(duì)較長(zhǎng)。
Activity間通過(guò)Intent傳遞數(shù)據(jù)大小有沒(méi)有限制舵揭?
重要
Intent在傳遞數(shù)據(jù)時(shí)是有大小限制的谤专,這里官方并未詳細(xì)說(shuō)明,不過(guò)通過(guò)實(shí)驗(yàn)的方法可以測(cè)出數(shù)據(jù)應(yīng)該被限制在1MB之內(nèi)(1024KB)午绳,筆者采用的是傳遞Bitmap的方法置侍,發(fā)現(xiàn)當(dāng)圖片大小超過(guò)1024(準(zhǔn)確地說(shuō)是1020左右)的時(shí)候,程序就會(huì)出現(xiàn)閃退拦焚、停止運(yùn)行等異常(不同的手機(jī)反應(yīng)不同)蜡坊,因此可以判斷Intent的傳輸容量在1MB之內(nèi)。
子線(xiàn)程發(fā)消息到主線(xiàn)程進(jìn)行更新 UI赎败,除了 handler 和 AsyncTask秕衙,還有什么?
一般
用 Activity 對(duì)象的 runOnUiThread 方法更新
在子線(xiàn)程中通過(guò) runOnUiThread()方法更新 UI:
如果在非上下文類(lèi)中(Activity)僵刮,可以通過(guò)傳遞上下文實(shí)現(xiàn)調(diào)用据忘;復(fù)制代碼
用 View.post(Runnable r)方法更新 UI
SQLite支持事務(wù)嗎? 添加刪除如何提高性能?
一般
在sqlite插入數(shù)據(jù)的時(shí)候默認(rèn)一條語(yǔ)句就是一個(gè)事務(wù),有多少條數(shù)據(jù)就有多少次磁盤(pán)操作 比如5000條記錄也就是要5000次讀寫(xiě)磁盤(pán)操作搞糕。
添加事務(wù)處理若河,把多條記錄的插入或者刪除作為一個(gè)事務(wù)
什么是 IntentService?有何優(yōu)點(diǎn)寞宫?
重要
IntentService 是 Service 的子類(lèi)萧福,比普通的 Service 增加了額外的功能。先看 Service 本身存在兩個(gè)問(wèn)題:
Service 不會(huì)專(zhuān)門(mén)啟動(dòng)一條單獨(dú)的進(jìn)程辈赋,Service 與它所在應(yīng)用位于同一個(gè)進(jìn)程中鲫忍;
Service 也不是專(zhuān)門(mén)一條新線(xiàn)程,因此不應(yīng)該在 Service 中直接處理耗時(shí)的任務(wù)钥屈;復(fù)制代碼
IntentService 特征
會(huì)創(chuàng)建獨(dú)立的 worker 線(xiàn)程來(lái)處理所有的 Intent 請(qǐng)求悟民;
會(huì)創(chuàng)建獨(dú)立的 worker 線(xiàn)程來(lái)處理 onHandleIntent()方法實(shí)現(xiàn)的代碼,無(wú)需處理多線(xiàn)程問(wèn)題篷就;
所有請(qǐng)求處理完成后射亏,IntentService 會(huì)自動(dòng)停止,無(wú)需調(diào)用 stopSelf()方法停止 Service竭业;
為 Service 的 onBind()提供默認(rèn)實(shí)現(xiàn)智润,返回null;
為 Service 的 onStartCommand 提供默認(rèn)實(shí)現(xiàn)未辆,將請(qǐng)求 Intent 添加到隊(duì)列中
ListView 中圖片錯(cuò)位的問(wèn)題是如何產(chǎn)生的
重要
圖片錯(cuò)位問(wèn)題的本質(zhì)源于我們的 listview 使用了緩存 convertView窟绷, 假設(shè)一種場(chǎng)景, 一個(gè) listview一屏顯示九個(gè) item咐柜,那么在拉出第十個(gè) item 的時(shí)候兼蜈,事實(shí)上該 item 是重復(fù)使用了第一個(gè) item攘残,也就是說(shuō)在第一個(gè) item 從網(wǎng)絡(luò)中下載圖片并最終要顯示的時(shí)候,其實(shí)該 item 已經(jīng)不在當(dāng)前顯示區(qū)域內(nèi)了为狸,此時(shí)顯示的后果將可能在第十個(gè) item 上輸出圖像歼郭,這就導(dǎo)致了圖片錯(cuò)位的問(wèn)題。所以解決辦法就是可見(jiàn)則顯示辐棒,不可見(jiàn)則不顯示病曾。
Manifest.xml文件中主要包括哪些信息?
重要
manifest:根節(jié)點(diǎn)涉瘾,描述了package中所有的內(nèi)容知态。
uses-permission:請(qǐng)求你的package正常運(yùn)作所需賦予的安全許可。
permission: 聲明了安全許可來(lái)限制哪些程序能你package中的組件和功能立叛。
instrumentation:聲明了用來(lái)測(cè)試此package或其他package指令組件的代碼负敏。
application:包含package中application級(jí)別組件聲明的根節(jié)點(diǎn)。
activity:Activity是用來(lái)與用戶(hù)交互的主要工具秘蛇。
receiver:IntentReceiver能使的application獲得數(shù)據(jù)的改變或者發(fā)生的操作其做,即使它當(dāng)前不在運(yùn)行。
service:Service是能在后臺(tái)運(yùn)行任意時(shí)間的組件赁还。
provider:ContentProvider是用來(lái)管理持久化數(shù)據(jù)并發(fā)布給其他應(yīng)用程序使用的組件妖泄。復(fù)制代碼
Activity的形態(tài)
重要
Active/Running:
Activity處于活動(dòng)狀態(tài),此時(shí)Activity處于棧頂艘策,是可見(jiàn)狀態(tài)蹈胡,可與用戶(hù)進(jìn)行交互。
Paused:
當(dāng)Activity失去焦點(diǎn)時(shí)朋蔫,或被一個(gè)新的非全屏的Activity罚渐,或被一個(gè)透明的Activity放置在棧頂時(shí),Activity就轉(zhuǎn)化為Paused狀態(tài)驯妄。但我們需要明白荷并,此時(shí)Activity只是失去了與用戶(hù)交互的能力,其所有的狀態(tài)信息及其成員變量都還存在青扔,只有在系統(tǒng)內(nèi)存緊張的情況下源织,才有可能被系統(tǒng)回收掉。
Stopped:
當(dāng)一個(gè)Activity被另一個(gè)Activity完全覆蓋時(shí)微猖,被覆蓋的Activity就會(huì)進(jìn)入Stopped狀態(tài)谈息,此時(shí)它不再可見(jiàn),但是跟Paused狀態(tài)一樣保持著其所有狀態(tài)信息及其成員變量励两。
Killed:
當(dāng)Activity被系統(tǒng)回收掉時(shí)黎茎,Activity就處于Killed狀態(tài)。
Activity會(huì)在以上四種形態(tài)中相互切換当悔,至于如何切換傅瞻,這因用戶(hù)的操作不同而異。了解了Activity的4種形態(tài)后盲憎,我們就來(lái)聊聊Activity的生命周期嗅骄。
Fragemnt
非常重要
fragemnt
創(chuàng)建方式
(1)靜態(tài)創(chuàng)建
首先我們需要?jiǎng)?chuàng)建一個(gè)xml文件,然后創(chuàng)建與之對(duì)應(yīng)的java文件饼疙,通過(guò)onCreatView()的返回方法進(jìn)行關(guān)聯(lián)溺森,最后我們需要在Activity中進(jìn)行配置相關(guān)參數(shù)即在Activity的xml文件中放上fragment的位置。
? ? ? ? android:name="xxx.BlankFragment"
? ? ? ? android:layout_width="match_parent"
? ? ? ? android:layout_height="match_parent">
(2)動(dòng)態(tài)創(chuàng)建
動(dòng)態(tài)創(chuàng)建Fragment主要有以下幾個(gè)步驟:
創(chuàng)建待添加的fragment實(shí)例窑眯。
獲取FragmentManager屏积,在Activity中可以直接通過(guò)調(diào)用 getSupportFragmentManager()方法得到。
開(kāi)啟一個(gè)事務(wù)磅甩,通過(guò)調(diào)用beginTransaction()方法開(kāi)啟炊林。
向容器內(nèi)添加或替換fragment,一般使用repalce()方法實(shí)現(xiàn)卷要,需要傳入容器的id和待添加的fragment實(shí)例渣聚。
提交事務(wù),調(diào)用commit()方法來(lái)完成僧叉。
Adapter對(duì)比
FragmnetPageAdapter在每次切換頁(yè)面時(shí)奕枝,只是將Fragment進(jìn)行分離,適合頁(yè)面較少的Fragment使用以保存一些內(nèi)存瓶堕,對(duì)系統(tǒng)內(nèi)存不會(huì)多大影響隘道。
FragmentPageStateAdapter在每次切換頁(yè)面的時(shí)候,是將Fragment進(jìn)行回收郎笆,適合頁(yè)面較多的Fragment使用谭梗,這樣就不會(huì)消耗更多的內(nèi)存
Activity生命周期
Activity的生命周期如下圖:
(1)動(dòng)態(tài)加載:
動(dòng)態(tài)加載時(shí),Activity的onCreate()調(diào)用完题画,才開(kāi)始加載fragment并調(diào)用其生命周期方法默辨,所以在第一個(gè)生命周期方法onAttach()中便能獲取Activity以及Activity的布局的組件;
(2)靜態(tài)加載:
1.靜態(tài)加載時(shí)苍息,Activity的onCreate()調(diào)用過(guò)程中缩幸,fragment也在加載,所以fragment無(wú)法獲取到Activity的布局中的組件竞思,但為什么能獲取到Activity呢表谊?
2.原來(lái)在fragment調(diào)用onAttach()之前其實(shí)還調(diào)用了一個(gè)方法onInflate(),該方法被調(diào)用時(shí)fragment已經(jīng)是和Activity相互結(jié)合了盖喷,所以可以獲取到對(duì)方爆办,但是Activity的onCreate()調(diào)用還未完成,故無(wú)法獲取Activity的組件课梳;
3.Activity的onCreate()調(diào)用完成是距辆,fragment會(huì)調(diào)用onActivityCreated()生命周期方法余佃,因此在這兒開(kāi)始便能獲取到Activity的布局的組件;
與Activity通信
fragment不通過(guò)構(gòu)造函數(shù)進(jìn)行傳值的原因是因?yàn)闄M屏切換的時(shí)候獲取不到值跨算。
Activity向Fragment傳值:
Activity向Fragment傳值爆土,要傳的值放到bundle對(duì)象里;
在Activity中創(chuàng)建該Fragment的對(duì)象fragment诸蚕,通過(guò)調(diào)用setArguments()傳遞到fragment中步势;
在該Fragment中通過(guò)調(diào)用getArguments()得到bundle對(duì)象,就能得到里面的值背犯。
Fragment向Activity傳值:
第一種:
在Activity中調(diào)用getFragmentManager()得到fragmentManager,坏瘩,調(diào)用findFragmentByTag(tag)或者通過(guò)findFragmentById(id),例如:
FragmentManager fragmentManager = getFragmentManager()漠魏;
Fragment fragment = fragmentManager.findFragmentByTag(tag)倔矾;
第二種:
通過(guò)回調(diào)的方式,定義一個(gè)接口(可以在Fragment類(lèi)中定義)蛉幸,接口中有一個(gè)空的方法破讨,在fragment中需要的時(shí)候調(diào)用接口的方法,值可以作為參數(shù)放在這個(gè)方法中奕纫,然后讓Activity實(shí)現(xiàn)這個(gè)接口提陶,必然會(huì)重寫(xiě)這個(gè)方法,這樣值就傳到了Activity中
Fragment與Fragment之間是如何傳值的:
第一種:
通過(guò)findFragmentByTag得到另一個(gè)的Fragment的對(duì)象匹层,這樣就可以調(diào)用另一個(gè)的方法了隙笆。
第二種:
通過(guò)接口回調(diào)的方式。
第三種:
通過(guò)setArguments升筏,getArguments的方式撑柔。
api區(qū)別
add
一種是add方式來(lái)進(jìn)行show和add,這種方式你切換fragment不會(huì)讓fragment重新刷新您访,只會(huì)調(diào)用onHiddenChanged(boolean isHidden)铅忿。
replace
而用replace方式會(huì)使fragment重新刷新,因?yàn)閍dd方式是將fragment隱藏了而不是銷(xiāo)毀再創(chuàng)建灵汪,replace方式每次都是重新創(chuàng)建檀训。
commit/commitAllowingStateLoss
兩者都可以提交fragment的操作,唯一的不同是第二種方法享言,允許丟失一些界面的狀態(tài)和信息峻凫,幾乎所有的開(kāi)發(fā)者都遇到過(guò)這樣的錯(cuò)誤:無(wú)法在activity調(diào)用了onSaveInstanceState之后再執(zhí)行commit(),這種異常時(shí)可以理解的览露,界面被系統(tǒng)回收(界面已經(jīng)不存在)荧琼,為了在下次打開(kāi)的時(shí)候恢復(fù)原來(lái)的樣子,系統(tǒng)為我們保存界面的所有狀態(tài),這個(gè)時(shí)候我們?cè)偃バ薷慕缑胬碚撋峡隙ㄊ遣辉试S的命锄,所以為了避免這種異常堰乔,要使用第二種方法。
3.懶加載
我們經(jīng)常在使用fragment時(shí)累舷,常常會(huì)結(jié)合著viewpager使用浩考,那么我們就會(huì)遇到一個(gè)問(wèn)題夹孔,就是初始化fragment的時(shí)候被盈,會(huì)連同我們寫(xiě)的網(wǎng)絡(luò)請(qǐng)求一起執(zhí)行,這樣非常消耗性能搭伤,最理想的方式是只怎,只有用戶(hù)點(diǎn)開(kāi)或滑動(dòng)到當(dāng)前fragment時(shí),才進(jìn)行請(qǐng)求網(wǎng)絡(luò)的操作怜俐。因此身堡,我們就產(chǎn)生了懶加載這樣一個(gè)說(shuō)法。
Viewpager配合fragment使用拍鲤,默認(rèn)加載前兩個(gè)fragment贴谎。很容易造成網(wǎng)絡(luò)丟包、阻塞等問(wèn)題季稳。
在Fragment中有一個(gè)setUserVisibleHint這個(gè)方法擅这,而且這個(gè)方法是優(yōu)于onCreate()方法的,它會(huì)通過(guò)isVisibleToUser告訴我們當(dāng)前Fragment我們是否可見(jiàn)景鼠,我們可以在可見(jiàn)的時(shí)候再進(jìn)行網(wǎng)絡(luò)加載仲翎。
從log上看setUserVisibleHint()的調(diào)用早于onCreateView,所以如果在setUserVisibleHint()要實(shí)現(xiàn)懶加載的話(huà)铛漓,就必須要確保View以及其他變量都已經(jīng)初始化結(jié)束溯香,避免空指針。
使用步驟:
申明一個(gè)變量isPrepare=false浓恶,isVisible=false,標(biāo)明當(dāng)前頁(yè)面是否被創(chuàng)建了
在onViewCreated周期內(nèi)設(shè)置isPrepare=true
在setUserVisibleHint(boolean isVisible)判斷是否顯示玫坛,設(shè)置isVisible=true
判斷isPrepare和isVisible,都為true開(kāi)始加載數(shù)據(jù)包晰,然后恢復(fù)isPrepare和isVisible為false湿镀,防止重復(fù)加載。
Handler.post和View.post的區(qū)別
重要
在Android開(kāi)發(fā)中杜窄,我們經(jīng)常會(huì)見(jiàn)到下面的代碼肠骆,比如:
protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? System.out.println("onCreate===");
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? rootBtn = findViewById(R.id.rootBtn);
? ? ? ? // 代碼1
? ? ? ? UIHandler.post(new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? System.out.println("Handler.post===");
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? // 代碼2
? ? ? ? rootBtn.post(new Runnable() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? System.out.println("View.post===");
? ? ? ? ? ? }
? ? ? ? });
? ? }
你曾經(jīng)有沒(méi)有想過(guò)這兩者到底有什么區(qū)別?我該使用哪種呢塞耕?
常見(jiàn)的Handler.post揭秘
Handler的工作機(jī)制蚀腿,網(wǎng)上介紹的文章太多了,這里我就不贅述了。一句話(huà)總結(jié)就是通過(guò)Handler對(duì)象莉钙,不論是post Msg還是Runnable廓脆,最終都是構(gòu)造了一個(gè)Msg對(duì)象,插入到與之對(duì)應(yīng)的Looper的MessageQueue中磁玉,不同的是Running時(shí)msg對(duì)象的callback字段設(shè)成了Runnable的值停忿。稍后這條msg會(huì)從隊(duì)列中取出來(lái)并且得到執(zhí)行,UI線(xiàn)程就是這么一個(gè)基于事件的循環(huán)蚊伞。所以可以看出Handler.post相關(guān)的代碼在onCreate里那一刻時(shí)就已經(jīng)開(kāi)始了執(zhí)行(加入到了隊(duì)列席赂,下次循環(huán)到來(lái)時(shí)就會(huì)被真正執(zhí)行了)。
View.post揭秘
要解釋它的行為时迫,我們就必須深入代碼中去找答案了颅停,其代碼如下:
public boolean post(Runnable action) {
? ? ? ? final AttachInfo attachInfo = mAttachInfo;
? ? ? ? if (attachInfo != null) {
? ? ? ? ? ? // 注意這個(gè)判斷,這個(gè)變量時(shí)機(jī)太早的話(huà)是沒(méi)值的掠拳,
? ? ? ? ? // 比如在act#onCreate階段
? ? ? ? ? ? return attachInfo.mHandler.post(action);
? ? ? ? }
? ? ? ? // 仔細(xì)閱讀下面這段注釋?zhuān)癞揉。。?/p>
? ? ? ? // Postpone the runnable until we know on which thread it needs to run.
? ? ? ? // Assume that the runnable will be successfully placed after attach.
? ? ? ? getRunQueue().post(action);
? ? ? ? return true;
? ? }
從上面的源碼,我們大概可以看出mAttachInfo字段在這里比較關(guān)鍵,當(dāng)其有值時(shí)森爽,其實(shí)和普通的Handler.post就沒(méi)區(qū)別了人灼,但有時(shí)它是沒(méi)值的,比如我們上面示例代碼里的onCreate階段,那么這時(shí)執(zhí)行到了getRunQueue().post(action);這行代碼,從這段注釋也大概可以看出來(lái)真正的執(zhí)行會(huì)被延遲(這里的Postpone注釋?zhuān)晃覀兘又驴纯磄etRunQueue相關(guān)的代碼胳泉,如下:
/**? 其實(shí)這段注釋已經(jīng)說(shuō)的很清楚明了了!Q乙拧扇商!
? ? * Queue of pending runnables. Used to postpone calls to post() until this
? ? * view is attached and has a handler.
? ? */
private HandlerActionQueue mRunQueue;
private HandlerActionQueue getRunQueue() {
? ? ? ? if (mRunQueue == null) {
? ? ? ? ? ? mRunQueue = new HandlerActionQueue();
? ? ? ? }
? ? ? ? return mRunQueue;
? ? }
從上面我們可以看出,mRunQueue就是View用來(lái)處理它還沒(méi)attach到window(還沒(méi)對(duì)應(yīng)的handler)時(shí)宿礁,客戶(hù)代碼發(fā)起的post調(diào)用的案铺,起了一個(gè)臨時(shí)緩存的作用。不然總不能丟棄吧梆靖,這樣開(kāi)發(fā)體驗(yàn)就太差了?睾骸!返吻!
緊接著姑子,我們繼續(xù)看下HandlerActionQueue類(lèi)型的定義,代碼如下:
public class HandlerActionQueue {
? ? private HandlerAction[] mActions;
? ? private int mCount;
? ? public void post(Runnable action) {
? ? ? ? postDelayed(action, 0);
? ? }
? ? public void postDelayed(Runnable action, long delayMillis) {
? ? ? ? final HandlerAction handlerAction = new HandlerAction(action, delayMillis);
? ? ? ? synchronized (this) {
? ? ? ? ? ? if (mActions == null) {
? ? ? ? ? ? ? ? mActions = new HandlerAction[4];
? ? ? ? ? ? }
? ? ? ? ? ? mActions = GrowingArrayUtils.append(mActions, mCount, handlerAction);
? ? ? ? ? ? mCount++;
? ? ? ? }
? ? }
? ? public void executeActions(Handler handler) {
? ? ? ? synchronized (this) {
? ? ? ? ? ? final HandlerAction[] actions = mActions;
? ? ? ? ? ? for (int i = 0, count = mCount; i < count; i++) {
? ? ? ? ? ? ? ? final HandlerAction handlerAction = actions[i];
? ? ? ? ? ? ? ? handler.postDelayed(handlerAction.action, handlerAction.delay);
? ? ? ? ? ? }
? ? ? ? ? ? mActions = null;
? ? ? ? ? ? mCount = 0;
? ? ? ? }
? ? }
? ? private static class HandlerAction {
? ? ? ? final Runnable action;
? ? ? ? final long delay;
? ? ? ? public HandlerAction(Runnable action, long delay) {
? ? ? ? ? ? this.action = action;
? ? ? ? ? ? this.delay = delay;
? ? ? ? }
? ? ? ? public boolean matches(Runnable otherAction) {
? ? ? ? ? ? return otherAction == null && action == null
? ? ? ? ? ? ? ? ? ? || action != null && action.equals(otherAction);
? ? ? ? }
? ? }
}
注意:這里的源碼部分测僵,我們只摘錄了部分關(guān)鍵代碼街佑,其余不太相關(guān)的直接略去了谢翎。
從這里可以看出,我們前面的View.post調(diào)用里的Runnable最終會(huì)被存儲(chǔ)在這里的mActions數(shù)組里沐旨,這里最關(guān)鍵的一點(diǎn)就是其executeActions方法森逮,因?yàn)檫@個(gè)方法里我們之前post的Runnable才真正通過(guò)handler.postDelayed方式使其進(jìn)入handler對(duì)應(yīng)的消息隊(duì)列里等待執(zhí)行;
到此為止磁携,我們還差知道View里的mAttachInfo字段何時(shí)被賦值以及這里的executeActions方法是什么時(shí)候被觸發(fā)的褒侧,答案就是在View的dispatchAttachedToWindow方法,其關(guān)鍵源碼如下:
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
? ? ? ? mAttachInfo = info;
? ? ? ? ...
? ? ? ? // Transfer all pending runnables.
? ? ? ? if (mRunQueue != null) {
? ? ? ? ? ? mRunQueue.executeActions(info.mHandler);
? ? ? ? ? ? mRunQueue = null;
? ? ? ? }
? ? ? ? performCollectViewAttributes(mAttachInfo, visibility);
? ? ? ? onAttachedToWindow();
? ? ? ? ...
}
而通過(guò)之前的文章谊迄,我們已經(jīng)知道了此方法是當(dāng)Act Resume之后闷供,在ViewRootImpl.performTraversals()中觸發(fā)的,參考View.onAttachedToWindow調(diào)用時(shí)機(jī)分析鳞上。
總結(jié)
Handler.post这吻,它的執(zhí)行時(shí)間基本是等同于onCreate里那行代碼觸達(dá)的時(shí)間;
View.post篙议,則不同,它說(shuō)白了執(zhí)行時(shí)間一定是在Act#onResume發(fā)生后才開(kāi)始算的怠硼;或者換句話(huà)說(shuō)它的效果相當(dāng)于你上面的View.post方法是寫(xiě)在Act#onResume里面的(但只執(zhí)行一次鬼贱,因?yàn)閛nCreate不像onResume會(huì)被多次觸發(fā));
當(dāng)然香璃,雖然這里說(shuō)的是post方法这难,但對(duì)應(yīng)的postDelayed方法區(qū)別也是類(lèi)似的。
平時(shí)當(dāng)你項(xiàng)目很小葡秒,MainActivity的邏輯也很簡(jiǎn)單時(shí)是看不出啥區(qū)別的姻乓,但當(dāng)act的onCreate到onResume之間耗時(shí)比較久時(shí)(比如2s以上),就能明顯感受到這2者的區(qū)別了眯牧,而且本身它們的實(shí)際含義也是很不同的蹋岩,前者的Runnable真正執(zhí)行時(shí),可能act的整個(gè)view層次都還沒(méi)完整的measure学少、layout完成剪个,但后者的Runnable執(zhí)行時(shí),則一定能保證act的view層次結(jié)構(gòu)已經(jīng)measure版确、layout并且至少繪制完成了一次扣囊。
橫豎屏切換的Activity 生命周期變化?
重要
不設(shè)置 Activity 的 android:configChanges 時(shí)绒疗,切屏?xí)N(xiāo)毀當(dāng)前Activity侵歇,然后重新加載調(diào)用各個(gè)生命周期,切橫屏?xí)r會(huì)執(zhí)行一次吓蘑,切豎屏?xí)r會(huì)執(zhí)行兩次惕虑;onPause()→onStop()→onDestory()→onCreate()→onStart()→onResume()
設(shè)置 Activity 的 android:configChanges=" orientation",經(jīng)過(guò)機(jī)型測(cè)試
在 Android5.1 即 即 API 3 23 級(jí)別下,切屏還是會(huì)重新調(diào)用各個(gè)生命周期枷遂,切橫樱衷、豎屏?xí)r只會(huì)執(zhí)行一次
在 Android9 即 即 API 8 28 級(jí)別下,切屏不會(huì)重新調(diào)用各個(gè)生命周期酒唉,只會(huì)執(zhí)行 onConfigurationChanged方法
官方糾正后矩桂,原話(huà)如下
如果您的應(yīng)用面向 Android 2 3.2 即 即 API 級(jí)別 3 13 或更
高級(jí)別(按照 minSdkVersion 和 targetSdkVersion)
說(shuō)下Activity 的四種啟動(dòng)模式、應(yīng)用場(chǎng)景 痪伦?
重要
standard 標(biāo)準(zhǔn)模式: 每次啟動(dòng)一個(gè) Activity 都會(huì)重新創(chuàng)建一個(gè)新的實(shí)例侄榴,不管這個(gè)實(shí)例是否已經(jīng)存在,此模式的 Activity 默認(rèn)會(huì)進(jìn)入啟動(dòng)它的 Activity 所屬的任務(wù)棧中网沾;
singleTop 棧頂復(fù)用模式: 如果新 Activity 已經(jīng)位于任務(wù)棧的棧頂癞蚕,那么此 Activity 不會(huì)被重新創(chuàng)建,同時(shí)會(huì)回調(diào) onNewIntent方法辉哥,如果新 Activity 實(shí)例已經(jīng)存在但不在棧頂桦山,那么Activity 依然會(huì)被重新創(chuàng)建;
singleTask 棧內(nèi)復(fù)用模式: 只要 Activity 在一個(gè)任務(wù)棧中存在醋旦,那么多次啟動(dòng)此 Activity 都不會(huì)重新創(chuàng)建實(shí)例恒水,并回調(diào)onNewIntent 方法,此模式啟動(dòng) Activity A饲齐,系統(tǒng)首先會(huì)尋找是否存在 A 想要的任務(wù)棧钉凌,如果不存在,就會(huì)重新創(chuàng)建一個(gè)任務(wù)棧捂人,然后把創(chuàng)建好 A 的實(shí)例放到棧中御雕;
singleInstance單實(shí)例模式: 這是一種加強(qiáng)的 singleTask 模式,具有此種模式的 Activity 只能單獨(dú)地位于一個(gè)任務(wù)棧中滥搭,且此任務(wù)棧中只有唯一一個(gè)實(shí)例酸纲;
FragmentPagerAdapter 與 與 FragmentStatePagerAdapter 的區(qū)別與使用場(chǎng)景?
重要
FragmentPagerAdapter 的每個(gè) Fragment 會(huì)持久的保存在 FragmentManager 中论熙,只要用戶(hù)可以返回到頁(yè)面中福青,它都不會(huì)被銷(xiāo)毀。因此適用于那些數(shù)據(jù) 相對(duì)靜態(tài)的頁(yè)脓诡,F(xiàn)ragment 數(shù)量也比較少的那種;FragmentStatePagerAdapter 只保留當(dāng)前頁(yè)面无午,當(dāng)頁(yè)面不可見(jiàn)時(shí),該 Fragment 就會(huì)被消除祝谚,釋放其資源宪迟。因此適用于那些 數(shù)據(jù)動(dòng)態(tài)性較大、 占用內(nèi)存較多交惯,多 Fragment 的情況次泽;
Android 中如何捕獲未捕獲的異常
重要
UncaughtExceptionHandler
自 定 義 一 個(gè) Application 穿仪, 比 如 叫 MyApplication 繼 承 Application 實(shí) 現(xiàn)UncaughtExceptionHandler。
覆寫(xiě) UncaughtExceptionHandler 的 onCreate 和 uncaughtException 方法意荤。 注意:上面的代碼只是簡(jiǎn)單的將異常打印出來(lái)啊片。在 onCreate 方法中我們給 Thread 類(lèi)設(shè)置默認(rèn)異常處理handler,如果這句代碼不執(zhí)行則一切都是白搭玖像。在 uncaughtException 方法中我們必須新開(kāi)辟個(gè)線(xiàn)程進(jìn)行我們異常的收集工作紫谷,然后將系統(tǒng)給殺死。
在 AndroidManifest 中配置該 Application:
Bug 收集工具 Crashlytics
Crashlytics是專(zhuān)門(mén)為移動(dòng)應(yīng)用開(kāi)發(fā)者提供的保存和分析應(yīng)用崩潰的工具捐寥。國(guó)內(nèi)主要使用的是友盟做數(shù)據(jù)統(tǒng)計(jì)笤昨。
Crashlytics的好處:
1.Crashlytics不會(huì)漏掉任何應(yīng)用崩潰信息。
2.Crashlytics可以象Bug管理工具那樣握恳,管理這些崩潰日志瞒窒。
3.Crashlytics可以每天和每周將崩潰信息匯總發(fā)到你的郵箱,所有信息一目了然
activity與fragment區(qū)別
重要
生命周期:
fragment從創(chuàng)建倒銷(xiāo)毀整個(gè)生命周期依次為onAttach()→onCreate()→onCreateView()→onActivityCreated()→onStart()→onResume()→onPause()→onStop()→onDestroyView()→onDestroy()→onDetach()
與activity不同的方法有
onAttach():當(dāng)Fragment和Activity建立關(guān)聯(lián)的時(shí)候調(diào)用乡洼;
onCreateView():當(dāng)Fragment創(chuàng)建視圖調(diào)用崇裁;
onActivityCreated:與Fragment相關(guān)聯(lián)的Activity完成onCreate()之后調(diào)用;
onDestoryView():在Fragment中的布局被移除時(shí)調(diào)用就珠;
onDetach():當(dāng)Fragment和Activity解除關(guān)聯(lián)時(shí)調(diào)用寇壳;
activity常用的生命周期只有以下幾個(gè);
onCreate(): 表示 Activity 正在被創(chuàng)建妻怎,常用來(lái) 初始化工作,比如調(diào)用 setContentView 加載界面布局資源泞歉,初始化 Activity 所需數(shù)據(jù)等逼侦;
onRestart():表示 Activity 正在重新啟動(dòng),一般情況下腰耙,當(dāng)前Acitivty 從不可見(jiàn)重新變?yōu)榭梢?jiàn)時(shí)榛丢,OnRestart就會(huì)被調(diào)用;
onStart(): 表示 Activity 正在被啟動(dòng)挺庞,此時(shí) Activity 可見(jiàn)但不在前臺(tái)晰赞,還處于后臺(tái),無(wú)法與用戶(hù)交互选侨;
onResume(): 表示 Activity 獲得焦點(diǎn)掖鱼,此時(shí) Activity 可見(jiàn)且在前臺(tái)并開(kāi)始活動(dòng),這是與 onStart 的區(qū)別所在援制;
onPause(): 表示 Activity 正在停止戏挡,此時(shí)可做一些 存儲(chǔ)數(shù)據(jù)、停止動(dòng)畫(huà)等工作晨仑,但是不能太耗時(shí)褐墅,因?yàn)檫@會(huì)影響到新 Activity的顯示拆檬,onPause 必須先執(zhí)行完,新 Activity 的 onResume 才會(huì)執(zhí)行妥凳;
onStop(): 表示 Activity 即將停止竟贯,可以做一些稍微重量級(jí)的回收工作,比如注銷(xiāo)廣播接收器逝钥、關(guān)閉網(wǎng)絡(luò)連接等屑那,同樣不能太耗時(shí);
onDestroy(): 表示 Activity 即將被銷(xiāo)毀晌缘,這是 Activity 生命周期中的最后一個(gè)回調(diào)齐莲,常做 回收工作、資源釋放磷箕;
區(qū)別:
Fragment比Activity多出四個(gè)回調(diào)周期选酗,控制操作上更靈活;
Fragment可以在xml文件中直接寫(xiě)入岳枷,也可以在Activity中動(dòng)態(tài)添加芒填;
Fragment可以使用show()/hide()或者replace()對(duì)Fragment進(jìn)行切換,切換的時(shí)候不會(huì)出現(xiàn)明顯的效果空繁,Activity切換的時(shí)候會(huì)有明顯的翻頁(yè)或其他效果殿衰;
View的分發(fā)機(jī)制,滑動(dòng)沖突
重要
View的事件傳遞順序有3個(gè)重要的方法盛泡,dispatchTouchEvent()是否消耗了本次事件闷祥,onInterceptTouchEvent()是否攔截了本次事件,onTouchEvent()是否處理本次事件傲诵,滑動(dòng)沖突分為同方向滑動(dòng)沖突凯砍,例如ScrollView和ListView,同方向滑動(dòng)沖突拴竹,可以計(jì)算ListView高度而動(dòng)態(tài)設(shè)置ListView的高度悟衩,ScrollView高度可變。例如ViewPager和ListView,不同方向滑動(dòng)沖突栓拜,一個(gè)是橫向滑動(dòng)一個(gè)是豎直滑動(dòng)座泳,不同方向滑動(dòng)可以判斷滑動(dòng)的x,y軸是橫向還是豎直滑動(dòng)幕与,如果判斷得到是橫向滑動(dòng)挑势,就攔截ListView的事件,豎則反之纽门。
音視頻相關(guān)類(lèi)
一般
總體來(lái)說(shuō)薛耻,分為幾個(gè)類(lèi)
視頻錄制方面,Camear攝像頭錄制視頻類(lèi)赏陵,MediaProjection屏幕錄制視頻類(lèi)
編碼方面饼齿,MediaCodec饲漾,MediaRecorder
預(yù)覽方面,SurfaceView,GLSurfaceView,TextureView,VideoView
Message.obtain() 是從消息池取 Message缕溉,消息池其實(shí)是使用 Message 鏈表結(jié)構(gòu)實(shí)現(xiàn)考传,消息池默認(rèn)最大值 50。 Message.obtain() 每次都是把消息池表頭的 Message 取走 证鸥,再把表頭指向 next僚楞。
public static Message obtain() {
? ? synchronized (sPoolSync) {
? ? ? ? if (sPool != null) {
? ? ? ? ? ? Message m = sPool;
? ? ? ? ? ? sPool = m.next;
? ? ? ? ? ? m.next = null; ?//從sPool中取出一個(gè)Message對(duì)象,并消息鏈表斷開(kāi)
? ? ? ? ? ? m.flags = 0; // 清除in-use flag
? ? ? ? ? ? sPoolSize--; //消息池的可用大小進(jìn)行減1操作
? ? ? ? ? ? return m;
? ? ? ? }
? ? }
? ? return new Message(); // 當(dāng)消息池為空時(shí)枉层,直接創(chuàng)建Message對(duì)象
}
消息在 loop 中被 handler 分發(fā)消費(fèi)之后會(huì)執(zhí)行回收的操作泉褐,將該消息內(nèi)部數(shù)據(jù)清空并添加到消息鏈表的表頭。
public void recycle() {
? ? if (isInUse()) { //判斷消息是否正在使用
? ? ? ? if (gCheckRecycle) { //Android 5.0以后的版本默認(rèn)為true,之前的版本默認(rèn)為false.
? ? ? ? ? ? throw new IllegalStateException("This message cannot be recycled because it is still in use.");
? ? ? ? }
? ? ? ? return;
? ? }
? ? recycleUnchecked();
}
//對(duì)于不再使用的消息鸟蜡,加入到消息池
void recycleUnchecked() {
? ? //將消息標(biāo)示位置為IN_USE膜赃,并清空消息所有的參數(shù)。
? ? flags = FLAG_IN_USE;
? ? what = 0;
? ? arg1 = 0;
? ? arg2 = 0;
? ? obj = null;
? ? replyTo = null;
? ? sendingUid = -1;
? ? when = 0;
? ? target = null;
? ? callback = null;
? ? data = null;
? ? synchronized (sPoolSync) {
? ? ? ? if (sPoolSize < MAX_POOL_SIZE) { //當(dāng)消息池沒(méi)有滿(mǎn)時(shí)揉忘,將Message對(duì)象加入消息池
? ? ? ? ? ? next = sPool;
? ? ? ? ? ? sPool = this;
? ? ? ? ? ? sPoolSize++; //消息池的可用大小進(jìn)行加1操作
? ? ? ? }
? ? }
}
你了解 HandlerThread 嗎?
重要
HandlerThread 繼承自 Thread跳座,它是一種可以使用 Handler 的 Thread,它的實(shí)現(xiàn)也很簡(jiǎn)單泣矛,在 run方法中也是通過(guò) Looper.prepare() 來(lái)創(chuàng)建消息隊(duì)列疲眷,并通過(guò)Looper.loop()來(lái)開(kāi)啟消息循環(huán)(與我們手動(dòng)創(chuàng)建方法基本一致),這樣在實(shí)際的使用中就允許在 HandlerThread 中創(chuàng)建 Handler 了您朽。
public class HandlerThread extends Thread {
? ? @Override
? ? public void run() {
? ? ? ? mTid = Process.myTid();
? ? ? ? Looper.prepare();
? ? ? ? synchronized (this) {
? ? ? ? ? ? mLooper = Looper.myLooper();
? ? ? ? ? ? notifyAll();
? ? ? ? }
? ? ? ? Process.setThreadPriority(mPriority);
? ? ? ? onLooperPrepared();
? ? ? ? Looper.loop();
? ? ? ? mTid = -1;
? ? }
}
由于 HandlerThread 的run方法是一個(gè)無(wú)限循環(huán)狂丝,因此當(dāng)不需要使用的時(shí)候通過(guò)quit或者quitSafely方法來(lái)終止線(xiàn)程的執(zhí)行。
你對(duì) IdleHandler 有多少了解?
一般
IdleHandler 是一個(gè)接口哗总, 這個(gè)接口方法是在消息隊(duì)列全部處理完成后或者是在阻塞的過(guò)程中等待更多的消息的時(shí)候調(diào)用的美侦,返回值 false 表示只回調(diào)一次,true 表示可以接收多次回調(diào)魂奥。
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
? ? @Override
? ? public boolean queueIdle() {
? ? ? ? return false;
? ? }
});
MessageQueue 是隊(duì)列嗎?它是什么數(shù)據(jù)結(jié)構(gòu)易猫?
重要
MessageQueue 不是隊(duì)列耻煤,它內(nèi)部使用一個(gè) Message 鏈表實(shí)現(xiàn)消息的存和取。 鏈表的排列依據(jù)是 Message.when准颓,表示 Message 期望被分發(fā)的時(shí)間哈蝇,該值是 SystemClock. uptimeMillis() 與 delayMillis 之和。
##7攘已、 handler.postDelayed() 函數(shù)延時(shí)執(zhí)行計(jì)時(shí)是否準(zhǔn)確炮赦?
當(dāng)上一個(gè)消息存在耗時(shí)任務(wù)的時(shí)候,會(huì)占用延時(shí)任務(wù)執(zhí)行的時(shí)機(jī)样勃,實(shí)際延遲時(shí)間可能會(huì)超過(guò)預(yù)設(shè)延時(shí)時(shí)間吠勘,這時(shí)候就不準(zhǔn)確了性芬。
Looper 死循環(huán)為什么不會(huì)導(dǎo)致應(yīng)用卡死,會(huì)消耗大量資源嗎剧防?
重要
對(duì)于線(xiàn)程即是一段可執(zhí)行的代碼植锉,當(dāng)可執(zhí)行代碼執(zhí)行完成后,線(xiàn)程生命周期便該終止了峭拘,線(xiàn)程退出俊庇。而對(duì)于主線(xiàn)程,我們是絕不希望會(huì)被運(yùn)行一段時(shí)間鸡挠,自己就退出辉饱,那么如何保證能一直存活呢?簡(jiǎn)單做法就是可執(zhí)行代碼是能一直執(zhí)行下去的拣展,死循環(huán)便能保證不會(huì)被退出彭沼,例如,binder 線(xiàn)程也是采用死循環(huán)的方法瞎惫,通過(guò)循環(huán)方式不同與 Binder 驅(qū)動(dòng)進(jìn)行讀寫(xiě)操作溜腐,當(dāng)然并非簡(jiǎn)單地死循環(huán),無(wú)消息時(shí)會(huì)休眠瓜喇。但這里可能又引發(fā)了另一個(gè)問(wèn)題挺益,既然是死循環(huán)又如何去處理其他事務(wù)呢?通過(guò)創(chuàng)建新線(xiàn)程的方式乘寒。真正會(huì)卡死主線(xiàn)程的操作是在回調(diào)方法 onCreate/onStart/onResume 等操作時(shí)間過(guò)長(zhǎng)望众,會(huì)導(dǎo)致掉幀,甚至發(fā)生ANR伞辛,looper.loop本身不會(huì)導(dǎo)致應(yīng)用卡死烂翰。
主線(xiàn)程的死循環(huán)一直運(yùn)行是不是特別消耗CPU資源呢? 其實(shí)不然蚤氏,這里就涉及到 Linux pipe/epoll 機(jī)制甘耿,簡(jiǎn)單說(shuō)就是在主線(xiàn)程的 MessageQueue 沒(méi)有消息時(shí),便阻塞在 Loop 的 queue.next() 中的 nativePollOnce() 方法里竿滨,此時(shí)主線(xiàn)程會(huì)釋放 CPU 資源進(jìn)入休眠狀態(tài)佳恬,直到下個(gè)消息到達(dá)或者有事務(wù)發(fā)生,通過(guò)往 pipe 管道寫(xiě)端寫(xiě)入數(shù)據(jù)來(lái)喚醒主線(xiàn)程工作于游。這里采用的epoll機(jī)制毁葱,是一種IO多路復(fù)用機(jī)制,可以同時(shí)監(jiān)控多個(gè)描述符贰剥,當(dāng)某個(gè)描述符就緒(讀或?qū)懢途w)倾剿,則立刻通知相應(yīng)程序進(jìn)行讀或?qū)懖僮鳎举|(zhì)同步 I/O蚌成,即讀寫(xiě)是阻塞的前痘。 所以說(shuō)凛捏,主線(xiàn)程大多數(shù)時(shí)候都是處于休眠狀態(tài),并不會(huì)消耗大量 CPU 資源际度。
可參見(jiàn):https://www.zhihu.com/question/34652589
既然線(xiàn)程中創(chuàng)建 Handler 時(shí)需要 Looper 對(duì)象葵袭,為什么主線(xiàn)程不用調(diào)用 Looper.prepare() 創(chuàng)建 Looper 對(duì)象?
重要
在 App 啟動(dòng)的時(shí)候系統(tǒng)默認(rèn)啟動(dòng)了一個(gè)主線(xiàn)程的 Looper(ActivityThread 的 main 方法中)乖菱,Loop.prepareMainLooper 方法也是調(diào)用了 Looper.prepare方法坡锡,里面會(huì)創(chuàng)建一個(gè)不可退出的 Looper, 并 set 到 sThreadLocal 對(duì)象當(dāng)中。
public static void main(String[] args) {
? ? Looper.prepareMainLooper();
? ? Looper.loop();
}
可以在子線(xiàn)程直接創(chuàng)建一個(gè) Handler 嗎窒所?會(huì)出現(xiàn)什么問(wèn)題鹉勒,那該怎么做?
重要
不能在子線(xiàn)程直接 new 一個(gè) Handler吵取。因?yàn)?Handler 的工作依賴(lài)于 Looper禽额,而 Looper 又是屬于某一個(gè)線(xiàn)程的,其他線(xiàn)程不能訪問(wèn)皮官,所以在線(xiàn)程中使用 Handler 時(shí)必須要保證當(dāng)前線(xiàn)程中 Looper 對(duì)象并且啟動(dòng)循環(huán)脯倒。不然會(huì)拋出異常。
throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()");
1
正確做法是:
class LooperThread extends Thread {
? ? public Handler mHandler;
? ? public void run() {
? ? ? ? Looper.prepare(); ? // 為線(xiàn)程創(chuàng)建 Looper 對(duì)象
? ? ? ? mHandler = new Handler() { ?
? ? ? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? Looper.loop(); ? // 啟動(dòng)消息循環(huán)
? ? }
}
可以在子線(xiàn)程直接創(chuàng)建一個(gè) Handler 嗎捺氢?會(huì)出現(xiàn)什么問(wèn)題藻丢,那該怎么做?
重要
不能在子線(xiàn)程直接 new 一個(gè) Handler摄乒。因?yàn)?Handler 的工作依賴(lài)于 Looper悠反,而 Looper 又是屬于某一個(gè)線(xiàn)程的,其他線(xiàn)程不能訪問(wèn)馍佑,所以在線(xiàn)程中使用 Handler 時(shí)必須要保證當(dāng)前線(xiàn)程中 Looper 對(duì)象并且啟動(dòng)循環(huán)斋否。不然會(huì)拋出異常。
throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread() + " that has not called Looper.prepare()");
1
正確做法是:
class LooperThread extends Thread {
? ? public Handler mHandler;
? ? public void run() {
? ? ? ? Looper.prepare(); ? // 為線(xiàn)程創(chuàng)建 Looper 對(duì)象
? ? ? ? mHandler = new Handler() { ?
? ? ? ? ? ? public void handleMessage(Message msg) {
? ? ? ? ? ? }
? ? ? ? };
? ? ? ? Looper.loop(); ? // 啟動(dòng)消息循環(huán)
? ? }
}
一個(gè)線(xiàn)程可以有幾個(gè) Looper拭荤、幾個(gè) MessageQueue 和幾個(gè) Handler茵臭?
重要
在 Android 中,Looper 類(lèi)利用了 ThreadLocal 的特性舅世,保證了每個(gè)線(xiàn)程只存在一個(gè) Looper 對(duì)象笼恰。
關(guān)于 ThreadLocal 可以看:理解 ThreadLocal
static final ThreadLocalsThreadLocal = new ThreadLocal();
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));
}
Looper 構(gòu)造函數(shù)中創(chuàng)建了 MessageQueue 對(duì)象,因此一個(gè)線(xiàn)程只有一個(gè) MessageQueue歇终。
private Looper(boolean quitAllowed) {
? ? ? ? mQueue = new MessageQueue(quitAllowed);
? ? ? ? mThread = Thread.currentThread();
}
可以有多個(gè) Handler。
Handler 在創(chuàng)建時(shí)與 Looper 和 MessageQueue 關(guān)聯(lián)起來(lái):
public Handler(Callback callback, boolean async) {
? ? ...
? ? mLooper = Looper.myLooper();
? ? if (mLooper == null) {
? ? ? ? throw new RuntimeException(
? ? ? ? ? ? "Can't create handler inside thread that has not called Looper.prepare()");
? ? }
? ? mQueue = mLooper.mQueue;
? ? ...
}
Handler 發(fā)送消息是將消息傳遞給 MessageQueue:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
? ? msg.target = this;
? ? if (mAsynchronous) {
? ? ? ? msg.setAsynchronous(true);
? ? }
? ? return queue.enqueueMessage(msg, uptimeMillis);
}
注意 msg.target = this;逼龟, 這里將當(dāng)前的 Handler 賦值給 Message 對(duì)象评凝,在后面處理消息時(shí)就能依據(jù) msg.target 區(qū)分不同的 Handler。
什么是協(xié)程腺律?
重要
官方描述:協(xié)程通過(guò)將復(fù)雜性放入庫(kù)來(lái)簡(jiǎn)化異步編程奕短。程序的邏輯可以在協(xié)程中順序地表達(dá)宜肉,而底層庫(kù)會(huì)為我們解決其異步性。該庫(kù)可以將用戶(hù)代碼的相關(guān)部分包裝為回調(diào)翎碑、訂閱相關(guān)事件谬返、在不同線(xiàn)程(甚至不同機(jī)器)上調(diào)度執(zhí)行,而代碼則保持如同順序執(zhí)行一樣簡(jiǎn)單日杈。
協(xié)程就像非常輕量級(jí)的線(xiàn)程遣铝。線(xiàn)程是由系統(tǒng)調(diào)度的,線(xiàn)程切換或線(xiàn)程阻塞的開(kāi)銷(xiāo)都比較大莉擒。而協(xié)程依賴(lài)于線(xiàn)程酿炸,但是協(xié)程掛起時(shí)不需要阻塞線(xiàn)程,幾乎是無(wú)代價(jià)的涨冀,協(xié)程是由開(kāi)發(fā)者控制的填硕。所以協(xié)程也像用戶(hù)態(tài)的線(xiàn)程,非常輕量級(jí)鹿鳖,一個(gè)線(xiàn)程中可以創(chuàng)建任意個(gè)協(xié)程扁眯。
協(xié)程很重要的一點(diǎn)就是當(dāng)它掛起的時(shí)候,它不會(huì)阻塞其他線(xiàn)程翅帜。協(xié)程底層庫(kù)也是異步處理阻塞任務(wù)姻檀,但是這些復(fù)雜的操作被底層庫(kù)封裝起來(lái),協(xié)程代碼的程序流是順序的藕甩,不再需要一堆的回調(diào)函數(shù)施敢,就像同步代碼一樣,也便于理解狭莱、調(diào)試和開(kāi)發(fā)痢士。它是可控的,線(xiàn)程的執(zhí)行和結(jié)束是由操作系統(tǒng)調(diào)度的田度,而協(xié)程可以手動(dòng)控制它的執(zhí)行和結(jié)束幕庐。
什么是AIDL?
重要
什么是AIDL骤素?
AIDL是Android中IPC的方式的一種匙睹。AIDL的作用是讓你可以在自己的APP里綁定一個(gè)其他的APP的Service,這樣你的APP可以與其他APP交互济竹。
AIDL解決了什么問(wèn)題痕檬?
只有當(dāng)你允許來(lái)自不同的客戶(hù)端訪問(wèn)你的服務(wù)并且需要處理多線(xiàn)程問(wèn)題時(shí)才必須用AIDL。
AIDL如何使用送浊?
服務(wù)端:
(1)梦谜、定義一個(gè)*.aidl的文件
(2)、實(shí)現(xiàn)AIDL文件生成的java接口
(3)、定義一個(gè)自己的Service唁桩,在實(shí)現(xiàn)Service時(shí)闭树,為了其他應(yīng)用可以通過(guò)bindService來(lái)和我們的Service進(jìn)行交互,我們都要實(shí)現(xiàn)Service中的onBind()方法荒澡,并且返回一個(gè)繼承了Binder的內(nèi)部類(lèi)报辱。
(4)、同一應(yīng)用中的Activity為該Service賦值单山,使用Service
客戶(hù)端:
(1)碍现、客戶(hù)端要想使用該服務(wù),需要知道服務(wù)在aidl文件中提供了什么服務(wù)饥侵,所以需要將服務(wù)端的aidl文件拷貝到客戶(hù)端鸵赫,且包名和文件名需要與服務(wù)端一致。
(2)躏升、通過(guò)bindService方法與Service交互辩棒,該方法中有一個(gè)ServiceConnection類(lèi)型的參數(shù),主要代碼便是在該接口中實(shí)現(xiàn)膨疏。
(3)一睁、在ServiceConnection實(shí)現(xiàn)類(lèi)中onServiceConnected方法中通過(guò)*.asInterface(service)獲取一個(gè)aidl接口的實(shí)例。
https://www.cnblogs.com/yoyohong/p/5660406.html
進(jìn)程間通信的方式佃却?
重要
(1)者吁、AIDL:功能強(qiáng)大,支持進(jìn)程間一對(duì)多的實(shí)時(shí)并發(fā)通信饲帅,并可實(shí)現(xiàn)RPC(遠(yuǎn)程過(guò)程調(diào)用)
(2)复凳、Messenger:支持一對(duì)多的串行實(shí)時(shí)通信,AIDL的簡(jiǎn)化版本灶泵。
(3)育八、Bundle:四大組件的進(jìn)程通信方式,只能傳輸Bundle支持的數(shù)據(jù)類(lèi)型赦邻。
(4)髓棋、ContentProvider:強(qiáng)大的數(shù)據(jù)源訪問(wèn)支持,主要支持CRUD操作惶洲,一對(duì)多進(jìn)程間數(shù)據(jù)共享按声。
(5)、Broadcast Receiver:廣播恬吕,但只能單向通信签则,接收者只能被動(dòng)接受信息。
(6)铐料、文件共享:在非高并發(fā)的情況下共享簡(jiǎn)單的數(shù)據(jù)怀愧。
(7)侨颈、Socket:通過(guò)網(wǎng)絡(luò)傳輸數(shù)據(jù)。
jni如何調(diào)用java層代碼芯义?
重要
C調(diào)用java中的方法使用的是反射
(1)、獲取字節(jié)碼文件(jclass(FindClass)(JNIEnv,const char*);)
(2)妻柒、通過(guò)字節(jié)碼對(duì)象找到方法對(duì)象(jmethodID(GetMethodID)(JNIEnv,jclass,const char,const char);)
(3)扛拨、通過(guò)字節(jié)碼文件創(chuàng)建一個(gè)object對(duì)象(該方法可選,方法中已經(jīng)傳遞了一個(gè)object举塔,如果需要調(diào)用的方法與本地方法不在同一個(gè)文件夾則需要?jiǎng)?chuàng)建新的object(jobject(AllocObject)(JNIEvn,jclass);)绑警,如果需要反射調(diào)用的java方法與本地方法不在同一個(gè)類(lèi)中,需要?jiǎng)?chuàng)建該方法央渣,但是如果是這樣计盒,并且需要更新UI操作,這個(gè)時(shí)候就會(huì)報(bào)空指針異常芽丹,因?yàn)檫@個(gè)時(shí)候調(diào)用的方法只是一個(gè)方法北启,沒(méi)有Activity聲明周期)。
(4)拔第、通過(guò)對(duì)象調(diào)用方法咕村,可以調(diào)用空參數(shù)方法,也可以調(diào)用有參數(shù)的方法蚊俺,并將參數(shù)通過(guò)調(diào)用的方法傳入(void(CallVoidMethod)(JNIEvn,jobject,jmethodID,...))懈涛。
Fragment的懶加載實(shí)現(xiàn)
重要
Fragment可見(jiàn)狀態(tài)改變時(shí)會(huì)被調(diào)用setUserVisibleHint()方法,可以通過(guò)復(fù)寫(xiě)該方法實(shí)現(xiàn)Fragment的懶加載泳猬,但需要注意該方法可能在onVIewCreated之前調(diào)用批钠,需要確保界面已經(jīng)初始化完成的情況下再去加載數(shù)據(jù),避免空指針得封。
requestLayout埋心,invalidate,postInvalidate區(qū)別與聯(lián)系
重要
相同點(diǎn):三個(gè)方法都有刷新界面的效果呛每。
不同點(diǎn):invalidate和postInvalidate只會(huì)調(diào)用onDraw()方法踩窖;requestLayout則會(huì)重新調(diào)用onMeasure、onLayout晨横、onDraw洋腮。
調(diào)用了invalidate方法后,會(huì)為該View添加一個(gè)標(biāo)記位手形,同時(shí)不斷向父容器請(qǐng)求刷新啥供,父容器通過(guò)計(jì)算得出自身需要重繪的區(qū)域,直到傳遞到ViewRootImpl中库糠,最終觸發(fā)performTraversals方法伙狐,進(jìn)行開(kāi)始View樹(shù)重繪流程(只繪制需要重繪的視圖)涮毫。
調(diào)用requestLayout方法,會(huì)標(biāo)記當(dāng)前View及父容器贷屎,同時(shí)逐層向上提交罢防,直到ViewRootImpl處理該事件,ViewRootImpl會(huì)調(diào)用三大流程唉侄,從measure開(kāi)始咒吐,對(duì)于每一個(gè)含有標(biāo)記位的view及其子View都會(huì)進(jìn)行測(cè)量onMeasure、布局onLayout属划、繪制onDraw恬叹。
getWidth()方法和getMeasureWidth()區(qū)別呢?
重要
首先getMeasureWidth()方法在measure()過(guò)程結(jié)束后就可以獲取到了同眯,而getWidth()方法要在layout()過(guò)程結(jié)束后才能獲取到绽昼。另外,getMeasureWidth()方法中的值是通過(guò)setMeasuredDimension()方法來(lái)進(jìn)行設(shè)置的须蜗,而getWidth()方法中的值則是通過(guò)視圖右邊的坐標(biāo)減去左邊的坐標(biāo)計(jì)算出來(lái)的硅确。
什么是MeasureSpec
重要
MeasureSpec代表一個(gè)32位int值,高兩位代表SpecMode(測(cè)量模式)唠粥,低30位代表SpecSize(具體大惺栉骸)。
SpecMode有三類(lèi):
UNSPECIFIED 表示父容器不對(duì)View有任何限制晤愧,一般用于系統(tǒng)內(nèi)部大莫,表示一種測(cè)量狀態(tài);
EXACTLY 父容器已經(jīng)檢測(cè)出view所需的精確大小官份,這時(shí)候view的最終大小SpecSize所指定的值只厘,相當(dāng)于match_parent或指定具體數(shù)值。
AT_MOST 父容器指定一個(gè)可用大小即SpecSize舅巷,view的大小不能大于這個(gè)值羔味,具體多大要看view的具體實(shí)現(xiàn),相當(dāng)于wrap_content钠右。
View的繪制原理
重要
View的繪制從ActivityThread類(lèi)中Handler的處理RESUME_ACTIVITY事件開(kāi)始赋元,在執(zhí)行performResumeActivity之后,創(chuàng)建Window以及DecorView并調(diào)用WindowManager的addView方法添加到屏幕上飒房,addView又調(diào)用ViewRootImpl的setView方法搁凸,最終執(zhí)行performTraversals方法,依次執(zhí)行performMeasure狠毯,performLayout护糖,performDraw。也就是view繪制的三大過(guò)程嚼松。
measure過(guò)程測(cè)量view的視圖大小嫡良,最終需要調(diào)用setMeasuredDimension方法設(shè)置測(cè)量的結(jié)果锰扶,如果是ViewGroup需要調(diào)用measureChildren或者measureChild方法進(jìn)而計(jì)算自己的大小。
layout過(guò)程是擺放view的過(guò)程寝受,View不需要實(shí)現(xiàn)坷牛,通常由ViewGroup實(shí)現(xiàn),在實(shí)現(xiàn)onLayout時(shí)可以通過(guò)getMeasuredWidth等方法獲取measure過(guò)程測(cè)量的結(jié)果進(jìn)行擺放很澄。
draw過(guò)程先是繪制背景漓帅,其次調(diào)用onDraw()方法繪制view的內(nèi)容,再然后調(diào)用dispatchDraw()調(diào)用子view的draw方法痴怨,最后繪制滾動(dòng)條。ViewGroup默認(rèn)不會(huì)執(zhí)行onDraw方法器予,如果復(fù)寫(xiě)了onDraw(Canvas)方法浪藻,需要調(diào)用 setWillNotDraw(false);清楚不需要繪制的標(biāo)記。
IdleHandler (閑時(shí)機(jī)制)
重要
IdleHandler是一個(gè)回調(diào)接口乾翔,可以通過(guò)MessageQueue的addIdleHandler添加實(shí)現(xiàn)類(lèi)爱葵。當(dāng)MessageQueue中的任務(wù)暫時(shí)處理完了(沒(méi)有新任務(wù)或者下一個(gè)任務(wù)延時(shí)在之后),這個(gè)時(shí)候會(huì)回調(diào)這個(gè)接口反浓,返回false萌丈,那么就會(huì)移除它,返回true就會(huì)在下次message處理完了的時(shí)候繼續(xù)回調(diào)雷则。
Looper.loop()為什么不會(huì)阻塞主線(xiàn)程
重要
Android是基于事件驅(qū)動(dòng)的辆雾,即所有Activity的生命周期都是通過(guò)Handler事件驅(qū)動(dòng)的。loop方法中會(huì)調(diào)用MessageQueue的next方法獲取下一個(gè)message月劈,當(dāng)沒(méi)有消息時(shí)度迂,基于Linux pipe/epoll機(jī)制會(huì)阻塞在loop的queue.next()中的nativePollOnce()方法里,并不會(huì)消耗CPU猜揪。
Android類(lèi)加載器
重要
Android平臺(tái)上虛擬機(jī)運(yùn)行的是Dex字節(jié)碼,一種對(duì)class文件優(yōu)化的產(chǎn)物,傳統(tǒng)Class文件是一個(gè)Java源碼文件會(huì)生成一個(gè).class文件惭墓,而Android是把所有Class文件進(jìn)行合并,優(yōu)化而姐,然后生成一個(gè)最終的class.dex,目的是把不同class文件重復(fù)的東西只需保留一份,如果我們的Android應(yīng)用不進(jìn)行分dex處理,最后一個(gè)應(yīng)用的apk只會(huì)有一個(gè)dex文件腊凶。
Android中常用的有兩種類(lèi)加載器,DexClassLoader和PathClassLoader拴念,它們都繼承于BaseDexClassLoader钧萍。區(qū)別在于調(diào)用父類(lèi)構(gòu)造器時(shí),DexClassLoader多傳了一個(gè)optimizedDirectory參數(shù)丈莺,這個(gè)目錄必須是內(nèi)部存儲(chǔ)路徑划煮,用來(lái)緩存系統(tǒng)創(chuàng)建的Dex文件。而PathClassLoader該參數(shù)為null缔俄,只能加載內(nèi)部存儲(chǔ)目錄的Dex文件弛秋。所以我們可以用DexClassLoader去加載外部的apk器躏。
三級(jí)緩存
一般
網(wǎng)絡(luò)加載,不優(yōu)先加載蟹略,速度慢登失,浪費(fèi)流量
本地緩存,次優(yōu)先加載挖炬,速度快
內(nèi)存緩存揽浙,優(yōu)先加載,速度最快
首次加載Android App時(shí)意敛,肯定要通過(guò)網(wǎng)絡(luò)交互來(lái)獲取圖片馅巷,之后我們可以將圖片保存至本地SD卡和內(nèi)存中,之后運(yùn)行APP時(shí)草姻,優(yōu)先訪問(wèn)內(nèi)存中的圖片緩存钓猬,若內(nèi)存中沒(méi)有,則加載本地SD卡中圖片撩独,最后選擇訪問(wèn)網(wǎng)絡(luò)
如何優(yōu)化ListView
一般
①I(mǎi)tem布局敞曹,層級(jí)越少越好,使用hierarchyview工具查看優(yōu)化综膀。
②復(fù)用convertView
③使用ViewHolder
④item中有圖片時(shí)澳迫,異步加載
⑤快速滑動(dòng)時(shí),不加載圖片
⑥item中有圖片時(shí)剧劝,應(yīng)對(duì)圖片進(jìn)行適當(dāng)壓縮
⑦實(shí)現(xiàn)數(shù)據(jù)的分頁(yè)加載
Touch事件傳遞機(jī)制
非常重要
在我們點(diǎn)擊屏幕時(shí)橄登,會(huì)有下列事件發(fā)生:
Activity調(diào)用dispathTouchEvent()方法,把事件傳遞給Window担平;
Window再將事件交給DecorView(DecorView是View的根布局)示绊;
DecorView再傳遞給ViewGroup;
Activity ——> Window ——> DecorView ——> ViewGroup——> View
事件分發(fā)的主要有三個(gè)關(guān)鍵方法
dispatchTouchEvent() 分發(fā)
onInterceptTouchEvent() 攔截 暂论,只有ViewGroup獨(dú)有此方法
onTouchEvent() 處理觸摸事件
Activity首先調(diào)用dispathTouchEvent()進(jìn)行分發(fā)面褐,接著調(diào)用super向下傳遞
ViewGroup首先調(diào)用dispathTouchEvent()進(jìn)行分發(fā),接著會(huì)調(diào)用onInterceptTouchEvent()(攔截事件)取胎。若攔截事件返回為true展哭,表示攔截,事件不會(huì)向下層的ViewGroup或者View傳遞闻蛀;false匪傍,表示不攔截,繼續(xù)分發(fā)事件觉痛。默認(rèn)是false役衡,需要提醒一下,View是沒(méi)有onInterceptTouchEvent()方法的
事件在ViewGroup和ViewGroup薪棒、ViewGroup和View之間進(jìn)行傳遞手蝎,最終到達(dá)View榕莺;
View調(diào)用dispathTouchEvent()方法,然后在OnTouchEvent()進(jìn)行處理事件棵介;OnTouchEvent() 返回true钉鸯,表示消耗此事件,不再向下傳遞邮辽;返回false唠雕,表示不消耗事件,交回上層處理吨述。
Service和線(xiàn)程的區(qū)別岩睁,我們?yōu)槭裁床挥胹ervice代替線(xiàn)程,相應(yīng)在什么情況下使用揣云?
重要
Service:service是android的一種機(jī)制笙僚,對(duì)應(yīng)的service是運(yùn)行在主線(xiàn)程上的,它是由系統(tǒng)進(jìn)程托管灵再。他們之間的通信類(lèi)似于client和server,是一種輕量級(jí)的ipc通信亿笤,這種通信的載體是binder翎迁,它是在linux層交換信息的一種ipc。
線(xiàn)程:線(xiàn)程是程序執(zhí)行的最小單元净薛,它是分配CPU的基本單位汪榔。可以用線(xiàn)程來(lái)執(zhí)行一些異步的操作肃拜。
區(qū)別:Thread?的運(yùn)行是獨(dú)立于?Activity?的痴腌,也就是說(shuō)當(dāng)一個(gè)?Activity?被?finish?之后,如果你沒(méi)有主動(dòng)停止?Thread?或者?Thread?里的?run?方法沒(méi)有執(zhí)行完畢的話(huà)燃领,Thread?也會(huì)一直執(zhí)行士聪。因此這里會(huì)出現(xiàn)一個(gè)問(wèn)題:當(dāng)?Activity?被?finish?之后,你不再持有該?Thread?的引用猛蔽。另一方面剥悟,你沒(méi)有辦法在不同的?Activity?中對(duì)同一?Thread?進(jìn)行控制;而Service則可以被多個(gè)activity共用(當(dāng)然你也可以說(shuō)我可以在服務(wù)里面新起線(xiàn)程這樣不就可以被多個(gè)activity共用了,其實(shí)這樣的本質(zhì)還是共用的服務(wù)而不是線(xiàn)程)曼库。
我們不用服務(wù)替代線(xiàn)程是因?yàn)椋悍?wù)(子類(lèi)IntentService則是在內(nèi)部添加了子線(xiàn)程)也是運(yùn)行在主線(xiàn)程上面区岗,而不是子線(xiàn)程,相當(dāng)于你還是需要新起線(xiàn)程來(lái)完成相應(yīng)的操作毁枯,這又是何苦啦慈缔;并且一個(gè)類(lèi)里面需要多線(xiàn)程操作的情況,服務(wù)是不是顯得很無(wú)力种玛。各有各的優(yōu)點(diǎn)藐鹤,下面來(lái)看使用情況瓤檐。
使用情況:
1.在應(yīng)用中,如果是長(zhǎng)時(shí)間的在后臺(tái)運(yùn)行教藻,而且不需要交互的情況下距帅,使用服務(wù)。
同樣是在后臺(tái)運(yùn)行括堤,不需要交互的情況下碌秸,如果只是完成某個(gè)任務(wù),之后就不需要運(yùn)行悄窃,而且可能是多個(gè)任務(wù)讥电,不需要長(zhǎng)時(shí)間運(yùn)行的情況下使用線(xiàn)程。
2.如果任務(wù)占用CPU時(shí)間多轧抗,資源大的情況下恩敌,要使用線(xiàn)程。
3.一般我們做下載任務(wù)都是在服務(wù)里面新起線(xiàn)程做異步任務(wù)來(lái)操作横媚,或者直接使用IntentService纠炮。
IntentService和Service有什么區(qū)別?
重要
Service不是獨(dú)立的進(jìn)程灯蝴,也不是獨(dú)立的線(xiàn)程恢口,它是依賴(lài)于應(yīng)用程序的主線(xiàn)程(比喻成沒(méi)有界面的activity),也就是說(shuō)穷躁,在更多時(shí)候不建議在Service中編寫(xiě)耗時(shí)的邏輯和操作耕肩,否則會(huì)引起ANR。?
IntentService是Service的子類(lèi)问潭,IntentService在執(zhí)行onCreate操作的時(shí)候猿诸,內(nèi)部開(kāi)了一個(gè)線(xiàn)程,去你執(zhí)行你的耗時(shí)操作狡忙。通過(guò)Handler looper message的方式實(shí)現(xiàn)了一個(gè)多線(xiàn)程的操作梳虽,同時(shí)耗時(shí)操作也可以被這個(gè)線(xiàn)程管理和執(zhí)行,同時(shí)不會(huì)產(chǎn)生ANR的情況灾茁。
JVM和DVM有什么區(qū)別怖辆,以及ART垃圾回收機(jī)制?
重要
1.基于不同位置
Dalvik:基于寄存器删顶,編譯和運(yùn)行都會(huì)快一些?
JVM: 基于棧, 編譯和運(yùn)行都會(huì)慢些
2.字節(jié)碼的區(qū)別
Dalvik: 執(zhí)行.dex格式的字節(jié)碼竖螃,是對(duì).class文件進(jìn)行壓縮后產(chǎn)生的,文件變小
JVM: 執(zhí)行.class格式的字節(jié)碼
3.運(yùn)行環(huán)境的區(qū)別 ?
Dalvik : 一個(gè)應(yīng)用啟動(dòng)都運(yùn)行一個(gè)單獨(dú)的虛擬機(jī)運(yùn)行在一個(gè)單獨(dú)的進(jìn)程中
JVM: 只能運(yùn)行一個(gè)實(shí)例, 也就是所有應(yīng)用都運(yùn)行在同一個(gè)JVM中
但是在Android4.4引入了ART,也是 Android 5.0 及更高版本的默認(rèn) Android 運(yùn)行時(shí)逗余。目前google已經(jīng)不再維護(hù)和發(fā)布dalvik(DVM)特咆。
1.ART GC 與 Dalvik 的主要區(qū)別在于 ART GC 引入了移動(dòng)垃圾回收器。使用移動(dòng) GC 的目的在于通過(guò)堆壓縮來(lái)減少后臺(tái)應(yīng)用使用的內(nèi)存。目前腻格,觸發(fā)堆壓縮的事件是 ActivityManager 進(jìn)程狀態(tài)的改變画拾。當(dāng)應(yīng)用轉(zhuǎn)到后臺(tái)運(yùn)行時(shí),它會(huì)通知 ART 已進(jìn)入不再“感知”卡頓的進(jìn)程狀態(tài)菜职。此時(shí) ART 會(huì)進(jìn)行一些操作(例如青抛,壓縮和監(jiān)視器壓縮),從而導(dǎo)致應(yīng)用線(xiàn)程長(zhǎng)時(shí)間暫停酬核。目前正在使用的兩個(gè)移動(dòng) GC 是同構(gòu)空間壓縮和半空間壓縮蜜另。
2.與 Dalvik 相比,暫停次數(shù)從 2 次減少到 1 次嫡意。Dalvik 的第一次暫停主要是為了進(jìn)行根標(biāo)記举瑰,而這個(gè)動(dòng)作在 ART中已經(jīng)是讓線(xiàn)程自己去標(biāo)記,然后馬上恢復(fù)運(yùn)行蔬螟,這樣就減少了一次暫停此迅。
3.與 Dalvik 類(lèi)似,ART GC 在清除過(guò)程開(kāi)始之前也會(huì)暫停 1 次旧巾。兩者在這方面的主要差異在于:在此暫停期間耸序,某些 Dalvik 環(huán)節(jié)在 ART 中并發(fā)進(jìn)行。這些環(huán)節(jié)包括 java.lang.ref.Reference 處理鲁猩、系統(tǒng)弱清除(例如佑吝,jni 弱全局等)、重新標(biāo)記非線(xiàn)程根和卡片預(yù)清理绳匀。在 ART 暫停期間仍進(jìn)行的階段包括掃描臟卡片以及重新標(biāo)記線(xiàn)程根,這些操作有助于縮短暫停時(shí)間炸客。
4.相對(duì)于 Dalvik疾棵,ART GC 改進(jìn)的最后一個(gè)方面是粘性 CMS 回收器增加了 GC 吞吐量。不同于普通的分代 GC痹仙,粘性 CMS 不移動(dòng)是尔。系統(tǒng)會(huì)將年輕對(duì)象保存在一個(gè)分配堆棧(基本上是 java.lang.Object 數(shù)組)中,而非為其設(shè)置一個(gè)專(zhuān)屬區(qū)域开仰。這樣可以避免移動(dòng)所需的對(duì)象以維持低暫停次數(shù)拟枚,但缺點(diǎn)是容易在堆棧中加入大量復(fù)雜對(duì)象圖像而使堆棧變長(zhǎng)
最后說(shuō)一下回收機(jī)制:
1. 先回收與其他Activity 或Service/Intent Receiver 無(wú)關(guān)的進(jìn)程(即優(yōu)先回收獨(dú)立的Activity)因此建議,我們的一些(耗時(shí))后臺(tái)操作,最好是作成Service的形式
2.不可見(jiàn)(處于Stopped狀態(tài)的)Activity
3.Service進(jìn)程(除非真的沒(méi)有內(nèi)存可用時(shí)會(huì)被銷(xiāo)毀)
4.非活動(dòng)的可見(jiàn)的(Paused狀態(tài)的)Activity
5.當(dāng)前正在運(yùn)行(Active/Running狀態(tài)的)Activity?
Lru算法的原理众弓?
重要
LRU(Least Recently Used)最近最少使用恩溅,LRU使用的是LinkedHashMap,如果鏈表中存在一個(gè)數(shù)則將其置頂谓娃,如果沒(méi)有則直接在頂部加入這個(gè)數(shù)并將其底部的數(shù)移除脚乡。大致可以這樣理解,具體可以再搜索一下LRU算法滨达。
橫豎屏切換activity生命周期奶稠?
重要
1俯艰、AndroidManifest.xml不設(shè)置Activity的android:configChanges時(shí),切屏?xí)匦抡{(diào)用各個(gè)生命周期锌订,
切橫屏?xí)r會(huì)執(zhí)行一次竹握,切豎屏?xí)r會(huì)執(zhí)行兩次。生命周期如下:
onSaveInstanceState-onPause-onStop-onDestory-onCreate-onStart-onRestoreInstanceState-onResume
2辆飘、設(shè)置Activity的android:configChanges="orientation"時(shí)啦辐,切屏還是會(huì)重新調(diào)
用各個(gè)生命周期,切橫劈猪、豎屏?xí)r只會(huì)執(zhí)行一次昧甘。生命周期如下:
onSaveInstanceState-onPause-onStop-onDestory-onCreate-onStart-onRestoreInstanceState-onResume
3、設(shè)置Activity的android:configChanges="orientation|keyboardHidden"時(shí)战得,
切屏不會(huì)重新調(diào)用各個(gè)生命周期充边,只會(huì)執(zhí)行onConfigurationChanged方法。
線(xiàn)程間通信有哪些方式常侦?
一般
接口
Handler
觀察者模式(EventBus)
Android使用RunonUiThread可以切換到主線(xiàn)程
AsyncTask
BroadCast
SharedPreferences
Android的數(shù)據(jù)存儲(chǔ)方式有哪些浇冰?
一般
1.Sharedpreferences(適合輕量級(jí)數(shù)據(jù)存儲(chǔ),采用XML鍵值對(duì)的形式存儲(chǔ)到本地聋亡,只能運(yùn)用于一個(gè)App內(nèi))?
2.文件存儲(chǔ)
3.SQLite數(shù)據(jù)庫(kù)存儲(chǔ)
4.ContentProvider
5.網(wǎng)絡(luò)存儲(chǔ)
Handler機(jī)制的原理與RXJava有什么區(qū)別肘习?
一般
當(dāng)時(shí)問(wèn)到我的時(shí)候我還真不知道怎么回答,我只知道相同點(diǎn):都是為了線(xiàn)程間通信坡倔。
區(qū)別:
1.RxJava線(xiàn)程切換更方便(直接可以切換子線(xiàn)程和UI線(xiàn)程)漂佩,Handler需要在子線(xiàn)程去發(fā)送消息,在主線(xiàn)程去接受消息然后才能改變UI罪塔。
2.RxJava是觀察者模式投蝉,Handler是消息隊(duì)列用的是雙向鏈表。
SharedPreference跨進(jìn)程使用會(huì)怎么樣征堪?如何保證跨進(jìn)程使用安全瘩缆?
重要
在兩個(gè)應(yīng)用的manifest配置中好相同的shartdUserId屬性,A應(yīng)用正常保存數(shù)據(jù)佃蚜,B應(yīng)用
createPackageContext("com.netease.nim.demo", CONTEXT_IGNORE_SECURITY)
獲取context然后獲取應(yīng)用數(shù)據(jù)庸娱,為保證數(shù)據(jù)安全,使用加密存儲(chǔ)
recyclerView嵌套卡頓解決如何解決
重要
設(shè)置預(yù)加載的數(shù)量LinearLayoutManager.setInitialPrefetchItemCount(4)谐算,默認(rèn)是預(yù)加載2個(gè)熟尉,
設(shè)置子項(xiàng)緩存,
設(shè)置自帶滑動(dòng)沖突解決屬性rv.setHasFixedSize(true); rv.setNestedScrollingEnabled(false);
可以完美解決,不過(guò)Google不推薦RecyClerView嵌套使用,需要嵌套盡量找類(lèi)似于ExpandableListView 第三方控件來(lái)解決
RecyclerView和ListView的區(qū)別
一般
RecyclerView可以完成ListView,GridView的效果,還可以完成瀑布流的效果叨粘。同時(shí)還可以設(shè)置列表的滾動(dòng)方向(垂直或者水平)餐曼;
RecyclerView中view的復(fù)用不需要開(kāi)發(fā)者自己寫(xiě)代碼诚欠,系統(tǒng)已經(jīng)幫封裝完成了吧碾。
RecyclerView可以進(jìn)行局部刷新睦刃。
RecyclerView提供了API來(lái)實(shí)現(xiàn)item的動(dòng)畫(huà)效果不瓶。
在性能上:
如果需要頻繁的刷新數(shù)據(jù)棚放,需要添加動(dòng)畫(huà)枚粘,則RecyclerView有較大的優(yōu)勢(shì)。
如果只是作為列表展示飘蚯,則兩者區(qū)別并不是很大馍迄。
HybridApp WebView和JS交互
重要
Android與JS通過(guò)WebView互相調(diào)用方法,實(shí)際上是:
Android去調(diào)用JS的代碼
1. 通過(guò)WebView的loadUrl(),使用該方法比較簡(jiǎn)潔局骤,方便攀圈。但是效率比較低,獲取返回值比較困難峦甩。
2. 通過(guò)WebView的evaluateJavascript(),該方法效率高赘来,但是4.4以上的版本才支持,4.4以下版本不支持凯傲。所以建議兩者混合使用犬辰。
JS去調(diào)用Android的代碼
1. 通過(guò)WebView的addJavascriptInterface()進(jìn)行對(duì)象映射 ,該方法使用簡(jiǎn)單冰单,僅將Android對(duì)象和JS對(duì)象映射即可幌缝,但是存在比較大的漏洞。
漏洞產(chǎn)生原因是:當(dāng)JS拿到Android這個(gè)對(duì)象后诫欠,就可以調(diào)用這個(gè)Android對(duì)象中所有的方法涵卵,包括系統(tǒng)類(lèi)(java.lang.Runtime 類(lèi)),從而進(jìn)行任意代碼執(zhí)行荒叼。
解決方式:
(1)Google 在Android 4.2 版本中規(guī)定對(duì)被調(diào)用的函數(shù)以 @JavascriptInterface進(jìn)行注解從而避免漏洞攻擊轿偎。
(2)在Android 4.2版本之前采用攔截prompt()進(jìn)行漏洞修復(fù)。
2. 通過(guò) WebViewClient 的shouldOverrideUrlLoading ()方法回調(diào)攔截 url 甩挫。這種方式的優(yōu)點(diǎn):不存在方式1的漏洞;缺點(diǎn):JS獲取Android方法的返回值復(fù)雜椿每。(ios主要用的是這個(gè)方式)
(1)Android通過(guò) WebViewClient 的回調(diào)方法shouldOverrideUrlLoading ()攔截 url
(2)解析該 url 的協(xié)議
(3)如果檢測(cè)到是預(yù)先約定好的協(xié)議伊者,就調(diào)用相應(yīng)方法
3. 通過(guò) WebChromeClient 的onJsAlert()、onJsConfirm()间护、onJsPrompt()方法回調(diào)攔截JS對(duì)話(huà)框alert()亦渗、confirm()、prompt() 消息
這種方式的優(yōu)點(diǎn):不存在方式1的漏洞汁尺;缺點(diǎn):JS獲取Android方法的返回值復(fù)雜法精。
Handler的原理
非常重要
Handler 主要用于跨線(xiàn)程通信。涉及MessageQueue/Message/Looper/Handler 這 4 個(gè)類(lèi)。
Message:消息搂蜓,分為硬件產(chǎn)生的消息和軟件生成的消息狼荞。
MessageQueue:消息隊(duì)列,主要功能是向消息池投遞信息 (MessageQueue.enqueueMessage) 和取走消息池的信息 (MessageQueue.next) 帮碰。
Handler:消息處理者相味,負(fù)責(zé)向消息池中發(fā)送消息 (Handler.enqueueMessage) 和處理消息 (Handler.handleMessage) 。
Looper:消息泵殉挽,不斷循環(huán)執(zhí)行 (Looper.loop) 丰涉,按分發(fā)機(jī)制將消息分發(fā)給目標(biāo)處理者。
它們之間的類(lèi)關(guān)系:
Looper 有一個(gè) MessageQueue 消息隊(duì)列斯碌;MessageQueue 有一組待處理的 Message一死;Message 中有一個(gè)用于處理消息的 Handler;Handler 中有 Looper 和 MessageQueue傻唾。
Android中跨進(jìn)程通訊的幾種方式
重要
Android 跨進(jìn)程通信投慈,像intent,contentProvider,廣播策吠,service都可以跨進(jìn)程通信逛裤。
intent:這種跨進(jìn)程方式并不是訪問(wèn)內(nèi)存的形式猴抹,它需要傳遞一個(gè)uri,比如說(shuō)打電話(huà)蟀给。
contentProvider:這種形式蝙砌,是使用數(shù)據(jù)共享的形式進(jìn)行數(shù)據(jù)共享择克。
service:遠(yuǎn)程服務(wù),aidl
廣播
Android中的幾種動(dòng)畫(huà)
重要
幀動(dòng)畫(huà):指通過(guò)指定每一幀的圖片和播放時(shí)間前普,有序的進(jìn)行播放而形成動(dòng)畫(huà)效果拭卿,比如想聽(tīng)的律動(dòng)條峻厚。
補(bǔ)間動(dòng)畫(huà):指通過(guò)指定View的初始狀態(tài)惠桃、變化時(shí)間、方式罐孝,通過(guò)一系列的算法去進(jìn)行圖形變換,從而形成動(dòng)畫(huà)效果怒见,主要有Alpha遣耍、Scale、Translate纪隙、Rotate四種效果绵咱。注意:只是在視圖層實(shí)現(xiàn)了動(dòng)畫(huà)效果,并沒(méi)有真正改變View的屬性麸锉,比如滑動(dòng)列表花沉,改變標(biāo)題欄的透明度。
屬性動(dòng)畫(huà):在Android3.0的時(shí)候才支持忽媒,通過(guò)不斷的改變View的屬性晦雨,不斷的重繪而形成動(dòng)畫(huà)效果。相比于視圖動(dòng)畫(huà)奥邮,View的屬性是真正改變了洽腺。比如view的旋轉(zhuǎn),放大藕坯,縮小炼彪。
View,ViewGroup事件分發(fā)
非常重要
View的事件分發(fā)機(jī)制可以使用下圖表示:
need-to-insert-img
如上圖齐疙,圖分為3層,從上往下依次是Activity轿塔、ViewGroup勾缭、View。
事件從左上角那個(gè)白色箭頭開(kāi)始幻梯,由Activity的dispatchTouchEvent做分發(fā)
箭頭的上面字代表方法返回值咬摇,(return true肛鹏、return false、return super.xxxxx(),super
的意思是調(diào)用父類(lèi)實(shí)現(xiàn)健田。
dispatchTouchEvent和 onTouchEvent的框里有個(gè)【true---->消費(fèi)】的字,表示的意思是如果方法返回true好爬,那么代表事件就此消費(fèi)存炮,不會(huì)繼續(xù)往別的地方傳了,事件終止享完。
目前所有的圖的事件是針對(duì)ACTION_DOWN的,對(duì)于ACTION_MOVE和ACTION_UP我們最后做分析茴迁。
之前圖中的Activity 的dispatchTouchEvent 有誤(圖已修復(fù)),只有return
super.dispatchTouchEvent(ev) 才是往下走倦卖,返回true 或者 false 事件就被消費(fèi)了(終止傳遞)。
ViewGroup事件分發(fā)
當(dāng)一個(gè)點(diǎn)擊事件產(chǎn)生后嘉竟,它的傳遞過(guò)程將遵循如下順序:
Activity -> Window -> View
事件總是會(huì)傳遞給Activity,之后Activity再傳遞給Window边苹,最后Window再傳遞給頂級(jí)的View,頂級(jí)的View在接收到事件后就會(huì)按照事件分發(fā)機(jī)制去分發(fā)事件茬底。如果一個(gè)View的onTouchEvent返回了FALSE,那么它的父容器的onTouchEvent將會(huì)被調(diào)用最爬,依次類(lèi)推,如果所有都不處理這個(gè)事件的話(huà)蒜鸡,那么Activity將會(huì)處理這個(gè)事件。
對(duì)于ViewGroup的事件分發(fā)過(guò)程忘朝,大概是這樣的:如果頂級(jí)的ViewGroup攔截事件即onInterceptTouchEvent返回true的話(huà)溉箕,則事件會(huì)交給ViewGroup處理,如果ViewGroup的onTouchListener被設(shè)置的話(huà)寡痰,則onTouch將會(huì)被調(diào)用,否則的話(huà)onTouchEvent將會(huì)被調(diào)用贞滨,也就是說(shuō):兩者都設(shè)置的話(huà),onTouch將會(huì)屏蔽掉onTouchEvent,在onTouchEvent中腰池,如果設(shè)置了onClickerListener的話(huà),那么onClick將會(huì)被調(diào)用奏属。如果頂級(jí)ViewGroup不攔截的話(huà),那么事件將會(huì)被傳遞給它所在的點(diǎn)擊事件的子view嘱腥,這時(shí)候子view的dispatchTouchEvent將會(huì)被調(diào)用
View的事件分發(fā)
dispatchTouchEvent -> onTouch(setOnTouchListener) -> onTouchEvent -> onClick
onTouch和onTouchEvent的區(qū)別
兩者都是在dispatchTouchEvent中調(diào)用的,onTouch優(yōu)先于onTouchEvent添诉,如果onTouch返回true,那么onTouchEvent則不執(zhí)行须眷,及onClick也不執(zhí)行
四種LaunchMode及其使用場(chǎng)景
重要
此處延伸:棧(First In Last Out)與隊(duì)列(First In First Out)的區(qū)別
棧與隊(duì)列的區(qū)別:
1.?隊(duì)列先進(jìn)先出准浴,棧先進(jìn)后出
2. 對(duì)插入和刪除操作的"限定"求橄。 棧是限定只能在表的一端進(jìn)行插入和刪除操作的線(xiàn)性表罐农。 隊(duì)列是限定只能在表的一端進(jìn)行插入和在另一端進(jìn)行刪除操作的線(xiàn)性表涵亏。
3. 遍歷數(shù)據(jù)速度不同
standard 模式
這是默認(rèn)模式,每次激活A(yù)ctivity時(shí)都會(huì)創(chuàng)建Activity實(shí)例,并放入任務(wù)棧中搀矫。使用場(chǎng)景:大多數(shù)Activity。
singleTop 模式
如果在任務(wù)的棧頂正好存在該Activity的實(shí)例,就重用該實(shí)例( 會(huì)調(diào)用實(shí)例的 onNewIntent() )虹茶,否則就會(huì)創(chuàng)建新的實(shí)例并放入棧頂董济,即使棧中已經(jīng)存在該Activity的實(shí)例,只要不在棧頂封豪,都會(huì)創(chuàng)建新的實(shí)例。使用場(chǎng)景如新聞?lì)惢蛘唛喿x類(lèi)App的內(nèi)容頁(yè)面。
singleTask 模式
如果在棧中已經(jīng)有該Activity的實(shí)例刷袍,就重用該實(shí)例(會(huì)調(diào)用實(shí)例的 onNewIntent() )。重用時(shí),會(huì)讓該實(shí)例回到棧頂太闺,因此在它上面的實(shí)例將會(huì)被移出棧。如果棧中不存在該實(shí)例,將會(huì)創(chuàng)建新的實(shí)例放入棧中轧粟。使用場(chǎng)景如瀏覽器的主界面通惫。不管從多少個(gè)應(yīng)用啟動(dòng)瀏覽器惭嚣,只會(huì)啟動(dòng)主界面一次延旧,其余情況都會(huì)走onNewIntent,并且會(huì)清空主界面上面的其他頁(yè)面弯洗。
singleInstance 模式
在一個(gè)新棧中創(chuàng)建該Activity的實(shí)例藐吮,并讓多個(gè)應(yīng)用共享該棧中的該Activity實(shí)例。一旦該模式的Activity實(shí)例已經(jīng)存在于某個(gè)棧中句占,任何應(yīng)用再激活該Activity時(shí)都會(huì)重用該棧中的實(shí)例( 會(huì)調(diào)用實(shí)例的 onNewIntent() )。其效果相當(dāng)于多個(gè)應(yīng)用共享一個(gè)應(yīng)用,不管誰(shuí)激活該 Activity 都會(huì)進(jìn)入同一個(gè)應(yīng)用中哺壶。使用場(chǎng)景如鬧鈴提醒,將鬧鈴提醒與鬧鈴設(shè)置分離渊胸。singleInstance不要用于中間頁(yè)面,如果用于中間頁(yè)面,跳轉(zhuǎn)會(huì)有問(wèn)題疫稿,比如:A -> B (singleInstance) -> C俊扳,完全退出后号坡,在此啟動(dòng),首先打開(kāi)的是B。
講解一下Context
非常重要
Context是一個(gè)抽象基類(lèi)籽慢。
在翻譯為上下文,也可以理解為環(huán)境涕刚,是提供一些程序的運(yùn)行環(huán)境基礎(chǔ)信息嗡综。
Context下有兩個(gè)子類(lèi),ContextWrapper是上下文功能的封裝類(lèi)杜漠,而ContextImpl則是上下文功能的實(shí)現(xiàn)類(lèi)察净。而ContextWrapper又有三個(gè)直接的子類(lèi),?ContextThemeWrapper盼樟、Service和Application氢卡。
其中,ContextThemeWrapper是一個(gè)帶主題的封裝類(lèi)晨缴,而它有一個(gè)直接子類(lèi)就是Activity译秦,所以Activity和Service以及Application的Context是不一樣的,只有Activity需要主題击碗,Service不需要主題筑悴。
Context一共有三種類(lèi)型,分別是Application稍途、Activity和Service阁吝。這三個(gè)類(lèi)雖然分別各種承擔(dān)著不同的作用,但它們都屬于Context的一種械拍,而它們具體Context的功能則是由ContextImpl類(lèi)去實(shí)現(xiàn)的突勇,因此在絕大多數(shù)場(chǎng)景下,Activity坷虑、Service和Application這三種類(lèi)型的Context都是可以通用的甲馋。
不過(guò)有幾種場(chǎng)景比較特殊,比如啟動(dòng)Activity迄损,還有彈出Dialog定躏。出于安全原因的考慮,Android是不允許Activity或Dialog憑空出現(xiàn)的海蔽,一個(gè)Activity的啟動(dòng)必須要建立在另一個(gè)Activity的基礎(chǔ)之上共屈,也就是以此形成的返回棧绑谣。而Dialog則必須在一個(gè)Activity上面彈出(除非是System Alert類(lèi)型的Dialog)党窜,因此在這種場(chǎng)景下,我們只能使用Activity類(lèi)型的Context借宵,否則將會(huì)出錯(cuò)幌衣。
getApplicationContext()和getApplication()方法得到的對(duì)象都是同一個(gè)application對(duì)象,只是對(duì)象的類(lèi)型不一樣壤玫。
Context數(shù)量 = Activity數(shù)量 + Service數(shù)量 + 1 (1為Application)
Service生命周期
非常重要
Service 作為 Android四大組件之一豁护,應(yīng)用非常廣泛。和Activity一樣欲间,Service 也有一系列的生命周期回調(diào)函數(shù)楚里,具體如下圖。
通常猎贴,啟動(dòng)Service有兩種方式班缎,startService和bindService方式蝴光。
startService生命周期
當(dāng)我們通過(guò)調(diào)用了Context的startService方法后,我們便啟動(dòng)了Service达址,通過(guò)startService方法啟動(dòng)的Service會(huì)一直無(wú)限期地運(yùn)行下去蔑祟,只有在外部調(diào)用Context的stopService或Service內(nèi)部調(diào)用Service的stopSelf方法時(shí),該Service才會(huì)停止運(yùn)行并銷(xiāo)毀沉唠。
onCreate
onCreate: 執(zhí)行startService方法時(shí)疆虚,如果Service沒(méi)有運(yùn)行的時(shí)候會(huì)創(chuàng)建該Service并執(zhí)行Service的onCreate回調(diào)方法;如果Service已經(jīng)處于運(yùn)行中满葛,那么執(zhí)行startService方法不會(huì)執(zhí)行Service的onCreate方法径簿。也就是說(shuō)如果多次執(zhí)行了Context的startService方法啟動(dòng)Service,Service方法的onCreate方法只會(huì)在第一次創(chuàng)建Service的時(shí)候調(diào)用一次嘀韧,以后均不會(huì)再次調(diào)用牍帚。我們可以在onCreate方法中完成一些Service初始化相關(guān)的操作。
onStartCommand
onStartCommand: 在執(zhí)行了startService方法之后乳蛾,有可能會(huì)調(diào)用Service的onCreate方法暗赶,在這之后一定會(huì)執(zhí)行Service的onStartCommand回調(diào)方法。也就是說(shuō)肃叶,如果多次執(zhí)行了Context的startService方法蹂随,那么Service的onStartCommand方法也會(huì)相應(yīng)的多次調(diào)用。onStartCommand方法很重要因惭,我們?cè)谠摲椒ㄖ懈鶕?jù)傳入的Intent參數(shù)進(jìn)行實(shí)際的操作岳锁,比如會(huì)在此處創(chuàng)建一個(gè)線(xiàn)程用于下載數(shù)據(jù)或播放音樂(lè)等。
public @StartResult int onStartCommand(Intent intent, @StartArgFlags int flags, int startId) {
}
當(dāng)Android面臨內(nèi)存匱乏的時(shí)候蹦魔,可能會(huì)銷(xiāo)毀掉你當(dāng)前運(yùn)行的Service激率,然后待內(nèi)存充足的時(shí)候可以重新創(chuàng)建Service,Service被Android系統(tǒng)強(qiáng)制銷(xiāo)毀并再次重建的行為依賴(lài)于Service中onStartCommand方法的返回值勿决。我們常用的返回值有三種值乒躺,START_NOT_STICKY、START_STICKY和START_REDELIVER_INTENT低缩,這三個(gè)值都是Service中的靜態(tài)常量嘉冒。
START_NOT_STICKY
如果返回START_NOT_STICKY,表示當(dāng)Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后咆繁,不會(huì)重新創(chuàng)建該Service讳推,當(dāng)然如果在其被殺掉之后一段時(shí)間又調(diào)用了startService,那么該Service又將被實(shí)例化玩般。那什么情境下返回該值比較恰當(dāng)呢银觅?如果我們某個(gè)Service執(zhí)行的工作被中斷幾次無(wú)關(guān)緊要或者對(duì)Android內(nèi)存緊張的情況下需要被殺掉且不會(huì)立即重新創(chuàng)建這種行為也可接受,那么我們便可將 onStartCommand的返回值設(shè)置為START_NOT_STICKY坏为。舉個(gè)例子究驴,某個(gè)Service需要定時(shí)從服務(wù)器獲取最新數(shù)據(jù):通過(guò)一個(gè)定時(shí)器每隔指定的N分鐘讓定時(shí)器啟動(dòng)Service去獲取服務(wù)端的最新數(shù)據(jù)慨仿。當(dāng)執(zhí)行到Service的onStartCommand時(shí),在該方法內(nèi)再規(guī)劃一個(gè)N分鐘后的定時(shí)器用于再次啟動(dòng)該Service并開(kāi)辟一個(gè)新的線(xiàn)程去執(zhí)行網(wǎng)絡(luò)操作纳胧。假設(shè)Service在從服務(wù)器獲取最新數(shù)據(jù)的過(guò)程中被Android系統(tǒng)強(qiáng)制殺掉镰吆,Service不會(huì)再重新創(chuàng)建,這也沒(méi)關(guān)系跑慕,因?yàn)樵龠^(guò)N分鐘定時(shí)器就會(huì)再次啟動(dòng)該Service并重新獲取數(shù)據(jù)万皿。
START_STICKY
如果返回START_STICKY,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后核行,Android系統(tǒng)會(huì)將該Service依然設(shè)置為started狀態(tài)(即運(yùn)行狀態(tài))牢硅,但是不再保存onStartCommand方法傳入的intent對(duì)象,然后Android系統(tǒng)會(huì)嘗試再次重新創(chuàng)建該Service芝雪,并執(zhí)行onStartCommand回調(diào)方法减余,但是onStartCommand回調(diào)方法的Intent參數(shù)為null,也就是onStartCommand方法雖然會(huì)執(zhí)行但是獲取不到intent信息惩系。如果你的Service可以在任意時(shí)刻運(yùn)行或結(jié)束都沒(méi)什么問(wèn)題位岔,而且不需要intent信息,那么就可以在onStartCommand方法中返回START_STICKY堡牡,比如一個(gè)用來(lái)播放背景音樂(lè)功能的Service就適合返回該值抒抬。
START_REDELIVER_INTENT
如果返回START_REDELIVER_INTENT,表示Service運(yùn)行的進(jìn)程被Android系統(tǒng)強(qiáng)制殺掉之后晤柄,與返回START_STICKY的情況類(lèi)似擦剑,Android系統(tǒng)會(huì)將再次重新創(chuàng)建該Service,并執(zhí)行onStartCommand回調(diào)方法芥颈,但是不同的是惠勒,Android系統(tǒng)會(huì)再次將Service在被殺掉之前最后一次傳入onStartCommand方法中的Intent再次保留下來(lái)并再次傳入到重新創(chuàng)建后的Service的onStartCommand方法中,這樣我們就能讀取到intent參數(shù)爬坑。只要返回START_REDELIVER_INTENT纠屋,那么onStartCommand重的intent一定不是null。如果我們的Service需要依賴(lài)具體的Intent才能運(yùn)行(需要從Intent中讀取相關(guān)數(shù)據(jù)信息等)妇垢,并且在強(qiáng)制銷(xiāo)毀后有必要重新創(chuàng)建運(yùn)行巾遭,那么這樣的Service就適合返回START_REDELIVER_INTENT肉康。
onBind
Service中的onBind方法是抽象方法闯估,所以Service類(lèi)本身就是抽象類(lèi),也就是onBind方法是必須重寫(xiě)的吼和,即使我們用不到涨薪。在通過(guò)startService使用Service時(shí),我們?cè)谥貙?xiě)onBind方法時(shí)炫乓,只需要將其返回null即可刚夺。onBind方法主要是用于給bindService方法調(diào)用Service時(shí)才會(huì)使用到献丑。
onDestroy
onDestroy: 通過(guò)startService方法啟動(dòng)的Service會(huì)無(wú)限期運(yùn)行,只有當(dāng)調(diào)用了Context的stopService或在Service內(nèi)部調(diào)用stopSelf方法時(shí)侠姑,Service才會(huì)停止運(yùn)行并銷(xiāo)毀创橄,在銷(xiāo)毀的時(shí)候會(huì)執(zhí)行Service回調(diào)函數(shù)。
bindService生命周期
bindService方式啟動(dòng)Service主要有以下幾個(gè)生命周期函數(shù):
onCreate():
首次創(chuàng)建服務(wù)時(shí)莽红,系統(tǒng)將調(diào)用此方法妥畏。如果服務(wù)已在運(yùn)行,則不會(huì)調(diào)用此方法安吁,該方法只調(diào)用一次醉蚁。
onStartCommand():
當(dāng)另一個(gè)組件通過(guò)調(diào)用startService()請(qǐng)求啟動(dòng)服務(wù)時(shí),系統(tǒng)將調(diào)用此方法鬼店。
onDestroy():
當(dāng)服務(wù)不再使用且將被銷(xiāo)毀時(shí)网棍,系統(tǒng)將調(diào)用此方法。
onBind():
當(dāng)另一個(gè)組件通過(guò)調(diào)用bindService()與服務(wù)綁定時(shí)妇智,系統(tǒng)將調(diào)用此方法滥玷。
onUnbind():
當(dāng)另一個(gè)組件通過(guò)調(diào)用unbindService()與服務(wù)解綁時(shí),系統(tǒng)將調(diào)用此方法巍棱。
onRebind():
當(dāng)舊的組件與服務(wù)解綁后罗捎,另一個(gè)新的組件與服務(wù)綁定,onUnbind()返回true時(shí)拉盾,系統(tǒng)將調(diào)用此方法桨菜。
Activity生命周期
非常重要
完整生命周期:完整生命周期始于onCreate方法回調(diào),止于onDestroy方法回調(diào)
可見(jiàn)周期:可見(jiàn)周期始于onStart方法回調(diào)捉偏,止于onStop方法回調(diào)
前臺(tái)周期:前臺(tái)周期始于onResume方法回調(diào)倒得,止于onPause方法回調(diào)
下面簡(jiǎn)單介紹一下各個(gè)生命周期方法:
onCreate 生命周期的第一個(gè)方法,表示Activity正在創(chuàng)建(啟動(dòng))夭禽。特別說(shuō)明:若您在該方法內(nèi)調(diào)用finish方法霞掺,則會(huì)立即出發(fā)onDestroy回調(diào),其他生命周期不會(huì)執(zhí)行
onRestart 該方法觸發(fā)的前提:onStop方法被調(diào)用讹躯。onStop方法被調(diào)用而導(dǎo)致的Activity不可見(jiàn)到Activity再次可見(jiàn)時(shí)被調(diào)用菩彬。該方法回調(diào)之后系統(tǒng)會(huì)相繼觸發(fā)onStart和onResume方法。
onStart Activity可見(jiàn)時(shí)調(diào)用(此時(shí)Activity尚未處于前臺(tái)):在onCreate方法之后或由onStop方法被調(diào)用而導(dǎo)致的Activity不可見(jiàn)到Activity再次可見(jiàn)時(shí)被調(diào)用
onResume 該方法的回調(diào)標(biāo)識(shí)Activity處于前臺(tái)潮梯。官方文檔指明骗灶,這里比較適合動(dòng)畫(huà)啟動(dòng)及排他性設(shè)備訪問(wèn)(如相機(jī))等
onPause Activity即將進(jìn)入后臺(tái)時(shí)回調(diào)此方法。需要特別注意的是秉馏,若Activity A啟動(dòng)Activity B耙旦,則Activity A的onPause方法回調(diào)完成后,Activity B才會(huì)創(chuàng)建萝究,因此不要在該回調(diào)方法中做耗時(shí)操作免都。
onStop Activity由可見(jiàn)到不可見(jiàn)時(shí)回調(diào)此方法
onDestroy 生命周期的最后一個(gè)方法锉罐,表示Activity即將被銷(xiāo)毀。官方文檔指明绕娘,在某些情況下脓规,系統(tǒng)會(huì)簡(jiǎn)單粗暴的殺掉Activity的宿主進(jìn)程(如下文示意圖中的標(biāo)注1),因此我們不應(yīng)該依賴(lài)此方法做數(shù)據(jù)存儲(chǔ)工作险领,可在此方法中做資源釋放操作
下圖為官方文檔給出的示意圖
此圖很清晰嚴(yán)謹(jǐn)抖拦,但是官方文檔對(duì)各個(gè)生命周期回調(diào)方法的描述并不是很詳細(xì),因此如果不認(rèn)真研讀文檔舷暮,很難明白圖中的生命周期方法的走向态罪,并可能對(duì)某些方法產(chǎn)生誤解。例如對(duì)于onStop方法下面,可能會(huì)有部分童鞋對(duì)其有誤解复颈,誤認(rèn)為Activity進(jìn)入后臺(tái)時(shí)回調(diào)該方法,其實(shí)進(jìn)入后臺(tái)時(shí)回調(diào)的是onPause方法沥割,不可見(jiàn)時(shí)回調(diào)onStop方法耗啦;也有童鞋誤以為onResume方法回調(diào)意味著可以進(jìn)行交互,其實(shí)我們應(yīng)該以public void onWindowFocusChanged (boolean hasFocus)回調(diào)中的hasFocus參數(shù)為依據(jù)來(lái)判定是否可以進(jìn)行交互机杜。
Activity可見(jiàn)與否與其是否處于前臺(tái)是兩回事帜讲,onStart 與 onStop 配對(duì)描述Activity是否可見(jiàn);onPause 與
onResume 配對(duì)描述Activity是否處于前臺(tái)椒拗。Activity可見(jiàn)并不意味著可以交互似将,同樣的其處于前臺(tái)也未必可以交互。直接的例子是蚀苛,若Activity展示了Dialog在验,此時(shí)Activity仍處于前臺(tái),但我們卻不能與Activity交互
另外堵未,對(duì)于上述示意圖中的標(biāo)注2走向腋舌,我相信很多人并沒(méi)有親自驗(yàn)證過(guò),因?yàn)橥ǔD愫茈y通過(guò)交互來(lái)復(fù)現(xiàn)這一場(chǎng)景渗蟹。對(duì)于這種場(chǎng)景块饺,我們可以通過(guò)代碼控制來(lái)模擬:Activity A啟動(dòng)Activity B,在Activity B的onCreate方法中直接調(diào)用finish方法