Android面試主題整理合集(一)

面試主題我這裏打算分爲三部分去發佈,大家可以關注我一下,以免錯過。

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數據庫升級

  1. 直接刪除老數據庫,但會造成數據丟失,一般不採用。
  2. 對數據庫進行升級。

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()重繪

Android面試主題整理合集(一)

16.事件分發

Android面試主題整理合集(一)

Android面試主題整理合集(一)

Android面試主題整理合集(一)

Android面試主題整理合集(一)

Android面試主題整理合集(一)

Android面試主題整理合集(一)

  • 源碼
// 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的事件分發

Android面試主題整理合集(一)

 

Android面試主題整理合集(一)

 

17.View的測量

Android面試主題整理合集(一)

 

Android面試主題整理合集(一)

 

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 自行查閱。

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