前言
記錄我遇到的一些Android問題,目前還不全面,後續再整理。
推薦相關資料: Android校招面試指南
- 面試題彙總一 Java 語言基礎篇
- 面試題彙總二 Java 多線程篇
- 面試題彙總三 Java 集合篇
- 面試題彙總四 JVM 篇
- 面試題彙總五 Spring 篇
- 面試題彙總六 數據庫篇
- 面試題彙總七 計算機網絡篇
Android
ARN
Application Not Responding,應用無響應。如果App在特定時間無法響應屏幕觸摸或鍵盤輸入事件,或者特定事件沒有處理完畢,就會出現ANR。
Activity | 5s | |
---|---|---|
ContentProvider | 10s | |
Boardcast | 前臺 10s | 後臺 60s |
Service | 前臺 20s | 後臺 200s |
res和assert
Android 中資源分爲兩種:
①、第一種是res下可編譯的資源文件,這種資源文件系統會在R.java裏面自動生成該資源文件的ID,(除了raw外,其他資源目錄中的資源文件都會被編譯),這也是爲什麼將APK文件解壓後無法直接查看XML格式資源文件內容的原因。而assets與res/raw目錄中的資源文件不會做任何處理,所以將APK解壓後,這兩個目錄中的資源文件都會保持原樣。res目錄只能有一層子目錄,而且這些子目錄必須是預定義的,如res/layout、res/values等都是合法的,而res/abc,res/xyz並不是合法的資源目錄。
②、第二種就是放在assets文件夾下面的原生資源文件,放在這個文件夾下面的文件不會被R文件編譯,所以不能像第一種那樣直接使用.Android提供了一個工具類,方便我們操作獲取assets文件下的文件,在assets目錄中可以建任意層次的子目錄(只受操作系統的限制)。context.getResources().getAssets().open("xxx.txt");
佈局單位
px 像素
in 英寸
mm 毫米
density/dpi 每一in的px
pt 1/72 in
dp px = dp * dpi / 160
當dpi=160時 1dp=1px
當dpi=240時 1dp=1.5px
sp 與dp類似,不過允許用戶自定義,用於定義文本尺寸
Android內存泄漏
- 大對象、大集合;
- static變量保存activity,可以換成ApplicationContext;
- static變量保存非static內部類,會持有父對象的引用;
- 匿名內部類傳入異步線程,也是因爲持有引用;
- handler,類似匿名內部類問題,換成static內部類+軟引用;
Android線程/進程通信相關手段
- 線程間通信:handler
- 進程間通信:Intent Binder Messenger AIDL ContentProvider Socket
Android 常規的存儲手段
- SharedPreferences
- 文件存儲
- Sqlite
- ContentProvider
SharedPreference 是線程/進程安全的嗎
SharedPreferences 是線程安全的嗎?它的 commit 和 apply 方法有什麼區別
實現類內部大量synchronized,線程安全。
但不是進程安全的。
ContentProvider是線程/進程安全的嗎
Android ContentProvider的線程安全(一)
Android ContentProvider的線程安全(二)
多個進程的ContentResolver獲取ContentProvider的過程是進程安全的。
ContentProvider在使用時沒有額外機制保障線程安全。
Android事件分發機制
事件分發涉及到Activity、View Group和View,事件產生於Activity,Activity分發給View Group,View Group再分發給View。View收到事件後,可以在OnTouchEvent中處理,也可以往回傳給View Group的onTouchEvent事件,View Group也可以把事件繼續回傳給Activit。在從Activity分發到View的過程中,ViewGroup還有一個攔截方法,可以不把事件傳給下面的View而直接進入ViewGroup的onTouchEvent方法。整個過程是一個責任鏈模式,每個環節若認爲不能處理事件便可以把事件傳給下一個環節,直到處理事件並消耗。
Android view繪製流程
繪製流程包括測量、佈局和繪製。測量過程是確定View的長寬,佈局過程是確定View的位置,繪製過程就是將view顯示出來的過程。對於ViewGroup來說,不僅要控制自身的三個過程,還要控制子View的三個過程。
onNewIntent 調用時機
singleTask時調用。
Activity第一次啓動:onCreate() → onStart() → onResume() →
Activity再次啓動:onNewIntent() → onResart() → onStart() → onResume() →
Activity被釋放掉再啓動:onCreate() → onStart() → onResume() →
AsyncTask流程
onPreExecute → doInBackground → onPublishProgress → updateProgress → onPostExecute
- 內部worker和futureTask用於執行background和發送結果到主線程。
- 調用execute,先執行onPreExecute,再把futureTask丟進執行器executer。
- executor維護串行隊列,隊列內的任務會按順序丟進線程池執行。
- 其中,doInBackground的publishProgress和mWorker中的postResult會將信息發送給ui線程內的Handler來執行。
Android體系架構
- 應用程序
- 應用框架層
- Activity Manager
- Content Providers
- Resource Manager
- Notification Manager
- View System
- 系統運行庫層
- 程序庫:C/C++庫
- Android運行庫:虛擬機在的地方
- 硬件抽象層
- Linux內核層
Android六大布局
- LinearLayout
- TableLayout
- FrameLayout
- RelativeLayout
- GridLayout
- AbsoluteLayout
Android如何判斷非ui線程修改ui?
根據View內部 mThread != Thread.currentThread() 來判斷。
Android的界面非線程安全,因此規定只能在ui線程修改ui。
Android性能優化
- 佈局優化
- <include><merge>重用佈局;
- 使用ViewStub延遲加載佈局;
- RelativeLayout代替LinearLayout;
- 減少佈局層級;
- 刪除無用佈局;
- 少用wrap_content;
- 多度繪製優化;
- View的onDraw避免產生大量對象或執行耗時操作;
- ……
- 網絡優化
- 圖片壓縮;
- 合併網絡請求;
- GZip壓縮;
- ……
- 安裝包優化
- 混淆;
- 減少不必要的資源;
- 減少不必要的三方庫;
- 功能模塊插件化,按需下載;
- ……
- 內存泄露優化
- 其他優化
- ListView/RecyclerView使用ViewHolder;
- 頻繁創建線程用線程池;
- LRU緩存;
- ……
AIDL使用
- 聲明Parcelable數據類;
- 聲明AIDL接口MyInterface;
- AIDL會自動編譯成Java接口MyInterface;
- Service在onBind時返回MyInterface.Stub;
- 客戶端在onServiceConnection綁定Binder時通過Myinterface.Stub.asInterface(service)來獲取調用接口。
AIDL生成的Java接口分析
AIDL文件如下:
interface IDemandManager {
MessageBean getDemand();
//客戶端->服務端
void setDemandIn(in MessageBean msg);
//服務端->客戶端
void setDemandOut(out MessageBean msg);
//客戶端<->服務端
void setDemandInOut(inout MessageBean msg);
}
AIDL生成Java接口後同時會生成一個繼承Binder和相應接口的內部類Stub;
public interface IDemandManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IDemandManager {...}
public MessageBean getDemand() throws android.os.RemoteException;
// in
public void setDemandIn(MessageBean msg) throws android.os.RemoteException;
// out
public void setDemandOut(MessageBean msg) throws android.os.RemoteException;
//服務端->客戶端
// inout
public void setDemandInOut(MessageBean msg) throws android.os.RemoteException;
}
Stub內部類中還有一個用於遠程代理的內部類Proxy;
public interface IDemandManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IDemandManager {
private static class Proxy implements IDemandManager {...}
...
}
...
}
Stub沒有實現接口方法,需要開發人員自己重寫;
Stub會爲每個接口方法設定整型標識符;
public interface IDemandManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IDemandManager {
...
static final int TRANSACTION_getDemand = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_setDemandIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_setDemandOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_setDemandInOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
}
...
}
Stub的asInterface方法會判斷服務器和客戶端是同進程還是跨進程來返回Stub或Stub的代理,因此,同進程時客戶端會直接調用Stub的相應接口,而跨進程時調用順序爲:Proxy的接口代理方法 → Stub的onTransact方法 → Stub的接口方法;
public interface IDemandManager extends android.os.IInterface {
public static abstract class Stub extends android.os.Binder implements IDemandManager {
public IDemandManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IDemandManager))) {
return ((IDemandManager) iin);
}
return new IDemandManager.Stub.Proxy(obj);
}
...
}
...
}
代理模式下參數msg使用in標籤:
---- Proxy: setDemandIn(MessageBean msg) ----
1. 新建 Parcel: _data _reply 用於發送和接收參數;
2. _data 寫入 DESCRIPTOR 用於驗證;
3. 將 msg 寫入 _data;
4. mRemote.transact(Stub.TRANSACTION_setDemandIn, _data, _reply, 0);
---- Stub: onTransact(...) ----
5. 驗證 DESCRIPTOR;
6. 新建 MessageBean msg;
7. 把 _data 寫入 msg;
8. this.setDemandIn(msg);
其他標籤差後續再補。
Binder機制
四個角色:Client、Server、ServiceManager以及binder驅動。
下圖中虛線部分表示通過Binder通信,而Binder統一由Binder驅動管理。
Service Manager的Binder存放於固定地址,Client和Server可以方便拿到。
Server需要在Server Manager註冊,Client才能在Service Manager中拿到Server的聯繫方式,才能實現Client與Server的交流。
參考AIDL,Server持有Binder實體類,Client拿到的是Client的代理。
Client調用代理Binder的 transact() 方法並掛起,Server中會在Binder驅動的Binder線程中運行Binder的onTransact()方法並返回結果。
APP啓動流程
Android Launcher 啓動 Activity 的工作過程
- Launcher一口氣調用到Instrumentation.exeStartActivity;
- Instrumentation拿到ActivityManager的代理ApplicationThread,請求startActivity --- AIDL;
- ActivityManager判斷目標進程是否存在,如存在,直接進入reaStartActivityLocked環節;
- 若不存在,先從zygote進程fork一個新進程;
- 在新進程的ActivityThread.main方法中,初始化Looper,創建ui線程(但不繼承thread);
- activityThread.attach又通過ApplicationThread請求ActivityManager.attachApplication;
- AM調用ActivityThread.bindApplication;
- 回到ui線程做Application初始化操作;
- 回到reaStartActivityLocked環節;
- AM調用AT的scheduleLaunchActivity;
- 通過handler的方式新建Activity;
一個APP能否多進程運行
在AndroidManifest中指定android:process屬性可以讓相應組件運行在新進程中。
Android特有數據結構
- ArrayMap
- ArraySet
- SparseArray
- SparseIntArray
- SparseBooleanArray
- SparseLongArray
以上主要是Android中用於替換HashMap/HashSet的方案,通常內存佔用更小,但整體性能要差於HashMap/HashSet。
在結構上,整體是內部分別用兩個數組分別保存key和value,key爲順序存放,方便二分查找。
SparseXXArray是一個 int[] 保存key,一個 Object[] / int[] / boolean[] / long[] 保存值。
index | 0 | 1 | 2 | 3 |
---|---|---|---|---|
key int[] | key0 | key1 | key2 | …… |
value Object[] / int[] / boolean[] / long[] | value0 | value1 | value2 | …… |
ArrayMap用 int[] 保存key的hash,由於不同對象的hash可能相同,array數組會依次存放key和value。
index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
hash int[] | hash0 | hash1 |
hash2 |
…… | |||
array Object[] | key0 | value0 | key1 | value1 | key2 | value2 | …… |
ArraySet用 int[] 保存value的hash,array數組保存value對象。
index | 0 | 1 | 2 | 3 |
---|---|---|---|---|
hash int[] | hash0 | hash1 | hash2 | …… |
array Object[] | value0 | value1 | value2 | …… |
ArrayMap和ArraySet可能出現不同對象hash相同的情況,因此通過hash表查找到array表中的序號後,還要在相同hash範圍內向前或向後進行equals比較。
查找爲二分查找法,插入是二分定位加arraycopy移動元素。
Android事件分發機制
onTouch()與onClick()優先級
onTouch()在dispatchTouchEvent()中調用,onClick()在onTouchEvent()中調用,因此onTouch()優先級高於onClick()。
MVC MVP MVVM
Android App的設計架構:MVC,MVP,MVVM與架構經驗談