Android常見問題總結(六)

上一篇博客傳送門:Android常見問題總結(五)

26.如何處理Android Crash 並重啓手機

一般而言,發生了APP Crash是由於我們的程序拋出了我們未捕獲的RuntimeException
在Java中,我們可以通過setDefaultUncaughtExceptionHandler方法爲某個線程設置默認未捕獲異常處理器,當程序拋出未捕獲異常時,會交由改處理器處理
Android的默認UncaughtExceptionHandler爲彈出Dialog提醒用戶APP Crash,並殺死進程退出程序
我們可以通過編寫自己的UncaughtExceptionHandler來處理APP Crash的問題:
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(Thread t, Throwable e) {
                Intent intent = new Intent(MainActivity.this, MainActivity.class);
                startActivity(intent);
                Process.killProcess(Process.myPid());
            }
        });

下面例子中,點擊按鈕會拋出一個運行時異常,左圖爲未設置Handler之前,右圖爲設置Handler之後:



27.如何把view轉化爲bitmap

可以使用getDrawingCache方法:
// 設置能否緩存圖片信息(drawing cache)
view.setDrawingCacheEnabled(true);
// 如果能夠緩存圖片,則創建圖片緩存
view.buildDrawingCache();
// 如果圖片已經緩存,返回一個bitmap
Bitmap bitmap = view.getDrawingCache();
// 釋放緩存佔用的資源
view.destroyDrawingCache();

28.library 與 app資源同名衝突問題

假設有以下場景:


在一個Project中有兩個module,一個是應用app,另一個是android的library:mylibrary
兩個module中存在着相同的資源名稱

這種情況會導致什麼問題呢?
會導致mylibrary module中的activity_main.xml被覆蓋,無法獲取

在網上查到的R類索引生成規則如下所示:

資源ID是一個4字節的無符號整數,其中,最高字節表示Package ID,次高字節表示Type ID,最低兩字節表示Entry ID。        
Package ID相當於是一個命名空間,限定資源的來源。Android系統當前定義了兩個資源命令空間,其中一個系統資源命令空間,它的Package ID等於0x01,另外一個是應用程序資源命令空間,它的Package ID等於0x7f。所有位於[0x01, 0x7f]之間的Package ID都是合法的,而在這個範圍之外的都是非法的Package ID。前面提到的系統資源包package-export.apk的Package ID就等於0x01,而我們在應用程序中定義的資源的Package ID的值都等於0x7f,這一點可以通過生成的R.java文件來驗證。        
Type ID是指資源的類型ID。資源的類型有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干種,每一種都會被賦予一個ID。        
Entry ID是指每一個資源在其所屬的資源類型中所出現的次序。注意,不同類型的資源的Entry ID有可能是相同的,但是由於它們的類型不同,我們仍然可以通過其資源ID來區別開來。

由於名稱相同,兩個module會對該資源生成相同的索引即(app.R.layout.activity_main == mylibrary.R.layout.activity_main)
如果app module依賴mylibrary module,那麼最終生成的apk中所有的資源文件res和R類都會被集中到一起,也就是說mylibrary中的activity_main會被完全覆蓋
原本在mylibrary module中的獲取的activity_main資源均會變爲app中的activity_main資源

stackoverflow對此問題給出的解決方案是:爲你的android library資源添加相關的前綴,如AppCompat的前綴爲abc_

29.Android 揚聲器與聽筒切換

要實現揚聲器與聽筒的切換.而android中實現對音量和振鈴模式的控制主要通過AudioManager類來實現.

獲取到AudioManager後,我們可以通過其幾個關鍵的方法改變揚聲器與聽筒的狀態:
  1. isSpeakerphoneOn:用於檢測當前是否開啓了揚聲器
  2. setSpeakerphoneOn:設置是否打開揚聲器
  3. setMode:設置音頻模式,設置揚聲器時,使用MODE_NORMAL,設置聽筒時,使用MODE_IN_COMMUNICATION
具體實現如下:
    /**
     * 設置是否開啓揚聲器
     * @param isSpeakerOn 是否開啓
     */
    public void setSpeakerphone(boolean isSpeakerOn) {
        AudioManager audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
        if (audioManager != null) {
            audioManager.setMode(isSpeakerOn ? AudioManager.MODE_NORMAL : AudioManager.MODE_IN_COMMUNICATION);
            audioManager.setSpeakerphoneOn(isSpeakerOn);
        }
    }

值得注意的是直接調用這個方法並不能切換生效,我們還需要申請MODIFY_AUDIO_SETTINGS的權限纔行:
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

30.關於addView後寬高的一些細節

對於ViewGroup#addView方法,我們應該都不陌生,該方法經常可以用來動態添加子View
但如果我們使用時不注意的話,添加到ViewGroup中的View的寬高往往並不會如我們所願
想要避免這種情況,我們就需要確保調用addView方法時,我們的View具有正確的LayoutParams(寬高信息包含在裏面),以下有三種方式確認我們的View設置了正確的LayoutParams:
  1. 在使用LayoutInflater時,傳入父ViewGroup:LayoutInflater只有當我們傳入父ViewGroup時,纔會爲我們解析的View設置正確的LayoutParams,否則其實我們寫在xml上的佈局信息並不會準確的生效
  2. 在addView之前,調用setLayoutParams設置
  3. 調用addView帶有LayoutParams參數的方法
一般而言,最常用的ViewGroup#addView方法如下所示:
    public void addView(View child) {
        addView(child, -1);
    }

    public void addView(View child, int index) {
        if (child == null) {
            throw new IllegalArgumentException("Cannot add a null child view to a ViewGroup");
        }
        LayoutParams params = child.getLayoutParams();
        if (params == null) {
            params = generateDefaultLayoutParams();
            if (params == null) {
                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
            }
        }
        addView(child, index, params);
    }
	
	protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    }

從第11行我們可以看出,如果我們動態添加的View並沒有設置好相應的LayoutParams,系統變會填充上一個默認的LayoutParams,而大多數情況下,這個LayoutParams的佈局方式並不是我們想要的
這也是爲什麼有時候addView之後,我們添加的View寬高爲0的原因之一
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章