91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何優化內置圖網絡

發布時間:2021-10-15 09:46:59 來源:億速云 閱讀:146 作者:iii 欄目:編程語言

本篇內容主要講解“如何優化內置圖網絡”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“如何優化內置圖網絡”吧!

App包主要優化手段

通過apk包結構可以發現,對于包大小優化的主要手段都是集中在資源優化方向 如何優化內置圖網絡

內置圖網絡化技術分析

經過調研和總結可以分為以下四點,本文也主要是針對這四點展開。

  1. 攔截圖片加載時機

  2. 圖片如何顯示

  3. 圖片下載和緩存

  4. 內置圖片刪除

如何優化內置圖網絡

如何攔截view設置圖片的方法

圖片加載兩個攔截方法
  • getDrawable

  • loadDrawable

Android系統view顯示圖片最終都是通過Resources類獲得圖片的drawable對象顯示。獲得drawable對象有兩個接口getDrawable、loadDrawable。getDrawable是一個公共接口,可以重載這個方法達到攔截,一般setBackground或者setImageDrawable會調用,loadDrawable方法系統View初始化獲取Drawable調用。

//Resource#getDrawable,
public Drawable getDrawable(@DrawableRes int id, @Nullable Theme theme)
        throws NotFoundException {
         if(圖片是否需要網絡化){
             return 網絡加載
         } else {
             //返回正常流程
             return baseResources.getDrawable(id,  theme);
         }
}

getDrawable比較好處理,但是loadDrawable方法是一個受保護方法,無法攔截。查看源碼loadDrawable之后流程也沒有找到可以hook的機會。一度以為攔截drawable很容易就可以實現,最后沒想到在這個問題上花費很多時間。查資料、看源碼最終找到一種方法!

因為loadDrawable這個方法只有xml配置的系統基礎view (如"ImageView、TextView、各種布局管理器等")的src和background屬性,在初始化view過程獲得drawable對象才會用到。所以影響的只是xml布局文件配置的view。那么通過實現LayoutInflater.Factory2,攔截xml View創建過程將xml 的view替換為我們自定義基礎view。在自定義view內通過遍歷當前Attr屬性判斷使用src或者background,然后調用相應的setImageDrawable或者setBackground達到觸發Resournces#getDrawable接口完成hook。通過這種hook的方式可以達到我們對XML布局view設置drawable的攔截目的

class SkinTextView extend TextView {
public SkinTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setSkin(this,attrs);
    }
private static final int[] ATTR_ARRAY = {
       // 這個屬性是系統類View的屬性,對于APP領域是不可見的。
       // 但是這個值是固定的,所以可以這樣寫,這里參考了RecycleView#NESTED_SCROLLING_ATTRS實現
        16842964/* android.R.attr.background */,
        android.R.attr.src
};

public static void setSkin(View view, AttributeSet attrs, DraweeHolderSupplier supplier){

    Context context = view.getContext();
    Resources resources = context.getResources();

    TypedArray ta = context.obtainStyledAttributes(attrs, ATTR_ARRAY);
    Drawable background ;

    int drawableId ;
    for (int i = 0; i < ATTR_ARRAY.length; i++) {
        int attr = ATTR_ARRAY[i];
        drawableId = ta.getResourceId(i,0);
        if (drawableId == 0){
            continue;
        }
        background = resources.getDrawable(drawableId,context.getTheme());
        switch (attr) {
            case 16842964:
                view.setBackground(background);
                break;
            case android.R.attr.src:
                if (view instanceof ImageView) {
                    ((ImageView)view).setImageDrawable(background);
                }
                break;
        }
    }
    ta.recycle();
}
}

但是以上方案只能解決XML中系統基礎的View,如果XML中使用開發自定義View則不管用。為了解決自定義view的問題我想到了兩種解決方案。

方案一

  • 通過字節碼修改方式將所有自定義view繼承的系統基礎view改為繼承我們自定義的基礎view

我通過asm字節碼修改將APP內所有自定義view繼承的系統基礎view改為自定義基礎view,這個方案可行,但是缺點比較多需要全局修改所有庫的字節碼包括androidx庫AppCompatView,修改范圍太大,框架穩定性不太容易保證,由于自定義基礎view有一些攔截代碼所以對view初始化性能也有一定影響,且ASM代碼編寫出現bug不易排查。如果只修改我們業務線的字節碼,可以正常運行。但修改第三方aar字節碼后,遇到一個坑,應用一直ANR期間沒找到具體原因。

方案二

  • hook LayoutInflater解析XML自定義view過程

// LayoutInflater#createViewFromTag
View createViewFromTag(View parent, String name, Context context, AttributeSet attrs,
        boolean ignoreThemeAttr) {
    try {
        View view;
        if (mFactory2 != null) {
            view = mFactory2.onCreateView(parent, name, context, attrs);
        } else if (mFactory != null) {
            view = mFactory.onCreateView(name, context, attrs);
        } else {
            view = null;
        }

        if (view == null && mPrivateFactory != null) {
            view = mPrivateFactory.onCreateView(parent, name, context, attrs);
        }
       // 以下onCreateView方法可以重載,拿到view對象強制觸發getDrawable即可,中間需要一些過濾。講一下大致思路,細節就不加了。
        if (view == null) {
            final Object lastContext = mConstructorArgs[0];
            mConstructorArgs[0] = context;
            try {
                if (-1 == name.indexOf('.')) {
                    view = onCreateView(parent, name, attrs);
                } else {
                    view = createView(name, null, attrs);
                }
            } finally {
                mConstructorArgs[0] = lastContext;
            }
        }

        return view;
    }
}

