Android 面試

1,Activity 生命周期

onCreate()? 創(chuàng)建?

onRestart() 重新啟動(dòng)?

onStart()啟動(dòng)? ?

onResume()可見?

onPause()正在停止,不能交互?

onStop()不可見?

onDestroy() 即將銷毀。

第一次啟動(dòng): onCreate() > onStart> onResume();

A activity 到B activity? ? ? ? A_onPause()>B_onCreate()>B_onStart()>B_onResume()>A_onStop()

B activity 返回A activity? ? B_onPause >A_onRestart()> A_onStart()> A_onResume()>B_onStop()>B_onDestroy()

顯示和隱藏標(biāo)準(zhǔn)的 AlertDialog 不會(huì)對(duì) MainActivity 的生命周期有任何的影響。

顯示和隱藏全屏的 AlertDialog 不會(huì)對(duì) MainActivity 的生命周期有任何的影響。

主題為 Dialog 的 Activity 會(huì)對(duì) MainActivity 的生命周期有影響,并且跳轉(zhuǎn)主題為 Dialog 的 Activity 與跳轉(zhuǎn)普通的 Activity 的生命周期變化相同。

橫豎屏切換時(shí)的生命周期調(diào)用為:onPause() -> onStop() -> onDestory() -> onCreate() -> onStart() -> onResume() 。就是一個(gè)銷毀再重建的過程。

將 MainActivity 的 android:configChanges 設(shè)置為 orientation【o:rui:ten:tion】?,之后切換橫豎屏并不會(huì)有任何的生命周期方法的調(diào)用

內(nèi)存不足時(shí),殺死應(yīng)用,前臺(tái)的 Activity 生命周期為 onPause() -> onStop()?并沒有調(diào)用 onDestory() 方法,所以主進(jìn)程現(xiàn)在屬于后臺(tái)進(jìn)程。

fragment被創(chuàng)建的時(shí)候,經(jīng)歷包含onAttach、onCreate、onCreateView、onActivityCreated方法;fragment對(duì)用戶可見的時(shí)候,經(jīng)歷包含onStart、onResume方法;fragment進(jìn)入“后臺(tái)模式”的時(shí)候,經(jīng)歷onPause、onStop方法;fragment被銷毀了(或者持有它的activity被銷毀了),經(jīng)歷包含onPause、onStop、onDestroyView、onDestroy、onDetach方法;并且可用onCreate、onCreateView、onActivityCreated方法Bundle對(duì)象保存一個(gè)fragment的對(duì)象。

onAttach:當(dāng)Fragment與Activity發(fā)生關(guān)聯(lián)時(shí)調(diào)用

onCreate:創(chuàng)建Fragment時(shí)被回調(diào),經(jīng)歷暫?;蛲V?fàn)顟B(tài)繼而恢復(fù)后,想保留Fragment的基本組件,則在此進(jìn)行初始化。

onCreateView:首次繪制頁面時(shí)候調(diào)用,在此可以創(chuàng)建View,也可以返回null,這樣不建議耗時(shí)操作。

onActivityCreated:Fragment綁定Activity,在onCreate方法已經(jīng)執(zhí)行完成并返回,在該方法內(nèi)可以進(jìn)行與Activity交互的UI操作,不能在此之前跟Activity進(jìn)行交互。

onStart:啟動(dòng) Fragment 時(shí)被回調(diào),此時(shí)Fragment可見,只是還沒有在前臺(tái)顯示,因此無法與用戶進(jìn)行交互

onResume:Fragment在前臺(tái)可見,處于活動(dòng)狀態(tài),用戶可與之交互

onPause:Fragment處于暫停狀態(tài),但依然可見,用戶不能與之交互

onStop:停止Fragment回調(diào),F(xiàn)ragment完全不可見

onDestoryView:銷毀與Fragment有關(guān)的視圖,但未與Activity解除綁定

onDestory:銷毀 Fragment 時(shí)被回調(diào),通常按Back鍵退出或者Fragment被回收時(shí)調(diào)用此方法,此后接onDetach

