Android 面試中級篇

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

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

  • 儲存
  • View
  • WebView
  • 效能相關
  • 系統實現及原理
  • 專案構建
  • 功能的實現
  • 概念
  • 其他

儲存


0. Android中資料儲存的方式有哪些

  1. File
  2. SharedPreferences
  3. SQlite
  4. 網路
  5. ContentProvider

1. SharedPreference是程式同步的嘛,有沒有什麼方法程式同步

  1. 預設不是
  2. 可以設定模式MODE_MULTI_PROCESS做到程式同步,但因為該模式有很多坑,已經被Google棄用
  3. 官方建議使用ContentProvider

2. SharedPreferences commit和apply的區別

  1. commit是同步的提交,這種方式很常用,在比較早的SDK版本中就有了。這種提交方式會阻塞呼叫它的執行緒,並且這個方法會返回boolean值告知儲存是否成功(如果不成功,可以做一些補救措施)。
  2. apply是非同步的提交方式,目前Android Studio也會提示大家使用這種方式

3. 檔案儲存路徑與許可權許可權

  1. 檔案儲存分為內部儲存和外部儲存
  2. 內部儲存
    1. Environment.getDataDirectory() = /data //這個方法是獲取內部儲存的根路徑
    2. getFilesDir().getAbsolutePath() = /data/user/0/packname/files //這個方法是獲取某個應用在內部儲存中的files路徑
    3. getCacheDir().getAbsolutePath() = /data/user/0/packname/cache //這個方法是獲取某個應用在內部儲存中的cache路徑
    4. getDir(“myFile”, MODE_PRIVATE).getAbsolutePath() = /data/user/0/packname/app_myFile
  3. 外部儲存
    1. Environment.getExternalStorageDirectory().getAbsolutePath() = /storage/emulated/0 //這個方法是獲取外部儲存的根路徑
    2. Environment.getExternalStoragePublicDirectory(“”).getAbsolutePath() = /storage/emulated/0 這個方法是獲取外部儲存的根路徑 3. getExternalFilesDir(“”).getAbsolutePath() = /storage/emulated/0/Android/data/packname/files 這個方法是獲取某個應用在外部儲存中的files路徑 4. getExternalCacheDir().getAbsolutePath() = /storage/emulated/0/Android/data/packname/cache 這個方法是獲取某個應用在外部儲存中的cache路徑
  4. 清楚資料和解除安裝APP時, 內外儲存的file和cache都會被刪除
  5. 內部儲存file和cache不需要許可權;外部儲存低版本上(19以下)file和cache需要許可權,高版本不需要許可權;Environment.getExternalStorageDirectory()需要許可權

4. SQLite

  1. SQLite每個資料庫都是以單個檔案(.db)的形式存在,這些資料都是以B-Tree的資料結構形式儲存在磁碟上。
  2. 使用SQLiteDatabase的insert,delete等方法或者execSQL方法預設都開啟了事務,如果操作順利完成才會更新.db資料庫。事務的實現是依賴於名為rollback journal檔案,藉助這個臨時檔案來完成原子操作和回滾功能。在/data/data//databases/目錄下看到一個和資料庫同名的.db-journal檔案。

如何對SQLite資料庫中進行大量的資料插入?
顯示的開啟事務

db.beginTransaction();
try {
   ...
   db.setTransactionSuccessful();
} finally {
   db.endTransaction();
  }
複製程式碼

5. 資料庫的升級

  1. Android中提供了SqLiteOpenHelper類,當版本更新時,會自動呼叫onUpgrade方法,我們在此方法中升級
  2. 如果修改已有表,可以才有臨時表的方法:
    1. 將已有表重新命名成一個臨時表
    2. 建立新表
    3. 拷貝
    4. 刪除臨時表

6. 如何匯入外部資料庫

  1. 把資料庫db檔案放在res/raw下打包進apk
  2. 通過FileInputStream讀取db檔案,通過FileOutputStream將檔案寫入/data/data/包名/database下

View


0. android:gravity與android:layout_gravity的區別

  1. gravity是控制當前View內佈局的位置
  2. layout_gravity是控制View在父佈局中的位置

1. View的繪製流程

從ViewRootImpl的performTraversals開始,經過measure,layout,draw 三個流程。draw流程結束以後就可以在螢幕上看到view了。

2. View的measureSpec 由誰決定?頂級view呢

  1. View的MeasureSpec由父容器的MeasureSpec和其自身的LayoutParams共同確定,
  2. 而對於DecorView是由它的MeasureSpec由視窗尺寸和其自身的LayoutParams共同確定。

3. View和ViewGroup的基本繪製流程

View

  1. measure -> onMeasure
  2. layout(onLayout方法是空的,因為他沒有child了)
  3. draw -> ondraw

ViewGroup

  1. measure -> onMeasure (onMeasure中需要呼叫childView的measure計算大小)
  2. layout -> onLayout (onLayout方法中呼叫childView的layout方法)
  3. draw -> onDraw (ViewGroup一般不繪製自己,ViewGroup預設實現dispatchDraw去繪製孩子)

4. draw方法 大概有幾個步驟

  1. drawbackground
  2. 如果要檢視顯示漸變框,這裡會做一些準備工作
  3. draw自身內容
  4. drawChild
  5. 如果需要, 繪製當前檢視在滑動時的邊框漸變效果
  6. 繪製裝飾,如滾動條

5. 怎麼控制另外一個程式的View顯示

RemoteView:RemoteViews實現了Parcelable介面,通過binder機制傳遞給遠端程式,程式間view的顯示

6. 兩指縮放

  1. 為了解決多點觸控問題,android在MotionEvent中引入了pointer概念
  2. 通過ACTION_DOWN、ACTION_POINTER_DOWN、ACTION_MOVE、ACTION_POINTER_UP、ACTION_UP來檢測手機的動作
  3. 每個手指的位置可以通過getX(pointIndex)來獲得,這樣我們就能判斷出滑動的距離
  4. 縮放有多種實現: 1. ImageView可以通過setImageMatrix(martix)來實現 2. 自定義View可以縮放Canvas的大小 3. 還可以設定LayoutParams來改變大小

7. Scroller

  1. Scroller 通常用來實現平滑的滾動
  2. 實現平滑滾動:
    1. 新建Scroller,並設定合適的插值器
    2. 在View的computeScroll方法中呼叫scroller,檢視當前應該滑動到的位置,並呼叫view的scrollTo或者scrollBy方法滑動
    3. 呼叫Scroller的start方法開始滑動

8. ScrollView時候滑動到底部

  1. 滑動時會呼叫onScrollChange方法,在該方法中監聽狀態
  2. 判斷childView.getMeasureHeight(總高度) == getScrollY(滑動的高度) + chilView.getHeight(可見高度)

9. View事件的分發

  1. 思想:委託子View處理,子View不能處理則自己處理
  2. 委託過程:activity -> window -> viewGroup -> view
  3. 處理事件方法的優先順序:onTouchListener > onTouchEvent > onClickListener
虛擬碼
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){
  consume = onTouchEvent(ev)
} else {
  consume = child.dispatchTouchEvent(ev);
}
return consume;
}

複製程式碼
  1. 完整的事件通常包括Down、Move、Up,當down事件被攔截下來以後,move和up就不再走intercept方法,而是直接被傳遞給當前view處理

