【建議收藏】2020年中高級Android大廠面試祕籍,爲你保駕護航金三銀四,直通大廠(Android基礎篇)...

前言

成爲一名優秀的Android開發,需要一份完備的知識體系,在這裏,讓我們一起成長爲自己所想的那樣~。

A awesome android expert interview questions and answers(continuous updating ...)

從幾十份頂級面試倉庫和300多篇高質量面經中總結出一份全面成體系化的Android高級面試題集。

歡迎來到2020年中高級Android大廠面試祕籍,爲你保駕護航金三銀四,直通大廠的Android基礎篇。

Android基礎面試題 (⭐⭐⭐)


1、什麼是ANR 如何避免它?

答:在Android上,如果你的應用程序有一段時間響應不夠靈敏,系統會向用戶顯示一個對話框,這個對話框稱作應 用程序無響應(ANR:Application NotResponding)對話框。 用戶可以選擇讓程序繼續運行,但是,他們在使用你的 應用程序時,並不希望每次都要處理這個對話框。因此 ,在程序裏對響應性能的設計很重要這樣,這樣系統就不會顯 示ANR給用戶。

不同的組件發生ANR的時間不一樣,Activity是5秒,BroadCastReceiver是10秒,Service是20秒(均爲前臺)。

如果開發機器上出現問題,我們可以通過查看/data/anr/traces.txt即可,最新的ANR信息在最開始部分。

  • 主線程被IO操作(從4.0之後網絡IO不允許在主線程中)阻塞。
  • 主線程中存在耗時的計算
  • 主線程中錯誤的操作,比如Thread.wait或者Thread.sleep等 Android系統會監控程序的響應狀況,一旦出現下面兩種情況,則彈出ANR對話框
  • 應用在5秒內未響應用戶的輸入事件(如按鍵或者觸摸)
  • BroadcastReceiver未在10秒內完成相關的處理
  • Service在特定的時間內無法處理完成 20秒

修正:

1、使用AsyncTask處理耗時IO操作。

2、使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設置優先級,否則仍然會降低程序響應,因爲默認Thread的優先級和主線程相同。

3、使用Handler處理工作線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程。

4、Activity的onCreate和onResume回調中儘量避免耗時的代碼。 BroadcastReceiver中onReceive代碼也要儘量減少耗時,建議使用IntentService處理。

解決方案:

將所有耗時操作,比如訪問網絡,Socket通信,查詢大 量SQL 語句,複雜邏輯計算等都放在子線程中去,然 後通過handler.sendMessage、runonUIThread、AsyncTask、RxJava等方式更新UI。無論如何都要確保用戶界面的流暢 度。如果耗時操作需要讓用戶等待,那麼可以在界面上顯示度條。

深入回答

2、Activity和Fragment生命週期有哪些?

3、橫豎屏切換時候Activity的生命週期

  • 1)、Android 3.2 (API 13) 之前:
    • 不設置 Activity 的 android:configChanges 時,切屏會重新調用生命週期,切橫屏會調用一次,切豎屏會調用兩次。
    • 設置 Activity 的 android:configChanges="orientation" 時,切屏會重新調用生命週期,且橫豎屏都是調用一次生命週期。
    • 設置 Activity 的 android:configChanges="orientation|keyboardHidden" 時,切屏不會重新調用 Activity 的生命週期,但是會調用 onConfigurationChanges() 方法。
  • 2)、從Android 3.2 (API 13) 開始
    • 不設置 Activity 的 android:configChanges 時、設置 Activity 的 android:configChanges="orientation"
    • 設置 Activity 的 android:configChanges="orientaion|keyboardHidden"時切換橫屏和豎屏都會重新調用一次生命週期。
    • 設置 Activity 的 android:configChanges="orientation|screenSize"時不會重新調用 Activity 的生命週期,但是會調用 onConfigurationChanges() 方法。

4、AsyncTask的缺陷和問題,說說他的原理。

AsyncTask是什麼?

AsyncTask是一種輕量級的異步任務類,它可以在線程池中執行後臺任務,然後把執行的進度和最終結果傳遞給主線程並在主線程中更新UI。

AsyncTask是一個抽象的泛型類,它提供了Params、Progress和Result這三個泛型參數,其中Params表示參數的類型,Progress表示後臺任務的執行進度和類型,而Result則表示後臺任務的返回結果的類型,如果AsyncTask不需要傳遞具體的參數,那麼這三個泛型參數可以用Void來代替。

關於線程池:

AsyncTask對應的線程池ThreadPoolExecutor都是進程範圍內共享的,且都是static的,所以是Asynctask控制着進程範圍內所有的子類實例。由於這個限制的存在,當使用默認線程池時,如果線程數超過線程池的最大容量,線程池就會爆掉(3.0後默認串行執行,不會出現個問題)。針對這種情況,可以嘗試自定義線程池,配合Asynctask使用。

關於默認線程池:

AsyncTask裏面線程池是一個核心線程數爲CPU + 1,最大線程數爲CPU * 2 + 1,工作隊列長度爲128的線程池,線程等待隊列的最大等待數爲28,但是可以自定義線程池。線程池是由AsyncTask來處理的,線程池允許tasks並行運行,需要注意的是併發情況下數據的一致性問題,新數據可能會被老數據覆蓋掉。所以希望tasks能夠串行運行的話,使用SERIAL_EXECUTOR。

AsyncTask在不同的SDK版本中的區別:

調用AsyncTask的execute方法不能立即執行程序的原因及改善方案通過查閱官方文檔發現,AsyncTask首次引入時,異步任務是在一個獨立的線程中順序的執行,也就是說一次只執行一個任務,不能並行的執行,從1.6開始,AsyncTask引入了線程池,支持同時執行5個異步任務,也就是說只能有5個線程運行,超過的線程只能等待,等待前的線程直到某個執行完了才被調度和運行。換句話說,如果進程中的AsyncTask實例個數超過5個,那麼假如前5都運行很長時間的話,那麼第6個只能等待機會了。這是AsyncTask的一個限制,而且對於2.3以前的版本無法解決。如果你的應用需要大量的後臺線程去執行任務,那麼只能放棄使用AsyncTask,自己創建線程池來管理Thread。不得不說,雖然AsyncTask較Thread使用起來方便,但是它最多隻能同時運行5個線程,這也大大侷限了它的作用,你必須要小心設計你的應用,錯開使用AsyncTask時間,盡力做到分時,或者保證數量不會大於5個,否就會遇到上面提到的問題。可能是Google意識到了AsynTask的侷限性了,從Android 3.0開始對AsyncTask的API做出了一些調整:每次只啓動一個線程執行一個任務,完了之後再執行第二個任務,也就是相當於只有一個後臺線程在執行所提交的任務。

一些問題:

1.生命週期

很多開發者會認爲一個在Activity中創建的AsyncTask會隨着Activity的銷燬而銷燬。然而事實並非如此。AsynTask會一直執行,直到doInBackground()方法執行完畢,然後,如果cancel(boolean)被調用,那麼onCancelled(Result result)方法會被執行;否則,執行onPostExecute(Result result)方法。如果我們的Activity銷燬之前,沒有取消AsyncTask,這有可能讓我們的應用崩潰(crash)。因爲它想要處理的view已經不存在了。所以,我們是必須確保在銷燬活動之前取消任務。總之,我們使用AsyncTask需要確保AsyncTask正確的取消。

2.內存泄漏

如果AsyncTask被聲明爲Activity的非靜態內部類,那麼AsyncTask會保留一個對Activity的引用。如果Activity已經被銷燬,AsyncTask的後臺線程還在執行,它將繼續在內存裏保留這個引用,導致Activity無法被回收,引起內存泄漏。

3.結果丟失

屏幕旋轉或Activity在後臺被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。

4.並行還是串行

在Android1.6之前的版本,AsyncTask是串行的,在1.6之後的版本,採用線程池處理並行任務,但是從Android 3.0開始,爲了避免AsyncTask所帶來的併發錯誤,又採用一個線程來串行執行任務。可以使用executeOnExecutor()方法來並行地執行任務。

AsyncTask原理
  • AsyncTask中有兩個線程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一個Handler(InternalHandler),其中線程池SerialExecutor用於任務的排隊,而線程池THREAD_POOL_EXECUTOR用於真正地執行任務,InternalHandler用於將執行環境從線程池切換到主線程。
  • InternalHandler是一個靜態的Handler對象,爲了能夠將執行環境切換到主線程,這就要求sHandler這個對象必須在主線程創建。由於靜態成員會在加載類的時候進行初始化,因此這就變相要求AsyncTask的類必須在主線程中加載,否則同一個進程中的AsyncTask都將無法正常工作。

5、onSaveInstanceState() 與 onRestoreIntanceState()

Activity的 onSaveInstanceState() 和 onRestoreInstanceState()並不是生命週期方法,它們不同於 onCreate()、onPause()等生命週期方法,它們並不一定會被觸發。當應用遇到意外情況(如:內存不足、用戶直接按Home鍵)由系統銷燬一個Activity時,onSaveInstanceState() 會被調用。但是當用戶主動去銷燬一個Activity時,例如在應用中按返回鍵,onSaveInstanceState()就不會被調用。因爲在這種情況下,用戶的行爲決定了不需要保存Activity的狀態。通常onSaveInstanceState()只適合用於保存一些臨時性的狀態,而onPause()適合用於數據的持久化保存。 在activity被殺掉之前調用保存每個實例的狀態,以保證該狀態可以在onCreate(Bundle)或者onRestoreInstanceState(Bundle) (傳入的Bundle參數是由onSaveInstanceState封裝好的)中恢復。這個方法在一個activity被殺死前調用,當該activity在將來某個時刻回來時可以恢復其先前狀態。 例如,如果activity B啓用後位於activity A的前端,在某個時刻activity A因爲系統回收資源的問題要被殺掉,A通過onSaveInstanceState將有機會保存其用戶界面狀態,使得將來用戶返回到activity A時能通過onCreate(Bundle)或者onRestoreInstanceState(Bundle)恢復界面的狀態

深入理解

6、android中進程的優先級?

1. 前臺進程:

即與用戶正在交互的Activity或者Activity用到的Service等,如果系統內存不足時前臺進程是最晚被殺死的

2. 可見進程:

可以是處於暫停狀態(onPause)的Activity或者綁定在其上的Service,即被用戶可見,但由於失了焦點而不能與用戶交互

3. 服務進程:

其中運行着使用startService方法啓動的Service,雖然不被用戶可見,但是卻是用戶關心的,例如用戶正在非音樂界面聽的音樂或者正在非下載頁面下載的文件等;當系統要空間運行,前兩者進程纔會被終止

4. 後臺進程:

其中運行着執行onStop方法而停止的程序,但是卻不是用戶當前關心的,例如後臺掛着的QQ,這時的進程系統一旦沒了有內存就首先被殺死

5. 空進程:

不包含任何應用程序的進程,這樣的進程系統是一般不會讓他存在的

7、Bunder傳遞對象爲什麼需要序列化?Serialzable和Parcelable的區別?

因爲bundle傳遞數據時只支持基本數據類型,所以在傳遞對象時需要序列化轉換成可存儲或可傳輸的本質狀態(字節流)。序列化後的對象可以在網絡、IPC(比如啓動另一個進程的Activity、Service和Reciver)之間進行傳輸,也可以存儲到本地。

Serializable(Java自帶):

Serializable 是序列化的意思,表示將一個對象轉換成存儲或可傳輸的狀態。序列化後的對象可以在網絡上進傳輸,也可以存儲到本地。

