android基本功
這幾天一直在看Android一些基本知識(shí),都比較零散,先各個(gè)知識(shí)點(diǎn)分析記錄總結(jié)著,等日后整理成各個(gè)模塊。也為自己以后面試做準(zhǔn)備吧,未完待續(xù)。
一、 Android四大組件
1. Application相關(guān)
1.1 Application實(shí)例
在一個(gè)Dalvik虛擬機(jī)里只會(huì)存在一個(gè)實(shí)例,一個(gè)app可以有多個(gè)Dalvik,每個(gè)Dalvik都會(huì)存在一個(gè)Application實(shí)例,這就是多進(jìn)程模式。
1.2 Application的繼承關(guān)系
Application實(shí)質(zhì)上是一個(gè)Context,它繼承自ContextWrapper,而ContextWrapper繼承自Cotext,對(duì)Context的一個(gè)包裝。
1.3 Application生命周期
應(yīng)用啟動(dòng)時(shí),會(huì)先調(diào)用Application.attach()方法,再調(diào)用Application.onCreate()方法
但別在此方法中進(jìn)行太多耗時(shí)操作,否則會(huì)影響app啟動(dòng)速度
1.4 怎么獲取Application實(shí)例
獲取Application對(duì)象可通過Context的getApplicationContext()或是通過Activity.getApplication()\Service.getApplication,三者獲取到的都是同一個(gè)Application對(duì)象。
1.5 為什么最好不要在Application對(duì)象中緩存數(shù)據(jù)
在低內(nèi)存情況下,Application可能被銷毀,而Activity棧并沒有為空,這時(shí)如果要恢復(fù)的Activity用到了Application中緩存的數(shù)據(jù),很可以報(bào)空指針異常。解決辦法,如果在Application保存數(shù)據(jù),最好先判空,并將數(shù)據(jù)持久化保存,在使用時(shí)如果為空再?gòu)氖謾C(jī)中讀取。
2. Activity生命周期
1) 整個(gè)的生命周期,從onCreate(Bundle)開始到onDestroy()結(jié)束。
2) 可見的生命周期,從onStart()開始到onStop()結(jié)束。在這段時(shí)間,可以看到Activity在屏幕上,盡管有可能不在前臺(tái),不能和用戶交互。在這兩個(gè)接口之間,需要保持顯示給用戶的UI數(shù)據(jù)和資源等,例如:可以在onStart中注冊(cè)一個(gè)IntentReceiver來監(jiān)聽數(shù)據(jù)變化導(dǎo)致UI的變動(dòng),當(dāng)不再需要顯示時(shí)候,可以在onStop()中注銷它。
3) 前臺(tái)的生命周期,從onResume()開始到onPause()結(jié)束。在這段時(shí)間里,該Activity處于所有 Activity的最前面,和用戶進(jìn)行交互。Activity可以經(jīng)常性地在resumed和paused狀態(tài)之間切換。
4) 從界面A跳轉(zhuǎn)到界面B,生命周期變化情況:
當(dāng)用戶點(diǎn)擊A中按鈕來到B時(shí),假設(shè)B全部遮擋住了A,將依次執(zhí)行A:onPause -> B:onCreate -> B:onStart -> B:onResume -> A:onStop。
此時(shí)如果點(diǎn)擊Back鍵,將依次執(zhí)行B:onPause -> A:onRestart -> A:onStart -> A:onResume -> B:onStop -> B:onDestroy。
5) 對(duì)于棧最頂上的界面A,按Back鍵和按Home鍵的區(qū)別:
如果按下Back鍵,系統(tǒng)返回到桌面,并依次執(zhí)行A:onPause -> A:onStop -> A:onDestroy。
此時(shí)如果按下Home鍵(非長(zhǎng)按),系統(tǒng)返回到桌面,并依次執(zhí)行A:onPause -> A:onStop。由此可見,Back鍵和Home鍵主要區(qū)別在于是否會(huì)執(zhí)行onDestroy。
3. Activity的4種啟動(dòng)模式
3.1 standard:默認(rèn)的標(biāo)準(zhǔn)啟動(dòng)模式
不管有沒有已存在的實(shí)例,都生成新的實(shí)例。即使是A startActivity A,也會(huì)重新生成一個(gè)新的實(shí)例,再回退時(shí),A也會(huì)出現(xiàn)兩次;
3.2 singleTop
如果發(fā)現(xiàn)有對(duì)應(yīng)的Activity實(shí)例正位于棧頂,則重復(fù)利用,不再生成新的實(shí)例,如A啟動(dòng)A,不會(huì)生成新的實(shí)例,會(huì)走A的onNewIntent方法,而不是onCreate方法,回退時(shí),也只會(huì)回退一次;
3.3 singleTask
所在Activity棧中有對(duì)應(yīng)的Activity實(shí)例,則使此Activity實(shí)例之上的其他Activity實(shí)例統(tǒng)統(tǒng)出棧,使此Activity實(shí)例成為棧頂對(duì)象,顯示到幕前,這一般用在程序的主界面上;
3.4 singleInstance
當(dāng)被啟動(dòng)時(shí),系統(tǒng)會(huì)首先判斷系統(tǒng)其他棧中是否已經(jīng)存在此Activity實(shí)例,有則直接使用,并且其所在的Activity棧理論上只有它一個(gè)Activity元素。
singleInstance表示該Activity在系統(tǒng)范圍內(nèi)“實(shí)例唯一”。ingInstance和singleTask主要區(qū)別在與系統(tǒng)范圍內(nèi)的“實(shí)例唯一”還是當(dāng)前Activity棧“實(shí)例唯一”。
4. Activity的狀態(tài)保存
4.1 onSaveInstanceState()、onRestoreInstanceState()
Activity的狀態(tài)保存onSaveInstanceState()、onRestoreInstanceState()
Activity的 onSaveInstanceState() 和onRestoreInstanceState()并不是生命周期方法,當(dāng)應(yīng)用遇到意外情況(如:內(nèi)存不足、用戶直接按Home鍵)由系統(tǒng)銷毀一個(gè)Activity時(shí),onSaveInstanceState()會(huì)被調(diào)用。但是當(dāng)用戶主動(dòng)去銷毀一個(gè)Activity時(shí),例如在應(yīng)用中按返回鍵,onSaveInstanceState()就不會(huì)被調(diào)用。通常onSaveInstanceState()只適合用于保存一些臨時(shí)性的狀態(tài),而onPause()適合用于數(shù)據(jù)的持久化保存。當(dāng)某個(gè)activity變得“容易”被系統(tǒng)銷毀時(shí),該activity的onSaveInstanceState就會(huì)被執(zhí)行,開發(fā)者可以覆寫onSaveInstanceState()方法。
4.2 如何保存數(shù)據(jù)
onSaveInstanceState()方法接受一個(gè)Bundle類型的參數(shù), 開發(fā)者可以將狀態(tài)數(shù)據(jù)存儲(chǔ)到這個(gè)Bundle對(duì)象中, 這樣即使activity被系統(tǒng)摧毀, 當(dāng)用戶重新啟動(dòng)這個(gè)activity而調(diào)用它的onCreate()方法時(shí), 上述的Bundle對(duì)象會(huì)作為實(shí)參傳遞給onCreate()方法, 開發(fā)者可以從Bundle對(duì)象中取出保存的數(shù)據(jù), 然后利用這些數(shù)據(jù)將activity恢復(fù)到被摧毀之前的狀態(tài)。如在橫豎屏切換時(shí),應(yīng)用的Activity可能要保存edittext中的值,需要調(diào)用onSaveInstanceState()來何存輸入值。
5. Activity啟動(dòng)方式
5.1 startActivityForResult會(huì)立馬執(zhí)行情況
如果被啟動(dòng)的與Activity在AndroidManifest.xml文件中配置的launchMode設(shè)為”singleTask”的時(shí)候,startActivityForResult執(zhí)行后,onActivityResult立即會(huì)被執(zhí)行到。
原因:startActivityForResult(Intent,int,Bundle)有說明:if the activity you are launching uses thesingleTask launch mode, it will not run in your task and thus you willimmediately receive a cancel result.因?yàn)锳ctivity1用startActivityForResult啟動(dòng)Activity2, 如果一個(gè)Activity2的啟動(dòng)方式是singleTask時(shí),若Activity2之前在任務(wù)棧中存在,再次啟動(dòng)它時(shí),它會(huì)把它上面所有的Activity實(shí)例都pop出棧,這樣再調(diào)setResult時(shí),返回的是Activity2任務(wù)棧下面的界面,而不是Activity1,所以,Activity1收到的一個(gè)cancel的Result。
5.2 要求requestCode>0
startActivityForResult的requestCode值必須要大于等于0,不然,startActivityForResult就變成了 startactivity。
6. Activity的isFinish/finish()和destroy的區(qū)別
activity的finish()方法有兩個(gè)層面含義:將此Activity從Activity棧中移除;調(diào)用了此Activity的onDestroy方法。
1) finish()方法用于結(jié)束一個(gè)Activity的生命周期,但是并沒有釋放他的資源。
2) onDestory()方法則是Activity的一個(gè)生命周期方法,其作用是在一個(gè)Activity對(duì)象被銷毀之前,Android系統(tǒng)會(huì)調(diào)用該方法,用于釋放此Activity之前所占用的資源。
有可能程序在運(yùn)行了finish()方法而沒有運(yùn)行onDestory()方法,因?yàn)锳ctivity生命周期里的方法,不確定什么時(shí)候會(huì)被調(diào)用,皆是由系統(tǒng)確定;
3) isFinishing方法來判斷Activity是否處于銷毀狀態(tài)。
7. Activity和Fragment的區(qū)別
一個(gè)fragment必須總是嵌入在一個(gè)activity中,同時(shí)fragment的生命周期受activity而影響,一個(gè)Activity可以運(yùn)行多個(gè) Fragment,一個(gè)fragment也可以在多個(gè)activity中作為一個(gè)模塊,fragment有自己的生命周期,接收自己的輸入事件,可以從運(yùn)行中的activity添加或移除。
Activity 的后退棧由系統(tǒng)管理,而 Fragment 的后退棧由所在的Activity 管理。
8.Broadcast廣播相關(guān)
8.1 Broadcast的生命周期
廣播接收器僅在它執(zhí)行這個(gè)方法時(shí)處于活躍狀態(tài)。當(dāng) onReceive() 返回后,它即為失活狀態(tài)。
8.2 Broadcast的兩種注冊(cè)方式
1) 靜態(tài)注冊(cè),在AndroidManifest.xml中用標(biāo)簽聲明注冊(cè),并在標(biāo)簽內(nèi)用標(biāo)簽設(shè)置過濾器。
2) 動(dòng)態(tài)注冊(cè):動(dòng)態(tài)地在代碼中先定義并設(shè)置好一個(gè) IntentFilter對(duì)象,然后在需要注冊(cè)的地方調(diào) Context.registerReceiver()方法,如果取消時(shí)就調(diào)用Context.unregisterReceiver()方法。如果用動(dòng) 態(tài)方式注冊(cè)的BroadcastReceiver的Context對(duì)象被銷毀時(shí),BroadcastReceiver也就自動(dòng)取消注冊(cè)了。
注意事項(xiàng):若在使用sendBroadcast()的方法是指定了接收權(quán)限,則只有在AndroidManifest.xml中用標(biāo)簽聲明了擁有此權(quán)限的BroascastReceiver才會(huì)有可能接收到發(fā)送來的Broadcast。
9. Service服務(wù)相關(guān)
9.1 定義
一個(gè)專門在后臺(tái)處理時(shí)間任務(wù),沒有UI界面;
9.2 Service的生命周期
① 通過bindService啟動(dòng)的Service生命周期:onCreate()->onBind()->onUnbind()->onDestory()
② 通過startService啟動(dòng)的Service生命周期:onCreate()->onStartCommand()->onDestory()
9.3 Service兩種啟動(dòng)方式和區(qū)別:bindService, startService
① startService只是啟動(dòng)Service,與啟動(dòng)的組件沒有關(guān)聯(lián),當(dāng)Service調(diào)用stopSelf或是相關(guān)組件調(diào)用stopService服務(wù)才會(huì)終止;
② bindService,和組件綁定,其他組件可通過回調(diào)獲取Service代理來和Service交互,當(dāng)啟動(dòng)方銷毀,Service也會(huì)調(diào)用unBinder進(jìn)行解綁,只有所有綁定該Service的組件都解綁了,Service才會(huì)銷毀。
9.4 Service和Activity怎么進(jìn)行通信
9.5 Service與IntentService
(1) Service:與啟動(dòng)它的應(yīng)用位于同一個(gè)進(jìn)程中; Service還是在主線程中跑,所以不應(yīng)該在Service中直接處理耗時(shí)的任務(wù);
(2) IntentService:IntentService是Service的子類,比普通的Service增加了額外的功能。
1) 它會(huì)創(chuàng)建獨(dú)立的worker線程來處理所有的Intent請(qǐng)求;
2) 會(huì)創(chuàng)建獨(dú)立的worker線程來處理onHandleIntent()方法實(shí)現(xiàn)的代碼,無需處理多線程問題;
3) 所有請(qǐng)求處理完成后,IntentService會(huì)自動(dòng)停止,無需調(diào)用stopSelf()方法停止Service;
4) 為Service的onBind()提供默認(rèn)實(shí)現(xiàn),返回null,故適合用startService來啟動(dòng);
5) 為Service的onStartCommand提供默認(rèn)實(shí)現(xiàn),將請(qǐng)求Intent添加到隊(duì)列中;
10. Fragment相關(guān)
10.1 Fragment的生命周期
10.2 Fragment之間數(shù)據(jù)傳遞方式
10.2.1 通過共同的Activity傳遞
在Activity中定義一個(gè)字段,然后添加set和get方法,在相應(yīng)的Fragment中只要采用(XXXActivity(getActivity)).getXXX();方法叉可以獲取到變量。
10.2.2 使用bundle進(jìn)行參數(shù)傳遞
DemoFragment demoFragment = new DemoFragment(); Bundle bundle = new Bundle(); bundle.putString("key", "這是方法二"); demoFragment.setArguments(bundle); ft.add(R.id.fragmentRoot, demoFragment, SEARCHPROJECT); ft.commit();10.2.3 采取接口回調(diào)的方式
實(shí)現(xiàn)步驟如下:
1) 在FragmentA中創(chuàng)建一個(gè)接口以及接口對(duì)應(yīng)的set方法:
2) 在FragmentB中實(shí)現(xiàn)此接口,并在相應(yīng)的地方調(diào)用setXX方法
10.2.4 使用RxBus或是EventBus
10.3 Fragment與Activity之間數(shù)據(jù)傳遞方式
10.3.1 通過Bundle傳值
10.3.2 使用RxBus或是EventBus
10.3.3 采取接口回調(diào)的方式
二、 開發(fā)中遇到的一些問題
1. app被殺死怎么啟動(dòng)
1.1 場(chǎng)景
用戶按了home鍵,系統(tǒng)內(nèi)存不足,導(dǎo)致應(yīng)用被強(qiáng)殺;
1.2 可能引起的問題
當(dāng)類中設(shè)置了static變量而在A類中初始化,在B類頁(yè)面調(diào)用,應(yīng)用被強(qiáng)殺時(shí),再點(diǎn)擊應(yīng)用回到B類,由于static尚未初始化,會(huì)導(dǎo)致空指針,應(yīng)用崩潰;
1.3 被強(qiáng)殺后應(yīng)用變量情況
當(dāng)app在后臺(tái)被強(qiáng)殺后,app中所有變量都被清空(包括application實(shí)例),整個(gè)app進(jìn)程都被銷毀,但Activity的棧信息依然保存著,所以在回到應(yīng)用時(shí)還能得知頁(yè)面的打開順序;就用被強(qiáng)殺時(shí),會(huì)自動(dòng)調(diào)用onSaveInstance方法保存一些核心變量。
1.4 解決辦法
1.4.1 如何判斷應(yīng)用是否被強(qiáng)殺
可以在Application中設(shè)置一個(gè)靜態(tài)變量,并賦一個(gè)初始值,在應(yīng)用的啟動(dòng)頁(yè),對(duì)Application中的靜態(tài)變量進(jìn)行重新賦其他值,在其他Activity進(jìn)行檢測(cè)該值,如果是Application中賦的初始值,說明應(yīng)用被強(qiáng)殺了直接恢復(fù)該Activity,如果是啟動(dòng)頁(yè)重新賦過的值,則說明應(yīng)用是正常啟動(dòng)的;
1.4.2 應(yīng)用被強(qiáng)殺后該如何處理避免Crash
1) 將需要的變量,保存在手機(jī)本地一份,如SharedPerference或是數(shù)據(jù)庫(kù)/sd卡等,在Application的onCreate()中進(jìn)行相應(yīng)靜態(tài)變量的初始化操作,如用戶Id等信息,保證其他Activity使用的變量不會(huì)因?yàn)閼?yīng)用重新初化Applciation而靜態(tài)變量變?yōu)榭?#xff1b;
2) 如果應(yīng)用中有BaseActivity,可以在BaseActivity的onCreate()函數(shù)中進(jìn)行判斷應(yīng)用是否被強(qiáng)殺,如果應(yīng)用被強(qiáng)殺,重新啟動(dòng)應(yīng)用,重在應(yīng)用流程。
if(isForceKilled) {
startActivity(new Intent(this, SplashActivity.class));
}
3) Activity中跳轉(zhuǎn)的變量盡量采用Intent傳值,少用靜態(tài)變量;
2. 如何完全退出一個(gè)應(yīng)用
2.2 BaseActivity + RxBus(EventBus)
如果采用Rxjava開發(fā),直接采用RxBus, 也可以自己寫個(gè)廣播,所有的類都繼承自BaseActivity。
直接用RxBus發(fā)送退出應(yīng)用的消息,在BaseActivty接收消息,再調(diào)用finish();所有棧內(nèi)繼承于BaseActivity的Activity都會(huì)接收到事件,并調(diào)用finish結(jié)束Activity。在finish之后還可以使用System.exit(0),這樣會(huì)提示虛擬機(jī)kill掉進(jìn)程。
3. 為什么service里startActivity必須添加FLAG_ACTIVITY_NEW_TASK否則拋異常,而Activity里不會(huì)?
3.1 啟動(dòng)activity的兩種方式
一種是直接調(diào)用context類的startActivity方法,這種方式啟動(dòng)的Activity沒有Activity棧,因此不能以standard方式啟動(dòng),必須添加上FLAG_ACTIVITY_NEW_TASK這個(gè)Flag,服務(wù)就是通過context調(diào)用;
另一種方式是調(diào)用被Activity類重載過的tartActivity方法;
3.2 兩種啟動(dòng)方式為什么會(huì)不同
Activity是繼承自ContextThemeWrapper,而service是繼承自ContextWrapper,ContextWrapper里是通過調(diào)用ContextImpl的startActivity方法,它會(huì)判斷intent.getFlags有沒有還FLAG_ACTIVITY_NEW_TASK標(biāo)記。而ContextThemeWrapper調(diào)用startActivity方法不會(huì)去檢查標(biāo)記。最后它們都通過Instrumentation輔助類的ActivityManagerNative這個(gè)方法。
三、 Android自定義View
1. 事件分發(fā)機(jī)制
1.1 事件傳遞
ViewGroup接收到事件后進(jìn)行事件分派(dispatchTouchEvent),如果自己需要處理事件則攔截(interceptTouchEvent返回true),調(diào)用自己的onTouchEvent進(jìn)行事件處理;不處理(interceptTouchEvent返回false)則傳給子view進(jìn)行處理,再由子view進(jìn)行分派(dispatchTouchEvent),處理/攔截;
1.2 事件處理
子View的onTouchEvent進(jìn)行事件處理,若返回true,則消耗事件,不再繼續(xù)傳遞;返回false則不處理,把這個(gè)事件往上一級(jí)的viewGroup進(jìn)行傳遞,由上一級(jí)進(jìn)行處理。
1.3 view的dispatchTouchEvent
1) view之所有dispatchTouchEvent方法,因?yàn)関iew可以注冊(cè)很多事件監(jiān)聽器,如onClick, onLongClick, onTouch, view自身的onTouchEvent方法,這么多事件需要管理者dispatchTouchEvent。所以view也會(huì)有事件分發(fā)。
2) view事件相關(guān)的方法調(diào)用順序:onTouchListener > onTouchEvent > onLongClickListener > onClickListener
1.4 注意點(diǎn)
1) 不論view自身是否注冊(cè)事件,只要view可點(diǎn)擊就會(huì)消費(fèi)事件,如view注冊(cè)了onClickListener\ onLongClickListener\ onContextClickListener\ android:clickable = “true”;
onTouchListener返回false才會(huì)調(diào)用onClickListener
2) 即使給view注冊(cè)了onTouchListener也不會(huì)影響view的可點(diǎn)擊狀態(tài),只要不返回true就不會(huì)消費(fèi)事件;
3) ViewGroup和ChildView同時(shí)注冊(cè)了事件監(jiān)聽器,由ChildView消費(fèi);
4) 一次觸摸流程中產(chǎn)生事件應(yīng)被同一個(gè)view消費(fèi),全部接收或全部拒絕;
2. view繪制原理
view繪制到手機(jī)屏幕上需要經(jīng)過三個(gè)步驟:measure(測(cè)量控件大小)、layout(view擺放位置)、draw(view的繪制)
2.1 view的測(cè)量measure
1) 作用:測(cè)量視圖大小,即視圖寬高,在view中measure為final型,子類不能修改,它內(nèi)部會(huì)調(diào)用onMeasure(),視圖大小最終在這里確定,調(diào)用setMeasuredDimension保存計(jì)算結(jié)果;
2) MeasureSpec:view的測(cè)量模式MeasureSpec封裝的是父容器傳遞給子容器的布局要求,MeasureSpec是由父view的MeasureSpec和子view的LayoutParams(定義view的layout_width\layout_height)通過計(jì)算得出子view的測(cè)量要求。
MeasureSpec是由一個(gè)32位整型數(shù)組成,高2位是mode,后面30位是size。
1) mode一共有三種模式:UNSPECIFIED:父容器對(duì)子容器沒有任何限制,任意大;EXACTLY:父容器為子容器設(shè)置了尺寸,子容器應(yīng)該服務(wù)這些邊界;AT_MOST:子容器可以聲指指定大小內(nèi)的任意大小,最大不得超過指定大小。
2) measure會(huì)遍歷整棵view樹,測(cè)量每個(gè)view真實(shí)尺寸,ViewGrou向它內(nèi)部的每個(gè)子view發(fā)送measure命令,子view在onMeasure()函數(shù)來測(cè)量自己的尺寸,再調(diào)用setMeasuredDimension()方法將測(cè)量結(jié)果保存至View的mMeasuredWidth和mMeasureHeight中,單位是像素。
2.2 layout
layout的主要作用 :根據(jù)子視圖的大小以及布局參數(shù)將View樹放到合適的位置上。
ViewGroup:1)根據(jù)parent傳遞的位置信息,設(shè)置自己的位置;2)根據(jù)自己的layout規(guī)則,為每一個(gè)子view計(jì)算出準(zhǔn)確的位置,并將layout流程傳遞給子view
View:根據(jù)parent傳遞的位置信息,設(shè)置自己的位置
2.3 draw
根據(jù)measure, layout得到的參數(shù)將視圖顯示在屏幕上,子類不能修改此方法;
繪制步驟:
1)繪制背景(drawBackground)
2)保存canvas的layer,準(zhǔn)備fading;
3)繪制view的content(onDraw方法);
4)繪制children(dispatchDraw方法);
5)繪制fading edges,再還原layer;
6)繪制裝飾器,如scrollBar
四、性能方面
1、 耗電太多怎么優(yōu)化
1.1 應(yīng)用耗電主要體現(xiàn)在以下幾方面
1) 常規(guī)使用:大數(shù)據(jù)量的傳輸(IO),不停的在網(wǎng)絡(luò)間切換(網(wǎng)絡(luò)請(qǐng)求操作);解析大量的文本數(shù)據(jù);
2) 對(duì)設(shè)備的頻繁喚醒:在Android中AlarmManager可喚醒設(shè)備,WakeLock能讓CPU保持喚醒狀態(tài),這兩者不恰當(dāng)使用,沒有合理釋放掉,將會(huì)使系統(tǒng)長(zhǎng)時(shí)間無法進(jìn)入休眠,導(dǎo)致高耗電;
3) GPS:對(duì)GPS設(shè)備的使用控制,GPS實(shí)時(shí)性所以電量消耗也比較嚴(yán)重;
4) 傳感器和屏幕;
1.2 優(yōu)化途徑
1) 在需要網(wǎng)絡(luò)連接的程序中,先檢查網(wǎng)絡(luò)連接是否正常,若沒有網(wǎng)絡(luò)連接,就不需要執(zhí)行相應(yīng)程序;在蜂窩移動(dòng)網(wǎng)絡(luò)下,批量執(zhí)行網(wǎng)絡(luò)請(qǐng)求,盡量避免頻繁間隔網(wǎng)絡(luò)請(qǐng)求;應(yīng)該盡量減少移動(dòng)網(wǎng)絡(luò)下數(shù)據(jù)傳輸,多在WiFi環(huán)境下傳輸數(shù)據(jù);
2) 使用效率高的數(shù)據(jù)格式和解析方法,如JSON和Protobuf;
3) 在進(jìn)行大數(shù)據(jù)下載時(shí),盡量使用GZIP方式下載;
4) 回收J(rèn)ava對(duì)象,特別是較大的java對(duì)象,使用reset方法,對(duì)定位要求不太高的不要使用GPS定位,使用wifi和移動(dòng)網(wǎng)絡(luò)cell定位;盡量不要使用浮點(diǎn)運(yùn)算;獲取屏幕尺寸等信息可以使用緩存技術(shù);使用AlarmManager定時(shí)啟動(dòng)服務(wù)替代使用sleep方式的定時(shí)任務(wù)。
5)使用JobScheduler來管理后臺(tái)工作,將一些不是特別緊急的任務(wù)放到更合適的時(shí)機(jī)批量處理;
6) 監(jiān)控電池當(dāng)前的電量和充放電狀態(tài),在程序中做相應(yīng)參數(shù)設(shè)置以減少耗電量。
2. 怎么統(tǒng)計(jì)crash
可集成第三方sdk進(jìn)行統(tǒng)計(jì),如友盟、騰訊Bugly,可以分析出每個(gè)版本出現(xiàn)的bug數(shù),每個(gè)bug出現(xiàn)的時(shí)間、頻次、機(jī)型、應(yīng)用版本、bug出現(xiàn)的位置;計(jì)算bug率可以通過計(jì)算bug的次數(shù)/應(yīng)用啟動(dòng)次數(shù)
3. 怎么減少用戶流量消耗
3.1 流量怎么監(jiān)控
1) Android Studio自帶的Network Monitor可看出時(shí)間段內(nèi)網(wǎng)絡(luò)請(qǐng)求數(shù)量及訪問速率;
2) 使用代理工具,如Wireshark, Fiddler, Charles,可以查看每一次網(wǎng)絡(luò)請(qǐng)求,傳輸?shù)臄?shù)據(jù)量;
3.2 怎么優(yōu)化手機(jī)流量使用
1) 使用GZIP壓縮進(jìn)行數(shù)據(jù)傳輸,不僅可以減少流量消耗,還可減少傳輸時(shí)間;
2) 使用IP直連,而不用域名,省去DNS解析過程;
3) 圖片方面:圖片使用WebP格式,可節(jié)省流量;使用壓縮圖,圖片按需加載,只有用戶查看大圖時(shí)才加載原圖;圖片上傳時(shí),采用分片傳輸,每個(gè)分片失敗重傳,根據(jù)網(wǎng)絡(luò)類型及傳輸過程中的變化動(dòng)態(tài)修改分片大小;
4) 協(xié)議層優(yōu)化,Http1.1引入了“持久連接”,多個(gè)請(qǐng)求被復(fù)用,無需重建TCP連接;Http2引入了“多頭”、頭信息壓縮、服務(wù)器推送等;
5) 合并網(wǎng)絡(luò)請(qǐng)求,減少請(qǐng)求次數(shù)(這樣頭信息僅需上傳一次,減少流量也節(jié)省了資耗);
6) 網(wǎng)絡(luò)緩存,對(duì)服務(wù)端返回?cái)?shù)據(jù)緩存,設(shè)定有效時(shí)間,也可以使用第三方庫(kù)如Okhttp, Volley
7) 斷點(diǎn)續(xù)傳,重試策略,Protocol Buffer;避免客戶端輪詢,使用服務(wù)器推送方式,數(shù)據(jù)采用增量更新,應(yīng)用可設(shè)置僅在wifi環(huán)境下升級(jí)應(yīng)用;善用緩存服務(wù);在WIFI環(huán)境下下載文件;使用離線地圖;暫時(shí)關(guān)閉同步;壓縮數(shù)據(jù)
4. 方法數(shù)超過65535的解決辦法
4.1 出現(xiàn)原因
1) android中單個(gè)dex文件能包含的最大方法數(shù)65535,類的數(shù)量也有這個(gè)限制,這包含android framework, 依賴的jar包及應(yīng)用本身的代碼中所有的方法。
2) 超過了LinearAlloc緩沖區(qū)大小,應(yīng)用在安裝時(shí),系統(tǒng)會(huì)通過dexopt來優(yōu)化dex文件,在優(yōu)化過程中dexopt采用了一個(gè)固定大小的緩沖區(qū)來存儲(chǔ)應(yīng)用中所有的方法信息,該緩沖區(qū)就是LinearAlloc。它在新版本中是8mb或16mb,但在android2.2,2.3上只有5mb。有時(shí)方法數(shù)沒超,但是存儲(chǔ)空間超過了5mb,dexopt程序會(huì)報(bào)錯(cuò),導(dǎo)致安裝失敗。
4.2 解決方案
1) 刪除無用的代碼和第三方庫(kù)
2) 利用插件來動(dòng)態(tài)加載部分dex
3) multidex解決方案(可從apk中加載多個(gè)dex文件)
使用方法:build.gradle中添加multidex的依賴;
讓自定義的Application繼承MultidexApplication(在attachBaseContext方法中加入multiDex.install(this)),或是在mainfest文件的Application直接設(shè)成MultidexApplication。
4.3 multidex存在的問題
1) 應(yīng)用啟動(dòng)速度會(huì)降低,由于應(yīng)用啟動(dòng)時(shí)會(huì)加載額外的dex文件,將會(huì)造成啟動(dòng)速度除低,還可能造成ANR,所以盡可能第二個(gè)dex不要太大;
2) 由于dalvik LinearAlloc的bug,這可能導(dǎo)致multidex的應(yīng)用無法在android 4.0之前的手機(jī)上運(yùn)行。
4.4 針對(duì)multidex的問題,可改進(jìn)的地方
1) 啟動(dòng)時(shí)間過長(zhǎng)
Application.attachBaseContext是在Application.onCreate()之前運(yùn)行的,如果首次啟動(dòng)Dalvik虛擬機(jī)會(huì)對(duì)classes.dex執(zhí)行dexopt操作,生成ODEX文件,此過程非常耗時(shí),而執(zhí)行MultiDex.install()必然會(huì)再次對(duì)class2.dex執(zhí)行dexopt等操作,所以如果首次啟動(dòng)應(yīng)用可以將MultiDex.install(context)放在子線程中執(zhí)行;
2) ANR/Crash
在classes2.dex沒有加載完,程序調(diào)用了classes2.dex中的類或方法,關(guān)于MultixDex編譯過程相關(guān)的任務(wù)主要有:
collectDebugMultiDexComponents:掃描AndroidMainfest.xml中的application, activity, receiver, provider,service等相關(guān)類,將信息寫入mainfest_keep.txt中;
shinkDebugMultiDexComponents:再壓縮,根據(jù)proguard規(guī)則及mainfest_keep.txt文件進(jìn)一步優(yōu)化mainfest_keep.txt,將沒有用到的類刪除,最終生成componentClasses.jar文件;
createDebugMainDexClassList: 上一步生成的componentClasses.jar文件中的類,遞歸掃描這些類所有相關(guān)的依賴類,最終形成maindexlist.txt文件,最終打包進(jìn)classes.dex中。
如果遇到找不到類的情況,只需要將該類的完整路徑添加到maindexlist.text中即可。
5. anr
5.1 android怎么定義應(yīng)用出現(xiàn)ANR
1) 應(yīng)用在5秒內(nèi)未響應(yīng)用戶的輸入事件;
2) BroadcastReceiver未在10秒內(nèi)完成相關(guān)的處理;
5.2 出現(xiàn)場(chǎng)景
1) 主線程阻塞、掛起、死循環(huán);應(yīng)用進(jìn)程的其他線程CPU占用率高,使主線程無法搶占CPU時(shí)間片;
2) 其他應(yīng)用進(jìn)程搶占CPU時(shí)間片,使當(dāng)前應(yīng)用無法搶占到;其他進(jìn)程CPU占用率高;當(dāng)前應(yīng)用進(jìn)程進(jìn)行進(jìn)程間通信請(qǐng)求其他進(jìn)程,其他進(jìn)程操作長(zhǎng)時(shí)間沒有反饋等;
5.3 從哪可以查看到引起的原因
當(dāng)前運(yùn)行的程序發(fā)生ANR時(shí),會(huì)將函數(shù)的堆棧信息輸出到/data/anr/trace.txt文件中,最新的ANR信息在最開始部分;
后臺(tái)的ANR一般不彈框,可以在開發(fā)者選項(xiàng)里勾選“應(yīng)用程序無響應(yīng)”即可對(duì)后臺(tái)ANR也進(jìn)行彈窗顯示。
5.4 如何避免
1) 使用AsyncTask或是Thread/HandlerThread來進(jìn)行耗時(shí)操作,還可設(shè)置Thread的優(yōu)先級(jí)設(shè)置成Process.THREAD_PRIORITY_BACKGROUND;
2) 使用Handler處理工作線程結(jié)果,而不使用Thread.wait()或Thread.sleep()來阻塞主線程;
3) 在onCreate和onResume回調(diào)中盡量避免耗時(shí)操作;
4) BoardCastReceiver的onReceive代碼要盡量減少耗時(shí),可使用IntentService處理。
6. listview優(yōu)化
6.1 每次getView執(zhí)行時(shí)間最長(zhǎng)不得超過多少否則給用戶卡頓感覺
1S內(nèi)屏幕大概繪制30幀,用戶才會(huì)覺得它比較流暢,每幀可用時(shí)間為33.33ms,每一屏一般要6個(gè)ListItem,加上一個(gè)重用的convertView:33.33ms/7=4.76ms,即每個(gè)getView要在4.76ms內(nèi)完成工作才會(huì)較流暢。但每個(gè)getView間的調(diào)用會(huì)有一定間隔,所以留給getView使用的時(shí)間應(yīng)該控制在4ms內(nèi),否則滑動(dòng)ListView會(huì)有卡頓的感覺。
6.2 優(yōu)化方法
1) 使用ViewHolder+converView模式,這樣避免每次在調(diào)用getView時(shí),都重新inflate一個(gè)view,通過findViewById實(shí)例化數(shù)據(jù);
2) adapter中有耗時(shí)操作,異步加載;
3) 為圖片設(shè)置緩存;圖片加載可使用第三主庫(kù)如Glide, Picasso
4) ListView滑動(dòng)時(shí)停止加載圖片和分頁(yè)加載;
5) 在adapter的getView中盡可能減少邏輯判斷;
6) 將ListView的scrollingCache和animateCache的屬性設(shè)置為false;
7) 盡可能減少List Item的Layout層次,如可用RelativeLayout替換LinearLaout
7. bitmap怎么避免OOM
7.1 加載大圖時(shí),可先對(duì)圖片進(jìn)行壓縮
在一個(gè)很小的ImageView上顯示一張大圖,是不值當(dāng)?shù)摹itmapFactory提供了多個(gè)解析方法(dcodeByteArray, decodeFile, decodeResource)用于創(chuàng)建Bitmap對(duì)象,它提供了一個(gè)可選的BitmapFactory.Options參數(shù),將inJustDecodeBounds屬性設(shè)置為true就可以讓解析方法禁止為bitmap分配內(nèi)存,返回值不再是一個(gè)bitmap,而是null,但BitmapFactory.Options的outWidth, outHeight和outMineType屬性都會(huì)被賦值,此時(shí)可根據(jù)圖片情況進(jìn)行壓縮。能過設(shè)置BitmapFactory.Options中的inSampleSize值就可以實(shí)現(xiàn)。
7.2 使用圖片緩存技術(shù)
LrcCache來緩存圖片,LruCache
7.3 選擇RGB_565或是ARGB_4444的編碼格式,ARGB_8888的編碼格式占4個(gè)字節(jié)。
7.4 android系統(tǒng)怎么判斷發(fā)生OOM
在Android2.x系統(tǒng): dalvik allocated + external allocated + 新分配的大小 >= getMemoryClass值時(shí)就會(huì)發(fā)生OOM
在Android4.x系統(tǒng)廢除了external的計(jì)數(shù)器bitmap的分配改到dalvik的java heap中申請(qǐng),只要allocated + 新分配的內(nèi)存 >= getMemoryClass()就會(huì)發(fā)生OOM;
7.5 其他避免OOM辦法
使用輕量級(jí)數(shù)據(jù)結(jié)構(gòu),如用SparseArray代替HashMap, int代替Integer等;少用枚舉;
內(nèi)存對(duì)象復(fù)用,ListView/recyclerView使用viewholder;用StringBuilder/Stringbuffer代替String字符串拼接;減少內(nèi)存泄漏
8. android UI性能優(yōu)化
8.1 卡頓(Jack)
安卓設(shè)備的屏幕刷新率一般是60幀每秒,所要要渲染的內(nèi)容能在16ms內(nèi)完成,每丟一幀,用戶就會(huì)感覺動(dòng)畫在跳動(dòng)。
8.2 Choreographer幀率檢測(cè)方案
8.2.1 VSync信號(hào)及作用
Android系統(tǒng)一幀繪制完成,顯示器會(huì)發(fā)出一個(gè)垂直同步信息(vertical synchronization)VSync信號(hào),顯示器通常以固定頻率進(jìn)行刷新,這個(gè)刷新率就是VSync信號(hào)產(chǎn)生的頻率。為解決GPU和視頻控制器不同步,GPU有一個(gè)垂直同步(V-Sync)來通知界面進(jìn)行渲染、繪制,每一次同步周期為16.6ms,代表一幀的刷頻率,一次界面渲染會(huì)回調(diào)doFrame方法,如果兩次doFrame之間的間隔大于16.6ms,說明發(fā)生了卡頓,可保存兩次doFrame時(shí)間進(jìn)行相減再除以刷新頻率,這樣算出來的結(jié)果就是兩次doFrame的掉幀數(shù)。
8.2.2 Choreographer怎么檢測(cè)掉幀
Choregrapher對(duì)VSync信號(hào)做了監(jiān)聽,當(dāng)有VSync消息的時(shí)候會(huì)執(zhí)行onVsync,最終走到Choregrapher的doFrame,這里會(huì)將callback隊(duì)列中取出runnable進(jìn)行繪制。
通過Choreographer設(shè)置它的FrameCallback,在每一幀被渲染時(shí)記錄下它開始渲染的時(shí)間,在下一幀渲染過程中判斷是否出現(xiàn)掉幀。
3)Android的一個(gè)顯示周期可表現(xiàn)為以下幾部分:
CPU+GPU繪制幀數(shù)據(jù),繪制結(jié)束的數(shù)據(jù)存放在緩沖區(qū)BufferQueue中;
SurfaceFlinger從BufferQueue中取數(shù)據(jù)并計(jì)算;
Choreographer完成最終繪制;
9. android app啟動(dòng)過程及優(yōu)化
9.1 App應(yīng)用啟動(dòng)方式
9.1.1 冷啟動(dòng)
后臺(tái)沒有被啟動(dòng)應(yīng)用的進(jìn)程,系統(tǒng)會(huì)重新創(chuàng)建一個(gè)新的進(jìn)程分配給當(dāng)前應(yīng)用(AMS調(diào)用startProcessLocked()方法創(chuàng)建新進(jìn)程)。它會(huì)先創(chuàng)建和初始化Application類,再將進(jìn)程和指定的Applciation綁定起來,再創(chuàng)建和初始化MainActivity類(包括一系列的測(cè)量、布局、繪制),最后顯示在界面上。
Launcher是一個(gè)進(jìn)程,被啟動(dòng)的應(yīng)用是另一個(gè)進(jìn)程,這里涉及到跨進(jìn)程調(diào)用,Launcher通知Binder通過ActivityManagerService來啟動(dòng)另一個(gè)應(yīng)用Activity.
具體可控制流程:
Application的構(gòu)造方法——》attachBaseContext() ——》onCreate() ——》Activity構(gòu)造方法->onCreate() –> 配置主題中背景 –》 onStart() –> onResume() –> 測(cè)量布局繪制顯示界面。
9.1.2 熱啟動(dòng)
后臺(tái)已有被啟動(dòng)應(yīng)用的進(jìn)程,會(huì)直接從已有的進(jìn)程中來啟動(dòng)應(yīng)用,不會(huì)再走Application,直接到MainActivity就行。
調(diào)用application線程對(duì)象中的scheduleLaunchActivity()發(fā)送一個(gè)LAUNCH_ACTIVITY消息到消息隊(duì)列中,通過handleLaunchActivity()來處理該消息。
9.2 測(cè)量應(yīng)用啟動(dòng)時(shí)間
應(yīng)用啟動(dòng)時(shí)間是指從點(diǎn)擊應(yīng)用啟動(dòng)圖標(biāo)開始創(chuàng)建出一個(gè)進(jìn)程直到看到界面上第一幀。
在命令行中運(yùn)行命令:adb shell am start -W [packageName]/[packageName.MainActivity]
會(huì)有三個(gè)時(shí)間,ThisTime(調(diào)用過程中最后一個(gè)Activity啟動(dòng)時(shí)間到這個(gè)Activity的startActivityAndWait調(diào)用結(jié)束)、WaitTime(startActivityAndWait方法的耗時(shí))、TotalTime(調(diào)用過程中第一個(gè)Activity的啟動(dòng)時(shí)間到最后一個(gè)Activity的startActivityAndWait結(jié)束)
9.3 app啟動(dòng)的優(yōu)化(冷啟動(dòng))
1) Application的創(chuàng)建過程中盡量少進(jìn)行耗時(shí)操作
2) 如果用到SharePreference,盡量在異步線程中操作
3) 首次啟動(dòng)的Activity減少布局層次,并在生命周期回調(diào)的方法盡量減少耗時(shí)操作
4) 為主界面單獨(dú)寫一個(gè)主題style,并設(shè)置成啟動(dòng)圖,然后在啟動(dòng)的Activity設(shè)置成主題,在Activity加載布局前把主題重新設(shè)置回來。
10. apk 瘦包機(jī)制
上圖是一般的apk安裝包含的文件,如果加入混淆等還會(huì)有proguard.cfg、project.properties等文件,從圖中可以看出AndroidManifest.xml、META-INF這些本身就很小沒有必要做進(jìn)一步壓縮的文件,而其它文件或者文件夾都可以考慮進(jìn)行優(yōu)化,從而減小APK的體積。下面具體說說android apk的一些瘦包實(shí)用技巧:
10.1 使用lint減小resources.arsc體積
使用lint除一些無用的代碼、無用的資源、無用的變量等;還可以借助lint消除一些隱藏的bug,從頁(yè)減小resources.arsc體積,用法是:Android Studio→Inspect Code…對(duì)工程做靜態(tài)代碼檢查;
10.2 代碼混淆減少classes.dex大小
代碼混淆可以減小該文件的大小,因?yàn)榛煜蟮拇a將較長(zhǎng)的文件名、實(shí)例、變量、方法名等等做了簡(jiǎn)化,從而實(shí)現(xiàn)字節(jié)長(zhǎng)度上的優(yōu)化;
10.3 壓縮資源
1) 使用一些小圖片代替大圖,有些適配圖片可能只需要保留一套如xxhdpi,有些背景圖片用代碼實(shí)現(xiàn),用.9.png;
2) 使用tinypng壓縮圖片;
3) 對(duì)圖片質(zhì)量要求不是很嚴(yán)格,可以考慮不帶alpha值的jpg圖片、同等質(zhì)量下文件更小的webP圖片格式;
4) 借助微信提供的AndResGuard資源文件混淆工具對(duì)資源文件做混淆,進(jìn)一步壓縮資源文件所占用的空間;
10.4 去掉一些不用的適配
1) 如果只需要支持中文,可在build.grade中添加resConfigs “zh”去除無用的語(yǔ)言資源;
2) 去掉一些so包支持
一種CPU架構(gòu) = 一種ABI = 一種對(duì)應(yīng)的SO庫(kù);
現(xiàn)在手機(jī)市場(chǎng)總共支持以下七種不同的CPU架構(gòu):ARMv5,ARMv7,x86,MIPS,ARMv8,MIPS64和x86_64, 這7種CPU類型對(duì)應(yīng)的SO庫(kù)的文件夾名是:armeabi,armeabi-v7a,x86,mips,arm64-v8a,mips64,x86_64。所有的x86/x86_64/armeabi-v7a/arm64-v8a設(shè)備都支持armeabi架構(gòu)的.so文件,64位設(shè)備(arm64-v8a, x86_64, mips64)能夠運(yùn)行32位的函數(shù)庫(kù),所以應(yīng)用不需要支持很多設(shè)備,建議實(shí)際工作的配置是只保留armable、armable-x86下的so文件,當(dāng)然so包是向下兼容的,如果提供了其他cpu類型的文件夾,也需要在相應(yīng)的文件夾里補(bǔ)全所有的so包,要不手機(jī)到時(shí)適配找不到合適的so包導(dǎo)致crash。
10.5 減小或慎用第三方依賴庫(kù)
如果只使用第三方依賴庫(kù)的少部分功能,可以考慮只提取少部分代碼,而不是直接把第三庫(kù)全部引入;
用較少的庫(kù)替換大庫(kù);
10.6 插件化和動(dòng)態(tài)加載
上面提到的so包,可以只提供一套,在應(yīng)用運(yùn)行時(shí),需要用到so包的地方,可以從服務(wù)器下載so包,再動(dòng)態(tài)加載;
10.7 使用7zip打包
五、源碼或?qū)崿F(xiàn)原理
1. 消息機(jī)制
handler消息機(jī)制,它可以將一個(gè)任務(wù)切換到handler所在的線程中去執(zhí)行;handler需要MessageQueue\Looper\Message的支持;各自的任務(wù)是:
Handler–負(fù)責(zé)消息的發(fā)送、接收處理;
MessageQueue–消息隊(duì)列,一個(gè)消息存儲(chǔ)單位,經(jīng)常需要進(jìn)行增減,內(nèi)部使用單鏈表結(jié)構(gòu);
Looper–消息循環(huán),不停地從MessageQueue中取消息,如有新消息就會(huì)立即處理,否則一直阻塞;
Message–消息載體。
1.1 Handler是怎么接收到消息
1) Looper的prepare()會(huì)為當(dāng)前線程創(chuàng)建一個(gè)Looper實(shí)例,該實(shí)例會(huì)創(chuàng)建一個(gè)消息隊(duì)列MessageQueue對(duì)象,將線程設(shè)為當(dāng)前線程;最后將該實(shí)例保存在當(dāng)前線程中;
2) 構(gòu)建Handler時(shí),會(huì)先獲取到當(dāng)前Handler所在線程的Looper并得到其中的MessageQueue;
2) 使用Handler發(fā)送消息時(shí),會(huì)將一個(gè)Message保存到當(dāng)前線程Looper中的MessageQueue中;
3) 調(diào)用loop()方法會(huì)開啟消息循環(huán),不斷從MessageQueue中取消息,當(dāng)收到消息后,會(huì)調(diào)用msg.target.dispatchMessage(msg);來處理消息;最終會(huì)調(diào)用Handler的handlerMessage進(jìn)行處理消息;
1.2 Handler是怎么發(fā)送消息
Handler的post一個(gè)Runnable對(duì)象和sendMessage一個(gè)Message對(duì)象
2. binder
2.1 Android為什么要采用Binder作為IPC機(jī)制
1)性能方面:Binder數(shù)據(jù)拷貝只需一次,而管道、消息隊(duì)列、Socket都需要2次,共享內(nèi)存不需要拷貝,性能解度,Binder僅次于共享內(nèi)存;
2) 穩(wěn)定性方面:Binder是基于C/S架構(gòu),架構(gòu)清晰,穩(wěn)定性好。共享內(nèi)存實(shí)現(xiàn)方式復(fù)雜,沒有客戶和服務(wù)端之別,需要充分考慮訪問臨界資源的并發(fā)同步問題,否則會(huì)出現(xiàn)死鎖等問題,故Binder穩(wěn)定性優(yōu)于共享內(nèi)存;
3) 安全方面:傳統(tǒng)的IPC接收方無法獲得對(duì)方進(jìn)程可靠的UID/PID,無法鑒別對(duì)方身份。而Binder機(jī)制的Server端可根據(jù)權(quán)限控制策略,判斷UID/PID是否滿足訪問權(quán)限。
2.2 實(shí)現(xiàn)方法
1) 在服務(wù)端新建需要的AIDL類(聲明以.aidl為后綴的接口類,并聲明相應(yīng)的方法),保存后會(huì)在gen目錄下生成相應(yīng)的java類,它是系統(tǒng)為我們聲明的aidl接口生成的Binder類,繼承自android.os.IInterface,自己也還是一個(gè)接口。
2) 編寫服務(wù)端Service類,新建一個(gè).Stub對(duì)象,在onBind方法中返回定義的aidl的Binder對(duì)象。
3) 在服務(wù)端注冊(cè)服務(wù);
4) 在客戶端新建ServiceConnection對(duì)象,重寫onServiceConnected方法和onServiceDisconnected方法,在onServiceConnected方法可以獲取到服務(wù)端的Binder對(duì)象,從而可以調(diào)用服務(wù)端的方法進(jìn)行通信。
5) 在客戶端綁定service對(duì)象
3. LeankCanary檢測(cè)內(nèi)存泄漏原理
1) RefWatcher.watch()會(huì)以監(jiān)控對(duì)象來創(chuàng)建一個(gè)KeyedWeakReference弱引用對(duì)象;
2) 在AndroidWatchExecutor的后臺(tái)線程里,來檢查弱引用是否被清除,沒有則執(zhí)行一次GC;
3) 若弱引用對(duì)象仍沒有被清除,說明內(nèi)存泄漏了,系統(tǒng)導(dǎo)出hprof文件,保存在app的文件系統(tǒng)目錄下;
4) HeapAnalyzerService啟動(dòng)一個(gè)單獨(dú)進(jìn)程,使用HeapAnalyzer來分析hprof文件,它使用了另一個(gè)開源庫(kù)HAHA.
5) HeapAnalyzer通過查找KeyedWeakReference弱引用對(duì)象來查找內(nèi)在泄漏;
6) HeapAnalyzer計(jì)算KeyedWeakReference所引用對(duì)象的最短強(qiáng)引用路徑,來分析內(nèi)存泄漏,并構(gòu)建出對(duì)象引用鏈出來;
7) 內(nèi)存泄漏信息送回給DisplayLeakService,它是運(yùn)行在app進(jìn)程的一個(gè)服務(wù),再在設(shè)備通知欄顯示內(nèi)存泄漏信息。
六、其他
1. 雙親委派機(jī)制
1.1 類加載器的作用
類加載器(class loader):用來加載Java類到Java虛擬機(jī)中。Java源程序(.java)在經(jīng)過Java編譯器后被轉(zhuǎn)換成Java字節(jié)代碼(.class)。類加載器負(fù)責(zé)讀取Java字節(jié)代碼,并轉(zhuǎn)換成java.lang.Class類的一個(gè)實(shí)例。
1.2 什么叫雙親委派
某個(gè)特定類的加載器在接到加載類的請(qǐng)求時(shí),先將加載任務(wù)委托給父類加載器,依次遞歸(如果沒有繼續(xù)向上尋找父類加載器),如果父類加載器可以完成類加載任務(wù),就成功返回;只有父類加載器無法完成加載任務(wù),才自己加載;
android中加載類的順序:
自定義類加載器CustomClassLoader > 應(yīng)用程序類加載器AppClassLoader > 擴(kuò)展類加載器 ExtClassLoader > 啟動(dòng)類加載器(BootStrapClassLoader)
1.3 雙親委派的意義
防止內(nèi)存中出現(xiàn)多份同樣的字節(jié)碼;只有當(dāng)使用該class時(shí)才會(huì)去裝載,一個(gè)classloader只會(huì)裝載同一個(gè)class一次。
2. 子線程和UI線程
2.1 android為什么不能在子線程更新UI
因?yàn)锳ndroid的UI線程訪問是非線程安全,沒有加鎖,這樣在多個(gè)線程訪問UI是不安全的, 所以Android中規(guī)定只能在UI線程中訪問UI。
2.2 子線程一定不能更新UI線程么
當(dāng)訪問UI控件時(shí),android是在ViewRootImpl的checkThread方法中檢測(cè)當(dāng)前線程是否是主線程,如果不是會(huì)報(bào)錯(cuò)。而ViewRootImpl的在WindowManagerGlobal的addView方法中創(chuàng)建,而addView方法是在在onResume方法回調(diào)之后。所以如果在onCreate方法中創(chuàng)建了子線程并訪問UI,ViewRootImpl還沒創(chuàng)建,無法檢測(cè)當(dāng)前線程是否是UI線程,程序可以在子線程中訪問UI線程。
3. RxBus和EventBus
3.1 EventBus作為一個(gè)消息總線,主要有三個(gè)元素
1) Event:要發(fā)送的事件對(duì)象
2) Publisher: 事件發(fā)布者,用于通知Subscriber有事件發(fā)生。直接調(diào)用eventBus.post(Object)可以在任意線程任意位置發(fā)送事件,根據(jù)post函數(shù)參數(shù)類型,會(huì)自動(dòng)調(diào)用訂閱相應(yīng)類型事件的函數(shù)。
3) Subscriber: 事件訂閱者,接收特定事件,所有事件訂閱都是以onEvent開頭的函數(shù),有onEvent, onEventMainThread, onEventBackgroundThread, onEventAsync這四個(gè)。與ThreadMode有關(guān)。
3.2 RxBus,結(jié)合Rxajva的便利
先要定義一個(gè)RxBus的類:
里邊包含一個(gè)單例RxBus類對(duì)象:private static volatile RxBus instance;
一個(gè)final類型的Subject對(duì)象:bus = new SerilizedSubject<>(PublishSubject.create());
一個(gè)發(fā)送事件的方法:public void post(Object o){bus.onNext(o);}
一個(gè)接收事件的方法:public Observable toObservable(Class eventType){return bus.ofType(eventType);}
Subject同時(shí)充當(dāng)了Observer和Observable角色,Subject是非線程安全,所以將它轉(zhuǎn)成SerilizedSubject,包裝成線程安全的Subject.
4.RecyclerView和listview的比較
1) ViewHolder模式————ListView通過創(chuàng)建ViewHolder來提升性能不是必須的,RecyclerView的Adapter必須至少一個(gè)ViewHolder,必須遵循ViewHolder設(shè)計(jì)模式;
2) Item布局————ListView只能實(shí)現(xiàn)垂直線性布局,RecyclerView可以實(shí)現(xiàn)垂直、水平、瀑布流等;
3) Item動(dòng)畫,ListView沒有提供增刪Item的動(dòng)畫,RecyclerView可以在RecyclerView.ItemAnimator來為條目增加動(dòng)畫效果;
4) 設(shè)置數(shù)據(jù)源————ListView對(duì)Adapter封裝了一些數(shù)據(jù),如ArrayAdapter, CursorAdapter,而RecyclerView.Adapter則必須實(shí)現(xiàn)RecyclerView.Adapter
5) 分隔線————ListView可以直接設(shè)置android:divider設(shè)置分割線,RecyclerView必須使用RecyclerView.ItemDecoration來設(shè)置;
6) 點(diǎn)擊響應(yīng)————ListView提供了AdapterView.OnItemClickListener接口,RecyclerView必須使用RecyclerView.OnItemTouchListener來響應(yīng)條目觸摸事件;
7) 緩存————ListView采用兩級(jí)緩存,RecyclerView采用四級(jí)緩存
ListView的緩存,先判斷數(shù)據(jù)源數(shù)據(jù)是否改變,a)沒有則從mActiveViews中通過匹配pos讀取view緩存;b)有則從mScrapViews中讀取view緩存;再通過mAdapter.getView(pos, ScrapView, parent)將緩存用于getView(),若可重用,則避免createView耗時(shí),無法避免bindView耗時(shí);
RecyclerView的緩存,a) 從mAttachedScrap中通過匹配pos獲取holder緩存,成功直接顯示,b) 不成功從mCacheViews中匹配pos獲取holder緩存,c) 不成功再?gòu)膍ViewCacheExtension中自定義獲取holder緩存;d) 不成功再?gòu)膍RecycledViewPool中獲取holder緩存;成功則清除holder所有標(biāo)志位,不成功則調(diào)用mAdapter.createViewHolder(parent, type)
ListView通過pos獲取view, pos–>view
RecyclerView通過pos獲取viewHolder,pos–>(view, viewHolder, flag)
5. Serializable和Parcelable的比較
5.1 設(shè)計(jì)作用
Serializable: 標(biāo)記接口,為了保存對(duì)象的屬性到本地文件、數(shù)據(jù)庫(kù)、網(wǎng)絡(luò)流、rmi以方便數(shù)據(jù)傳輸;
Parcelable:為了在程序內(nèi)不同組件間及Android程序間高效的傳輸數(shù)據(jù)而設(shè)計(jì),這些數(shù)據(jù)僅在內(nèi)存中存在;
5.2 效率方面
Parcelable性能比Serializable好,因?yàn)楹笳咴诜瓷溥^程頻繁GC
Parcelabe: 適合在內(nèi)存中數(shù)據(jù)傳輸,如activity間傳輸數(shù)據(jù);
5.3 使用方面
Serializable: 只需要實(shí)現(xiàn)Serializable接口
Parcelabe:需要實(shí)現(xiàn)writeToParcel, describeContents函數(shù)及靜太CREATOR變量
6. java靜態(tài)內(nèi)部類和內(nèi)部類區(qū)別
6.1 持有引用
非靜態(tài)內(nèi)部類會(huì)隱式持有一個(gè)外圍類的引用,而靜態(tài)內(nèi)部類不會(huì)持有外部類的引用;
6.2 訪問外部類
1) 非靜態(tài)內(nèi)部類能訪問外圍類的一切成員,包括私有變量,外部類不能直接訪問內(nèi)部類的成員,但可通過內(nèi)部類的實(shí)例訪問;靜態(tài)內(nèi)部類不能訪問外圍類的非公開成員,因?yàn)樗鼈兪莾蓚€(gè)不同的類
6.3 構(gòu)造內(nèi)部類
非靜態(tài)內(nèi)部類的構(gòu)造需要借助外部類的實(shí)例進(jìn)行構(gòu)造:
OuterClass.InnerClass innerObject = new OuterClass().new InnerClass();靜態(tài)內(nèi)部類可以直接進(jìn)行實(shí)例化:
OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();7. TCP/IP五層通信體系結(jié)構(gòu)
應(yīng)用層(應(yīng)用程序數(shù)據(jù))》傳輸層(Segment/Datagram)傳輸?shù)氖荰CP數(shù)據(jù)或是UDP數(shù)據(jù) 》網(wǎng)絡(luò)層(Packet,路由) 》數(shù)據(jù)鏈路層(Frame,以太網(wǎng)交換機(jī)) 》物理層(Bit,集線器,雙絞線)
總結(jié)
以上是生活随笔為你收集整理的android基本功的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 谭浩强-习题6.5
- 下一篇: 电容三点式LC振荡器电路组成及工作原理简