10. 什麼時候執行ACTION_CANCEL

  1. 一個點選或者活動事件包含ACTION_DOWN,ACTION_MOVE,ACTION_UP等
  2. 當子View處理了ACTION_DOWN事件之後,後續的ACTION_MOVE,ACTION_UP都會直接交由這個子View處理
  3. 如果此時父View攔截了ACTION_MOVE,ACTION_UP,那麼子View會收到一個ACTION_CANCEL
  4. 場景舉例:點選一個控制元件,並滑動到控制元件外,此時次控制元件會收到一個ACTION_CALNCEL

11. 滑動衝突

外部攔截:重寫onInterceptTouchEvent方法
內部攔截:重寫dispatchTouchEvent方法,同時配合requestDisAllowInterceptTouchEvent方法

12. ListView怎麼優化(舉例)

  1. 複用convertView,對convetView進行判空,當convertView不為空時重複使用,為空則初始化,從而減少了很多不必要的View的建立、減少findViewById的次數,
  2. 採用ViewHolder模式快取item條目的引用
  3. 避免在getview方法中做耗時操作
  4. 避免使用半透明或者在活動中取消半透明
  5. 圖片非同步載入,待滑動停止後再載入
  6. 區域性重新整理

13. RecyclerView

RecyclerView 與 ListView 類似,都是通過快取view提高效能,但是RecyclerView有更高的可定製性。下面是使用時的一些設定,通過這些設定來達到對view樣式的自定義:其中1、2是必須設定的,3、4可選

  1. 想要控制其item們的排列方式,請使用佈局管理器LayoutManager
  2. 如果要建立一個介面卡,請使用RecyclerView.Adapter (Adapter通過範型的方式,幫助我們生成ViewHolder)
  3. 想要控制Item間的間隔,請使用RecyclerView.ItemDecoration
  4. 想要控制Item增刪的動畫,請使用RecyclerView.ItemAnimator
    擴充套件:RecyclerView可以很方便的進行區域性重新整理(notifyItemChanged())

14. RecyclerView繪製流程

RecyclerView的Measure和Layout是委託LayoutManager進行的

15. RecyclerView的區域性重新整理

  1. 呼叫notifyItemChange方法
  2. 如果想重新整理某個item的區域性,可以有兩種方法
    1. notifyItemRangeChanged方法
    2. 根據position獲取對應的ViewHolder,更新其View

16. RecyclerView的快取

  1. RecyclerView採用四級快取,ListView採用兩級快取
  2. 四級快取:
    1. mAttachedScrap:用於螢幕內的itemView快速複用,不需要create和bind
    2. mCacheViews:用於螢幕外的itemView快速複用,預設為兩個,通過postion查詢,不需要create和bind
    3. mViewCacheExtentsion:需要使用者定製,預設不實現
    4. mRecyclerPool:預設上限5個;不需要create,需要bind;多個RecyclerView可以共用一個pool
  3. 總結:快取方面和ListView最大區別是mCacheViews可以快取螢幕外的item,並且不需要重新bind

17. RecyclerView 自定義LayoutManager

  1. 重寫onLayoutChildren方法
    1. 呼叫detachAndScrapAttachedViews方法,快取View
    2. 計算並設定每個children的位置
    3. 呼叫fill方法
  2. 重寫fill方法進行佈局
  3. 重寫canScrollVertically和scrollVerticallyBy方法,支援滑動

18. SurfaceView與View的區別

  1. View主要適用於主動更新的情況下,而SurfaceView主要適用於被動更新,例如頻繁地重新整理;
  2. View在主執行緒中對畫面進行重新整理,而SurfaceView通常會通過一個子執行緒來進行頁面重新整理
  3. 而SurfaceView在底層實現機制中就已經實現了雙緩衝機制

19. view 的佈局

佈局全都繼承自ViewGroup

  1. FrameLayout(框架佈局) :沒有對child view的擺佈進行控制,這個佈局中所有的控制元件都會預設出現在檢視的左上角。
  2. LinearLayout(線性佈局):橫向或豎向排列內部View
  3. RelativeLayout(相對佈局):以view的相對位置進行佈局
  4. TableLayout(表格佈局):將子元素的位置分配到行或列中,一個TableLayout由許多的TableRow組成
  5. GridLayout:和TableLayout類似
  6. ConstraintLayout(約束佈局):和RelativeLayout類似,還可以通過GuideLine輔助佈局,適合圖形化操作推薦使用
  7. AbsoluteLayout(絕對佈局):已經被廢棄

20. 線性佈局 相對佈局 效率哪個高

相同層次下,因為相對佈局會呼叫兩次measure,所以線性高 當層次較多時,建議使用相對佈局

21. View 的invalidate\postInvalidate\requestLayout方法

  1. invalidate 會呼叫onDraw進行重繪,只能在主執行緒
  2. postInvalidate 可以在其他執行緒
  3. requestLayout會呼叫onLayout和onMeasure,不一定會呼叫onDraw

22. 更新UI方式

  1. Activity.runOnUiThread(Runnable)
  2. View.post(Runnable),View.postDelay(Runnable,long)
  3. Handler

23. postDelayed原理

  1. 不管是view的postDelayed方法,還是Handler的post方法,通過包裝後最終都會走Handler的sendMessageAtTime方法
  2. 隨後會通過MessageQueue的enqueueMessage方法將message加入佇列,加入時按時間排序,我們可以理解成Message是一個有序佇列,時間是其排序依據
  3. 當Looper從MessageQueue中呼叫next方法取出message時,如果還沒有到時間,就會阻塞等待
  4. 2中當有新的message加完成後,會檢查當前有沒有3中設定的阻塞,需不需要喚起,如果需要喚起則喚起

24. 當一個TextView的例項呼叫setText()方法後執行了什麼

  1. setText後會對text做一些處理,如設定AutoLink,Ellipsize等
  2. 在合適的位置呼叫TextChangeListener
  3. 呼叫requestLayout和invalidate方法

25. 自定義View執行invalidate()方法,為什麼有時候不會回撥onDraw()

  1. View 的draw方法會根據很多標識位來決定是否需要呼叫onDraw,包括是否繫結在當前視窗等

26. View 的生命週期

  1. 構造方法
  2. onFinishInflate:該方法當View及其子View從XML檔案中載入完成後會被呼叫。
  3. onVisibilityChanged
  4. onAttachedToWindow
  5. onMeasure
  6. onSizeChanged
  7. onLayout
  8. onDraw
  9. onWindowFocusChanged
  10. onWindowVisibilityChanged
  11. onDetachedFromWindow

27. ListView針對多種item的快取是如何實現的

  1. 維護一個快取view的陣列,陣列長度是 adapter的getViewItemTypeCount
  2. 通過getItemViewType獲得快取view 的陣列,取出快取的view

28. Canvas.save()跟Canvas.restore()的呼叫時機

save:用來儲存Canvas的狀態。save之後,可以呼叫Canvas的平移、放縮、旋轉、錯切、裁剪等操作。 restore:用來恢復Canvas之前儲存的狀態。防止save後對Canvas執行的操作對後續的繪製有影響。

WebView