onDetach:與onAttach相對(duì)應(yīng),當(dāng)Fragment與Activity關(guān)聯(lián)被取消時(shí)調(diào)用

setUserVisibleHint:調(diào)用次方法可以設(shè)置Fragment可見或者不可見??梢哉{(diào)用getUserVisibleHint()獲得Fragment的可見或不可見狀態(tài),如果可見則進(jìn)行懶加載操作

在異常情況下,系統(tǒng)默認(rèn)恢復(fù) TextView 的文本信息

在切換橫豎屏?xí)r,onPause() 方法中打印了 TextView 的文本內(nèi)容,切換重建后,在 onResume() 中獲取到 TextView 的內(nèi)容與之前的內(nèi)容相同,并且要注意,重建后 onCreate() 與 onStart() 方法中獲取 TextView 的文本內(nèi)容是布局文件中的默認(rèn)內(nèi)容。

2, Activity 啟動(dòng)模式

standard 標(biāo)準(zhǔn)模式 每啟動(dòng)一個(gè)都會(huì)創(chuàng)建一個(gè)實(shí)例

singleTop 棧頂復(fù)用 如果在棧頂就調(diào)用onNewintent ,從onResume()開始

singleTask 棧內(nèi)復(fù)用 棧內(nèi)有,就復(fù)用,并將其上的Activity 移出

singleInstance 單例模式 系統(tǒng)會(huì)給該Activity 創(chuàng)建一個(gè)棧

3 .Service

onCreat()創(chuàng)建服務(wù)

onStartCommand()開始服務(wù)

onDestroy()銷毀服務(wù)

onBind()綁定服務(wù)

onUnbind()解綁服務(wù)

1)啟動(dòng)Service服務(wù)

單次:startService() —> onCreate() —> onStartCommand()

多次:startService() —> onCreate() —> onStartCommand() —> onStartCommand()

2)停止Service服務(wù)

stopService() —> onDestroy()

3)綁定Service服務(wù)

bindService() —> onCreate() —> onBind()

4)解綁Service服務(wù)

unbindService() —> onUnbind() —> onDestroy()

5)啟動(dòng)綁定Service服務(wù)

startService() —> onCreate() —> onStartCommand() —> bindService() —> onBind()

6)解綁停止Service服務(wù)

unbindService() —> onUnbind() —> stopService() —> onDestroy()

7)解綁綁定Service服務(wù)

unbindService() —> onUnbind(ture) —> bindService() —> onRebind()

8 ,stopSelf()也可以停止服務(wù)

。如果是多個(gè)組件綁定到一個(gè)服務(wù)上,當(dāng)綁定到該服務(wù)的所有組件都被銷毀時(shí),服務(wù)才會(huì)停止。

4,什么時(shí)候用Service

service是一個(gè)應(yīng)用組件,長時(shí)間運(yùn)行在后臺(tái),不需要直接跟用戶交互。

Service不是運(yùn)行在獨(dú)立的線程,所以不建議在Service中編寫耗時(shí)的邏輯和操作,否則會(huì)引起ANR。

1) 默認(rèn)情況下,Service其實(shí)是運(yùn)行在主線程中的,如果需要執(zhí)行復(fù)雜耗時(shí)的操作,必須在Service中再創(chuàng)建一個(gè)Thread來執(zhí)行任務(wù)。

(2) Service的優(yōu)先級(jí)高于后臺(tái)掛起的Activity,當(dāng)然,也高于Activity所創(chuàng)建的Thread,因此,系統(tǒng)可能在內(nèi)存不足的時(shí)候優(yōu)先殺死后臺(tái)的Activity或者Thread,而不會(huì)輕易殺死Service組件,即使被迫殺死Service,也會(huì)在資源可用時(shí)重啟被殺死的Service

在Service中創(chuàng)建的Thread,適合長期執(zhí)行一些獨(dú)立于APP的后臺(tái)任務(wù),比較常見的就是:在Service中保持與服務(wù)器端的長連接

5,intentService

