面試主題我這里打算分為三部分去發布,大家可以關注我一下,以免錯過,
Android基礎知識
1.Looper總結
- Looper通過prepare方法進行實體化,先從他的成員變數sThreadLocal中拿取,沒有的話就new 一個Looper,然后放到sThreadLocal中快取,每個執行緒只能創建一個Looper實體
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
- Looper通過loop方法開啟回圈佇列,里面開啟了死回圈,沒有msg時候會阻塞
- 在ActivityThread的main方法中也就是Activity啟動的時候,已經呼叫了Looper.prepareMainLopper()方法
2.ThreadLocal在Looper中的使用
為了解決多個執行緒訪問同一個資料問題,同步鎖的思路是執行緒不能同時訪問一片記憶體區域.而ThreadLocal的思路是,干脆給每個執行緒Copy一份一摸一樣的物件,執行緒之間各自玩自己的,互相不影響對方 常見ThreadLocal應用場景:確保在每一個執行緒中只有一個Looper的實體物件
- ThreadLocal的set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
- ThreadLocal的get方法
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
簡而言之:先拿到當前執行緒,再從當前執行緒中拿到ThreadLocalMap,通過ThreadLocalMap來存盤資料,(ThreadLocalMap是Thread的成員變數)
- handler postDelay這個延遲是怎么實作的:handler.postDelay直接將訊息插入MessageQueue,以MessageQueue的時間順序排列和喚醒的方式結合實作的,
3.Service 和 IntentService
Activity對事件回應不超過5秒,BroadcastReceiver執行不超過10秒,Service耗時操作為20秒,否則系統會報ANR
- 使用startService()方法啟用服務后,呼叫者與服務之間沒有關連,呼叫者直接退出而沒有呼叫stopService的話,Service會一直在后臺運行
- 使用bindService()方法啟用服務,呼叫者與服務系結在一起了,呼叫者一旦退出,服務也就自動終止
- IntentService是Service的子類,會創建子執行緒來處理所有的Intent請求,其onHandleIntent()方法實作的代碼,無需處理多執行緒問題
4.FragmentPageAdapter和FragmentPageStateAdapter的區別
- FragmentPageAdapter在每次切換頁面的的時候,沒有完全銷毀Fragment,適用于固定的,少量的Fragment情況,默認notifyDataSetChanged()重繪是無效的
- FragmentPageStateAdapter在每次切換頁面的時候,是將Fragment進行回收,適合頁面較多的Fragment使用,這樣就不會消耗更多的記憶體
5.Sqlite資料庫,什么是事務
事務是由一個或多個sql陳述句組成的一個整體,如果所有陳述句執行成功那么修改將會全部生效,如果一條sql陳述句將銷量+1,下一條再+1,倘若第二條失敗,那么銷量將撤銷第一條sql陳述句的+1操作,只有在該事務中所有的陳述句都執行成功才會將修改加入資料庫中
sqlite資料庫相關操作,主要包括創建和增刪改查,事務
6.怎么做Sqlite資料庫升級
- 直接洗掉老資料庫,但會造成資料丟失,一般不采用,
- 對資料庫進行升級,
7.invalidate與requestLayout區別
- view呼叫invalidate將導致當前view的重繪,viewGroup呼叫invalidate會使viewGroup的子view呼叫draw
- requestLayout方法只會導致當前view的measure和layout,而draw不一定被執行,只有當view的位置發生改變才會執行draw方法
8.View和ViewGroup區別
- ViewGroup的onInterceptTouchEvent默認回傳false,即不攔截事件,View沒有攔截事件方法,View默認時消耗事件的
- ViewGroup默認不會呼叫onDraw方法,View默認會呼叫onDraw方法,可以通過setWillNotDraw(boolean willNotDraw)來指定是否呼叫onDraw方法
/**
* If this view doesn't do any drawing on its own, set this flag to
* allow further optimizations. By default, this flag is not set on
* View, but could be set on some View subclasses such as ViewGroup.
*
* Typically, if you override {@link #onDraw(android.graphics.Canvas)}
* you should clear this flag.
*
* @param willNotDraw whether or not this View draw on its own
*/
public void setWillNotDraw(boolean willNotDraw) {
setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
}
9.android版本新特性
- 5.0 引入Material Design主題
- 6.0 運行時權限
- 7.0 檔案讀寫權限適配FileProvider 移除了對 Apache HTTP 客戶端的支持,建議使用 HttpURLConnection 代替,繼續使用 Apache HTTP API,必須先在 build.gradle 檔案中配置: android { useLibrary 'org.apache.http.legacy' } 復制代碼
- 8.0 為所有通知分配渠道 app內更新下載好的apk檔案,需要用戶開啟未知應用安裝權限
- 9.0 使用安全的網路訪問,如果使用http請求會報錯,Android 9.0 網路適配
- 10.0 存盤空間磁區存盤,沙盒模式
10.Android中一張圖片占據的記憶體大小是如何計算
- 圖片來源是 res 內的不同資源目錄時,系統會根據設備當前的 dpi 值以及資源目錄所對應的 dpi 值,做一次解析度轉換,規則如下:新解析度 = 原圖橫向解析度 * (設備的 dpi / 目錄對應的 dpi ) * 原圖縱向解析度 * (設備的 dpi / 目錄對應的 dpi )
- 其他圖片的來源,如磁盤,檔案,流等,均按照原圖的解析度來進行計算圖片的記憶體大小
- 一張圖片占用的記憶體大小的計算公式:解析度 * 像素點大小;但解析度不一定是原圖的解析度,需要結合一些場景來討論,像素點大小就幾種情況:ARGB_8888(4B)、RGB_565(2B) 等等
11.APP啟動速度優化
- 用adb命令可以檢測啟動時間,示例如下:
adb shell am start -W [packageName]/[.MainActivity]
./adb shell am start -W "com.hchstudio.dict"/".MainActivity"
WaitTime為我們所關注的啟動時間
- app的啟動流程,主要需要減少Application和啟動界面的onCreate方法
- 的app首頁主題樣式加上android:windowBackground,放一下app的背景圖片,這樣即使app啟動慢,也會首先加載背景,這樣就會給用戶造成一種假象,認為是app已經啟動
<!--AppTheme.Launcher為啟動界面的主題樣式-->
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@color/colorLauncher</item>
</style>
12.記憶體抖動
記憶體抖動是由于短時間內有大量物件進出新生區導致的,它伴隨著頻繁的GC,gc會大量占用ui執行緒和cpu資源,會導致app整體卡頓,
避免發生記憶體抖動的幾點建議:
- 盡量避免在回圈體內創建物件,應該把物件創建移到回圈體外,
- 注意自定義View的onDraw()方法會被頻繁呼叫,所以在這里面不應該頻繁的創建物件,
- 當需要大量使用Bitmap的時候,試著把它們快取在陣列或容器中實作復用,
- 對于能夠復用的物件,同理可以使用物件池將它們快取起來,
13.Android中ClassLoader的種類&特點:
- BootClassLoader(Java的BootStrap ClassLoader):
用于加載Android Framework層class檔案,
- PathClassLoader(Java的App ClassLoader):
只能加載已經安裝過的apk的dex檔案
- DexClassLoader(Java的Custom ClassLoader):
可以從一個jar包或者未安裝的apk中加載dex檔案
- BaseDexClassLoader:
是PathClassLoader和DexClassLoader的父類,
14.SharePreference為什么不能存盤較大value
public String getString(String key, @Nullable String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
private void awaitLoadedLocked() {
while (!mLoaded) {
try {
wait();
} catch (InterruptedException unused) {
}
}
- SharePreference存值的時候,內部會有一個靜態的map保存了你所有的key和value
private ArrayMap<File, SharedPreferencesImpl> getSharedPreferencesCacheLocked() {
if (sSharedPrefsCache == null) {
sSharedPrefsCache = new ArrayMap<>();
}
final String packageName = getPackageName();
ArrayMap<File, SharedPreferencesImpl> packagePrefs = sSharedPrefsCache.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<>();
sSharedPrefsCache.put(packageName, packagePrefs);
}
return packagePrefs;
}
15.實作View滑動的幾個辦法
- View自身提供的scrollTo()和scrollBy方法,但只適合對View內容的滑動
- 使用影片,但滑動后的View點擊沒有效果,所以適用于沒有互動的View
- 改變布局引數,比如layoutParams.left,比影片稍微復雜,適合有互動的View
Scroller使用,呼叫startScroll方法,然后invidate() --> View會呼叫onDraw(),里面會呼叫computeScroll(),此方法默認空實作,需要自行實作 --> 重寫computeScroll(),實作滑動,如果沒有結束,postInvalidate()重繪

16.事件分發






- 原始碼
// 1. Activity的dispatchTouchEvent()方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
//交給PhoneWindow處理
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
// 2. PhoneWindow的superDispatchTouchEvent()方法
public boolean superDispatchTouchEvent(MotionEvent event) {
//交給DecorView處理
return mDecor.superDispatchTouchEvent(event);
}
// 3. 由于DecorView是FrameLayout子類,所以事件會被傳遞到DecorView的子View也就是,setContentView設定的View中
- View的事件分發


17.View的測量


18.記憶體泄露
產生記憶體泄露的原因:某個物件應該銷毀,但因為被其他物件持有無法銷毀就會產生記憶體泄漏,
比如:Handler 引起的記憶體泄漏,Activity已經銷毀,但銷毀后handleMessage方法被呼叫,內部類還持有外部類Activity的參考,解決辦法:
1.將內部類宣告為靜態(kotlin內部類默認就是靜態)
2.或者用弱參考
3.用生命周期更長的比如applicationContext代替activity
- 為什么內部類會持有外部類參考
因為編譯時候,內部類構造方法中會傳入外部類的參考
//原代碼
class InnerClassOutClass{
class InnerUser {
private int age = 20;
}
}
//class代碼
class InnerClassOutClass$InnerUser {
private int age;
InnerClassOutClass$InnerUser(InnerClassOutClass var1) {
this.this$0 = var1;
this.age = 20;
}
}
19.Activity,View,Window三者關系
- 一個Activity包含了一個Window物件,這個物件是由PhoneWindow來實作的
- PhoneWindow將DecorView作為整個應用視窗的根View
- 而DecorView又將螢屏劃分為兩個區域:一個是TitleView,另一個是ContentView,我們平時所寫的就是展示在ContentView中的
最后
在這里我也分享一份由幾位大佬一起收錄整理的 Flutter進階資料以及Android學習PDF+架構視頻+面試檔案+原始碼筆記 ,并且還有 高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料……
這些都是我閑暇時還會反復翻閱的精品資料,可以有效的幫助大家掌握知識、理解原理,當然你也可以拿去查漏補缺,提升自身的競爭力,
如果你有需要的話,可以前往 GitHub 自行查閱,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/256398.html
標籤:其他
上一篇:android dimens density適配檔案生成
下一篇:monkey測驗 --kill-process-after-error引數原理決議;android不彈ANR彈窗;ANR時行程被殺重啟