0. WebView 優化

  1. 替換核心:不同的rom廠商對webview的優化不同,可能出現相容性和速度問題,可以替換成X5核心等來解決相容性問題,同時提高載入速度
  2. WebView初始化提前:WebView初始化是一個耗時的過程,我們可以預先將WebView初始化好,例如使用全域性webview
  3. 開啟快取,可以明顯提升第二次載入的速度
  4. 優化dns解析速度:使用和api一樣的域名,這樣dns不用二次解析,可以提高速度
  5. 預載入:將需要的檔案資源通過native方法提前載入好或者打包進apk,需要使用時直接使用
  6. 延遲載入:延遲載入部分資源,在介面要顯示的資料載入完成後再載入,如圖片資源,js等
  7. 對於webview記憶體洩漏:單獨開一個程式,Activity銷燬時手動回收資源

1. WebView與Native互動

  1. WebView中攔截網址:設定setWebViewClient,重寫shouldOverrideUrlLoading
  2. js與native互動
    1. mWebView.getSettings().setJavaScriptEnabled(true);
    2. mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
    3. 其他js呼叫Native方案:通過prompt, alert等,在webClient中重寫攔截相應的方法

效能相關


0. Android中ViewHolder,Handler等為什麼要被宣告成靜態內部類(static)

非靜態內部類會持有外部類的引用,在一些情況下很可能造成記憶體洩漏,所以一般宣告為靜態內部類,但並不是說一定要生命成靜態內部類。

1. Android中捕獲 App崩潰和重啟

  1. 實現Thread.UncaughtExceptionHandler()介面,在uncaughtException方法中完成對崩潰的上報和對App的重啟。
  2. 實現自定義Application,並在Application中註冊1中Handler例項。

2. LruCache實現原理

最近最少使用演算法:內部通過LinkedHashMap來實現

3. Parcelable和Serializable

  1. 都是序列化的方式
  2. Serializable只需實現Serializable介面即可
  3. Parcelable需要實現Parcelable介面,並重寫writeToParcel和describeContents方法,並且實現一個Creator
  4. Serializable雖然操作簡單,但是需要大量IO操作,效率慢;Parcelable自已實現封送和解封(marshalled &unmarshalled)操作不需要用反射,資料也存放在Native記憶體中,效率要快很多,在Android中更推薦使用Parcelable
  5. 由於不同版本Parcelable可能存在不同,在網路和磁碟儲存時,推薦使用Parcelable

4. 載入合適比例的Bitmap到記憶體中

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
        int reqWidth, int reqHeight) {

    // 首先只解析圖片資源的邊界
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    //計算縮放值
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // 用計算出來的縮放值解析圖片
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}