Service不是運(yùn)行在獨(dú)立的線程,所以不建議在Service中編寫耗時(shí)的邏輯和操作,否則會(huì)引起ANR。

1,可用于執(zhí)行后臺(tái)耗時(shí)的任務(wù),任務(wù)執(zhí)行后會(huì)自動(dòng)停止。

2,具有高優(yōu)先級(jí),適合高優(yōu)先級(jí)的后臺(tái)任務(wù),且不容易被系統(tǒng)殺死。

3,可以多次啟動(dòng),每個(gè)耗時(shí)操作都會(huì)以工作隊(duì)列的方式在IntentService的onHandleIntent回調(diào)方法中執(zhí)行

內(nèi)部其實(shí)就是 HandlerThread + Handler ,? Handler發(fā)消息調(diào)用onHandleIntent()

執(zhí)行完任務(wù)會(huì) 執(zhí)行 stopSelf(), 自己會(huì)自殺,

優(yōu)先級(jí)高, 設(shè)為0 ,為高優(yōu)先級(jí)

6,Android 里的多線程

多線程的應(yīng)用在Android開發(fā)中是非常常見的,常用方法主要有:

繼承Thread類

實(shí)現(xiàn)Runnable接口

Handler

AsyncTask

HandlerThread

HandlerThread的本質(zhì):繼承Thread類 & 封裝Handler類

// 步驟1:創(chuàng)建HandlerThread實(shí)例對(duì)象

// 傳入?yún)?shù) = 線程名字,作用 = 標(biāo)記該線程

? HandlerThread mHandlerThread = new HandlerThread("handlerThread");

/ 步驟2:啟動(dòng)線程

? mHandlerThread.start();

// 步驟3:創(chuàng)建工作線程Handler & 復(fù)寫handleMessage()

// 作用:關(guān)聯(lián)HandlerThread的Looper對(duì)象、實(shí)現(xiàn)消息處理操作 & 與其他線程進(jìn)行通信

// 注:消息處理操作(HandlerMessage())的執(zhí)行線程 = mHandlerThread所創(chuàng)建的工作線程中執(zhí)行

? Handler workHandler = new Handler( handlerThread.getLooper() ) {

? ? ? ? ? ? @Override

? ? ? ? ? ? public boolean handleMessage(Message msg) {

? ? ? ? ? ? ? ? ...//消息處理

? ? ? ? ? ? ? ? return true;

? ? ? ? ? ? }

? ? ? ? });

// 步驟4:使用工作線程Handler向工作線程的消息隊(duì)列發(fā)送消息

// 在工作線程中,當(dāng)消息循環(huán)時(shí)取出對(duì)應(yīng)消息 & 在工作線程執(zhí)行相關(guān)操作

? // a. 定義要發(fā)送的消息

? Message msg = Message.obtain();

? msg.what = 2; //消息的標(biāo)識(shí)

? msg.obj = "B"; // 消息的存放

? // b. 通過Handler發(fā)送消息到其綁定的消息隊(duì)列

? workHandler.sendMessage(msg);

// 步驟5:結(jié)束線程,即停止線程的消息循環(huán)

? mHandlerThread.quit();

由run方法可知HandlerThrea線程運(yùn)行創(chuàng)建了Looper實(shí)例,并開啟了Looper循環(huán),循環(huán)從消息隊(duì)列中獲取消息并給Handler進(jìn)行處理。

注:子線程 用Handler 用法

7,Looper

1,主線程(UI線程)

UI線程中Looper已經(jīng)都創(chuàng)建好了,不用我們?nèi)?chuàng)建和循環(huán)。

2,普通線程

普通線程中使用Looper需要我們自己去prepare()、loop()。

看一下普通線程中創(chuàng)建使用Looper的方式,代碼如下:

class LooperThread extends Thread {

? ? public Handler mHandler;

? ? public void run() {

? ? ? ? Looper.prepare();

? ? ? ? mHandler = new Handler() {

? ? ? ? ? ? public void handleMessage(Message msg) {

? ? ? ? ? ? ? ? // process incoming messages here

? ? ? ? ? ? }

? ? ? ? };

? ? ? ? Looper.loop();

? }

}

