本文整理自:https://github.com/JsonChao/Awesome-Android-Interview
溫馨提示:文章幹貨内容較多,可能一次看不完,建議點個“在看”并收藏,下次再查閱不迷路~
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。無論如何都要確定使用者界面的流暢 度。如果耗時操作需要讓使用者等待,那麼可以在界面上顯示度條。
深入回答:https://www.jianshu.com/p/ad1a84b6ec69
2、Activity和Fragment生命周期有哪些?

3、橫豎屏切換時候Activity的生命周期
不設定Activity的android:configChanges時,切屏會重新回調各個生命周期,切橫屏時會執行一次,切豎屏時會執行兩次。設定Activity的android:configChanges=”orientation”時,切屏還是會調用各個生命周期,切換橫豎屏隻會執行一次 設定Activity的android:configChanges=”orientation |keyboardHidden”時,切屏不會重新調用各個生命周期,隻會執行onConfigurationChanged方法
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用于将執行環境從線程池切換到主線程。
- sHandler是一個靜态的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)恢複界面的狀态
深入了解: https://www.jianshu.com/p/89e0a7533dbe
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的使用場景與特點。
- Thread線程,獨立運作與于 Activity 的,當Activity 被 finish 後,如果沒有主動停止 Thread或者 run 方法沒有執行完,其會一直執行下去。
- AsyncTask 封裝了兩個線程池和一個Handler(SerialExecutor用于排隊,THREAD_POOL_EXECUTOR為真正的執行任務,Handler将工作線程切換到主線程),其必須在 UI線程中建立,execute 方法必須在 UI線程中執行,一個任務執行個體隻允許執行一次,執行多次抛出異常,用于網絡請求或者簡單資料處理。
- 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的時間順序排列和喚醒的方式結合實作的。
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隻會更新清單,然後繪制清單内的控件。
- 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有什麼差別?
這兩個方法的差別在于:
- apply沒有傳回值而commit傳回boolean表明修改是否送出成功。
- apply是将修改資料原子送出到記憶體, 而後異步真正送出到硬體磁盤, 而commit是同步的送出到硬體磁盤,是以,在多個并發的送出commit的時候,他們會等待正在處理的commit儲存到磁盤後在操作,進而降低了效率。而apply隻是原子的送出到内容,後面有調用apply的函數的将會直接覆寫前面的記憶體資料,這樣從一定程度上提高了很多效率。
- 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設定成視窗的樣式。
Manifest中配置:
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、動态布局的了解
96、怎麼去除重複代碼?
97、Recycleview和ListView的差別
98、動态權限适配方案,權限組的概念
99、Android系統為什麼會設計ContentProvider?
100、下拉狀态欄是不是影響activity的生命周期
101、如果在onStop的時候做了網絡請求,onResume的時候怎麼恢複?
102、Debug和Release狀态的不同
103、dp是什麼,sp呢,有什麼差別
103、自定義View,ViewGroup注意那些回調?
104、android中的存儲類型
105、Activity的生命周期,finish調用後其他生命周期還會走麼?
106、有遇到過哪些螢幕和資源适配問題?
107、項目中遇到哪些難題,最終你是如何解決的?
108、listview圖檔加載錯亂的原理和解決方案。
109、invalidate和requestLayout的差別及使用。
110、如何反編譯,對代碼逆向分析。
111、RemoteViews實作和使用場景
112、對伺服器衆多錯誤碼的處理(錯誤碼有好幾萬個)
113、adb常用指令行
114、Android中如何檢視一個對象的回收情況?
115、Activity正常和異常情況下的生命周期
116、關于< include >< merge >< stub >三者的使用場景
117、Android對HashMap做了優化後推出的新的容器類是什麼?
118、說下你對服務的了解,如何殺死一個服務。
119、斷點續傳實作?
在本地下載下傳過程中要使用資料庫實時存儲到底存儲到檔案的哪個位置了,這樣點選開始繼續傳遞時,才能通過HTTP的GET請求中的setRequestProperty("Range","bytes=startIndex-endIndex");方法可以告訴伺服器,資料從哪裡開始,到哪裡結束。同時在本地的檔案寫入時,RandomAccessFile的seek()方法也支援在檔案中的任意位置進行寫入操作。最後通過廣播或事件總線機制将子線程的進度告訴Activity的進度條。關于斷線續傳的HTTP狀态碼是206,即HttpStatus.SC_PARTIAL_CONTENT。
文章内容全是幹貨,内容有點多,建議點個“在看”或者收藏,平時有空看一看,面試基本問題不大~