Android 面試基礎篇

一隻有交流障礙的醜程式猿發表於2018-03-31

本文是Android面試題整理中的一篇,結合右下角目錄食用更佳,包括:

  • 架構
  • Activity
  • Service
  • BroadCast
  • ContentProvider
  • Fragment

架構


Android的大體架構圖

分為四個層次:linux核心;libraies和Android runntime;framework;Application

Android 面試基礎篇

Android的四大元件是哪些,它們的作用?

  1. Activity:Activity是Android程式與使用者互動的視窗,對使用者來說是可見的
  2. service:後臺服務於Activity,是一個服務,不可見
  3. Content Provider:對外提提供資料
  4. BroadCast Receiver:接受一種或者多種Intent作觸發事件,接受相關訊息,做一些簡單處理

Android 中程式的優先順序

  1. 前臺程式
  2. 可見程式
  3. 服務程式
  4. 後臺程式
  5. 空程式

Android中asset和res目錄的區別

  1. res目錄下的資原始檔會在R檔案中生成對應的id,asset不會\
  2. res目錄下的檔案在生成apk時,除raw(即res/raw)目錄下檔案不進行編譯外,都會被編譯成二進位制檔案;asset目錄下的檔案不會進行編譯
  3. asset目錄允許有子目錄

Android中App 是如何沙箱化的,為何要這麼做

  1. 沙箱化可以提升安全性和效率
  2. Android的底層核心為Linux,因此繼承了Linux良好的安全性,並對其進行了優化。在Linux中,一個使用者對應一個uid,而在Android中,(通常)一個APP對應一個uid,擁有獨立的資源和空間,與其他APP互不干擾。如有兩個APP A和B,A並不能訪問B的資源,A的崩潰也不會對B造成影響,從而保證了安全性和效率

Activity


Activity 生命週期

Android 面試基礎篇

Activity在螢幕旋轉時的生命週期

  1. 沒有任何設定時,會呼叫整個生命週期方法,並且會呼叫onSaveInstance和onRestoreInstanceState方法
  2. 在Manifest中為Activity設定android:configChanges="orientation"時,只呼叫onConfigChanges方法
  3. android:configChanges="orientation"屬性有可能不起作用,依然會呼叫整個生命週期方法,這是因為不同版本處理方式可能不同,有時候還需要加上android:configChanges="orientation|keyboardHidden|screenSize"等。

onSaveInstanceState 什麼時候呼叫

  1. 非使用者主動明確結束(按back鍵,自定義click方法呼叫finish)時都會呼叫onSaveInstanceState:
    1. 螢幕旋轉
    2. 按HOME鍵
    3. 記憶體不足
    4. 從一個activity啟動另一個activity
  2. 這個方法的呼叫時機是在onStop前,但是它和onPause沒有既定的時序關係

自定義View控制元件的狀態被儲存需要滿足兩個條件

  1. View有唯一的ID
  2. View的初始化時要呼叫setSaveEnabled(true)

configChanges屬性

對Activity配置了android:configChanges="xxx"屬性之後,Activity就不會在對應變化發生時重新建立,而是呼叫Activity的onConfigurationChanged方法。常用的有local:裝置的本地位置發生了變化,一般指切換了系統語言;keyboardHidden:鍵盤的可訪問性發生了變化,比如使用者調出了鍵盤;orientation:螢幕方向發生了變化,比如旋轉了手機螢幕。

A activity啟動B activity和B activity返回A activity的生命週期執行過程

  1. A啟動B:A.onPause()→B.onCreate()→B.onStart()→B.onResume()→A.onStop
  2. B返回A:B.onPause()→A.onRestart()/A.onCreate()→A.onStart()→A.onResume()→B.onStop()

Activity執行finish後的生命週期

  1. 在onCreate中執行:onCreate -> onDestroy
  2. 在onStart中執行:onCreate -> onStart -> onStop -> onDestroy
  3. 在onResume中執行:onCreate -> onStart -> onResume -> onpause -> onStop -> onDestroy

如果用了一些解耦的策略,怎麼管理生命週期的?

  1. 可以用Google的LifeCycle框架 0. 引入LifeCycle框架
    1. 將控制元件實現LifecycleObserver介面
    2. 在Activity中中註冊控制元件:getLifeCycle().addObderver(View);
    3. 在控制元件中使用: @OnLifecycleEvent(Lifecycle.Event.ON_START)