印象中在UI線程沒有出現(xiàn)過Looper相關(guān)的東東,這是因?yàn)閁I線程中會(huì)自動(dòng)創(chuàng)建Looper對(duì)象并進(jìn)行消息循環(huán),我們不再需要調(diào)用Looper.prepare()和Looper.loop(),但是在子線程中如果想要?jiǎng)?chuàng)建使用Handelr則需要向如上所示。

我們通過源碼看一下Looper實(shí)例創(chuàng)建的方法:

public static void prepare() {

? ? prepare(true);

}

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));

}

sThreadLocal為ThreadLocal類型變量,用來存儲(chǔ)線程中的Looper對(duì)象。prepare方法中首先判斷sThreadLocal是否存儲(chǔ)對(duì)象,如果存儲(chǔ)了則拋出異常,這是因?yàn)樵谕粋€(gè)線程中Loop.prepare()方法不能調(diào)用兩次,也就是同一個(gè)線程中最多有一個(gè)Looper實(shí)例

接著看Looper的構(gòu)造器:

private Looper(boolean quitAllowed) {

? mQueue = new MessageQueue(quitAllowed);

? ? mRun = true;

? mThread = Thread.currentThread();

}

在構(gòu)造器中,創(chuàng)建了一個(gè)MessageQueue消息隊(duì)列;然后獲取當(dāng)前的線程,使Looper實(shí)例與線程綁定。由prepare方法可知一個(gè)線程只會(huì)有一個(gè)Looper實(shí)例,所以一個(gè)Looper實(shí)例也只有一個(gè)MessageQueue實(shí)例。但這并不代表一個(gè)線程只能有一個(gè)MessageQueue實(shí)例,這是為什么呢?很簡單,我們可以自己new 一個(gè)MessageQueue實(shí)例就可以了,但這個(gè)MessageQueue并不是該線程中Handelr對(duì)應(yīng)的消息隊(duì)列。

總結(jié):

UI線程會(huì)自動(dòng)創(chuàng)建Looper實(shí)例、并且調(diào)用loop()方法,不需要我們?cè)僬{(diào)用prepare()和loop().

Looper與創(chuàng)建它的線程綁定,確保一個(gè)線程最多有一個(gè)Looper實(shí)例,同時(shí)一個(gè)Looper實(shí)例只有一個(gè)MessageQueue實(shí)例。

loop()函數(shù)循環(huán)從MessageQueue中獲取消息,并將消息交給消息的target的dispatchMessage去處理。如果MessageQueue中沒有消息則獲取消息可能會(huì)阻塞。

通過調(diào)用Looper的quit或quitsafely終止消息循環(huán)。

相同點(diǎn):

將不在接受新的事件加入消息隊(duì)列。

不同點(diǎn)

當(dāng)我們調(diào)用Looper的quit方法時(shí),實(shí)際上執(zhí)行了MessageQueue中的removeAllMessagesLocked方法,該方法的作用是把MessageQueue消息池中所有的消息全部清空,無論是延遲消息(延遲消息是指通過sendMessageDelayed或通過postDelayed等方法發(fā)送的需要延遲執(zhí)行的消息)還是非延遲消息。

當(dāng)我們調(diào)用Looper的quitSafely方法時(shí),實(shí)際上執(zhí)行了MessageQueue中的removeAllFutureMessagesLocked方法,通過名字就可以看出,該方法只會(huì)清空MessageQueue消息池中所有的延遲消息,并將消息池中所有的非延遲消息派發(fā)出去讓Handler去處理,quitSafely相比于quit方法安全之處在于清空消息之前會(huì)派發(fā)所有的非延遲消息。

6, Parcelable 與 Serializable 區(qū)別

兩者最大的區(qū)別在于存儲(chǔ)媒介的不同,Serializable使用IO讀寫存儲(chǔ)在硬盤上,而Parcelable是直接在內(nèi)存中讀寫,很明顯內(nèi)存的讀寫速度通常大于IO讀寫,

android上應(yīng)該盡量采用Parcelable,效率至上

編碼上:

Serializable代碼量少,寫起來方便

Parcelable代碼多一些

效率上:

Parcelable的速度比高十倍以上

serializable的迷人之處在于你只需要對(duì)某個(gè)類以及它的屬性實(shí)現(xiàn)Serializable 接口即可。Serializable 接口是一種標(biāo)識(shí)接口(marker interface),這意味著無需實(shí)現(xiàn)方法,Java便會(huì)對(duì)這個(gè)對(duì)象進(jìn)行高效的序列化操作。

這種方法的缺點(diǎn)是使用了反射,序列化的過程較慢。這種機(jī)制會(huì)在序列化的時(shí)候創(chuàng)建許多的臨時(shí)對(duì)象,容易觸發(fā)垃圾回收。

Parcelable方式的實(shí)現(xiàn)原理是將一個(gè)完整的對(duì)象進(jìn)行分解,而分解后的每一部分都是Intent所支持的數(shù)據(jù)類型,這樣也就實(shí)現(xiàn)傳遞對(duì)象的功能了

7內(nèi)存抖動(dòng);

避免在ondraw()創(chuàng)建對(duì)象,因?yàn)槟銜?huì)頻繁創(chuàng)建只使用一次的對(duì)象,就會(huì)導(dǎo)致內(nèi)存的迅速攀升,就很可能觸犯GC的回收,