Parcelable(android專用):

除了Serializable之外,使用Parcelable也可以實現相同的效果,不過不同於將對象進行序列化,Parcelable方式的實現原理是將一個完整的對象進行分解,而分解後的每一部分都是Intent所支持的數據類型,這也就實現傳遞對象的功能了。

區別總結如下圖所示:

8、動畫

  • tween 補間動畫。通過指定View的初末狀態和變化方式,對View的內容完成一系列的圖形變換來實現動畫效果。 Alpha, Scale ,Translate, Rotate。
  • frame 幀動畫。AnimationDrawable控制animation-list.xml佈局
  • PropertyAnimation 屬性動畫3.0引入,屬性動畫核心思想是對值的變化。

Property Animation 動畫有兩個步聚:

1.計算屬性值

2.爲目標對象的屬性設置屬性值,即應用和刷新動畫

計算屬性分爲3個過程:

過程一:

計算已完成動畫分數 elapsed fraction。爲了執行一個動畫,你需要創建一個ValueAnimator,並且指定目標對象屬性的開始、結束和持續時間。在調用 start 後的整個動畫過程中,ValueAnimator 會根據已經完成的動畫時間計算得到一個0 到 1 之間的分數,代表該動畫的已完成動畫百分比。0表示 0%,1 表示 100%。

過程二:

計算插值(動畫變化率)interpolated fraction 。當 ValueAnimator計算完已完成的動畫分數後,它會調用當前設置的TimeInterpolator,去計算得到一個interpolated(插值)分數,在計算過程中,已完成動畫百分比會被加入到新的插值計算中。

過程三:

計算屬性值當插值分數計算完成後,ValueAnimator會根據插值分數調用合適的 TypeEvaluator去計算運動中的屬性值。 以上分析引入了兩個概念:已完成動畫分數(elapsed fraction)、插值分數( interpolated fraction )。

原理及特點:

1.屬性動畫:

插值器:作用是根據時間流逝的百分比來計算屬性變化的百分比

估值器:在1的基礎上由這個東西來計算出屬性到底變化了多少數值的類

其實就是利用插值器和估值器,來計出各個時刻View的屬性,然後通過改變View的屬性來實現View的動畫效果。

2.View動畫:

只是影像變化,view的實際位置還在原來地方。

3.幀動畫:

是在xml中定義好一系列圖片之後,使用AnimatonDrawable來播放的動畫。

它們的區別:

屬性動畫纔是真正的實現了 view 的移動,補間動畫對view 的移動更像是在不同地方繪製了一個影子,實際對象還是處於原來的地方。 當動畫的 repeatCount 設置爲無限循環時,如果在Activity退出時沒有及時將動畫停止,屬性動畫會導致Activity無法釋放而導致內存泄漏,而補間動畫卻沒問題。 xml 文件實現的補間動畫,複用率極高。在 Activity切換,窗口彈出時等情景中有着很好的效果。 使用幀動畫時需要注意,不要使用過多特別大的圖,容導致內存不足。

爲什麼屬性動畫移動後仍可點擊?

播放補間動畫的時候,我們所看到的變化,都只是臨時的。而屬性動畫呢,它所改變的東西,卻會更新到這個View所對應的矩陣中,所以當ViewGroup分派事件的時候,會正確的將當前觸摸座標,轉換成矩陣變化後的座標,這就是爲什麼播放補間動畫不會改變觸摸區域的原因了。

9、Context相關

  • 1、Activity和Service以及Application的Context是不一樣的,Activity繼承自ContextThemeWraper.其他的繼承自ContextWrapper。

  • 2、每一個Activity和Service以及Application的Context是一個新的ContextImpl對象。

  • 3、getApplication()用來獲取Application實例的,但是這個方法只有在Activity和Service中才能調用的到。那也許在絕大多數情況下我們都是在Activity或者Servic中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的實例,這時就可以藉助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域會更廣一些,任何一個Context的實例,只要調用getApplicationContext()方法都可以拿到我們的Application對象。

  • 4、創建對話框時不可以用Application的context,只能用Activity的context。

  • 5、Context的數量等於Activity的個數 + Service的個數 +1,這個1爲Application。

10、Android各版本新特性

Android5.0新特性
  • MaterialDesign設計風格

  • 支持64位ART虛擬機(5.0推出的ART虛擬機,在5.0之前都是Dalvik。他們的區別是: Dalvik,每次運行,字節碼都需要通過即時編譯器轉換成機器碼(JIT)。 ART,第一次安裝應用的時候,字節碼就會預先編譯成機器碼(AOT))

  • 通知詳情可以用戶自己設計

Android6.0新特性
  • 動態權限管理

  • 支持快速充電的切換

  • 支持文件夾拖拽應用

  • 相機新增專業模式

Android7.0新特性
  • 多窗口支持

  • V2簽名

  • 增強的Java8語言模式

  • 夜間模式

Android8.0(O)新特性
  • 優化通知

    通知渠道 (Notification Channel) 通知標誌 休眠 通知超時 通知設置 通知清除

  • 畫中畫模式:清單中Activity設置android:supportsPictureInPicture

  • 後臺限制

  • 自動填充框架

  • 系統優化

  • 等等優化很多

Android9.0(P)新特性
  • 室內WIFI定位

  • “劉海”屏幕支持

  • 安全增強

  • 等等優化很多

Android10.0(Q)目前曝光的新特性
  • 夜間模式:包括手機上的所有應用都可以爲其設置暗黑模式。
  • 桌面模式:提供類似於PC的體驗,但是遠遠不能代替PC。
  • 屏幕錄製:通過長按“電源”菜單中的"屏幕快照"來開啓。

11、Json

JSON的全稱是JavaScript Object Notation,也就是JavaScript 對象表示法 JSON是存儲和交換文本信息的語法,類似XML,但是比XML更小、更快,更易解析 JSON是輕量級的文本數據交換格式,獨立於語言,具有可描述性,更易理解,對象可以包含多個名稱/值對,比如:

{"name":"zhangsan" , "age":25}
複製代碼

使用谷歌的GSON包進行解析,在 Android Studio 裏引入依賴:

compile 'com.google.code.gson:gson:2.7'
複製代碼

值得注意的是實體類中變量名稱必須和json中的值名字相同。

使用示例:

1、解析成實體類:

Gson gson = new Gson();
Student student = gson.fromJson(json1, Student.class);
複製代碼

2、解析成int數組:

Gson gson = new Gson();
int[] ages = gson.fromJson(json2, int[].class);
複製代碼

3、直接解析成List.

Gson gson = new Gson();
List<Integer> ages = gson.fromJson(json2,  newTypeToken<List<Integer>>(){}.getType);

Gson gson = new Gson();
List<Student> students = gson.fromJson(json3, newTypeToke<List<Student>>(){}.getType);
複製代碼
優點:
  • 輕量級的數據交換格式
  • 讀寫更加容易
  • 易於機器的解析和生成
缺點:
  • 語義性較差,不如 xml 直觀

12、android中有哪幾種解析xml的類,官方推薦哪種?以及它們的原理和區別?

DOM解析

優點:

1.XML樹在內存中完整存儲,因此可以直接修改其數據結構.

2.可以通過該解析器隨時訪問XML樹中的任何一個節點.

3.DOM解析器的API在使用上也相對比較簡單.

缺點:

如果XML文檔體積比較大時,將文檔讀入內存是非消耗系統資源的.

使用場景:

  • DOM 是與平臺和語言無關的方式表示 XML文檔的官方 W3C 標準.
  • DOM 是以層次結構組織的節點的集合.這個層次結構允許開人員在樹中尋找特定信息.分析該結構通常需要加載整個文檔和構造層次結構,然後才能進行任何工作.
  • DOM 是基於對象層次結構的.
SAX解析

優點:

SAX 對內存的要求比較低,因爲它讓開發人員自己來決定所要處理的標籤.特別是當開發人員只需要處理文檔中包含的部分數據時,SAX 這種擴展能力得到了更好的體現.

缺點:

用SAX方式進行XML解析時,需要順序執行,所以很難訪問同一文檔中的不同數據.此外,在基於該方式的解析編碼程序也相對複雜.

使用場景:

對於含有數據量十分巨大,而又不用對文檔的所有數據行遍歷或者分析的時候,使用該方法十分有效.該方法不將整個文檔讀入內存,而只需讀取到程序所需的文檔標記處即可.

Xmlpull解析

android SDK提供了xmlpullapi,xmlpull和sax類似,是基於流(stream)操作文件,後者根據節點事件回調開發者編寫的處理程序.因爲是基於流的處理,因此xmlpull和sax都比較節約內存資源,不會像dom那樣要把所有節點以對象樹的形式展現在內存中.xmpull比sax更簡明,而且不需要掃描完整個流.

13、Jar和Aar的區別

Jar包裏面只有代碼,aar裏面不光有代碼還包括資源文件,比如 drawable 文件,xml資源文件。對於一些不常變動的 Android Library,我們可以直接引用 aar,加快編譯速度。

14、Android爲每個應用程序分配的內存大小是多少

android程序內存一般限制在16M,也有的是24M。近幾年手機發展較快,一般都會分配兩百兆左右,和具體機型有關。

15、更新UI方式

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable),View.postDelay(Runnable, long)(可以理解爲在當前操作視圖UI線程添加隊列)
  • Handler
  • AsyncTask
  • Rxjava
  • LiveData

16、ContentProvider使用方法。

進行跨進程通信,實現進程間的數據交互和共享。通過Context 中 getContentResolver() 獲得實例,通過 Uri匹配進行數據的增刪改查。ContentProvider使用表的形式來組織數據,無論數據的來源是什麼,ConentProvider 都會認爲是一種表,然後把數據組織成表格。

17、Thread、AsyncTask、IntentService的使用場景與特點。

  1. Thread線程,獨立運行與於 Activity 的,當Activity 被 finish 後,如果沒有主動停止 Thread或者 run 方法沒有執行完,其會一直執行下去。

  2. AsyncTask 封裝了兩個線程池和一個Handler(SerialExecutor用於排隊,THREAD_POOL_EXECUTOR爲真正的執行任務,Handler將工作線程切換到主線程),其必須在 UI線程中創建,execute 方法必須在 UI線程中執行,一個任務實例只允許執行一次,執行多次拋出異常,用於網絡請求或者簡單數據處理。

  3. IntentService:處理異步請求,實現多線程,在onHandleIntent中處理耗時操作,多個耗時任務會依次執行,執行完畢自動結束。

18、Merge、ViewStub 的作用。

Merge: 減少視圖層級,可以刪除多餘的層級。

ViewStub: 按需加載,減少內存使用量、加快渲染速度、不支持 merge 標籤。

19、activity的startActivity和context的startActivity區別?

(1)、從Activity中啓動新的Activity時可以直接mContext.startActivity(intent)就好

(2)、如果從其他Context中啓動Activity則必須給intent設置Flag:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ; 
mContext.startActivity(intent);
複製代碼
20、怎麼在Service中創建Dialog對話框?

1.在我們取得Dialog對象後,需給它設置類型,即:

dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT)
複製代碼

2.在Manifest中加上權限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINOW" />
複製代碼

21、Asset目錄與res目錄的區別?

assets:不會在 R 文件中生成相應標記,存放到這裏的資源在打包時會打包到程序安裝包中。(通過 AssetManager 類訪問這些文件)

res:會在 R 文件中生成 id 標記,資源在打包時如果使用到則打包到安裝包中,未用到不會打入安裝包中。

res/anim:存放動畫資源。