public static int calculateInSampleSize(
            BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

複製程式碼

5. 圖片的三級快取

  1. 首次載入 Android App 時,肯定要通過網路互動來獲取圖片,之後我們可以將圖片儲存至本地SD卡和記憶體中
  2. 之後執行 App 時,優先訪問記憶體中的圖片快取
  3. 若記憶體中沒有,則載入本地SD卡中的圖片

6. App保活

隨著Google對Android系統的更新,以及國內廠商堆Room的定製,一些保活的手段已經失效或者不再適用,這裡列舉一些保活手段,實際中常常是多個方式並用

  1. 聯絡Room廠商加入白名單(或者引導使用者手動加入白名單)
  2. 利用系統漏洞root進行提權,或者直接把本應用變成系統應用
  3. Service設定成START_STICKY:kill 後會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣
  4. 提升service優先順序:通過android:priority = "1000"這個屬性設定最高優先順序(可能已經失效)
  5. onDestroy方法裡重啟service(效果不好)
  6. 監聽廣播(除了監聽系統廣播外,還可以利用友盟等第三方sdk做到應用相互喚起)
  7. 啟動兩個程式service相互監聽,互相喚起(大部分room上失效)
  8. 在螢幕上保留1畫素
  9. 註冊系統同步服務
  10. 建立Native程式,雙程式互相監聽保活(5.0 以上失效)
  11. 利用JobSheduler保活

7. 如何判斷APP被強殺

這裡所說的強殺場景如這篇文章

  1. 在Application中定義一個static常量,賦值為-1
  2. 在歡迎介面改為0
  3. 在BaseActivity判斷該常量的值

8. 常見的記憶體洩漏(舉例)

  1. 不恰當的使用static變數(或者向static集合中新增資料卻忘了必要時移除資料)
  2. 忘記關閉各種連線,如IO流等
  3. 不恰當的內部類:因為內部類持有外部類的引用,當內部類存活時間較長時,導致外部類也不能正確的回收(常發生在使用Handler的時候)
  4. 不恰當的單例模式:例如錯誤的將某個Activity給單例持有,或者在不該使用單例的地方使用了單例
  5. 使用錯誤的Context:Application 和 Activity的context生命週期不一樣
  6. webview造成的記憶體洩漏

9. OOM異常是否可以被try...catch捕獲

  1. 在發生地點可以捕獲
  2. 但是OOM往往是由於記憶體洩漏造成的,洩漏的部分多數情況下不在try語句塊裡,所以catch後不久就會再次發生OOM
  3. 對待OOM的方案應該是找到記憶體洩漏的地方以及優化記憶體的佔用

10. 什麼是ANR,如何避免它

  1. ANR是Application Not Responding 的縮寫,當應用程式無響應時,會彈出ANR視窗,讓使用者選擇繼續等待還是關閉應用。
  2. 處理超過時間會造成ANR的地方:觸控操作等(5s);BroadCast(10s);Service(20s)
  3. 避免:不要在主執行緒中做耗時操作

11. 什麼情況會導致Force Close ?如何避免?能否捕獲導致其的異常?

  1. 出現執行時異常(如nullpointer/陣列越界等),而我們又沒有try catch捕獲,可能造成Force Close
  2. 避免:需要我們在程式設計時謹慎處理邏輯,提高程式碼健壯性。如對網路傳過來的未知資料先判空,再處理;此外還可以通過靜態程式碼檢查來幫助我們提高程式碼質量
  3. 此外,我們還可以在Application初始化時註冊UncaultExceptionHandler,來捕捉這些異常重啟我們的程式

12. DDMS和TraceView

  1. DDMS是一個程式執行檢視器,在裡面可以看見執行緒和堆疊等資訊
  2. TraceView是一個效能分析器
    擴充套件: Android Studio 3 中用Profiler代替了DDMS,可以監視分析CPU,網路,記憶體的實時情況,更加方便

13. LRUCache原理

  1. 通過LinkedHashMap實現的
  2. LinkedHashMap的特性:LinkedHashMap是一個雙向連結串列,當建構函式中accessOrder為true時:呼叫put()方法,會在集合頭部新增元素時,會呼叫trimToSize()判斷快取是否已滿,如果滿了就用LinkedHashMap的迭代器刪除隊尾元素,即近期最少訪問的元素。當呼叫get()方法訪問快取物件時,就會呼叫LinkedHashMap的get()方法獲得對應集合元素,同時會更新該元素到隊頭

14. 模組化的好處

  1. 不同模組間解偶,方便複用
  2. 單個模組支援獨立編譯,並行開發,提高開發和測試效率

15. 元件化

  1. 元件化是已複用為目的的
  2. 多個團隊可能公用一個元件

16. SparseArray

  1. SparseArray通過兩個陣列實現,key為int型,value為Object型
  2. 適用於資料量較小時
  3. 通過二分法插入和刪除資料

17. APP啟動耗時

  1. 啟動分為冷啟動(包含初始化Application)和熱啟動,冷啟動又可以分為第一次啟動(會有額外的初始化資料庫等操作)和不是第一次啟動
  2. 開發時本地可以用adb命令獲取啟動時常:adb shell am start -w packagename/activity
  3. 線上可以在各個關鍵地方埋點統計時常

18. Android啟動優化

  1. 啟動優化的目的是提高使用者感知的啟動速度
  2. 可以採用TraceView等分析耗時,找出優化方向
  3. 優化的思路:
    1. 利用提前展示出來的Window,設定Theme,快速展示出來一個介面,給使用者快速反饋的體驗(啟動後再改變回來)
    2. 非同步初始化一些第三方SDK
    3. 延遲初始化
    4. 針對性的優化演算法,減少耗時

19. 效能調優

  1. 效能調優包括很多方面:包括程式碼執行效率、網路、佈局、記憶體、資料庫、業務邏輯,打包速度等
  2. 需要針對具體問題具體分析,找到效能瓶頸:
  3. 善於藉助工具,例如使用traceview跟蹤,或者打點統計,profiler,leak canary等

網路優化

  1. 不用域名,用ip直連
  2. 請求合併與拆分
  3. 請求資料的縮小:刪除無用資料,開啟Gzip壓縮
  4. 精簡的資料格式:json/webp/根據裝置和網路環境的不同採用不同解析度圖片
  5. 資料快取
  6. 預載入
  7. 連線的複用:使用http2.0 (效率提升30%)
    擴充套件:JD Android客戶端網路效能調優之HTTP/2協議升級

佈局優化

  1. 合理的使用include/merge/viewstub等
  2. 減少佈局層次,減少不必要的view和節點
  3. 使用佈局快取,避免重複inflate
    其他:可以用hierarchy viewer等工具進行分析

程式碼優化

  1. 降低執行時間:如優化演算法和資料結構/利用多執行緒/快取(包括物件快取、執行緒池、IO 快取、網路快取等)/JNI/需求優化
  2. 同步改非同步:例如使用surfaceview
  3. 錯峰:提前或者延遲操作,比如啟動中第三方sdk的載入

APK瘦身

  1. 分析APK
    1. 使用Android Studio的分析器分析apk結構
    2. 使用lint分析無用程式碼和資源
    3. 或者使用第三方工具,如NimbleDroid/ClassShark
  2. 刪除無用資源
    1. 對無用資源和程式碼刪除
    2. 優化結構,對重複資源去重
    3. 對依賴去重,依賴多個功能類似的sdk時,只保留一個
    4. 去除不需要的依賴,如語言support包可能包含多種語言,配置只保留我們需要的資源等
    5. 開啟gradle的ProGuard/Code shrinking/minifyEnabled等,自動去除不需要的資源和程式碼】
  3. 壓縮已用資源
    1. 選取適當的圖片格式,如webp
    2. 對圖片進行壓縮
    3. 選取合適的依賴包,而不是直接依賴V7包等
    4. 使用微信的打包外掛AndResGuard對圖片進行壓縮
    5. 使用facebook的ReDex對dex檔案進行壓縮
  4. 通過網路按需載入

20. 有什麼提高編譯速度的方法

  1. 提高電腦配置
  2. 優化Android studio配置
    1. 加大記憶體;
    2. 使用守護程式;
    3. 開啟instant Run
    4. 使用離線gradle
    5. 使用新版的gradle
  3. 優化專案
    1. 使用第三方編譯如[阿里的Freeline](https://www.freelinebuild.com/docs/zh_cn/###
    2. debug模式下可以不開啟混淆等
    3. 模組化,多模組時使用aar包避免反覆編譯

21. 延遲載入的實現

  1. myHandler.postDelayed(mLoadingRunnable, DEALY_TIME);
 getWindow().getDecorView().post(new Runnable() {
    @Override
   public void run() {
        myHandler.post(mLoadingRunnable);
   }
  });
複製程式碼
  1. 監聽MessageQueue,當空閒時執行任務
 // 拿到主執行緒的MessageQueue
 Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() { 
      @Override public boolean queueIdle() { 
           // 在這裡去處理你想延時載入的東西 
           delayLoad(); 
           
           // 最後返回false,後續不用再監聽了。
           return false; 
           } 
  });

複製程式碼

22. ArrayMap

  1. ArrayMap 是Android中的一種用時間換空間的容器,用來替代HashMap
  2. 不適合大量資料或者有大量刪除
  3. 採用二分查詢
  4. 原理: 0. 使用兩個陣列進行儲存
    1. 陣列1儲存key的HashCode
    2. 陣列2儲存key和value;value位置為key位置左移1位再加1
    3. hash衝突的解決:尋找下一個空位,因為查詢時會比較hashcode和key,所以不會有問題

系統實現及原理


0. AsyncTask

  1. 為我們封裝了thread和handler,並在內部實現了執行緒池,是一個輕量級的執行緒方案
  2. 缺點:不同版本實現方式可能不一樣,例如執行緒池有可能是序列的(最新版本是並行的)
  3. 缺點:可定製化程度不高,例如我們不能很方便地cancel執行緒

1. AsyncTask是順序執行麼,for迴圈中執行200次new AsyncTask並execute,會有異常嗎

  1. 不同版本的AsyncTask 不一樣,最新版本中是序列的
  2. 不會,因為序列執行時用的是一個ArrayDeque來存放Runnable
  3. 擴充套件:AsyncTask內部有並行的ThreadPoolExecutor,儲存佇列用的是LinkedBlockingQueue,大小是128;如果我們並行執行,會丟擲執行時異常

2. Android 渲染機制

  1. CPU對檢視進行必要的計算:measure等
  2. 通過OpenGL 將CPU 處理過的資料交給GPU
  3. GPU進行柵格化並存入快取
  4. Android 系統每隔16.6ms發出一個垂直同步訊號,通知渲染

3. Android中Application和Activity的Context物件的區別

  1. 生命週期不一樣
  2. Application 不能showDialog
  3. Application startActivity時必須new一個Task
  4. Application layoutInflate直接使用預設主題,可能與當前主題不一樣

4. Zygote的啟動過程

  1. 註冊Zygote的socket監聽埠,應用接收啟動應用程式的訊息
  2. 呼叫preload()方法載入系統資源,包括預載入類,Framework資源等
  3. 呼叫startSystemServer()方法啟動SystemServer程式
  4. 呼叫runSelectLoop()方法進入監聽和接收訊息迴圈

5. Handler、Looper、Message、MessageQueue

它們的存在主要是為了執行緒間通訊,通訊的方式為:

  1. 線上程中呼叫Looper.prepare方法,生成一個looper與當前執行緒繫結(在生成looper的過程中,其構造方法在其內部建立了一個MessageQueue)
  2. 呼叫looper.loop方法,使當前執行緒迴圈讀取MessageQueue中的message,並呼叫 msg.target.dispatchMessage方法,交由target處理(這裡target是一個Handler例項)
  3. new 一個Handler,在建立Handler時,需要為他指定looper(若不指定則是當前執行緒的looper),Handler會取出looper中的MessageQueue也作為自己的MessageQueue。
  4. 呼叫Handler的sendMessage方法傳送Message資訊(這個方法將Message的target設定成當前handler,並把它加入到MessageQueue中)

6. 為什麼主執行緒Looper.loop不會ANR

  1. ANR是應用程式無響應,原因是有事件在主執行緒執行時間過長造成新的事件無法處理,或者當前事件執行時間太長
  2. Looper.loop會迴圈處理到來的Message,當MessageQueue為空是,執行緒處於阻塞狀態,釋放cpu資源

7. Messenger 和 AIDL

都是進城間通訊的方式,AIDL使用Binder通訊,Messenger通過AIDL實現

Messenger原理:Handler內部持有一個IMessenger例項,IMssenger時一個aidl的介面,Messenger初始化時這個IMssenger例項也傳給了Messenger

區別:1.Messenger是對AIDL的封裝,使用簡單;2.Messenger通過Handler處理傳過來的Message,只能執行在一個執行緒中,我們在AIDL中可以執行多個執行緒;3.Messenger客戶端調取服務方法的結果只能非同步回撥給客戶端,AIDL可以同步回撥

8. Binder

瞎jj寫點湊個數吧

  1. Binder是Android中的一種程式間通訊方式
  2. Binder通訊是通過驅動來實現的,原理是在核心空間中進行記憶體對映,以為只有一次記憶體拷貝,所以速度較快
  3. Binder會驗證許可權,鑑定UID/PID來驗證身份,保證了程式通訊的安全性
  4. 系統的Service會想ServiceManager註冊,使用時向ServiceManager獲取
  5. Service/Client同ServiceManager通訊的過程本身也是通過binder驅動實現的
  6. android中Service的bind通訊是通過ActivityManagerService實現的
  7. Binder中的代理模式:
    1. 客戶端代理物件和服務端實現統一介面
    2. 客戶端獲取服務端的引用,如果位於同一程式,那麼獲取的是服務端本身,如果是不同程式,獲取到的是服務端的代理
    3. 通過代理請向服務端請求
    4. 服務端接收請求並處理,返回給客戶端

9. AIDL

  1. 全稱:Android Interface Define Language(Android介面定義語言)
  2. 目的是為了進行程式間通訊
  3. 使用方式:定義aidl檔案,編譯器編譯時會幫我們生成對應的JAVA程式碼;通過呼叫生成的java程式碼,來進行程式間通訊
  4. 原理:通過Binder方式進行通訊

10. Window、WindowManager以及Activity

  1. Window 0. Window有三類:系統Window、應用Window、子Window
    1. Window是介面,具體實現類是PhoneWindow
    2. Window 是一個抽象概念,我們並不能直接操作window
    3. Activity在建立的時候attach方法中會建立Window並使之與Activity關聯
    4. Window中會建立Decorview,並通過ViewRootImpl與View互動
  2. WindowManager 0. 在Activity啟動時,handleResumeActivity方法中啟動activity的時候,會將主視窗加入到WindowManager中
    1. 我們並不能直接操作window,而是通過WindowManager
    2. WindowManagerImpl是其實現類,他將view增刪改的操作交給 WindowManagerGlobal處理
    3. WindowManagerGlobal 中會呼叫 ViewRootImpl的方法
    4. ViewRootImpl通過IWindowSession與WindowManagerService互動

11. 理解Window和WindowManager

  1. Window用於顯示View和接收各種事件,Window有三種型別:應用Window(每個Activity對應一個Window)、子Window(不能單獨存在,附屬於特定Window)、系統window(Toast和狀態列)
  2. Window分層級,應用Window在1-99、子Window在1000-1999、系統Window在2000-2999.WindowManager提供了增刪改View三個功能。
  3. Window是個抽象概念:每一個Window對應著一個View和ViewRootImpl,Window通過ViewRootImpl來和View建立聯絡,View是Window存在的實體,只能通過WindowManager來訪問Window。
  4. WindowManager的實現是WindowManagerImpl其再委託給WindowManagerGlobal來對Window進行操作,其中有四個List分別儲存對應的View、ViewRootImpl、WindowManger.LayoutParams和正在被刪除的View
  5. Window的實體是存在於遠端的WindowMangerService中,所以增刪改Window在本端是修改上面的幾個List然後通過ViewRootImpl重繪View,通過WindowSession(每個應用一個)在遠端修改Window。
  6. Activity建立Window:Activity會在attach()中建立Window並設定其回撥(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy類建立PhoneWindow實現的。然後通過Activity#setContentView()呼叫PhoneWindow的setContentView。

12. Window新增的過程

  1. ActivityThread做為APP的入口,會執行 handleLaunchActivity -> performLaunchActivity
  2. performLaunchActivity中通過ClassLoader建立Activity物件 -> 呼叫Activity.attach方法 -> callActivityOnCreate
  3. 在Activity.attach方法中會建立一個Window例項PhoneWindow,並將activity作為callback傳遞給window
  4. 在Activity的onCreate方法中,會呼叫setContentView,setContentView會呼叫PhoneWindow 的 setContentView
  5. PhoneWindow 的 setContentView會建立DecorView,並把我們自己設定的ContentView和DecorView繫結
  6. performLaunchActivity至此走完,之後的performResumeActivity會呼叫handleResumeActivity方法
  7. 在handleResumeActivity中,會呼叫Activity的getWindowManager()獲取一個WindowManager,接著呼叫WindowManager的addView方法
  8. addView實際執行的是WindowManagerGlobal的addView(),這裡會建立一個ViewRootImpl,並呼叫ViewRootImpl的setView方法
  9. 在setView這個方法內部,會通過跨程式的方式向WMS(WindowManagerService)發起一個呼叫,從而將DecorView最終新增到Window上

13. APK的安裝流程

Android 面試中級篇

  1. 解壓檔案到data/app目錄下
  2. 資源管理器載入資原始檔
  3. 解析解析AndroidManifest檔案,並在/data/data/目錄下建立對應的應用資料目錄。
  4. 然後對dex檔案進行優化,並儲存在dalvik-cache目錄下。
  5. 將AndroidManifest檔案解析出的四大元件資訊註冊到PackageManagerService中。
  6. 安裝完成後,傳送廣播。

14. 雙親委託模式類載入的過程

  1. 呼叫當前類載入器的loadClass載入類
  2. loadClass中先呼叫findLoadedClass檢視類是否已載入,已載入->over
  3. 還沒有載入,呼叫父類載入器的loadClass,父類載入器載入完成 -> over
  4. 如果父類載入器沒有載入,呼叫本類載入器的findClass,並在findClass中呼叫·defineClass方法載入

15. Android中ClassLoader

  1. Android中載入的是Dex檔案
  2. ClassLoader 是個抽象類,其具體實現的子類有 BaseDexClassLoader 和SecureClassLoader,(SecureClassLoader 的子類是 URLClassLoader ,其只能用來載入 jar 檔案,這在 Android 的 Dalvik/ART 上沒法使用的)
  3. BaseDexClassLoader 的子類是 PathClassLoader 和 DexClassLoader 。
  4. PathClassLoader 在應用啟動時建立,從 data/app/… 安裝目錄下載入 apk 檔案。
  5. DexClassLoader 則沒有此限制,可以從 SD 卡或網路載入包含 class.dex 的 .jar 和 .apk 檔案,這也是外掛化和熱修復的基礎

專案構建


0. 點選AndroidStudio的build按鈕後發生了什麼

build過程即執行gradle task 打包生成apk的過程:

  1. 通過appt工具,將資原始檔生成R.java檔案;將aild檔案轉換成對應的java檔案
  2. 編譯java檔案,生成.class檔案
  3. 將.class檔案轉換成Android虛擬機器支援的.dex檔案
  4. 通過apkbuilder將dex檔案和編譯後的資原始檔生成apk檔案
  5. 對apk進行簽名和對齊

1. Android Debug和Release狀態的不同

除錯模式允許我們為優化程式碼而新增許多額外的功能,這些功能在Release時都應該去掉;Release包可以為了安全等做一些額外的優化,這些優化可能比較耗時,在Debug時是不需要的

  1. log日誌只在debug時輸入,release時應該關掉(為了安全)
  2. 簽名/混淆/壓縮等在debug編譯時可以加入,減少打包時間
  3. 可以在debug包中加入一些額外的功能輔助我們開發,如直接列印網路請求的控制元件,記憶體洩漏檢測工具LeakCanary等
  4. 在打Release包時,除了混淆等操作,往往還需要加固操作,保證APP的安全

2. Dalvik和Art

  1. Dalvik 是 Android 中使用的虛擬機器,執行dex位元組碼
  2. Dalvik 與 JVM 相比
    1. JVM執行class位元組碼檔案,Dalvik執行dex位元組碼檔案,dex檔案做了優化,提及更小
    2. Dalvik是基於暫存器的,VM基於棧
  3. Art是Dalvik虛擬機器的升級版,Dalvik是解釋型的,Art是翻譯型的
    1. Dalvik虛擬機器執行的是dex位元組碼,ART虛擬機器執行的是本地機器碼
    2. 相對於Dalvik,Art安裝佔用記憶體更大,安裝時間更長,但是執行速度會有提升

3. 解決方法數超過65535的方法

  1. 程式碼混淆(原理是減小方法數)
  2. sdk裁減(原理是減小方法數)
  3. multi-dex(原理是打包成多個dex)

4. multi-dex 問題

引入multi-dex後,在5.0以下手機上,第一次安裝後啟動速度可能變慢甚至anr,需要進行優化:如單獨開一個執行緒;修改keep檔案等

5. App簽名

  1. 原理:先計算出hash值,再對hash值進行非對稱加密

  2. V1版本簽名生成的APK中與簽名有關的檔案:

    1. MANIFEST.MF: jar 包的檔案清單,在 apk 中我們用來記錄所有非目錄檔案的 資料指紋
    2. CERT.SF:根據MANIFEST.MF生成的檔案
    3. CERT.RSA:這裡會把之前生成的CERT.SF檔案,用私鑰計算出簽名, 然後將簽名以及包含公鑰資訊的數字證照一同寫入CERT.RSA 中儲存
  3. V1版本存在安全漏洞,google推出了v2版

6. 常用的adb 和 adb shell命令

檢視當前連線的裝置:adb devices
結束adb連線: adb kill-server
安裝apk: adb install test.apk
從手機獲取檔案和推送檔案到手機:adb push <本地檔案> <遠端路徑> ; adb pull <遠端路徑> <本地路徑>
獲取log資訊:adb logcat > log.txt

啟動Activity: adb shell am start -n 包名/包名+類名
顯示系統Activity棧資訊:adb shell dumpsys activity
傳送廣播:adb shell am broadcast -a "android.intent.action.AdupsFota.WriteCommandReceiver"
檢視程式資訊:adb shell ps <package_name|PID>
殺掉某個程式:adb shell kill pidNumber
檢視記憶體佔用:adb shell dumpsys meminfo <package_name|PID>

7. jar和aar的區別

Jar包裡面只有程式碼,aar裡面不光有程式碼還包括程式碼還包括資原始檔,比如 drawable 檔案,xml 資原始檔。對於一些不常變動的 Android Library,我們可以直接引用 aar,加快編譯速度

8. 不同的CPU架構對APP的影響

  1. cpu的架構有armeabi、armeabi-v7a、x86等
  2. 針對不同的CPU,使用不同的so包,可以使效能最大化
  3. 如果a.so提供了armeabi、armeabi-v7a、x86格式,那麼b.so也要提供這幾個格式,否則可能崩潰
  4. 當沒有對應cpu的so時,會選擇其他so,但執行速度會變慢:當一個應用安裝在裝置上,只有該裝置支援的CPU架構對應的.so檔案會被安裝。在x86裝置上,libs/x86目錄中如果存在.so檔案的話,會被安裝,如果不存在,則會選擇armeabi-v7a中的.so檔案,如果也不存在,則選擇armeabi目錄中的.so檔案(因為x86裝置也支援armeabi-v7a和armeabi)

9. compileSdkVersion,minSdkVersion,targetSdkVersion 都是啥

  1. compileSdkVersion :編譯所依賴的版本,它可以讓我們在寫程式碼時呼叫最新的api,告知我們過時的api
  2. minSdkVersion:最小的可安裝此App的版本,意味著我們不用做低於此版本的相容
  3. targetSdkVersion: 目標版本,可以讓我們雖然執行在最新的手機上,但是行為和target版本一致,比如:如果targetSdkVersion小於Android 6.0,那麼即使我們的app執行在6.0系統上,也不需要執行時許可權

10. 低版本SDK實現高版本api

  1. 使用@TargetApi 來標明api版本,這樣編譯器就不會報錯了
  2. 在程式碼邏輯中判斷版本,在低版本上呼叫替代api或自己實現的演算法。

部分功能的實現


0. 怎樣退出終止自己的APP

  1. 記錄啟動的activity
  2. 需要退出時呼叫存活activity的finish方法,並呼叫System.exit(0)方法

1. Android中啟動執行緒的幾種方式

  1. java中可以用實現Runnable介面、實現Callable介面、繼承Thread類三種方式
  2. Android中還可以用AsyncTaskHandlerThreadIntendService

2. 長連結+心跳包

  1. 長連線:App 與伺服器建立一個生命週期很長的連線,伺服器通過push向App推送訊息
  2. 心跳包:App 每隔一段時間就會向伺服器查詢是否有新的訊息
  3. 長連線可能因為各種原因被打斷,心跳包接收訊息可能不及時,所以我們可以採取長連線+心跳包的方式:通過Socket建立一個長連線,並通過心跳包檢測這個長連線是否存活,長連線中斷的話則重新建立

3. Android 中XML的解析

Androi中主要有DOM,SAX,PULL三種方式
DOM將檔案都載入到記憶體中,比較消耗記憶體;SAX和PULL節省記憶體,PULL使用比SAX更簡單

4. 系統上安裝了多種瀏覽器,能否指定某瀏覽器訪問指定頁面?請說明原由。

  1. 指定瀏覽器:intent.setClassName(“com.android.browser”,”com.android.browser.BrowserActivity”);
  2. 指定網址: Uri uriBrowsers = Uri.parse(“http://www.sina.com.cn”); intent.setData(uriBrowsers);

5. java中如何引用本地語言

可以用JNI(java native interface java 本地介面)介面

6. JNI的使用方式

  1. 下載NDK,配置環境變數,配置gradle檔案
  2. JAVA中宣告native 方法如private native String printJNI(String inputStr);
  3. 生成或寫對應的標頭檔案
  4. 編寫對應檔案實現程式碼
  5. 編譯成so檔案
  6. 使用
  7. 擴充套件:native呼叫java程式碼
    1. 獲取你需要訪問的Java物件的類
jclass cls = (*env)->GetObjectClass(env, obj);       //使用GetObjectClass方法獲取obj對應的jclass。   
class cls = (*env)->FindClass(“android/util/log”) //直接搜尋類名,需要是static修飾的類。  
複製程式碼
  1. 獲取MethodID:
methodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); //GetStaticMethodID(…),獲取靜態方法的ID使用GetMethdoID方法獲取你要使用的方法的MethdoID  
複製程式碼
  1. 呼叫:
    (*env)->CallVoidMethod(env, obj, mid, depth);// CallStaticIntMethod(….) , 呼叫靜態方法  
複製程式碼

7. NDK是什麼

NDK 是Native Development Kit 的縮寫,是一些列工具的集合,幫助開發者迅速的開發C/C++的動態庫

8. 什麼是JVM

  1. JVM是JAVA虛擬機器,保證了java語言的跨平臺性,是編譯後的 Java 程式(.class檔案)和硬體系統之間的介面
  2. JVM = 類載入器 classloader + 執行引擎 execution engine + 執行時資料區域 runtime data area

9. 視訊加密

視訊加密根據場景的不同有很多種方式

  1. 如僅對地址加密,可以起到防盜鏈的目的,可以與其他方法一起使用
  2. 對整個檔案加密,加解密時間長,不實用
  3. 對檔案的頭中尾加密,播放器可以直接跳過,破解簡單,不實用
  4. 對視訊流加密(基於蘋果HLS協議的加密 基於RTPE協議)
  5. 關鍵幀加密

10. 繪製印章

  1. 建立一個bitmap,拿到canvas
  2. 在canvas上繪製圓,繪製五角星,繪製文字,返回bitmap

11. 文字陰影和描邊的實現

  1. 陰影:shadow屬性
  2. 描邊:兩個TextView疊加;或者重寫onDraw方法

12. Android生成裝置唯一識別符號

選取 DeviceId,AndroidId,Serial Number,Mac,藍芽地址等中的一個或者幾個作為種子,生成UUID。

13. 實現客戶端的自動登入

  1. 第一次登入時儲存兩個token,一個長效一個短效
  2. 短效token用於每次網路請求的使用者標識
  3. 長效token用於當短效token失效時自動登入,重新獲取token

14. Android如何在不壓縮的情況下載入高清大圖

使用BitmapRegionDecoder

15. SSL證照的驗證

  1. 在使用Https時,我們需要對SSL證照做驗證以確保有效
  2. 證照需要驗證證照有效性,時效,域名等
  3. Android中WebView可以重寫onReceivedSslError方法來處理ssl證照不對時的情況
  4. OkHttp設定證照驗證
    1. 驗證可以是雙向的,也可以是單向的
    2. 單向:將伺服器對應的Server.cer檔案打包進Apk中,通過cer檔案生成SSLSocketFactory,並將其設定給okHttpClient
    3. 雙向:用Server.cer和Client.key生成SSLSocketFactory,並將其設定給okHttpClient

16. 計算一張100px*100px的圖片在記憶體中會佔用多大記憶體

  1. 記憶體大小 = 100*100*畫素點大小
  2. 畫素點大小和編碼方式有關:ARGB_8888佔8+8+8+8=32bit;ARGB_4444佔4+4+4+4 = 16bit;

17. 如何實現動態代理

  1. 建立一個實現InvocationHandler介面的類,它必須實現invoke方法
  2. 呼叫Proxy的靜態方法newProxyInstance,建立一個代理類

18. Android中有哪些方法實現定時和延時任務?它們的適用場景是什麼?

  1. 倒數計時類:用CountDownTimer
  2. 延遲類: 1. CountDownTimer,可巧妙的將countDownInterval設成和millisInFuture一樣,這樣就只會呼叫一次onTick和一次onFinish 2. handler.sendMessageDelayed,可參考CountDownTimer的內部實現,簡化一下,個人比較推薦這個 3. TimerTask,程式碼寫起來比較亂 4. Thread.sleep,感覺這種不太好
  3. 定時類: 1. 參照延遲類的,自己計算好要延遲多少時間 2. handler.sendMessageAtTime 3. AlarmManager,適用於定時比較長遠的時間,例如鬧鈴

概念


0. Json有什麼優勢

有比較才會有優勢,我們通常將Json與Xml進行比較,Json更加輕量。我覺得在某些程度上講,這是一個仁者見仁智者見智的問題。例如有些人認為Json相比Xml更易讀,有些人責認為不然,這裡大致列舉幾條,僅供參考

  1. 結構簡單,可讀性更強,讀寫更加容易
  2. 格式是壓縮的,佔用頻寬小
  3. 支援多種語言
  4. 因為JSON格式能夠直接為伺服器端程式碼使用,大大簡化了伺服器端和客戶端的程式碼開發量

1. MVC

MVC是model,view,controller的縮寫

  1. 模型(model)物件:是應用程式的主體部分,所有的業務邏輯都應該寫在該層;
  2. 檢視(view)物件(對應Android中的佈局xml檔案):是應用程式中負責生成使用者介面的部分。也是在整個mvc架構中使用者唯一可以看到的一層,接收使用者的輸入,顯示處理結果; 
  3. 控制器(control)物件(對應Android中的Activity):是根據使用者的輸入,控制使用者介面資料顯示及更新model物件狀態的部分,控制器更重要的一種導航功能,響應使用者出發的相關事件,交給m層處理。 擴充套件:和MVP最大的區別是View和Model可以直接互動

2. 什麼是控制反轉(IOC Inversion of Control)

  1. 在Java開發中,IoC意 味著將你設計好的類交給系統去控制,而不是在你的類內部控制
  2. 是框架的重要特徵
  3. Android中Activity 的生命週期都是框架控制的,是一種控制反轉

3. Android中的IOC(控制反轉)框架

  1. 控制反轉:將物件的建立交給框架去做
  2. 常用的框架:ButterNife/Android Annotation
  3. 2中兩個框架都是通過java的註釋框架實現的,並且都是作用在編譯期

4. 請解釋下Android程式執行時許可權與檔案系統許可權的區別

  1. 執行時許可權是APP啟動後由Android虛擬機器(如Dalvik)控制的
  2. 檔案系統許可權是Linux核心授權

5. AOP 面向切面程式設計

  1. 面向切面程式設計是通過預編譯方式和執行期動態代理實現程式功能的統一維護的一種技術
  2. 例如很多功能需要先登陸,登陸在這裡就是一個切面。

6. MVP 架構中 Presenter 定義為介面有什麼好處

  1. 在goole官方的demo中,通過一個Contract把將View和Presenter管理起來,強化其一一對應的關係,便於操作
  2. [但是也有人認為不應該將Presenter定義為介面]http://www.infoq.com/cn/articles/do-not-give-the-prensenter-in-mvp-interface),因為這並不會方便測試,還會增加複雜性

其他


0. Android中的幾種動畫

  1. 普通動畫(檢視動畫、補間動畫)
  2. 屬性動畫
  3. 幀動畫
  4. 物理動畫(Android 8.0)

1. HttpClient和HttpURLConnection的區別

HttpClient

  1. Apache公司提供的庫
  2. 擁有豐富的API,但也因為這個原因,在不破壞相容性的前提下,其龐大的API也使人難以改進
  3. Android 6.0中拋棄了Http Client,替換成OkHttp

HttpURLConnection

  1. Sun公司提供的庫
  2. 功能比較簡單,可擴充性強
  3. 直接支援GZIP壓縮,並且在Android 4.0 以上支援cache快取,提高了網路效率

2. Intent

  1. Intent是Android中的信使,可以啟動Activity,Service等
  2. Intent可以設定的幾項值:Action, Category, Data/Type,Component
  3. 當設定Component時,是顯式呼叫,其餘是隱式呼叫

3. IntentFilter

  1. IntentFilter 在AndroidMainifest中註冊,用來幫助系統選出使用者定義的隱式Intent對應的Activity /Service等
  2. Android是通過Intent的action、data、category這三個屬性來進行匹配判斷的
    1. action:隱式啟動需要給Intent設定Action,如果沒有設定這條匹配則自動通過;必須給IntentFilter設定一個action
    2. data:如果Intent沒有提供type,系統將從data中得到資料型別。同action類似,只要Intent的data只要與Intent Filter中的任一個data宣告完全相同,data方面就完全匹配成功。
    3. category:Intent的Category可以有多個,每一個都需要和IntentFilter匹配才能算匹配上,不設定Intent的category時是預設的(DEFAULT)
    4. 總結:只有一個Intent的action、data、category匹配上IntenFilter中的一組資料時,才算匹配成功。

4. Intent/Bundle支援傳送哪種型別的資料

  1. 基本型別及其陣列
  2. 實現了Serializable或者Parcelable的型別及其陣列

5. Manifest.xml檔案中主要包括哪些資訊

  1. manifest:根節點,描述了包名,版本號等。
  2. application:包含package中application級別元件宣告的根節點。
  3. activity:Activity是用來與使用者互動的主要工具。
  4. receiver:IntentReceiver能使的application獲得資料的改變或者發生的操作,即使它當前不在執行。
  5. service:Service是能在後臺執行任意時間的元件。
  6. provider:ContentProvider是用來管理持久化資料併發布給其他應用程式使用的元件。
  7. uses-permission:請求你的package正常運作所需賦予的安全許可。
  8. permission: 宣告瞭安全許可來限制哪些程式能你package中的元件和功能。
  9. uses-feature:使用到的硬體資訊,如nfc
  10. upports-screens:支援的螢幕型別
  11. meta-data:data資料
  12. instrumentation:宣告瞭用來測試此package或其他package指令元件的程式碼。

6. dp, dip, dpi, px, sp是什麼意思

  1. dp = dip(device independent pixels),是裝置獨立畫素
  2. sp:scaled pixels(放大畫素),主要用於字型顯示。
  3. px(pixel):畫素
  4. dpi(dot per inch)

7. dp與px的換算公式

  1. px = dp*畫素密度/160
 public static int dp2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

複製程式碼

8. layout-sw400dp, layout-w400dp分別代表什麼意思

  1. layout-w400dp:當螢幕相對寬度大於400dp時,來這裡取layout(與橫豎屏有關)
  2. layout-sw400dp:當螢幕絕對寬度大於400dp時,來這裡取layout(與橫豎屏無關)

9. Android 樣式和主題

  1. 樣式(Styles):可以理解成是針對View或者視窗(Window)設定外觀或者格式的一個屬性集合
  2. 主題(Themes):主題相比單個檢視而言,是應用到整個 Activity 或者 application 的樣式
  3. 區別:
    1. Theme作用域是Activity或者Application,Stytle針對View或者視窗(Window)
    2. 某些主題樣式不可以在View中使用,例如"@android:style/Theme.NoTitleBar" 等 擴充套件: 屬性(Attributes):你也可以將單個屬性應用到 Android 樣式上,通常會在自定義View 的時候,自定義屬性。

10. Android P的新特性(2018/5/5)

  1. Goole 下個Android版本的預覽已經放出,代號p
  2. 支援wifi室內定位
  3. 適配劉海屏
  4. 通知欄改進:可以顯示對話,附加照片和表情等
  5. 多攝像頭API
  6. 神經網路API 1.1

11. 熱修復原理

熱修復的原理是讓我們的新類替換掉原來類的載入,從而達到修復的目的,以下是一種思路:

  1. java中通過PathClassLoader和DexClassLoader來載入類,類載入的方式是雙親委派模式
  2. PathClassLoader和DexClassLoader都繼承自BaseDexClassLoader
  3. BaseDexClassLoader中維護了一個dex的陣列
  4. 我們可以通過DexClassLoader載入類,然後通過反射的機制將載入進來的陣列新增到path陣列的前面
  5. 載入的時候找到我們需要的class後,就不再繼續向後找了,所以可以達到修復的目的

12. Android中的程式間通訊(IPC)

  1. Bundle : 只支援四大元件
  2. 檔案共享:不適合併發
  3. Messenger:封裝了AIDL
  4. AIDL:通過binder實現
  5. ContentProvider:共享資料
  6. Socket:適用於網路等

13. 解決Android7.0 更新安裝包時不能自動安裝問題

  1. Android 7.0中私有目錄訪問會被限制,導致不能自動安裝
  2. 可以使用FileProvider來解決

如何開啟多程式?應用是否可以開啟N個程式?

  1. 通過在AndroidManifest中給Activity設定process屬性開啟新的程式
  2. 可以開啟N個程式,例如給webview單獨開啟一個程式,但要處理多程式間通訊和多次初始化Handler問題

Service先start再bind如何關閉service

先unbind,再stop

為什麼bindService可以跟Activity生命週期聯動

  1. 在Activity退出時呼叫unbind方法,service會銷燬
  2. 如果不呼叫unbind方法,service也會銷燬,但是會丟擲leaked serviceConnection 異常 (參考2)

子執行緒中如何使用Handler

  1. 使用HandlerThread,新建Handler時通過呼叫HandlerThread 的 getLooper方法拿到looper
  2. 原理:HandlerThread在run時會為我們生成一個looper,getLooper方法會阻塞等待直到 looper!=null 才返回。

如何進行單元測試

  1. Junit:不需要依賴android環境,適合於邏輯測試
  2. Instrumentation:依賴android環境,可以啟動Activity,模擬記憶體回收,獲取元件等,模擬點選等。需要在AndroidManifest中進行配置,適合於更復雜的測試

TabLayout如何設定指示器的寬度

通過反射拿到對應的指示器,設定LayoutParams

Android中如何檢視一個物件的回收情況

  1. 外部:通過adb shell 命令匯出記憶體,藉助工具分析
  2. 內部:通過將物件加入WeakReference,配合RefernceQueue觀察物件是否被回收,被回收的物件會被加入到RefernceQueue中

回形列印二維陣列

思路:遞迴實現,分別列印每一圈

APK內容

Android 面試中級篇

class檔案如何轉化成dex

使用build tools 下的dx工具

class 和dex檔案對比:
1. 都是二進位制檔案
2. class檔案存在容與,dex檔案將整個工程的類資訊整合到了一起,去掉了冗餘
複製程式碼

硬體加速

  1. 隨便寫點湊個數吧=-=
  2. 硬體加速的四個級別:Application/Activity/Window/View

為什麼選擇Binder作為通訊方式

  1. binder效率更高:socket是一個通用介面,效率低;管道和佇列記憶體拷貝兩次,效率低;共享記憶體控制複雜
  2. binder更加安全:binder可以建立私有通道,通過uid/pid驗證身份

參考資料

LearningNotes

40 個 Android 面試題

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

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

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

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

相關文章