Activity的啟動流程

Android 面試基礎篇

Android中Activity的啟動模式

  1. standard:每一次啟動,都會生成一個新的例項,放入棧頂中
  2. singleTop:通過singelTop啟動Activity時,如果發現有需要啟動的例項正在棧頂,責直接重用,否則生成新的例項
  3. singleTask:通過singleTask啟動Activity時,如果發現有需要啟動的例項正在棧中,責直接移除它上邊的例項,並重用該例項,否則生成新的例項
  4. singleInstance:通過singleTask啟動Activity時,會啟用一個新的棧結構,並將新生成的例項放入棧中。

TaskAffinity 屬性

  1. 任務相關性,標識一個Activity所需的任務棧的名字。預設情況下,所有的Activity所需的任務棧的名字是應用的包名,當然也可以單獨指定TaskAffinity屬性。
  2. TaskAffinity屬性主要和singleTask啟動模式和allowTaskRepeating屬性配對使用,在其他情況下使用沒有意義
  3. 當TaskAffinity和singleTask啟動模式配對使用的時候,它是具有該模式的Activity的目前任務棧的名字,待啟動的Activity會執行在名字和TaskAffinity相同的任務棧中
  4. 當TaskAffinity和allowTaskReparenting結合的時候,當一個應用A啟動了應用B的某個Activity C後,如果Activity C的allowTaskReparenting屬性設定為true的話,那麼當應用B被啟動後,系統會發現Activity C所需的任務棧存在了,就將Activity C從A的任務棧中轉移到B的任務棧中。

Activity啟動模式的TaskAffinity和allowTaskReparenting

  1. TaskAffinity配合singleTask使用,指定任務棧:如果沒有TaskAffinity指定的任務棧,則開啟新棧
  2. allowTaskReparenting配合standard和singleTop使用,標明該Activity的任務棧可以重新設定

當前應用有兩個Activity A和B,B的 android:launchMode 設定了singleTask模式,A是預設的standard,那麼A startActivity啟動B,B會新啟一個Task嗎?如果不會,那麼startActivity的Intent加上FLAG_ACTIVITY_NEW_TASK這個引數會不會呢?

設定了singleTask啟動模式的Activity,它在啟動的時會先在系統中檢視屬性值affinity等於它的屬性值taskAffinity ( taskAffinity預設為包名 ) 的任務棧是否存在。如果存在這樣的任務棧,它就會在這個任務棧中啟動,否則就會在新任務棧中啟動。

當Intent物件包含FLAG_ACTIVITY_NEW_TASK標記時,系統在查詢時仍然按Activity的taskAffinity屬性進行匹配,如果找到一個任務棧的taskAffinity與之相同,就將目標Activity壓入此任務棧中,如果找不到則建立一個新的任務棧。

設定了singleTask啟動模式的Activity在已有的任務棧中已經存在相應的Activity例項,再啟動它時會把這個Activity例項上面的Activity全部結束掉。也就是說singleTask自帶clear top的效果。

IntentFilter的匹配規則

IntentFilter中的過濾資訊有action、category、data,為了匹配過濾列表,需要同時匹配過濾列表中的action、category、data資訊,否則匹配失敗。

驗證是否有當前Activity

  1. PackageManager的resolveActivity方法或者Intent的resolveActivity方法:如果找不到就會返回null
  2. PackageManager的queryIntentActivities方法:它返回所有成功匹配的Activity資訊

如何獲取當前螢幕Activity的物件?

通過在Application中註冊Activity生命週期的監聽函式Application.registerActivityLifecycleCallbacks()

onNewIntent呼叫時機

一個Activity已經啟動,當再次啟動它時,如果他的啟動模式(如SingleTask,SingleTop)標明不需要重新啟動,會呼叫onNewIntent

除了用Intent 去啟動一個Activity,還有其他方法嗎

使用adb shell am 命令 :如adb shell am start com.example.fuchenxuan/.MainActivity 或者 adb shell am broadcast -a magcomm.action.TOUCH_LETTER

Android中子執行緒更新UI的方式

  1. activity.runOnUiThread(runnable)
  2. 通過主執行緒中的Handler進行更新
  3. 通過View的post()或者postDelayed方法進行更新

Activity之間的通訊方式

  1. Intent
  2. BroadCast或者LocalBroadCast
  3. 資料儲存的方式
  4. 靜態變數