這個方案可以將自定義view攔截,缺點就是依賴android系統版本,如果android系統這塊邏輯發生變化那么需要適配。不過對于后續需要使用Fresco框架加載圖片以及內存管理,這個方案無法做到融合Fresco,所以該方案最終也沒有利用起來。

最終決定放棄對自定義view這種情況處理。通過遍歷xml 將自定義attr和自定義view過濾。 字節碼修改和自定義屬性、view過濾方案可以參考下圖。 如何優化內置圖網絡

/**
 * 自定義屬性、view過濾
 * hook aapt打包過程,得到所有模塊res資源路徑,遍歷所有res/layout下的xml
 */
Pattern pattern = Pattern.compile("(?<=(android:(background|src)=\"@drawable/))([a-z_0-9]*)")
void eachLayoutXml(File[] resDirs){
    resDirs.each {
        if (it.isDirectory() && it.name == "res") {
            eachLayoutXml(it.listFiles())

        } else if (it.isDirectory() && it.name == "layout") {
            it.listFiles().each { xml ->
                // 獲得xml內容,通過正則表達式匹配字符串
            }
        }
    }
}

下載圖片的方案以及圖片如何顯示

下載圖片方案

當時考慮過兩種下載圖片方案

  • 圖片插件apk,將所有需要的圖片打包到apk,然后只下載一次插件,無需考慮圖片內存問題

  • 網絡直接下載圖片,通過Fresco管理內存問題 如何優化內置圖網絡

對比這兩種方案我選擇了實現比較容易的第二種。

圖片顯示

這個問題比較好解決,view、drawable之間是通過Drawable.Callback進行傳遞,所以下載圖片得到drawable對象后通過drawable callback#invalidateDrawable即可。當然這里返回的drawable應該是一個LayerDrawable,因為Drawable.Callback執行更新的Drawable必須是同一個Drawable對象,同時方便同步狀態下返回默認圖,異步網絡圖返回后刷新。

需要注意一點,這里不能直接使用Fresco RootDrawable對象返回,因為Fresco不支持view wrap_content屬性

圖片下載策略和緩存

因為需要用到Fresco,簡單介紹下。Fresco結構分層可以分為三層,分別是圖層、控制器、圖片獲取,每一層結構、功能如圖。

  • RootDrawable是最終返回的圖片Drawable對象

  • DataSources 返回圖片信息的訂閱源

  • Controler 圖片獲取和圖層顯示中間橋梁

  • 第三層是圖片三級緩存,獲取圖片可以從緩存和網絡獲取

如何優化內置圖網絡

大致了解Fresco,下面描述內置圖網絡化框架融合Fresco,使用Fresco進行圖片下載和緩存。

這個實現可以類比Fresco的DraweeView的實現,利用view的attach、detach、visible幾個生命周期函數通過DraweeHolder觸發drawble的加載和銷毀,做到對Fresco的圖片緩存和內存釋放。具體過程不介紹了感興趣可以閱讀Fresco源碼。

實現流程如下圖。

如何優化內置圖網絡

內置圖刪除

經過實際調研,不能直接刪除內置圖,否則在打包過程進行圖片鏈接的時候會拋出找不到資源錯誤,所以主要思路通過1像素圖片替換要刪除的圖片。

刪除內置圖有以下幾種方案 如何優化內置圖網絡

我選擇方案四,具體有以下優點

  • 方便根據圖片大小選擇批量刪除

  • 可以直接計算得到優化的包大小

  • 可以直接融合到APP編譯過程,編譯一步到位

實現方案如下

  • hook aapt資源打包過程moregeResources結束的時候

  • 遍歷所有生成的圖片flat二進制文件,將flat文件里png、webp、jpg二進制數據替換為一像素的默認圖

這個方案實現比較麻煩的是對flat文件二進制流的讀取過程 如何優化內置圖網絡

flat文件容器格式傳送門

到此,相信大家對“如何優化內置圖網絡”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

临城县| 平湖市| 东方市| 五大连池市| 获嘉县| 韶山市| 思茅市| 中江县| 清水河县| 顺昌县| 康乐县| 崇信县| 舟山市| 疏附县| 阿拉善盟| 尼木县| 游戏| 祁门县| 许昌县| 南川市| 邳州市| 松阳县| 沁阳市| 夏邑县| 禹城市| 旌德县| 乌兰察布市| 荥阳市| 辉南县| 天峻县| 阿坝县| 祁门县| 新疆| 武乡县| 博客| 大理市| 夏津县| 呼图壁县| 东莞市| 淮安市| 新龙县|