這種在短時(shí)間內(nèi)反復(fù)發(fā)生內(nèi)存增長和回收的循環(huán)。有界面卡頓的風(fēng)險(xiǎn)。Android 官方叫Memory churn

在onDraw()里創(chuàng)建對(duì)象往往跟繪制相關(guān),這些對(duì)象又經(jīng)常包含通往系統(tǒng)下層Native的對(duì)象引用,這就導(dǎo)致回收耗時(shí)會(huì)更高。

8 MVC、MVP 和 MVVM 區(qū)別

1.1 MVC

1.1.1 MVC三個(gè)字母的含義

View :對(duì)應(yīng)的XML文件

Model:實(shí)體類javabean

Controllor: 對(duì)應(yīng)Activity或者Fragment

? 1.1.3 優(yōu)缺點(diǎn)(以android的角度講)

優(yōu)點(diǎn)

Xml就是View層,與java邏輯代碼解耦,具有一定的解耦

缺點(diǎn)

沒有固定的model層,Activity中代表Controllor,網(wǎng)絡(luò)接口的model也在Activity中,一個(gè)邏輯稍微復(fù)雜的頁面代碼行數(shù)都上千行,影響閱讀,Activity既然控制著View,又含有Model。相當(dāng)耦合

2.2 MVP

2.2.1 MVP三個(gè)字母的含義

View 對(duì)應(yīng)于Activity和Xml,負(fù)責(zé)頁面的展示

Model 實(shí)體類javabean

Presenter 控制器 負(fù)責(zé)完成View于Model間的交互與邏輯處理(網(wǎng)絡(luò)和邏輯)

2.2.3 優(yōu)缺點(diǎn)

優(yōu)點(diǎn):

Model和View之間解耦,兩者交互由Presenter完成,把部分的邏輯的代碼從Fragment和Activity業(yè)務(wù)的邏輯移出來

Activity或者Fragment只是用來展示控件,或者控制控件的顯示和隱藏,以及View的變化

在MVP中View和presenter要相互持有,方便調(diào)用對(duì)方

缺點(diǎn):

隨著業(yè)務(wù)的增加,IView的接口和IPresenter的接口會(huì)越來越多

更換Xml里面的控件會(huì)引起IView接口和IPressenter接口的改動(dòng)

2.3 MVVM(只從android考慮)

2.3.1 MVVM三個(gè)字母的含義

View 還是Activity或者fragment,也就是Xml,負(fù)責(zé)頁面的展示,或者控制控件的顯示和隱藏,以及View的變化,不參與任何邏輯和數(shù)據(jù)的處理