Service


Service的啟動方式

  1. start
  2. bind

Service生命週期

Android 面試基礎篇

Service 和Activity 的通訊方式

  1. 如上Activity和Activity的通訊方式
  2. bind方式啟動時可以通過ServiceConnection通訊:在SerVice的onBind方法中返回一個binder,該binder可以是AIDL方法產生的,也可以是Messenger方法產生的

Service和Thread的區別

  1. 這是沒用任何關係的兩個概念,servie是系統的元件,Thread是CPU執行的最小單元
  2. Service不可見,我們可以把Service當成是不可見的Activity,用於在後臺執行一些服務;
  3. Service可以執行在任意執行緒上,如果我們生成它時沒有做特殊說明,那麼它執行在主執行緒上
  4. 很多時候我們需要在Activity中開啟一個Service,再在Service中開啟一個執行緒,這麼做的原因是Service只會初始化一次,我們可以隨時找到Service中生成的thread,使用場景舉例:
    1. 如我們需要在多個Activity中對同一個thread進行控制時;
    2. 如果你的 Thread 需要不停地隔一段時間就要連線伺服器做某種同步的話,該 Thread 需要在 Activity 沒有start的時候也在執行。這個時候當你 start 一個 Activity 就沒有辦法在該 Activity 裡面控制之前建立的 Thread。因此你便需要建立並啟動一個 Service ,在 Service 裡面建立、執行並控制該 Thread,這樣便解決了該問題

為什麼有時需要在Service中建立子執行緒而不是Activity中

這是因為Activity很難對Thread進行控制,當Activity被銷燬之後,就沒有任何其它的辦法可以再重新獲取到之前建立的子執行緒的例項。而且在一個Activity中建立的子執行緒,另一個Activity無法對其進行操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然後可以很方便地操作其中的方法,即使Activity被銷燬了,之後只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的例項。因此,使用Service來處理後臺任務,Activity就可以放心地finish,完全不需要擔心無法對後臺任務進行控制的情況。

IntentService

  1. IntentService 是繼承自 Service,內部通過HandlerThread啟動一個新執行緒處理耗時操作麼,可以看做是Service和HandlerThread的結合體,在完成了使命之後會自動停止,適合需要在工作執行緒處理UI無關任務的場景
  2. 如果啟動 IntentService 多次,那麼每一個耗時操作會以工作佇列的方式在 IntentService 的 onHandleIntent 回撥方法中執行,依次去執行,使用序列的方式,執行完自動結束。

IntentService生命週期是怎樣的

  1. 在所有任務執行完畢後,自動結束生命

BroadCast


BroadCast的註冊方式與區別

  1. 在manifest中靜態註冊:廣播是常駐的,App關閉後仍能接收廣播,喚醒App
  2. 動態的註冊和登出:動態註冊的廣播生命週期和他的宿主相同,或者呼叫登出方法登出廣播

Android中傳送BroadCast的方式

  1. 無序廣播:通過mContext.sendBroadcast(Intent)或mContext.sendBroadcast(Intent, String)傳送的是無序廣播(後者加了許可權);
  2. 通過mContext.sendOrderedBroadcast(Intent, String, BroadCastReceiver, Handler, int, String, Bundle)傳送的是有序廣播(不再推薦使用)。
  3. 在無序廣播中,所有的Receiver會接收到相同廣播;而在有序廣播中,我們可以為Receiver設定優先順序,優先順序高的先接收廣播,並有權對廣播進行處理和決定要不要繼續向下傳送

BroadCastReceiver處理耗時操作

  1. BroadcastReceiver的生命週期只有一個回撥方法onReceive(Context context, Intent intent);無法進行耗時操作,即使啟動執行緒處理,也是出於非活動狀態,有可能被系統殺掉。
  2. 如果需要進行耗時操作,可以啟動一個service處理。

廣播傳送和接收的原理了解嗎

  1. 繼承BroadcastReceiver,重寫onReceive()方法。
  2. 通過Binder機制向ActivityManagerService註冊廣播。
  3. 通過Binder機制向ActivityMangerService傳送廣播。
  4. ActivityManagerService查詢符合相應條件的廣播(IntentFilter/Permission)的BroadcastReceiver,將廣播傳送到BroadcastReceiver所在的訊息佇列中。
  5. BroadcastReceiver所在訊息佇列拿到此廣播後,回撥它的onReceive()方法。

