【安卓】嘮點兒你不一定知道的小知識

子曰: “學而時習之,不亦說乎?”

1. YYYY 和 yyyy 不一樣

以 2019年12月31日 舉例:
yyyy-MM-dd :2019-12-31
YYYY-MM-dd :2020-12-31

相信這個你已經知道了,很多大佬都寫過詳細的講解文章,也可直接看官方說明SimpleDateFormat

簡言之:Y 指的是 Week year,表示的是這個周所屬的年份;y 表示的纔是我們日常使用的年份。

https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html

2. getReadableDatabase 不是以只讀方式打開數據庫

Android 中 getWritableDatabase()getReadableDatabase() 方法都可以獲取到 SQLiteDatabase 實例。
getReadableDatabase()並不是以只讀方式打開數據庫,而是先執行getWritableDatabase(),失敗的情況下才以只讀方式打開數據庫.。
源碼如下:

public synchronized SQLiteDatabase getReadableDatabase() {
    // ...
    try {
        // 執行 getWritableDatabase() , 若出現異常,以只讀方式打開數據庫
        return getWritableDatabase();
    } catch (SQLiteException e) {
        if (mName == null) throw e;  
    }
    SQLiteDatabase db = null;
    try {
        // ... 
        // 以只讀方式打開數據庫
        db = SQLiteDatabase.openDatabase(path, mFactory, SQLiteDatabase.OPEN_READONLY);
        // ... 
        mDatabase = db;
        return mDatabase;
    } finally {
        // ... 
    }
}

3. 子線程未必不能更新UI

Android的UI訪問是沒有加鎖的,多線程訪問時並不安全。所以規定只能在UI線程中訪問UI。
負責檢查線程的就是 ViewRootImplcheckThread() 方法:

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

然而 ViewRootImpl 的創建在 onResume() 回調之後。那麼在 onResume() 之前,子線程裏也是可以更新 UI 的 。
即使是 ViewRootImpl 創建後,只要不調用 checkThread(),子線程裏更新也並不會報錯。

然而,大家開發的時候還是不要在子線程更新UI。

4. 代碼 new 的 View 沒有id

Android佈局文件中通過 @+id 的方式,可以在 R文件 中生成對應的一個Int值,用於在運行時保證資源唯一性,但動態在代碼中 new 的 View 沒有 id 。
如果你有需求使用它的id,可以調用 View 的 generateViewId() 方法來生成 id(API17+) ,而非用隨機數產生或手寫一個具體的值。

5. View 的 getContext 返回的未必是Activity

Activity中setContentView時一定是Activity;
通過 new View、View.inflate、LayoutInflater.inflate 這幾種方式添加View,我們傳參時傳的是什麼context, View中的就是什麼Context.

在5.0系統版本以下的手機,且 Activity 是繼承自 AppCompatActivity 的,那麼View的getConext方法,返回的就不是Activity而是TintContextWrapper。

參考博文:https://mp.weixin.qq.com/s/5Xj6O9wV_EnynpdnB5mJ5A (南塵)

6. RemoteViews 和 View 沒啥關係

RemoteViews 提供了一組基礎的操作用於跨進程更新,主要用於通知欄和桌面小部件的開發。從名稱來看,感覺應該是一種遠程的View。其實不然,源碼如下:

public class RemoteViews implements Parcelable, Filter {
    // ...
}

總而言之,RemoteViews 就是爲跨進程操作控件而提供一系列方法的一個類。

7. boolean 類型佔幾個字節

Java中 boolean 表示的實際信息是一位:1表示true,0表示false。但是,Java規範 數據類型文檔 沒有精確定義內存中布爾變量的實際大小。

其大小與虛擬機相關,可以肯定的是,不會是 1 個 bit 。 Java 虛擬機的建議如下:
1.boolean 類型被編譯成 int 類型來使用,佔 4 個 byte 。
2.boolean 數組被編譯成 byte 數組類型,每個 boolean 數組成員佔 1 個 byte

參考博文:https://binkery.com/archives/346.html

8. RecyclerView 佈局文件可指定layoutManager跟spanCount

RecyclerView 佈局文件可指定layoutManager跟spanCount

<declare-styleable name="RecyclerView">        
    <attr name="layoutManager" format="string" />
    <attr name="android:orientation" />
    <attr name="spanCount" format="integer"/>
    <attr name="reverseLayout" format="boolean" />
    <attr name="stackFromEnd" format="boolean" />
</declare-styleable>

它的屬性attr裏面可以指定layoutManager,spanCount,orientation的。不必我們在代碼裏設置。

9. 9-patch 圖片是有 padding 的

NinePatchDrawable 圖形是一種可拉伸的位圖,可用作視圖的背景。Android 會自動調整圖形的大小以適應視圖的內容。包含一個額外的 1 像素邊框,必須使用 9.png 擴展名將其保存在項目的 res/drawable/ 目錄下。
9patch圖片
線的作用:
左,上:定義允許複製圖片的哪些像素來拉伸圖片。
右,下:定義圖片中允許放置視圖內容的相對區域。

因此,9-patch 圖片可能帶有 padding,如果控件沒有明確設置,圖片的 padding 會作爲控件的 padding

所以,有時候,android:padding="0dp" 該寫也是得寫的。

https://developer.android.com/guide/topics/graphics/drawables#nine-patch

10. 硬件加速不是哪裏都能開關

硬件加速,直觀上說就是依賴GPU實現圖形繪製加速。由於 GPU 的引入不僅提高了繪製效率,還由於繪製機制的改變,極大地提高了界面內容改變時的刷新效率。
在 Android4.0 開始默認開啓硬件加速,也可以手動控制打開關閉:

需要注意的是:
硬件加速在Window級只能開不能關;View級只能關不能開。

Application 和 Activity 控制
在 AndroidManifest 文件中 Application 或 Activity 節點添加

android:hardwareAccelerated="true"

Window控制

getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED)

View控制

view.setLayerType(View.LAYER_TYPE_SOFTWARE,null);

查詢是否開啓硬件加速

View.isHardwareAccelerated()
Canvas.isHardwareAccelerated()

參考博文:https://www.jianshu.com/p/9cd7097a4fcf

11. 用 getVisibility() 判斷用戶是否能看見並不好

getVisibility()只判斷它自身是否是顯示狀態。但是如果它的父級不可見呢?
isShown() 方法更合適些,會先判斷當前 View 的flag, 然後循環拿到父View,判斷是不是可見。只要有一個是不可見的,就返回false。
源碼如下:

public boolean isShown() {
    View current = this;
    //noinspection ConstantConditions
    do {
        if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) {
            return false;
        }
        ViewParent parent = current.mParent;
        if (parent == null) {
            return false; // We are not attached to the view root
        }
        if (!(parent instanceof View)) {
            return true;
        }
        current = (View) parent;
    } while (current != null);
    return false;
}

很多知識點都來自於網上大佬們的博客,有些已經忘記從哪裏看到的了,在此向大佬們致敬!

如果本文對你有所幫助,還望可以點個贊哈~~

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