面試題彙總八 Android 篇

前言

記錄我遇到的一些Android問題,目前還不全面,後續再整理。

推薦相關資料: Android校招面試指南

 

 

 

Android

 

ARN

Android ARN 探究

Application Not Responding,應用無響應。如果App在特定時間無法響應屏幕觸摸或鍵盤輸入事件,或者特定事件沒有處理完畢,就會出現ANR。

Activity 5s
ContentProvider 10s
Boardcast 前臺 10s 後臺 60s
Service 前臺 20s 後臺 200s

 

res和assert

Android之assets資源目錄的各種操作

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線程/進程通信相關手段

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事件分發機制

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的位置,繪製過程就是將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體系架構

Android體系架構

  • 應用程序
  • 應用框架層
    • Activity Manager
    • Content Providers
    • Resource Manager
    • Notification Manager
    • View System
  • 系統運行庫層
    • 程序庫:C/C++庫
    • Android運行庫:虛擬機在的地方
  • 硬件抽象層
  • Linux內核層

 

Android六大布局

Android之四大組件、六大布局、五大存儲

  • LinearLayout
  • TableLayout
  • FrameLayout
  • RelativeLayout
  • GridLayout
  • AbsoluteLayout

 

Android如何判斷非ui線程修改ui?

Android:爲什麼子線程不能更新UI

根據View內部 mThread != Thread.currentThread() 來判斷。

Android的界面非線程安全,因此規定只能在ui線程修改ui。

 

Android性能優化

Android開發優化的幾點建議

Android性能優化彙總(逐步更新中...)

  • 佈局優化
    • <include><merge>重用佈局;
    • 使用ViewStub延遲加載佈局;
    • RelativeLayout代替LinearLayout;
    • 減少佈局層級;
    • 刪除無用佈局;
    • 少用wrap_content;
    • 多度繪製優化;
    • View的onDraw避免產生大量對象或執行耗時操作;
    • ……
  • 網絡優化
    • 圖片壓縮;
    • 合併網絡請求;
    • GZip壓縮;
    • ……
  • 安裝包優化
    • 混淆;
    • 減少不必要的資源;
    • 減少不必要的三方庫;
    • 功能模塊插件化,按需下載;
    • ……
  • 內存泄露優化
  • 其他優化
    • ListView/RecyclerView使用ViewHolder;
    • 頻繁創建線程用線程池;
    • LRU緩存;
    • ……

 

AIDL使用

Android 深入淺出AIDL(一)

  1. 聲明Parcelable數據類;
  2. 聲明AIDL接口MyInterface;
  3. AIDL會自動編譯成Java接口MyInterface;
  4. Service在onBind時返回MyInterface.Stub;
  5. 客戶端在onServiceConnection綁定Binder時通過Myinterface.Stub.asInterface(service)來獲取調用接口。

 

AIDL生成的Java接口分析

Android 深入淺出AIDL(二)

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機制

Android 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面試題(31)-App啓動流程

一個APP從啓動到主頁面顯示經歷了哪些過程?

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能否多進程運行

Android中的多進程模式

在AndroidManifest中指定android:process屬性可以讓相應組件運行在新進程中。

 

Android特有數據結構

ArrayMap解析

SparseArray解析

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事件分發機制

Android View的事件分發機制和滑動衝突解決

Android事件分發機制

 

onTouch()與onClick()優先級

onTouch()在dispatchTouchEvent()中調用,onClick()在onTouchEvent()中調用,因此onTouch()優先級高於onClick()。

 

MVC MVP MVVM

Android App的設計架構:MVC,MVP,MVVM與架構經驗談

 

 

 

 

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