廣播傳輸的資料是否有限制,是多少,為什麼要限制?

  1. 廣播是通過Intent攜帶需要傳遞的資料的
  2. Intent是通過Binder機制實現的
  3. Binder對資料大小有限制,不同room不一樣,一般為1M

Localbroadcast

本地廣播,只有本程式中的receivers能接收到此廣播

實現原理(監聽者模式):

  1. LocalBroadcastManager是一個單例
  2. 在LocalBroadcastManager例項中維護一個Action和ReceiverRecord的Map.(ReceiverRecord是reveiver和intentfilter的組合)
  3. 當呼叫LocalBroadcastManager的sendBroadcast方法時,會從2中的map找到合適的receiver,讓後加到待執行的佇列mPendingBroadcasts,並通過Handler傳送一個空訊息(此Handler執行在主執行緒中,是建立manager時建立的)
  4. handler 的handle方法收到訊息,從mPendingBroadcasts取出receiver並呼叫onreceive方法
    其他:刪除方法是通過一個輔助的hashmap實現的,hashmap儲存了receiver和receiverRecord

ContentProvider


請介紹下ContentProvider是如何實現資料共享的

  1. 準確的說,ContentProvider是一個APP間共享資料的介面。一個程式可以通過實現一個Content provider的抽象介面將自己的資料完全暴露出去,資料可以是SqLite中的,也可以是檔案或者其他型別。
  2. 使用方式:
    1. 在A APP中實現建ContentProvider,並在Manifest中生命它的Uri和許可權
    2. 在B APP中註冊許可權,並通過ContentResolver和Uri進行增刪改查
  3. 擴充套件:ContentProvider底層是通過Binder機制來實現跨程式間通訊,通過匿名共享記憶體方式進行資料的傳輸 一個應用程式有16個Binder執行緒去和遠端服務進行互動,而每個執行緒可佔用的快取空間是128KB,超過會報異常。

每個ContentProvider的操作是在哪個執行緒中執行的呢(其實我們關心的是UI執行緒和工作執行緒)?比如我們在UI執行緒呼叫getContentResolver().query查詢資料,而當資料量很大時(或者需要進行較長時間的計算)會不會阻塞UI執行緒呢?

  1. ContentProvider和呼叫者在同一個程式,ContentProvider的方法(query/insert/update/delete等)和呼叫者在同一執行緒中
  2. ContentProvider和呼叫者在不同的程式,ContentProvider的方法會執行在它自身所在程式的一個Binder執行緒中

ContentProvider、ContentResolver與ContentObserver之間的關係是什麼?

  1. ContentProvider:管理資料,提供資料的增刪改查操作,資料來源可以是資料庫、檔案、XML、網路等,ContentProvider為這些資料的訪問提供了統一的介面,可以用來做程式間資料共享。
  2. ContentResolver:ContentResolver可以不同URI操作不同的ContentProvider中的資料,外部程式可以通過ContentResolver與ContentProvider進行互動。
  3. ContentObserver:觀察ContentProvider中的資料變化,並將變化通知給外界。

Fragment


Fragment生命週期

onAttach -> onCreate -> onCreateView -> onActivityCreate -> onStart -> onResume -> onPause -> onStop -> onDestoryView -> onDestory -> onDetach

Android 面試基礎篇

遇到過哪些關於Fragment的問題,如何處理的

舉例:getActivity()空指標:這種情況一般發生在在非同步任務裡呼叫getActivity(),而Fragment已經onDetach()。

Fragment 有什麼優點, Fragment和View可以相互替換嘛

  1. Fragment為了解決Andriod碎片化而產生的
  2. Fragment和View都有助於介面複用
  3. Fragment的複用粒度更大,包含生命週期和業務邏輯,通常包含好幾個View
  4. View通常更關注檢視的實現

Fragment add replace 區別

  1. replace 先刪除容器中的內容,再新增
  2. add直接新增,可以配合hide適用

參考資料

LearningNotes

40 個 Android 面試題

https://www.nowcoder.com/discuss/3043

http://weixin.niurenqushi.com/article/2017-03-17/4790406.html

https://blog.csdn.net/vfush/article/details/51481127

https://blog.csdn.net/vfush/article/details/51790079

相關文章