Viewmodel 主要負(fù)責(zé)業(yè)務(wù)邏輯和數(shù)據(jù)處理,本身不持有 View 層引用,通過LiveData(如果項(xiàng)目中有Rxjava可以不引用LiveData) 向 View 層發(fā)送數(shù)據(jù),通過DataBinding更改View中的UI層

Model:實(shí)體類javabean,便是指這里的Repository ,主要負(fù)責(zé)從本地?cái)?shù)據(jù)庫或者遠(yuǎn)程服務(wù)器來獲取數(shù)據(jù),Repository統(tǒng)一了數(shù)據(jù)的入口,獲取到數(shù)據(jù),將數(shù)據(jù)發(fā)送?

2.3.3 優(yōu)缺點(diǎn)

優(yōu)點(diǎn) :

View和數(shù)據(jù)雙向綁定,當(dāng)model變化時(shí)會(huì)自動(dòng)同步給View

crash會(huì)降低,xml里面自己會(huì)有判斷

View(Activity或者Fragment)層就是View層,不處理任何關(guān)于數(shù)據(jù)的邏輯

缺點(diǎn) :

Xml里面寫代碼,對(duì)于android有點(diǎn)不習(xí)慣,并且會(huì)使xml臃腫

無法快速定位crash位置,Debug比較困難

雙向綁定不利于View的復(fù)用,因?yàn)槊總€(gè)xml里面都有一個(gè)model,但是每個(gè)頁面的mode有可能不一樣

xml里面寫java代碼,不能用kotlin的簡單語言

UI線程是不安全

悲觀鎖:

Binder 是基于文件的通信方式

RxJava


dispatch Touch Event? 事件分發(fā)

On Intercept? touch Event 是否攔截

On Touch Event? 消耗

ViewGroup和View 的dispatchTouchEvent 是做事件分發(fā),那么這個(gè)事件可能分發(fā)出去的四個(gè)目標(biāo)

注:------> 后面代表事件目標(biāo)需要怎么做。

1、 自己消費(fèi),終結(jié)傳遞。------->return true ;

2、 給自己的onTouchEvent處理-------> 調(diào)用super.dispatchTouchEvent()系統(tǒng)默認(rèn)會(huì)去調(diào)用 onInterceptTouchEvent,在onInterceptTouchEvent return true就會(huì)去把事件分給自己的onTouchEvent處理。

3、 傳給子View------>調(diào)用super.dispatchTouchEvent()默認(rèn)實(shí)現(xiàn)會(huì)去調(diào)用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就會(huì)把事件傳給子類。

4、 不傳給子View,事件終止往下傳遞,事件開始回溯,從父View的onTouchEvent開始事件從下到上回歸執(zhí)行每個(gè)控件的onTouchEvent------->return false;

注:?由于View沒有子View所以不需要onInterceptTouchEvent 來控件是否把事件傳遞給子View還是攔截,所以View的事件分發(fā)調(diào)用super.dispatchTouchEvent()的時(shí)候默認(rèn)把事件傳給自己的onTouchEvent處理(相當(dāng)于攔截),對(duì)比ViewGroup的dispatchTouchEvent 事件分發(fā),View的事件分發(fā)沒有上面提到的4個(gè)目標(biāo)的第3點(diǎn)。

ViewGroup和View的onTouchEvent方法是做事件處理的,那么這個(gè)事件只能有兩個(gè)處理方式:

1、自己消費(fèi)掉,事件終結(jié),不再傳給誰----->return true;

2、繼續(xù)從下往上傳,不消費(fèi)事件,讓父View也能收到到這個(gè)事件----->return false;View的默認(rèn)實(shí)現(xiàn)是不消費(fèi)的。所以super==false。

ViewGroup的onInterceptTouchEvent方法對(duì)于事件有兩種情況:

1、攔截下來,給自己的onTouchEvent處理--->return true;

2、不攔截,把事件往下傳給子View---->return false,ViewGroup默認(rèn)是不攔截的,所以super==false;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容