res/raw:和 asset 下文件一樣,打包時直接打入程序安裝包中(會映射到 R 文件中)。

22、Android怎麼加速啓動Activity?

  • onCreate() 中不執行耗時操作 把頁面顯示的 View 細分一下,放在 AsyncTask 裏逐步顯示,用 Handler 更好。這樣用戶的看到的就是有層次有步驟的一個個的 View 的展示,不會是先看到一個黑屏,然後一下顯示所有 View。最好做成動畫,效果更自然。
  • 利用多線程的目的就是儘可能的減少 onCreate() 和 onReume() 的時間,使得用戶能儘快看到頁面,操作頁面。
  • 減少主線程阻塞時間。
  • 提高 Adapter 和 AdapterView 的效率。
  • 優化佈局文件。

23、Handler機制

Android消息循環流程圖如下所示:

主要涉及的角色如下所示:

  • message:消息。
  • MessageQueue:消息隊列,負責消息的存儲與管理,負責管理由 Handler 發送過來的 Message。讀取會自動刪除消息,單鏈表維護,插入和刪除上有優勢。在其next()方法中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。
  • Looper:消息循環器,負責關聯線程以及消息的分發,在該線程下從 MessageQueue獲取 Message,分發給Handler,Looper創建的時候會創建一個 MessageQueue,調用loop()方法的時候消息循環開始,其中會不斷調用messageQueue的next()方法,當有消息就處理,否則阻塞在messageQueue的next()方法中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然後loop()方法也就跟着退出。
  • Handler:消息處理器,負責發送並處理消息,面向開發者,提供 API,並隱藏背後實現的細節。

整個消息的循環流程還是比較清晰的,具體說來:

  • 1、Handler通過sendMessage()發送消息Message到消息隊列MessageQueue。
  • 2、Looper通過loop()不斷提取觸發條件的Message,並將Message交給對應的target handler來處理。
  • 3、target handler調用自身的handleMessage()方法來處理Message。

事實上,在整個消息循環的流程中,並不只有Java層參與,很多重要的工作都是在C++層來完成的。我們來看下這些類的調用關係。

注:虛線表示關聯關係,實線表示調用關係。

在這些類中MessageQueue是Java層與C++層維繫的橋樑,MessageQueue與Looper相關功能都通過MessageQueue的Native方法來完成,而其他虛線連接的類只有關聯關係,並沒有直接調用的關係,它們發生關聯的橋樑是MessageQueue。

總結
  • Handler 發送的消息由 MessageQueue 存儲管理,並由 Looper 負責回調消息到 handleMessage()。
  • 線程的轉換由 Looper 完成,handleMessage() 所在線程由 Looper.loop() 調用者所在線程決定。
Handler 引起的內存泄露原因以及最佳解決方案

Handler 允許我們發送延時消息,如果在延時期間用戶關閉了 Activity,那麼該 Activity 會泄露。 這個泄露是因爲 Message 會持有 Handler,而又因爲 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導致 Activity 泄露。

解決:將 Handler 定義成靜態的內部類,在內部持有 Activity 的弱引用,並在Acitivity的onDestroy()中調用handler.removeCallbacksAndMessages(null)及時移除所有消息。

爲什麼我們能在主線程直接使用 Handler,而不需要創建 Looper ?

通常我們認爲 ActivityThread 就是主線程。事實上它並不是一個線程,而是主線程操作的管理者。在 ActivityThread.main() 方法中調用了 Looper.prepareMainLooper() 方法創建了 主線程的 Looper ,並且調用了 loop() 方法,所以我們就可以直接使用 Handler 了。

因此我們可以利用 Callback 這個攔截機制來攔截 Handler 的消息。如大部分插件化框架中Hook ActivityThread.mH 的處理。

主線程的 Looper 不允許退出

主線程不允許退出,退出就意味 APP 要掛。

Handler 裏藏着的 Callback 能幹什麼?

Handler.Callback 有優先處理消息的權利 ,當一條消息被 Callback 處理並攔截(返回 true),那麼 Handler 的 handleMessage(msg) 方法就不會被調用了;如果 Callback 處理了消息,但是並沒有攔截,那麼就意味着一個消息可以同時被 Callback 以及 Handler 處理。

創建 Message 實例的最佳方式

爲了節省開銷,Android 給 Message 設計了回收機制,所以我們在使用的時候儘量複用 Message ,減少內存消耗:

  • 通過 Message 的靜態方法 Message.obtain();
  • 通過 Handler 的公有方法 handler.obtainMessage()。
子線程裏彈 Toast 的正確姿勢

本質上是因爲 Toast 的實現依賴於 Handler,按子線程使用 Handler 的要求修改即可,同理的還有 Dialog。

妙用 Looper 機制
  • 將 Runnable post 到主線程執行;
  • 利用 Looper 判斷當前線程是否是主線程。
主線程的死循環一直運行是不是特別消耗CPU資源呢?

並不是,這裏就涉及到Linux pipe/epoll機制,簡單說就是在主線程的MessageQueue沒有消息時,便阻塞在loop的queue.next()中的nativePollOnce()方法裏,此時主線程會釋放CPU資源進入休眠狀態,直到下個消息到達或者有事務發生,通過往pipe管道寫端寫入數據來喚醒主線程工作。這裏採用的epoll機制,是一種IO多路複用機制,可以同時監控多個描述符,當某個描述符就緒(讀或寫就緒),則立刻通知相應程序進行讀或寫操作,本質是同步I/O,即讀寫是阻塞的。所以說,主線程大多數時候都是處於休眠狀態,並不會消耗大量CPU資源。

handler postDelay這個延遲是怎麼實現的?

handler.postDelay並不是先等待一定的時間再放入到MessageQueue中,而是直接進入MessageQueue,以MessageQueue的時間順序排列和喚醒的方式結合實現的。

如何保證在msg.postDelay情況下保證消息次序?

Handler 都沒搞懂,拿什麼去跳槽啊?

24、程序A能否接收到程序B的廣播?

能,使用全局的BroadCastRecevier能進行跨進程通信,但是注意它只能被動接收廣播。此外,LocalBroadCastRecevier只限於本進程的廣播間通信。

25、數據加載更多涉及到分頁,你是怎麼實現的?

分頁加載就是一頁一頁加載數據,當滑動到底部、沒有更多數據加載的時候,我們可以手動調用接口,重新刷新RecyclerView。

26、通過google提供的Gson解析json時,定義JavaBean的規則是什麼?

1). 實現序列化 Serializable

2). 屬性私有化,並提供get,set方法

3). 提供無參構造

4). 屬性名必須與json串中屬性名保持一致 (因爲Gson解析json串底層用到了Java的反射原理)

27、json解析方式的兩種區別?

1,SDK提供JSONArray,JSONObject

2,google提供的 Gson 通過fromJson()實現對象的反序列化(即將json串轉換爲對象類型) 通過toJson()實現對象的序列化 (即將對象類型轉換爲json串)

28、線程池的相關知識。

Android中的線程池都是直接或間接通過配置ThreadPoolExecutor來實現不同特性的線程池.Android中最常見的類具有不同特性的線程池分別爲FixThreadPool、CachedhreadPool、SingleThreadPool、ScheduleThreadExecutr.

1).FixThreadPool

只有核心線程,並且數量固定的,也不會被回收,所有線程都活動時,因爲隊列沒有限制大小,新任務會等待執行.

優點:更快的響應外界請求.

2).SingleThreadPool

只有一個核心線程,確保所有的任務都在同一線程中按序完成.因此不需要處理線程同步的問題.

3).CachedThreadPool

只有非核心線程,最大線程數非常大,所有線程都活動時會爲新任務創建新線程,否則會利用空閒線程(60s空閒時間,過了就會被回收,所以線程池中有0個線程的可能)處理任務.

優點:任何任務都會被立即執行(任務隊列SynchronousQuue相當於一個空集合);比較適合執行大量的耗時較少的任務.

4).ScheduledThreadPool

核心線程數固定,非核心線程(閒着沒活幹會被立即回收數)沒有限制.

優點:執行定時任務以及有固定週期的重複任務

29、內存泄露,怎樣查找,怎麼產生的內存泄露?

1.資源對象沒關閉造成的內存泄漏

描述: 資源性對象比如(Cursor,File文件等)往往都用了一些緩衝,我們在不使用的時候,應該及時關閉它們,以便它們的緩衝及時回收內存。它們的緩衝不僅存在於 java虛擬機內,還存在於java虛擬機外。如果我們僅僅是把它的引用設置爲null,而不關閉它們,往往會造成內存泄漏。因爲有些資源性對象,比如SQLiteCursor(在析構函數finalize(),如果我們沒有關閉它,它自己會調close()關閉),如果我們沒有關閉它,系統在回收它時也會關閉它,但是這樣的效率太低了。因此對於資源性對象在不使用的時候,應該調用它的close()函數,將其關閉掉,然後才置爲null.在我們的程序退出時一定要確保我們的資源性對象已經關閉。

程序中經常會進行查詢數據庫的操作,但是經常會有使用完畢Cursor後沒有關閉的情況。如果我們的查詢結果集比較小,對內存的消耗不容易被發現,只有在常時間大量操作的情況下才會復現內存問題,這樣就會給以後的測試和問題排查帶來困難和風險。

2.構造Adapter時,沒有使用緩存的convertView

描述: 以構造ListView的BaseAdapter爲例,在BaseAdapter中提供了方法: public View getView(int position, ViewconvertView, ViewGroup parent) 來向ListView提供每一個item所需要的view對象。初始時ListView會從BaseAdapter中根據當前的屏幕布局實例化一定數量的 view對象,同時ListView會將這些view對象緩存起來。當向上滾動ListView時,原先位於最上面的list item的view對象會被回收,然後被用來構造新出現的最下面的list item。這個構造過程就是由getView()方法完成的,getView()的第二個形參View convertView就是被緩存起來的list item的view對象(初始化時緩存中沒有view對象則convertView是null)。由此可以看出,如果我們不去使用 convertView,而是每次都在getView()中重新實例化一個View對象的話,即浪費資源也浪費時間,也會使得內存佔用越來越大。 ListView回收list item的view對象的過程可以查看: android.widget.AbsListView.java --> voidaddScrapView(View scrap) 方法。 示例代碼:

public View getView(int position, ViewconvertView, ViewGroup parent) {
View view = new Xxx(...); 
... ... 
return view; 
}
複製代碼

修正示例代碼:

public View getView(int position, ViewconvertView, ViewGroup parent) {
View view = null; 
if (convertView != null) { 
view = convertView; 
populate(view, getItem(position)); 
... 
} else { 
view = new Xxx(...); 
... 
} 
return view; 
}
複製代碼

3.Bitmap對象不在使用時調用recycle()釋放內存

描述: 有時我們會手工的操作Bitmap對象,如果一個Bitmap對象比較佔內存,當它不在被使用的時候,可以調用Bitmap.recycle()方法回收此對象的像素所佔用的內存,但這不是必須的,視情況而定。可以看一下代碼中的註釋:

/* •Free up the memory associated with thisbitmap's pixels, and mark the •bitmap as "dead", meaning itwill throw an exception if getPixels() or •setPixels() is called, and will drawnothing. This operation cannot be •reversed, so it should only be called ifyou are sure there are no •further uses for the bitmap. This is anadvanced call, and normally need •not be called, since the normal GCprocess will free up this memory when •there are no more references to thisbitmap. /

4.試着使用關於application的context來替代和activity相關的context

這是一個很隱晦的內存泄漏的情況。有一種簡單的方法來避免context相關的內存泄漏。最顯著地一個是避免context逃出他自己的範圍之外。使用Application context。這個context的生存週期和你的應用的生存週期一樣長,而不是取決於activity的生存週期。如果你想保持一個長期生存的對象,並且這個對象需要一個context,記得使用application對象。你可以通過調用 Context.getApplicationContext() or Activity.getApplication()來獲得。更多的請看這篇文章如何避免 Android內存泄漏。

5.註冊沒取消造成的內存泄漏

一些Android程序可能引用我們的Anroid程序的對象(比如註冊機制)。即使我們的Android程序已經結束了,但是別的引用程序仍然還有對我們的Android程序的某個對象的引用,泄漏的內存依然不能被垃圾回收。調用registerReceiver後未調用unregisterReceiver。 比如:假設我們希望在鎖屏界面(LockScreen)中,監聽系統中的電話服務以獲取一些信息(如信號強度等),則可以在LockScreen中定義一個 PhoneStateListener的對象,同時將它註冊到TelephonyManager服務中。對於LockScreen對象,當需要顯示鎖屏界面的時候就會創建一個LockScreen對象,而當鎖屏界面消失的時候LockScreen對象就會被釋放掉。 但是如果在釋放 LockScreen對象的時候忘記取消我們之前註冊的PhoneStateListener對象,則會導致LockScreen無法被垃圾回收。如果不斷的使鎖屏界面顯示和消失,則最終會由於大量的LockScreen對象沒有辦法被回收而引起OutOfMemory,使得system_process 進程掛掉。 雖然有些系統程序,它本身好像是可以自動取消註冊的(當然不及時),但是我們還是應該在我們的程序中明確的取消註冊,程序結束時應該把所有的註冊都取消掉。

6.集合中對象沒清理造成的內存泄漏

我們通常把一些對象的引用加入到了集合中,當我們不需要該對象時,並沒有把它的引用從集合中清理掉,這樣這個集合就會越來越大。如果這個集合是static的話,那情況就更嚴重了。

查找內存泄漏可以使用Android Studio 自帶的AndroidProfiler工具或MAT,也可以使用Square產品的LeakCanary.

1、使用AndroidProfiler的MEMORY工具:

運行程序,對每一個頁面進行內存分析檢查。首先,反覆打開關閉頁面5次,然後收到GC(點擊Profile MEMORY左上角的垃圾桶圖標),如果此時total內存還沒有恢復到之前的數值,則可能發生了內存泄露。此時,再點擊Profile MEMORY左上角的垃圾桶圖標旁的heap dump按鈕查看當前的內存堆棧情況,選擇按包名查找,找到當前測試的Activity,如果引用了多個實例,則表明發生了內存泄露。

2、使用MAT:

1、運行程序,所有功能跑一遍,確保沒有改出問題,完全退出程序,手動觸發GC,然後使用adb shell dumpsys meminfo packagename -d命令查看退出界面後Objects下的Views和Activities數目是否爲0,如果不是則通過Leakcanary檢查可能存在內存泄露的地方,最後通過MAT分析,如此反覆,改善滿意爲止。

1、在使用MAT之前,先使用as的Profile中的Memory去獲取要分析的堆內存快照文件.hprof,如果要測試某個頁面是否產生內存泄漏,可以先dump出沒進入該頁面的內存快照文件.hprof,然後,通常執行5次進入/退出該頁面,然後再dump出此刻的內存快照文件.hprof,最後,將兩者比較,如果內存相除明顯,則可能發生內存泄露。(注意:MAT需要標準的.hprof文件,因此在as的Profiler中GC後dump出的內存快照文件.hprof必須手動使用android sdk platform-tools下的hprof-conv程序進行轉換才能被MAT打開)

2、然後,使用MAT打開前面保存的2份.hprof文件,打開Overview界面,在Overview界面下面有4中action,其中最常用的就是Histogram和Dominator Tree。

Dominator Tree:支配樹,按對象大小降序列出對象和其所引用的對象,注重引用關係分析。選擇Group by package,找到當前要檢測的類(或者使用頂部的Regex直接搜索),查看它的Object數目是否正確,如果多了,則判斷髮生了內存泄露。然後,右擊該類,選擇Merge Shortest Paths to GC Root中的exclude all phantom/weak/soft etc.references選項來查看該類的GC強引用鏈。最後,通過引用鏈即可看到最終強引用該類的對象。

Histogram:直方圖注重量的分析。使用方式與Dominator Tree類似。

3、對比hprof文件,檢測出複雜情況下的內存泄露:

通用對比方式:在Navigation History下面選擇想要對比的dominator_tree/histogram,右擊選擇Add to Compare Basket,然後在Compare Basket一欄中點擊紅色感嘆號(Compare the results)生成對比表格(Compared Tables),在頂部Regex輸入要檢測的類,查看引用關係或對象數量去進行分析即可。

針對於Historam的快速對比方式:直接選擇Histogram上方的Compare to another Heap Dump選擇要比較的hprof文件的Historam即可。

30、類的初始化順序依次是?

(靜態變量、靜態代碼塊)>(變量、代碼塊)>構造方法

31、JSON的結構?

json是一種輕量級的數據交換格式, json簡單說就是對象和數組,所以這兩種結構就是對象和數組兩種結構,通過這兩種結構可以表示各種複雜的結構

1、對象:對象表示爲“{}”擴起來的內容,數據結構爲 {key:value,key:value,...}的鍵值對的結構,在面向對象的語言中,key爲對象的屬性,value爲對應的屬性值,所以很容易理解,取值方法爲 對象.key 獲取屬性值,這個屬性值的類型可以是 數字、字符串、數組、對象幾種。

2、數組:數組在json中是中括號“[]”擴起來的內容,數據結構爲 ["java","javascript","vb",...],取值方式和所有語言中一樣,使用索引獲取,字段值的類型可以是 數字、字符串、數組、對象幾種。 經過對象、數組2種結構就可以組合成複雜的數據結構了。

32、ViewPager使用細節,如何設置成每次只初始化當前的Fragment,其他的不初始化(提示:Fragment懶加載)?

自定義一個 LazyLoadFragment 基類,利用 setUserVisibleHint 和 生命週期方法,通過對 Fragment 狀態判斷,進行數據加載,並將數據加載的接口提供開放出去,供子類使用。然後在子類 Fragment 中實現 requestData 方法即可。這裏添加了一個 isDataLoaded 變量,目的是避免重複加載數據。考慮到有時候需要刷新數據的問題,便提供了一個用於強制刷新的參數判斷。

public abstract class LazyLoadFragment extends BaseFragment {
    protected boolean isViewInitiated;
    protected boolean isDataLoaded;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        isViewInitiated = true;
        prepareRequestData();
    }
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        prepareRequestData();
    }
    public abstract void requestData();
    public boolean prepareRequestData() {
        return prepareRequestData(false);
    }
    public boolean prepareRequestData(boolean forceUpdate) {
        if (getUserVisibleHint() && isViewInitiated && (!isDataLoaded || forceUpdate)) {
            requestData();
            isDataLoaded = true;
            return true;
        }
        return false;
    }
}
複製代碼

35、Android爲什麼引入Parcelable?

可以肯定的是,兩者都是支持序列化和反序列化的操作。

兩者最大的區別在於 存儲媒介的不同,Serializable 使用 I/O 讀寫存儲在硬盤上,而 Parcelable 是直接 在內存中讀寫。很明顯,內存的讀寫速度通常大於 IO 讀寫,所以在 Android 中傳遞數據優先選擇 Parcelable。

Serializable 會使用反射,序列化和反序列化過程需要大量 I/O 操作, Parcelable 自已實現封送和解封(marshalled &unmarshalled)操作不需要用反射,數據也存放在 Native 內存中,效率要快很多。

36、有沒有嘗試簡化Parcelable的使用?

使用Parcelable插件(Android Parcelable code generator)進行實體類的序列化的實現。

37、Bitmap 使用時候注意什麼?

1、要選擇合適的圖片規格(bitmap類型):

ALPHA_8   每個像素佔用1byte內存        
ARGB_4444 每個像素佔用2byte內存       
ARGB_8888 每個像素佔用4byte內存(默認)      
RGB_565 每個像素佔用2byte內存
複製代碼

2、降低採樣率。BitmapFactory.Options 參數inSampleSize的使用,先把options.inJustDecodeBounds設爲true,只是去讀取圖片的大小,在拿到圖片的大小之後和要顯示的大小做比較通過calculateInSampleSize()函數計算inSampleSize的具體值,得到值之後。options.inJustDecodeBounds設爲false讀圖片資源。

3、複用內存。即,通過軟引用(內存不夠的時候纔會回收掉),複用內存塊,不需要再重新給這個bitmap申請一塊新的內存,避免了一次內存的分配和回收,從而改善了運行效率。

4、使用recycle()方法及時回收內存。

5、壓縮圖片。

38、Oom 是否可以try catch ?

只有在一種情況下,這樣做是可行的:

在try語句中聲明瞭很大的對象,導致OOM,並且可以確認OOM是由try語句中的對象聲明導致的,那麼在catch語句中,可以釋放掉這些對象,解決OOM的問題,繼續執行剩餘語句。

但是這通常不是合適的做法。

Java中管理內存除了顯式地catch OOM之外還有更多有效的方法:比如SoftReference, WeakReference, 硬盤緩存等。 在JVM用光內存之前,會多次觸發GC,這些GC會降低程序運行的效率。 如果OOM的原因不是try語句中的對象(比如內存泄漏),那麼在catch語句中會繼續拋出OOM。

39、多進程場景遇見過麼?

1、在新的進程中,啓動前臺Service,播放音樂。 2、一個成熟的應用一定是多模塊化的。首先多進程開發能爲應用解決了OOM問題,因爲Android對內存的限制是針對於進程的,所以,當我們需要加載大圖之類的操作,可以在新的進程中去執行,避免主進程OOM。而且假如圖片瀏覽進程打開了一個過大的圖片,java heap 申請內存失敗,該進程崩潰並不影響我主進程的使用。

40、Canvas.save()跟Canvas.restore()的調用時機

save:用來保存Canvas的狀態。save之後,可以調用Canvas的平移、放縮、旋轉、錯切、裁剪等操作。

restore:用來恢復Canvas之前保存的狀態。防止save後對Canvas執行的操作對後續的繪製有影響。

save和restore要配對使用(restore可以比save少,但不能多),如果restore調用次數比save多,會引發Error。save和restore操作執行的時機不同,就能造成繪製的圖形不同。

41、數據庫升級增加表和刪除表都不涉及數據遷移,但是修改表涉及到對原有數據進行遷移。升級的方法如下所示:

將現有表命名爲臨時表。 創建新表。 將臨時表的數據導入新表。 刪除臨時表。

如果是跨版本數據庫升級,可以有兩種方式,如下所示:

逐級升級,確定相鄰版本與現在版本的差別,V1升級到V2,V2升級到V3,依次類推。 跨級升級,確定每個版本與現在數據庫的差別,爲每個case編寫專門升級大代碼。

public class DBservice extends SQLiteOpenHelper{
    private String CREATE_BOOK = "create table book(bookId integer primarykey,bookName text);";
    private String CREATE_TEMP_BOOK = "alter table book rename to _temp_book";
    private String INSERT_DATA = "insert into book select *,'' from _temp_book";
    private String DROP_BOOK = "drop table _temp_book";
    public DBservice(Context context, String name, CursorFactory factory,int version) {
    super(context, name, factory, version);
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (newVersion) {
        case 2:
            db.beginTransaction();

            db.execSQL(CREATE_TEMP_BOOK);
            db.execSQL(CREATE_BOOK);
            db.execSQL(INSERT_DATA);
            db.execSQL(DROP_BOOK);

            db.setTransactionSuccessful();
            db.endTransaction();

            break;
    }
}
複製代碼

42、編譯期註解跟運行時註解

運行期註解(RunTime)利用反射去獲取信息還是比較損耗性能的,對應@Retention(RetentionPolicy.RUNTIME)。

編譯期(Compile time)註解,以及處理編譯期註解的手段APT和Javapoet,對應@Retention(RetentionPolicy.CLASS)。 其中apt+javaPoet目前也是應用比較廣泛,在一些大的開源庫,如EventBus3.0+,頁面路由 ARout、Dagger、Retrofit等均有使用的身影,註解不僅僅是通過反射一種方式來使用,也可以使用APT在編譯期處理

43、bitmap recycler 相關

在Android中,Bitmap的存儲分爲兩部分,一部分是Bitmap的數據,一部分是Bitmap的引用。 在Android2.3時代,Bitmap的引用是放在堆中的,而Bitmap的數據部分是放在棧中的,需要用戶調用recycle方法手動進行內存回收,而在Android2.3之後,整個Bitmap,包括數據和引用,都放在了堆中,這樣,整個Bitmap的回收就全部交給GC了,這個recycle方法就再也不需要使用了。

bitmap recycler引發的問題:當圖像的旋轉角度小余兩個像素點之間的夾角時,圖像即使旋轉也無法顯示,因此,系統完全可以認爲圖像沒有發生變化。這時系統就直接引用同一個對象來進行操作,避免內存浪費。

44、強引用置爲null,會不會被回收?

不會立即釋放對象佔用的內存。 如果對象的引用被置爲null,只是斷開了當前線程棧幀中對該對象的引用關係,而 垃圾收集器是運行在後臺的線程,只有當用戶線程運行到安全點(safe point)或者安全區域纔會掃描對象引用關係,掃描到對象沒有被引用則會標記對象,這時候仍然不會立即釋放該對象內存,因爲有些對象是可恢復的(在 finalize方法中恢復引用 )。只有確定了對象無法恢復引用的時候纔會清除對象內存。

45、Bundle傳遞數據爲什麼需要序列化?

序列化,表示將一個對象轉換成可存儲或可傳輸的狀態。序列化的原因基本三種情況:

1.永久性保存對象,保存對象的字節序列到本地文件中;

2.對象在網絡中傳遞;

3.對象在IPC間傳遞。

46、廣播傳輸的數據是否有限制,是多少,爲什麼要限制?

Intent在傳遞數據時是有大小限制的,大約限制在1MB之內,你用Intent傳遞數據,實際上走的是跨進程通信(IPC),跨進程通信需要把數據從內核copy到進程中,每一個進程有一個接收內核數據的緩衝區,默認是1M;如果一次傳遞的數據超過限制,就會出現異常。

不同廠商表現不一樣有可能是廠商修改了此限制的大小,也可能同樣的對象在不同的機器上大小不一樣。

傳遞大數據,不應該用Intent;考慮使用ContentProvider或者直接匿名共享內存。簡單情況下可以考慮分段傳輸。

47、是否瞭解硬件加速?

硬件加速就是運用GPU優秀的運算能力來加快渲染的速度,而通常的基於軟件的繪製渲染模式是完全利用CPU來完成渲染。

1.硬件加速是從API 11引入,API 14之後才默認開啓。對於標準的繪製操作和控件都是支持的,但是對於自定義View的時候或者一些特殊的繪製函數就需要考慮是否需要關閉硬件加速。

2.我們面對不支持硬件加速的情況,就需要限制硬件加速,這個兼容性的問題是因爲硬件加速是把View的繪製函數轉化爲使用OpenGL的函數來進完成實際的繪製的,那麼必然會存在OpenGL中不支持原始回執函數的情況,對於這些繪製函數,就會失效。

3.硬件加速的消耗問題,因爲是使用OpenGL,需要把系統中OpenGL加載到內存中,OpenGL API調用就會佔用8MB,而實際上會佔用更多內存,並且使用了硬件必然增加耗電量了。

4.硬件加速的優勢還有display list的設計,使用這個我們不需要每次重繪都執行大量的代碼,基於軟件的繪製模式會重繪髒區域內的所有控件,而display只會更新列表,然後繪製列表內的控件。

  1. CPU更擅長複雜邏輯控制,而GPU得益於大量ALU和並行結構設計,更擅長數學運算。

48、ContentProvider的權限管理(讀寫分離,權限控制-精確到表級,URL控制)。

 對於ContentProvider暴露出來的數據,應該是存儲在自己應用內存中的數據,對於一些存儲在外部存儲器上的數據,並不能限制訪問權限,使用ContentProvider就沒有意義了。對於ContentProvider而言,有很多權限控制,可以在AndroidManifest.xml文件中對節點的屬性進行配置,一般使用如下一些屬性設置:

  • android:grantUriPermssions:臨時許可標誌。
  • android:permission:Provider讀寫權限。
  • android:readPermission:Provider的讀權限。
  • android:writePermission:Provider的寫權限。
  • android:enabled:標記允許系統啓動Provider。
  • android:exported:標記允許其他應用程序使用這個Provider。
  • android:multiProcess:標記允許系統啓動Provider相同的進程中調用客戶端。

49、Fragment狀態保存

Fragment狀態保存入口:

1、Activity的狀態保存, 在Activity的onSaveInstanceState()裏, 調用了FragmentManger的saveAllState()方法, 其中會對mActive中各個Fragment的實例狀態和View狀態分別進行保存.

2、FragmentManager還提供了public方法: saveFragmentInstanceState(), 可以對單個Fragment進行狀態保存, 這是提供給我們用的。

3、FragmentManager的moveToState()方法中, 當狀態回退到ACTIVITY_CREATED, 會調用saveFragmentViewState()方法, 保存View的狀態.

50、直接在Activity中創建一個thread跟在service中創建一個thread之間的區別?

在Activity中被創建:該Thread的就是爲這個Activity服務的,完成這個特定的Activity交代的任務,主動通知該Activity一些消息和事件,Activity銷燬後,該Thread也沒有存活的意義了。

在Service中被創建:這是保證最長生命週期的Thread的唯一方式,只要整個Service不退出,Thread就可以一直在後臺執行,一般在Service的onCreate()中創建,在onDestroy()中銷燬。所以,在Service中創建的Thread,適合長期執行一些獨立於APP的後臺任務,比較常見的就是:在Service中保持與服務器端的長連接。

51、如何計算一個Bitmap佔用內存的大小,怎麼保證加載Bitmap不產生內存溢出?

Bitamp 佔用內存大小 = 寬度像素 x (inTargetDensity / inDensity) x 高度像素 x (inTargetDensity / inDensity)x 一個像素所佔的內存
複製代碼

注:這裏inDensity表示目標圖片的dpi(放在哪個資源文件夾下),inTargetDensity表示目標屏幕的dpi,所以你可以發現inDensity和inTargetDensity會對Bitmap的寬高進行拉伸,進而改變Bitmap佔用內存的大小。

在Bitmap裏有兩個獲取內存佔用大小的方法。

getByteCount():API12 加入,代表存儲 Bitmap 的像素需要的最少內存。 getAllocationByteCount():API19 加入,代表在內存中爲 Bitmap 分配的內存大小,代替了 getByteCount() 方法。 在不復用 Bitmap 時,getByteCount() 和 getAllocationByteCount 返回的結果是一樣的。在通過複用 Bitmap 來解碼圖片時,那麼 getByteCount() 表示新解碼圖片佔用內存的大 小,getAllocationByteCount() 表示被複用 Bitmap 真實佔用的內存大小(即 mBuffer 的長度)。

爲了保證在加載Bitmap的時候不產生內存溢出,可以使用BitmapFactory進行圖片壓縮,主要有以下幾個參數:

BitmapFactory.Options.inPreferredConfig:將ARGB_8888改爲RGB_565,改變編碼方式,節約內存。 BitmapFactory.Options.inSampleSize:縮放比例,可以參考Luban那個庫,根據圖片寬高計算出合適的縮放比例。 BitmapFactory.Options.inPurgeable:讓系統可以內存不足時回收內存。

52、對於應用更新這塊是如何做的?(灰度,強制更新,分區域更新)

1、通過接口獲取線上版本號,versionCode 2、比較線上的versionCode 和本地的versionCode,彈出更新窗口 3、下載APK文件(文件下載) 4、安裝APK

灰度: (1)找單一渠道投放特別版本。 (2)做升級平臺的改造,允許針對部分用戶推送升級通知甚至版本強制升級。 (3)開放單獨的下載入口。 (4)是兩個版本的代碼都打到app包裏,然後在app端植入測試框架,用來控制顯示哪個版本。測試框架負責與服務器端api通信,由服務器端控制app上A/B版本的分佈,可以實現指定的一組用戶看到A版本,其它用戶看到B版本。服務端會有相應的報表來顯示A/B版本的數量和效果對比。最後可以由服務端的後臺來控制,全部用戶在線切換到A或者B版本~

無論哪種方法都需要做好版本管理工作,分配特別的版本號以示區別。 當然,既然是做灰度,數據監控(常規數據、新特性數據、主要業務數據)還是要做到位,該打的數據樁要打。 還有,灰度版最好有收回的能力,一般就是強制升級下一個正式版。

強制更新:一般的處理就是進入應用就彈窗通知用戶有版本更新,彈窗可以沒有取消按鈕並不能取消。這樣用戶就只能選擇更新或者關閉應用了,當然也可以添加取消按鈕,但是如果用戶選擇取消則直接退出應用。

增量更新:bsdiff:二進制差分工具bsdiff是相應的補丁合成工具,根據兩個不同版本的二進制文件,生成補丁文件.patch文件。通過bspatch使舊的apk文件與不定文件合成新的apk。 注意通過apk文件的md5值進行區分版本。

53、請解釋安卓爲啥要加簽名機制。

1、發送者的身份認證 由於開發商可能通過使用相同的 Package Name 來混淆替換已經安裝的程序,以此保證簽名不同的包不被替換。

2、保證信息傳輸的完整性 簽名對於包中的每個文件進行處理,以此確保包中內容不被替換。

3、防止交易中的抵賴發生, Market 對軟件的要求。

54、爲什麼bindService可以跟Activity生命週期聯動?

1、bindService 方法執行時,LoadedApk 會記錄 ServiceConnection 信息。

2、Activity 執行 finish 方法時,會通過 LoadedApk 檢查 Activity 是否存在未註銷/解綁的 BroadcastReceiver 和 ServiceConnection,如果有,那麼會通知 AMS 註銷/解綁對應的 BroadcastReceiver 和 Service,並打印異常信息,告訴用戶應該主動執行註銷/解綁的操作。

55、如何通過Gradle配置多渠道包?

用於生成不同渠道的包

android {  
    productFlavors {
        xiaomi {}
        baidu {}
        wandoujia {}
        _360 {}        // 或“"360"{}”,數字需下劃線開頭或加上雙引號
    }
}
複製代碼

執行./gradlew assembleRelease ,將會打出所有渠道的release包;

執行./gradlew assembleWandoujia,將會打出豌豆莢渠道的release和debug版的包;

執行./gradlew assembleWandoujiaRelease將生成豌豆莢的release包。

因此,可以結合buildType和productFlavor生成不同的Build Variants,即類型與渠道不同的組合。

56、activty和Fragmengt之間怎麼通信,Fragmengt和Fragmengt怎麼通信?

(一)Handler

(二)廣播

(三)事件總線:EventBus、RxBus、Otto

(四)接口回調

(五)Bundle和setArguments(bundle)

57、自定義view效率高於xml定義嗎?說明理由。

自定義view效率高於xml定義:

1、少了解析xml。

2.、自定義View 減少了ViewGroup與View之間的測量,包括父量子,子量自身,子在父中位置擺放,當子view變化時,父的某些屬性都會跟着變化。

58、廣播註冊一般有幾種,各有什麼優缺點?

第一種是常駐型(靜態註冊):當應用程序關閉後如果有信息廣播來,程序也會被系統調用,自己運行。

第二種不常駐(動態註冊):廣播會跟隨程序的生命週期。

動態註冊

優點: 在android的廣播機制中,動態註冊優先級高於靜態註冊優先級,因此在必要情況下,是需要動態註冊廣播接收者的。

缺點: 當用來註冊的 Activity 關掉後,廣播也就失效了。

靜態註冊

優點: 無需擔憂廣播接收器是否被關閉,只要設備是開啓狀態,廣播接收器就是打開着的。

59、服務啓動一般有幾種,服務和activty之間怎麼通信,服務和服務之間怎麼通信

方式:

1、startService:

onCreate()--->onStartCommand() ---> onDestory()

如果服務已經開啓,不會重複的執行onCreate(), 而是會調用onStartCommand()。一旦服務開啓跟調用者(開啓者)就沒有任何關係了。 開啓者退出了,開啓者掛了,服務還在後臺長期的運行。 開啓者不能調用服務裏面的方法。

2、bindService:

onCreate() --->onBind()--->onunbind()--->onDestory()

bind的方式開啓服務,綁定服務,調用者掛了,服務也會跟着掛掉。 綁定者可以調用服務裏面的方法。

通信:

1、通過Binder對象。

2、通過broadcast(廣播)。

60、ddms 和 traceView 的區別?

ddms 原意是:davik debug monitor service。簡單的說 ddms 是一個程序執行查看器,在裏面可以看見線程和堆棧等信息,traceView 是程序性能分析器。traceview 是 ddms 中的一部分內容。

Traceview 是 Android 平臺特有的數據採集和分析工具,它主要用於分析 Android 中應用程序的 hotspot(瓶頸)。Traceview 本身只是一個數據分析工具,而數據的採集則需要使用 Android SDK 中的 Debug 類或者利用DDMS 工具。二者的用法如下:開發者在一些關鍵代碼段開始前調用 Android SDK 中 Debug 類的 startMethodTracing 函數,並在關鍵代碼段結束前調用 stopMethodTracing 函數。這兩個函數運行過程中將採集運行時間內該應用所有線程(注意,只能是 Java線程) 的函數執行情況, 並將採集數據保存到/mnt/sdcard/下的一個文件中。 開發者然後需要利用 SDK 中的 Traceview工具來分析這些數據。

61、ListView卡頓原因

Adapter的getView方法裏面convertView沒有使用setTag和getTag方式;

在getView方法裏面ViewHolder初始化後的賦值或者是多個控件的顯示狀態和背景的顯示沒有優化好,抑或是裏面含有複雜的計算和耗時操作;

在getView方法裏面 inflate的row 嵌套太深(佈局過於複雜)或者是佈局裏面有大圖片或者背景所致;

Adapter多餘或者不合理的notifySetDataChanged;

listview 被多層嵌套,多次的onMessure導致卡頓,如果多層嵌套無法避免,建議把listview的高和寬設置爲match_parent. 如果是代碼繼承的listview,那麼也請你別忘記爲你的繼承類添加上LayoutPrams,注意高和寬都mactch_parent的;

62、AndroidManifest的作用與理解

AndroidManifest.xml文件,也叫清單文件,來獲知應用中是否包含該組件,如果有會直接啓動該組件。可以理解是一個應用的配置文件。

作用:

  • 爲應用的 Java 軟件包命名。軟件包名稱充當應用的唯一標識符。
  • 描述應用的各個組件,包括構成應用的 Activity、服務、廣播接收器和內容提供程序。它還爲實現每個組件的類命名併發布其功能,例如它們可以處理的 Intent - 消息。這些聲明向 Android 系統告知有關組件以及可以啓動這些組件的條件的信息。
  • 確定託管應用組件的進程。
  • 聲明應用必須具備哪些權限才能訪問 API 中受保護的部分並與其他應用交互。還聲明其他應用與該應用組件交互所需具備的權限
  • 列出 Instrumentation類,這些類可在應用運行時提供分析和其他信息。這些聲明只會在應用處於開發階段時出現在清單中,在應用發佈之前將移除。
  • 聲明應用所需的最低 Android API 級別
  • 列出應用必須鏈接到的庫

63、LaunchMode應用場景

standard,創建一個新的Activity。

singleTop,棧頂不是該類型的Activity,創建一個新的Activity。否則,onNewIntent。

singleTask,回退棧中沒有該類型的Activity,創建Activity,否則,onNewIntent+ClearTop。

注意:

設置了"singleTask"啓動模式的Activity,它在啓動的時候,會先在系統中查找屬性值affinity等於它的屬性值taskAffinity的Task存在;如果存在這樣的Task,它就會在這個Task中啓動,否則就會在新的任務棧中啓動。因此, 如果我們想要設置了"singleTask"啓動模式的Activity在新的任務中啓動,就要爲它設置一個獨立的taskAffinity屬性值。

如果設置了"singleTask"啓動模式的Activity不是在新的任務中啓動時,它會在已有的任務中查看是否已經存在相應的Activity實例, 如果存在,就會把位於這個Activity實例上面的Activity全部結束掉,即最終這個Activity 實例會位於任務的Stack頂端中。

在一個任務棧中只有一個”singleTask”啓動模式的Activity存在。他的上面可以有其他的Activity。這點與singleInstance是有區別的。

singleInstance,回退棧中,只有這一個Activity,沒有其他Activity。

singleTop適合接收通知啓動的內容顯示頁面。

例如,某個新聞客戶端的新聞內容頁面,如果收到10個新聞推送,每次都打開一個新聞內容頁面是很煩人的。

singleTask適合作爲程序入口點。

例如瀏覽器的主界面。不管從多少個應用啓動瀏覽器,只會啓動主界面一次,其餘情況都會走onNewIntent,並且會清空主界面上面的其他頁面。

singleInstance應用場景:

鬧鈴的響鈴界面。 你以前設置了一個鬧鈴:上午6點。在上午5點58分,你啓動了鬧鈴設置界面,並按 Home 鍵回桌面;在上午5點59分時,你在微信和朋友聊天;在6點時,鬧鈴響了,並且彈出了一個對話框形式的 Activity(名爲 AlarmAlertActivity) 提示你到6點了(這個 Activity 就是以 SingleInstance 加載模式打開的),你按返回鍵,回到的是微信的聊天界面,這是因爲 AlarmAlertActivity 所在的 Task 的棧只有他一個元素, 因此退出之後這個 Task 的棧空了。如果是以 SingleTask 打開 AlarmAlertActivity,那麼當鬧鈴響了的時候,按返回鍵應該進入鬧鈴設置界面。

64、說說Activity、Intent、Service 是什麼關係

他們都是 Android 開發中使用頻率最高的類。其中 Activity 和 Service 都是 Android 四大組件之一。他倆都是 Context 類的子類 ContextWrapper 的子類,因此他倆可以算是兄弟關係吧。不過兄弟倆各有各自的本領,Activity 負責用戶界面的顯示和交互,Service 負責後臺任務的處理。Activity 和 Service 之間可以通過 Intent 傳遞數據,因此 可以把 Intent 看作是通信使者。

65、ApplicationContext和ActivityContext的區別

這是兩種不同的context,也是最常見的兩種.第一種中context的生命週期與Application的生命週期相關的,context隨着Application的銷燬而銷燬,伴隨application的一生,與activity的生命週期無關.第二種中的context跟Activity的生命週期是相關的,但是對一個Application來說,Activity可以銷燬幾次,那麼屬於Activity的context就會銷燬多次.至於用哪種context,得看應用場景。還有就是,在使用context的時候,小心內存泄露,防止內存泄露,注意一下幾個方面:

  • 不要讓生命週期長的對象引用activity context,即保證引用activity的對象要與activity本身生命週期是一樣的。
  • 對於生命週期長的對象,可以使用application context。
  • 避免非靜態的內部類,儘量使用靜態類,避免生命週期問題,注意內部類對外部對象引用導致的生命週期變化。

66、Handler、Thread和HandlerThread的差別

1、Handler:在android中負責發送和處理消息,通過它可以實現其他支線線程與主線程之間的消息通訊。

2、Thread:Java進程中執行運算的最小單位,亦即執行處理機調度的基本單位。某一進程中一路單獨運行的程序。

3、HandlerThread:一個繼承自Thread的類HandlerThread,Android中沒有對Java中的Thread進行任何封裝,而是提供了一個繼承自Thread的類HandlerThread類,這個類對Java的Thread做了很多便利的封裝。HandlerThread繼承於Thread,所以它本質就是個Thread。與普通Thread的差別就在於,它在內部直接實現了Looper的實現,這是Handler消息機制必不可少的。有了自己的looper,可以讓我們在自己的線程中分發和處理消息。如果不用HandlerThread的話,需要手動去調用Looper.prepare()和Looper.loop()這些方法。

67、ThreadLocal的原理

ThreadLocal是一個關於創建線程局部變量的類。使用場景如下所示:

  • 實現單個線程單例以及單個線程上下文信息存儲,比如交易id等。

  • 實現線程安全,非線程安全的對象使用ThreadLocal之後就會變得線程安全,因爲每個線程都會有一個對應的實例。 承載一些線程相關的數據,避免在方法中來回傳遞參數。

當需要使用多線程時,有個變量恰巧不需要共享,此時就不必使用synchronized這麼麻煩的關鍵字來鎖住,每個線程都相當於在堆內存中開闢一個空間,線程中帶有對共享變量的緩衝區,通過緩衝區將堆內存中的共享變量進行讀取和操作,ThreadLocal相當於線程內的內存,一個局部變量。每次可以對線程自身的數據讀取和操作,並不需要通過緩衝區與 主內存中的變量進行交互。並不會像synchronized那樣修改主內存的數據,再將主內存的數據複製到線程內的工作內存。ThreadLocal可以讓線程獨佔資源,存儲於線程內部,避免線程堵塞造成CPU吞吐下降。

在每個Thread中包含一個ThreadLocalMap,ThreadLocalMap的key是ThreadLocal的對象,value是獨享數據。

68、計算一個view的嵌套層級

private int getParents(ViewParents view){
    if(view.getParents() == null) 
        return 0;
    } else {
    return (1 + getParents(view.getParents));
   }
}
複製代碼

69、MVP,MVVM,MVC解釋和實踐

MVC:
  • 視圖層(View) 對應於xml佈局文件和java代碼動態view部分
  • 控制層(Controller) MVC中Android的控制層是由Activity來承擔的,Activity本來主要是作爲初始化頁面,展示數據的操作,但是因爲XML視圖功能太弱,所以Activity既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多。
  • 模型層(Model) 針對業務模型,建立數據結構和相關的類,它主要負責網絡請求,數據庫處理,I/O的操作。
總結

具有一定的分層,model徹底解耦,controller和view並沒有解耦 層與層之間的交互儘量使用回調或者去使用消息機制去完成,儘量避免直接持有 controller和view在android中無法做到徹底分離,但在代碼邏輯層面一定要分清 業務邏輯被放置在model層,能夠更好的複用和修改增加業務。

MVP

通過引入接口BaseView,讓相應的視圖組件如Activity,Fragment去實現BaseView,實現了視圖層的獨立,通過中間層Preseter實現了Model和View的完全解耦。MVP徹底解決了MVC中View和Controller傻傻分不清楚的問題,但是隨着業務邏輯的增加,一個頁面可能會非常複雜,UI的改變是非常多,會有非常多的case,這樣就會造成View的接口會很龐大。

MVVM

MVP中我們說過隨着業務邏輯的增加,UI的改變多的情況下,會有非常多的跟UI相關的case,這樣就會造成View的接口會很龐大。而MVVM就解決了這個問題,通過雙向綁定的機制,實現數據和UI內容,只要想改其中一方,另一方都能夠及時更新的一種設計理念,這樣就省去了很多在View層中寫很多case的情況,只需要改變數據就行。

MVVM與DataBinding的關係?

MVVM是一種思想,DataBinding是谷歌推出的方便實現MVVM的工具。

看起來MVVM很好的解決了MVC和MVP的不足,但是由於數據和視圖的雙向綁定,導致出現問題時不太好定位來源,有可能數據問題導致,也有可能業務邏輯中對視圖屬性的修改導致。如果項目中打算用MVVM的話可以考慮使用官方的架構組件ViewModel、LiveData、DataBinding去實現MVVM。

三者如何選擇?
  • 如果項目簡單,沒什麼複雜性,未來改動也不大的話,那就不要用設計模式或者架構方法,只需要將每個模塊封裝好,方便調用即可,不要爲了使用設計模式或架構方法而使用。
  • 對於偏向展示型的app,絕大多數業務邏輯都在後端,app主要功能就是展示數據,交互等,建議使用mvvm。
  • 對於工具類或者需要寫很多業務邏輯app,使用mvp或者mvvm都可。

70、SharedPrefrences的apply和commit有什麼區別?

這兩個方法的區別在於:

  1. apply沒有返回值而commit返回boolean表明修改是否提交成功。

  2. apply是將修改數據原子提交到內存, 而後異步真正提交到硬件磁盤, 而commit是同步的提交到硬件磁盤,因此,在多個併發的提交commit的時候,他們會等待正在處理的commit保存到磁盤後在操作,從而降低了效率。而apply只是原子的提交到內容,後面有調用apply的函數的將會直接覆蓋前面的內存數據,這樣從一定程度上提高了很多效率。

  3. apply方法不會提示任何失敗的提示。 由於在一個進程中,sharedPreference是單實例,一般不會出現併發衝突,如果對提交的結果不關心的話,建議使用apply,當然需要確保提交成功且有後續操作的話,還是需要用commit的。

71、Base64、MD5是加密方法麼?

Base64是什麼?

Base64是用文本表示二進制的編碼方式,它使用4個字節的文本來表示3個字節的原始二進制數據。 它將二進制數據轉換成一個由64個可打印的字符組成的序列:A-Za-z0-9+/

MD5是什麼?

MD5是哈希算法的一種,可以將任意數據產生出一個128位(16字節)的散列值,用於確保信息傳輸完整一致。我們常在註冊登錄模塊使用MD5,用戶密碼可以使用MD5加密的方式進行存儲。如:md5(hello world,32) = 5eb63bbbe01eeed093cb22bb8f5acdc3

加密,指的是對數據進行轉換以後,數據變成了另一種格式,並且除了拿到解密方法的人,沒人能把數據轉換回來。 MD5是一種信息摘要算法,它是不可逆的,不可以解密。所以它只能算的上是一種單向加密算法。 Base64也不是加密算法,它是一種數據編碼方式,雖然是可逆的,但是它的編碼方式是公開的,無所謂加密。

72、HttpClient和HttpConnection的區別?

Http Client適用於web瀏覽器,擁有大量靈活的API,實現起來比較穩定,且其功能比較豐富,提供了很多工具,封裝了http的請求頭,參數,內容體,響應,還有一些高級功能,代理、COOKIE、鑑權、壓縮、連接池的處理。   但是,正因此,在不破壞兼容性的前提下,其龐大的API也使人難以改進,因此Android團隊對於修改優化Apache Http Client並不積極。(並在Android 6.0中拋棄了Http Client,替換成OkHttp)

HttpURLConnection對於大部分功能都進行了包裝,Http Client的高級功能代碼會較複雜,另外,HttpURLConnection在Android 2.3中增加了一些Https方面的改進(包括Http Client,兩者都支持https)。且在Android 4.0中增加了response cache。當緩存被安裝後(調用HttpResponseCache的install()方法),所有的HTTP請求都會滿足以下三種情況:

  • 所有的緩存響應都由本地存儲來提供。因爲沒有必要去發起任務的網絡連接請求,所有的響應都可以立刻獲取到。
  • 視情況而定的緩存響應必須要有服務器來進行更新檢查。比如說客戶端發起了一條類似於 “如果/foo.png這張圖片發生了改變,就將它發送給我” 這樣的請求,服務器需要將更新後的數據進行返回,或者返回一個304 Not Modified狀態。如果請求的內容沒有發生,客戶端就不會下載任何數據。
  • 沒有緩存的響應都是由服務器直接提供的。這部分響應會在稍後存儲到響應緩存中。

在Android 2.2版本之前,HttpClient擁有較少的bug,因此使用它是最好的選擇。 而在Android 2.3版本及以後,HttpURLConnection則是最佳的選擇。它的API簡單,體積較小,因而非常適用於Android項目。壓縮和緩存機制可以有效地減少網絡訪問的流量,在提升速度和省電方面也起到了較大的作用。對於新的應用程序應該更加偏向於使用HttpURLConnection,因爲在以後的工作當中Android官方也會將更多的時間放在優化HttpURLConnection上面。

73、ActivityA跳轉ActivityB然後B按back返回A,各自的生命週期順序,A與B均不透明。

ActivityA跳轉到ActivityB:
Activity A:onPause
Activity B:onCreate
Activity B:onStart
Activity B:onResume
Activity A:onStop
複製代碼
ActivityB返回ActivityA:
Activity B:onPause
Activity A:onRestart
Activity A:onStart
Activity A:onResume
Activity B:onStop
Activity B:onDestroy
複製代碼

74、如何通過廣播攔截和abort一條短信?

可以監聽這條信號,在傳遞給真正的接收程序時,我們將自定義的廣播接收程序的優先級大於它,並且取消廣播的傳播,這樣就可以實現攔截短信的功能了。

75、BroadcastReceiver,LocalBroadcastReceiver 區別?

1、應用場景

1、BroadcastReceiver用於應用之間的傳遞消息;

2、而LocalBroadcastManager用於應用內部傳遞消息,比broadcastReceiver更加高效。

2、安全

1、BroadcastReceiver使用的Content API,所以本質上它是跨應用的,所以在使用它時必須要考慮到不要被別的應用濫用;

2、LocalBroadcastManager不需要考慮安全問題,因爲它只在應用內部有效。

3、原理方面

(1) 與BroadcastReceiver是以 Binder 通訊方式爲底層實現的機制不同,LocalBroadcastManager 的核心實現實際還是 Handler,只是利用到了 IntentFilter 的 match 功能,至於 BroadcastReceiver 換成其他接口也無所謂,順便利用了現成的類和概念而已。

(2) LocalBroadcastManager因爲是 Handler 實現的應用內的通信,自然安全性更好,效率更高。

76、如何選擇第三方,從那些方面考慮?

大方向:從軟件環境做判斷

性能是開源軟件第一解決的問題。

一個好的生態,是一個優秀的開源庫必備的,取決標準就是觀察它是否一直在持續更新迭代,是否能及時處理github上用戶提出來的問題。大家在社區針對這個開源庫是否有比較活躍的探討。

背景,該開源庫由誰推出,由哪個公司推出來的。

用戶數和有哪些知名的企業落地使用

小方向:從軟件開發者的角度做判斷

是否解決了我們現有問題或長期來看帶來的維護成本。

公司有多少人會。

學習成本。

77、簡單說下接入支付的流程,是否自己接入過支付功能?

Alipay支付功能:

1.首先登錄支付寶開放平臺創建應用,並給應用添加App支付功能, 由於App支付功能需要簽約,因此需要上傳公司信息和證件等資料進行簽約。

2.簽約成功後,需要配置祕鑰。使用支付寶提供的工具生成RSA公鑰和私鑰,公鑰需要設置到管理後臺。

3.android studio集成

(1)copy jar包;
(2)發起支付請求,處理支付請求。
複製代碼

78、單例實現線程的同步的要求:

1.單例類確保自己只有一個實例(構造函數私有:不被外部實例化,也不被繼承)。

2.單例類必須自己創建自己的實例。

3.單例類必須爲其他對象提供唯一的實例。

79、如何保證Service不被殺死?

Android 進程不死從3個層面入手:

A.提供進程優先級,降低進程被殺死的概率

方法一:監控手機鎖屏解鎖事件,在屏幕鎖屏時啓動1個像素的 Activity,在用戶解鎖時將 Activity 銷燬掉。

方法二:啓動前臺service。

方法三:提升service優先級:

在AndroidManifest.xml文件中對於intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時適用於廣播。

B. 在進程被殺死後,進行拉活

方法一:註冊高頻率廣播接收器,喚起進程。如網絡變化,解鎖屏幕,開機等

方法二:雙進程相互喚起。

方法三:依靠系統喚起。

方法四:onDestroy方法裏重啓service:service + broadcast 方式,就是當service走ondestory的時候,發送一個自定義的廣播,當收到廣播的時候,重新啓動service;

C. 依靠第三方

根據終端不同,在小米手機(包括 MIUI)接入小米推送、華爲手機接入華爲推送;其他手機可以考慮接入騰訊信鴿或極光推送與小米推送做 A/B Test。

80、說說ContentProvider、ContentResolver、ContentObserver 之間的關係?

ContentProvider:管理數據,提供數據的增刪改查操作,數據源可以是數據庫、文件、XML、網絡等,ContentProvider爲這些數據的訪問提供了統一的接口,可以用來做進程間數據共享。

ContentResolver:ContentResolver可以爲不同URI操作不同的ContentProvider中的數據,外部進程可以通過ContentResolver與ContentProvider進行交互。

ContentObserver:觀察ContentProvider中的數據變化,並將變化通知給外界。

81、如何導入外部數據庫?

把原數據庫包括在項目源碼的 res/raw。

android系統下數據庫應該存放在 /data/data/com.(package name)/ 目錄下,所以我們需要做的是把已有的數據庫傳入那個目錄下。操作方法是用FileInputStream讀取原數據庫,再用FileOutputStream把讀取到的東西寫入到那個目錄。

82、LinearLayout、FrameLayout、RelativeLayout性能對比,爲什麼?

RelativeLayout會讓子View調用2次onMeasure,LinearLayout 在有weight時,也會調用子 View 2次onMeasure

RelativeLayout的子View如果高度和RelativeLayout不同,則會引發效率問題,當子View很複雜時,這個問題會更加嚴重。如果可以,儘量使用padding代替margin。

在不影響層級深度的情況下,使用LinearLayout和FrameLayout而不是RelativeLayout。

爲什麼Google給開發者默認新建了個RelativeLayout,而自己卻在DecorView中用了個LinearLayout?

因爲DecorView的層級深度是已知而且固定的,上面一個標題欄,下面一個內容欄。採用RelativeLayout並不會降低層級深度,所以此時在根節點上用LinearLayout是效率最高的。而之所以給開發者默認新建了個RelativeLayout是希望開發者能採用儘量少的View層級來表達佈局以實現性能最優,因爲複雜的View嵌套對性能的影響會更大一些。

83、scheme跳轉協議

Android中的scheme是一種頁面內跳轉協議,通過定義自己的scheme協議,可以跳轉到app中的各個頁面

服務器可以定製化告訴app跳轉哪個頁面

App可以通過跳轉到另一個App頁面

可以通過H5頁面跳轉頁面

84、HandlerThread

1、HandlerThread原理

當系統有多個耗時任務需要執行時,每個任務都會開啓個新線程去執行耗時任務,這樣會導致系統多次創建和銷燬線程,從而影響性能。爲了解決這一問題,Google提出了HandlerThread,HandlerThread本質上是一個線程類,它繼承了Thread。HandlerThread有自己的內部Looper對象,可以進行loopr循環。通過獲取HandlerThread的looper對象傳遞給Handler對象,可以在handleMessage()方法中執行異步任務。創建HandlerThread後必須先調用HandlerThread.start()方法,Thread會先調用run方法,創建Looper對象。當有耗時任務進入隊列時,則不需要開啓新線程,在原有的線程中執行耗時任務即可,否則線程阻塞。它在Android中的一個具體的使用場景是IntentService。由於HanlderThread的run()方法是一個無限循環,因此當明確不需要再使用HandlerThread時,可以通過它的quit或者quitSafely方法來終止線程的執行。

2、HanlderThread的優缺點

  • HandlerThread優點是異步不會堵塞,減少對性能的消耗。

  • HandlerThread缺點是不能同時繼續進行多任務處理,要等待進行處理,處理效率較低。

  • HandlerThread與線程池不同,HandlerThread是一個串隊列,背後只有一個線程。

85、IntentService

IntentService是一種特殊的Service,它繼承了Service並且它是一個抽象類,因此必須創建它的子類才能使用IntentService。

原理

在實現上,IntentService封裝了HandlerThread和Handler。當IntentService被第一次啓動時,它的onCreate()方法會被調用,onCreat()方法會創建一個HandlerThread,然後使用它的Looper來構造一個Handler對象mServiceHandler,這樣通過mServiceHandler發送的消息最終都會在HandlerThread中執行。

生成一個默認的且與主線程互相獨立的工作者線程來執行所有傳送至onStartCommand()方法的Intetnt。

生成一個工作隊列來傳送Intent對象給onHandleIntent()方法,同一時刻只傳送一個Intent對象,這樣一來,你就不必擔心多線程的問題。在所有的請求(Intent)都被執行完以後會自動停止服務,所以,你不需要自己去調用stopSelf()方法來停止。

該服務提供了一個onBind()方法的默認實現,它返回null。

提供了一個onStartCommand()方法的默認實現,它將Intent先傳送至工作隊列,然後從工作隊列中每次取出一個傳送至onHandleIntent()方法,在該方法中對Intent做相應的處理。

爲什麼在mServiceHandler的handleMessage()回調方法中執行完onHandlerIntent()方法後要使用帶參數的stopSelf()方法?

因爲stopSel()方法會立即停止服務,而stopSelf(int startId)會等待所有的消息都處理完畢後才終止服務,一般來說,stopSelf(int startId)在嘗試停止服務之前會判斷最近啓動服務的次數是否和startId相等,如果相等就立刻停止服務,不相等則不停止服務。

86、如何將一個Activity設置成窗口的樣式。

中配置:

android:theme="@android:style/Theme.Dialog"
複製代碼

另外

android:theme="@android:style/Theme.Translucnt"
複製代碼

是設置透明。

87、Android中跨進程通訊的幾種方式

1:訪問其他應用程序的Activity 如調用系統通話應用

Intent callIntent = new Intent(Intent.ACTION_CALL,Uri.parse("tel:12345678");
startActivity(callIntent);
複製代碼

2:Content Provider 如訪問系統相冊

3:廣播(Broadcast) 如顯示系統時間

4:AIDL服務

88、顯示Intent與隱式Intent的區別

對明確指出了目標組件名稱的Intent,我們稱之爲“顯式Intent”。

對於沒有明確指出目標組件名稱的Intent,則稱之爲“隱式 Intent”。

對於隱式意圖,在定義Activity時,指定一個intent-filter,當一個隱式意圖對象被一個意圖過濾器進行匹配時,將有三個方面會被參考到:

動作(Action)

類別(Category ['kætɪg(ə)rɪ] )

數據(Data )

<activity android:name=".MainActivity"  android:label="@string/app_name">
            <intent-filter>
                <action android:name="com.wpc.test" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="image/gif"/>
            </intent-filter>
</activity>
複製代碼

89、Android Holo主題與MD主題的理念,以及你的看法

Holo Theme

Holo Theme 是 Android Design 的最基礎的呈現方式。因爲是最爲基礎的 Android Design 呈現形式,每一臺 Android 4.X 的手機系統內部都有集成 Holo Theme 需要的控件,即開發者不需要自己設計控件,而是直接從系統裏調用相應的控件。在 UI 方面沒有任何的亮點,和 Android4.X 的設置/電話的視覺效果極度統一。由此帶來的好處顯而易見,這個應用作爲 Android 應用的辨識度極高,且完全不可能與系統風格產生衝突。

Material Design

Material design其實是單純的一種設計語言,它包含了系統界面風格、交互、UI,更加專注擬真,更加大膽豐富的用色,更加豐富的交互形式,更加靈活的佈局形式

1.鮮明、形象的界面風格,

2.色彩搭配使得應用看起來非常的大膽、充滿色彩感,凸顯內容

3.Material design對於界面的排版非常的重視

4.Material design的交互設計上採用的是響應式交互,這樣的交互設計能把一個應用從簡單展現用戶所請求的信息,提升至能與用戶產生更強烈、更具體化交互的工具。

90、如何讓程序自動啓動?

定義一個Braodcastreceiver,action爲BOOT——COMPLETE,接受到廣播後啓動程序。

91、Fragment 在 ViewPager 裏面的生命週期,滑動 ViewPager 的頁面時 Fragment 的生命週期的變化。

92、如何查看模擬器中的SP與SQList文件。如何可視化查看佈局嵌套層數與加載時間。

93、各大平臺打包上線的流程與審覈時間,常見問題(主流的應用市場說出3-4個)

94、屏幕適配的處理技巧都有哪些?

一、爲什麼要適配

爲了保證用戶獲得一致的用戶體驗效果,使得某一元素在Android不同尺寸、不同分辨率的、不同系統的手機上具備相同的顯示效果,能夠保持界面上的效果一致,我們需要對各種手機屏幕進行適配!

  • Android系統碎片化:基於Google原生系統,小米定製的MIUI、魅族定製的flyme、華爲定製的EMUI等等;
  • Android機型屏幕尺寸碎片化:5寸、5.5寸、6寸等等;
  • Android屏幕分辨率碎片化:320x480、480x800、720x1280、1080x1920等。
二、基本概念
  • 像素(px):像素就是手機屏幕的最小構成單元,px = 1像素點 一般情況下UI設計師的設計圖會以px作爲統一的計量單位。
  • 分辨率:手機在橫向、縱向上的像素點數總和 一般描述成 寬*高 ,即橫向像素點個數 * 縱向像素點個數(如1080 x 1920),單位:px。
  • 屏幕尺寸:手機對角線的物理尺寸。單位 英寸(inch),一英寸大約2.54cm 常見的尺寸有4.7寸、5寸、5.5寸、6寸。
  • 屏幕像素密度(dpi):每英寸的像素點數,例如每英寸內有160個像素點,則其像素密度爲160dpi,單位:dpi(dots per inch)。
  • 標準屏幕像素密度(mdpi): 每英寸長度上還有160個像素點(160dpi),即稱爲標準屏幕像素密度(mdpi)。
  • 密度無關像素(dp):與終端上的實際物理像素點無關,可以保證在不同屏幕像素密度的設備上顯示相同的效果,是安卓特有的長度單位,dp與px的轉換:1dp = (dpi / 160 ) * 1px。
  • 獨立比例像素(sp):字體大小專用單位 Android開發時用此單位設置文字大小,推薦使用12sp、14sp、18sp、22sp作爲字體大小。
三、適配方案

適配的最多的3個分辨率:1280720,19201080,800*480。

解決方案:

對於Android的屏幕適配,我認爲可以從以下4個方面來做:

1、佈局組件適配

  • 請務必使用密度無關像素 dp 或獨立比例像素 sp 單位指定尺寸。
  • 使用相對佈局或線性佈局,不要使用絕對佈局
  • 使用wrap_content、match_parent、權重
  • 使用minWidth、minHeight、lines等屬性

dimens使用:

不同的屏幕尺寸可以定義不同的數值,或者是不同的語言顯示我們也可以定義不同的數值,因爲翻譯後的長度一般都不會跟中文的一致。此外,也可以使用百分比佈局或者AndroidStudio2.2的新特性約束佈局。

2、佈局適配

使用限定符(屏幕密度限定符、尺寸限定符、最小寬度限定符、佈局別名、屏幕方向限定符)根據屏幕的配置來加載相應的UI佈局。

3、圖片資源適配

使用自動拉伸圖.9png圖片格式使圖片資源自適應屏幕尺寸。

普通圖片和圖標:

建議按照官方的密度類型進行切圖即可,但一般我們只需xxhdpi或xxxhdpi的切圖即可滿足我們的需求;

4、代碼適配:

在代碼中使用Google提供的API對設備的屏幕寬度進行測量,然後按照需求進行設置。

5、接口配合:

本地加載圖片前判斷手機分辨率或像素密度,向服務器請求對應級別圖片。

95、斷點續傳實現?

在本地下載過程中要使用數據庫實時存儲到底存儲到文件的哪個位置了,這樣點擊開始繼續傳遞時,才能通過HTTP的GET請求中的setRequestProperty("Range","bytes=startIndex-endIndex");方法可以告訴服務器,數據從哪裏開始,到哪裏結束。同時在本地的文件寫入時,RandomAccessFile的seek()方法也支持在文件中的任意位置進行寫入操作。最後通過廣播或事件總線機制將子線程的進度告訴Activity的進度條。關於斷線續傳的HTTP狀態碼是206,即HttpStatus.SC_PARTIAL_CONTENT。

96、項目中遇到哪些難題,最終你是如何解決的

97、Activity正常和異常情況下的生命週期

98、關於< include >< merge >< stub >三者的使用場景

99、Android對HashMap做了優化後推出的新的容器類是什麼?

公衆號

我的公衆號 JsonChao 開通啦,如果您想第一時間獲取最新文章和最新動態,歡迎掃描關注~

讚賞

如果這個庫對您有很大幫助,您願意支持這個項目的進一步開發和這個項目的持續維護。你可以掃描下面的二維碼,讓我喝一杯咖啡或啤酒。非常感謝您的捐贈。謝謝!


Contanct Me

● 微信 && 微信羣:

歡迎關注我的微信:bcce5360。由於微信羣人數太多無法生成羣邀二維碼,所以麻煩大家想進微信羣的朋友們,加我微信拉你進羣(PS:微信羣的學習氛圍與各項福利將會超乎你的想象)

● QQ羣:

2千人QQ羣,Awesome-Android學習交流羣,QQ羣號:959936182, 歡迎大家加入~

About me

很感謝您閱讀這篇文章,希望您能將它分享給您的朋友或技術羣,這對我意義重大。

希望我們能成爲朋友,在 Github掘金上一起分享知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章