1. 程式人生 > >APK外掛載入資源實現

APK外掛載入資源實現

前言

外掛化程式設計現在非常的火熱,通常用來解決65536問題,外掛通常被做成不同的apk模組,每個模組專門負責某種業務邏輯,主APK通過呼叫動態載入外掛裡的程式碼和資源實現宿主和外掛的互動。為了瞭解外掛APK檔案如何使用,這裡通過讀取APK外掛的資源來實現換膚功能。

實現效果

這裡寫圖片描述

生成資源外掛

只要在Android Studio中建立一個新的專案,專案裡不需要有任何的Activity元件,再把需要的圖片資源和顏色資源都定義一下,最後在命令列執行gradle assemble會發現在build/outputs/apk裡找到了app-debug.apk檔案,它就是包含資源的apk外掛包。可以通過adb push方法將這個外掛包推到Android手機的/sdcard/目錄下,方便主應用APK能夠找到外掛檔案。

// 在外掛資源包裡定義的顏色資源
<color name="content_text_color">#00ff00</color>
<color name="menu_text_color">#ff00ff</color>

C:\Users>adb push appres.apk /sdcard/
appres.apk: 1 file pushed. 4.1 MB/s (1720401 bytes in 0.399s)

換膚實現

前面已經將外掛資源放到了/sdcard/目錄下,為了能夠讀取到外部檔案對於Android6.0之後的系統需要申請外部儲存讀取許可權,關於動態申請許可權的程式碼邏輯這裡就不再贅述,現在開始定義簡單的介面佈局。佈局分為SlideMenu部分和內容部分,兩者都有一個背景ImageView和一個展示文案的TextView,這裡需要對背景圖和文案的顏色做修改,預設情況下使用的是主APK中的資源,如果使用者要求切換面板就會從外掛APK中讀取資源並且設定到背景和文案上。

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context
=".DrawerActivity">
<FrameLayout android:id="@+id/main_container" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/content" android:src="@drawable/skin_content_bg" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="match_parent" /> <TextView android:id="@+id/content_text" android:textColor="@color/skin_red" android:layout_gravity="center" android:textSize="50sp" android:text="@string/app_name" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </FrameLayout> <FrameLayout android:id="@+id/slideMenu" android:layout_gravity="left" android:layout_width="250dp" android:layout_height="match_parent"> <ImageView android:id="@+id/menu" android:src="@drawable/skin_menu_bg" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="match_parent" /> <TextView android:id="@+id/menu_text" android:textColor="@color/skin_orange" android:layout_gravity="center" android:textSize="50sp" android:text="@string/app_name" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </FrameLayout> </android.support.v4.widget.DrawerLayout>

使用者可以通過選項選單來切換不同的面板,其中ResourceManager物件儲存了預設的資源和外掛APK中的資源,使用者點選修改會使用外掛中的資源,點選還原會自動使用原始的資源。

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    switch (id) {
        case R.id.action_change_skin:
            ResourceManager.ResourceEntity entity = ResourceManager.getInstance()
            .getResource(ResourceManager.APK_RESOURCE);
            initViews(entity);
            return true;
        case R.id.action_reset_skin:
            ResourceManager.ResourceEntity defEntity = ResourceManager.getInstance()
            .getResource(ResourceManager.DEFAULT_RESOURCE);
            initViews(defEntity);
            return true;
    }
    return super.onOptionsItemSelected(item);
}

// 將資源設定到背景和文案檢視物件上
private void initViews(ResourceManager.ResourceEntity entity) {
    menuBg.setImageDrawable(entity.menuBg);
    contentBg.setImageDrawable(entity.contentBg);
    menuText.setTextColor(entity.menuColor);
    contentText.setTextColor(entity.contentColor);
}

其中的ResourceEntity就是用來儲存資源的資料類,這裡主要來看ResourceManager.getInstance().getResource(resourceType)根據使用者指定的資源型別載入資源物件的實現。

public class ResourceManager {
    // 資源型別
    public static final String DEFAULT_RESOURCE = "default_resource";
    public static final String APK_RESOURCE = "apk_resource";

    // 資源物件快取
    private Map<String, ResourceEntity> mCacheMap = new HashMap<>();

    private ResourceManager() {
        // 初始化預設的資源,使用的是主APK內定義的資源
        ResourceEntity defaultEntity = new ResourceEntity();
        Resources resources = Application.getContext().getResources();
        defaultEntity.contentBg = resources.getDrawable(R.drawable.skin_content_bg);
        defaultEntity.menuBg = resources.getDrawable(R.drawable.skin_menu_bg);
        defaultEntity.contentColor = resources.getColor(R.color.skin_red);
        defaultEntity.menuColor = resources.getColor(R.color.skin_orange);
        mCacheMap.put(DEFAULT_RESOURCE, defaultEntity);
    }

    private static class ResourceManagerHolder {
        private static final ResourceManager INSTANCE = new ResourceManager();
    }

    public static ResourceManager getInstance() {
        return ResourceManagerHolder.INSTANCE;
    }

    public void add(String key, ResourceEntity entity) {
        mCacheMap.put(key, entity);
    }

    public ResourceEntity getResource(String key) {
        ResourceEntity entity = mCacheMap.get(key);
        // 如果沒有對應的資源物件
        if (entity == null) {
            // 如果請求的是外掛資源型別
            if (APK_RESOURCE.equals(key)) {
                // 載入外掛資源型別
                entity = loadResources();
                mCacheMap.put(key, entity);
                return entity;
            } else {
                return mCacheMap.get(DEFAULT_RESOURCE);
            }
        } else {
            return entity;
        }
    }

    /**
     * 從外掛APK中獲取資源
    */
    private ResourceEntity loadResources() {
        try {
            // 反射生成AssetManager物件
            AssetManager assetManager = AssetManager.class.newInstance();
            // 獲取addAssetPath方法
            Method method = AssetManager.class.getMethod("addAssetPath", String.class);
            // 獲取外掛的路徑
            String path = Environment.getExternalStorageDirectory() + File.separator + "appres.apk";
            // 將外掛的路徑新增到AssetManager裡
            method.invoke(assetManager, path);

            Resources originResources = Application.getContext().getResources();
            // 外掛APK的packageName名稱
            String pkgName = "com.example.apkresource";

            // 生成外掛APK對應的Resources物件
            Resources resources = new Resources(assetManager, 
            originResources.getDisplayMetrics(), originResources.getConfiguration());
            // 獲取外掛中的背景圖資源id
            int contentBgId = resources.getIdentifier("content_bg", "drawable", pkgName);
            int menuBgId = resources.getIdentifier("menu_bg", "drawable", pkgName);
            // 獲取外掛中文案顏色資源id
            int contentColorId = resources.getIdentifier("content_text_color", "color", pkgName);
            int menuColorId = resources.getIdentifier("menu_text_color", "color", pkgName);

            // 從Resources物件中獲取外掛的資源資料並儲存到ResouceEntity中
            ResourceEntity entity = new ResourceEntity();
            entity.menuBg = resources.getDrawable(menuBgId);
            entity.contentBg = resources.getDrawable(contentBgId);
            entity.menuColor = resources.getColor(menuColorId);
            entity.contentColor = resources.getColor(contentColorId);

            return entity;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static class ResourceEntity {
        public Drawable contentBg;
        public Drawable menuBg;
        public int contentColor;
        public int menuColor;
    }
}

上面的ResourceManager物件使用了單例模式,內部會儲存外掛資源和預設資源,在外掛資源未被初始化的情況下使用AssetManager和Resources兩個工具類獲取外掛內的資源資料,最後通過前面定義的initViews方法將外掛資源設定到背景和文案檢視上實現換膚功能。

相關推薦

APK外掛載入資源實現

前言 外掛化程式設計現在非常的火熱,通常用來解決65536問題,外掛通常被做成不同的apk模組,每個模組專門負責某種業務邏輯,主APK通過呼叫動態載入外掛裡的程式碼和資源實現宿主和外掛的互動。為了瞭解外掛APK檔案如何使用,這裡通過讀取APK外掛的資源來實現換

Android外掛化完美實現程式碼資源載入及原理講解 附可執行demo

*本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家釋出 。 我們通過前4篇的分解,分別將外掛化設計到的知識點全部梳理了一遍,如果沒有看過的,建議先看前面4篇 6. 外掛化資源的使用及動態載入 附demo 好了上面介紹了之

Android如何載入外掛APK裡面的資源

前言:ClassLoader能夠動態載入類,Android裡面的DexClassLoader和PathClassLoader繼承自ClassLoader。DexClassLoader能夠載入未安裝的jar/apk/dex檔案。PathClassLoader只能載

Android中apk動態載入技術研究(2)android插件化及實現

name creat package path iss fontsize 調用 dex con 了解了android中類載入的前期知識點後,來看看android中DexClassLoader詳細的實現 詳細載入流程例如以下: 宿主程序會到文件系統比

Android apk動態載入機制的研究(二) 資源載入和activity生命週期管理

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

外掛載入帶有動態庫so的apk處理

     外掛載入帶有動態庫的apk時,會報UnsatisfiedLinkError找不到動態庫的錯誤。 解決方法是在DexClassLoader中 dalvik.system.DexClassLoader.DexClassLoader(String dexPath, S

Android外掛資源的使用及動態載入 附demo

上一篇我們已經完成了一個真正可執行的外掛化demo,而且demo中也解決了外掛中不可以使用資源的問題,但是由於篇幅的問題我們並沒有對原理講解,所以這一篇是對上一篇的一個收尾,如果沒有看過上一篇建議先看Android外掛化完美實現程式碼資源載入及原理講解 附可執行

Android 系統上實現APK外掛機制——360手機助手

Droid Plugin DroidPlugin 是360手機助手在Android系統上實現了一種新的外掛機制:它可以在無需安裝、修改的情況下執行APK檔案,此機制對改進大型APP的架構,實

Gradle外掛開發 APK瘦身資源自定義7z壓縮

APK瘦身實戰 資源自定義7z壓縮 專案開發中,隨著業務的增長,常常需要在apk編譯階段對包程式碼或是資源做一定的自定義修改,比如熱修復,外掛生成,無埋點統計,渠道包生成等等。 但是公司專案業務開發人員基本上都很少接觸到相關技術,這裡以學習的態度,實現一套用

外掛載入資源的核心程式碼

外掛化 載入其他apk 的資源的核心思想是 拿到其他apk 的Resources 物件,然後通過反射 , 可以拿到 我們想要的資源,然後使用。 拿到其他apk 的物件 ,主要是通過 AssetManager 的 addAssetPath,方法; 因為這個方法在frameW

通過Android manifest中的sharedUserId屬性的設定來實現apk之間的資源共享

在Android應用的開發中應該都會有兩個程序中需要互相訪問資料的需求,而在APK在安裝到裝置裡的時候該應用的userid就已經確定了, 一般情況下都不會再改變,每個應用的程序在預設情況下的userid是不一樣的,如adb shell下top命令顯示的程序資訊中的UID,

Android動態載入APK外掛

前言 外掛化開發目前是非常熱門的Android技術,它主要通過將不同的業務物件封裝到外掛中,這樣不同的業務可以獨立開發和除錯,提高專案的開發效率。APK檔案就是常見的外掛檔案格式,它包含了Android應用常見的資源和程式碼,不過由於外掛沒有被安裝到系統中還需

Android實現apk外掛方式換膚

換膚思路: 1.什麼時候換膚? xml載入前換膚,如果xml載入後換膚,使用者將會看見換膚之前的色彩,使用者體驗不好。 2.面板是什麼? 面板就是apk,是一個資源包,包含了顏色、圖片等。 3.什麼樣的控制元件應該進行換膚? 包含背景圖片的控制元件,例如textView文字顏色。 4.面板與已安裝的資源如何匹

逆向知識之CS輔助/外掛專題.2.實現CS1.6無限夜視儀.無限閃光煙霧高爆彈.

寫代碼 循環 編寫 編寫代碼 我們 思路 是個 代碼 打開     逆向知識之CS輔助/外掛專題.2.實現CS1.6無限夜視儀.無限閃光煙霧高爆彈. 關於人物子彈無限可以觀看上一篇博客. 一丶無限夜視儀. 無限夜視儀找法.     1.CE附加遊戲.     2.搜索0或者

Vue 進階系列(二)之外掛原理及實現

Vue進階系列彙總如下,歡迎閱讀,歡迎加群討論(文末)。 Vue 進階系列(一)之響應式原理及實現 Vue 進階系列(二)之外掛原理及實現 使用方法 外掛的詳細使用方法詳情看Vue官網 Vue官網之外掛Plugins 概括出來就是 1、通過Vue.use(MyPlugin)使用,

如何使用postman及外掛postman interceptor 實現模擬帶站點cookie的請求呼叫

前提:瀏覽器chrome及chrome外掛安裝 背景:前後端分離後,除錯後端介面需要模擬前端發起的請求,構建請求引數較為費事,不如將瀏覽器cookie及請求引數一起帶出通過postman 模擬前端請求。 1、安裝chrome外掛postman interceptor及谷歌應用postman

Egret製作Loading頁面及分步載入資源教程

我們都知道,當遊戲越做越大,資源越來越多的時候,載入資源會造成大量時間的浪費。為避免載入資源時遊戲黑屏,導致玩家誤認為遊戲非正常執行,Loading介面起到至關重要的作用。今天就為大家帶來用Egret製作Loading頁面及分步載入資源的教程。 本文涉及以下內容: · RES載入Loading介面所使用的

egret 全屏, 和載入資源, 以及回撥函式

1, 有時候在手機瀏覽器中因為有  虛擬按鍵以及標題欄, 使得即便設定了全屏也沒有辦法變成全屏, 但是好像JS 中有方法向瀏覽器請求全屏 2, 載入資源, 關閉後解除安裝, 第二次再進來的時候依然很快, 這是因為瀏覽器有快取 3, egret的回撥函式十分的隨便, 帶引數的回撥函式

菜鳥教程丨Egret製作Loading頁面及分步載入資源教程

我們都知道,當遊戲越做越大,資源越來越多的時候,載入資源會造成大量時間的浪費。為避免載入資源時遊戲黑屏,導致玩家誤認為遊戲非正常執行,Loading介面起到至關重要的作用。今天就為大家帶來用Egret製作Loading頁面及分步載入資源的教程。 本文涉及以下內容: RES載入Loadi

設定Sublime外掛快捷鍵--實現CSS顏色選取

安裝外掛ColorPicker 如果你經常要檢視或設定顏色值,這個外掛可以很方便地呼叫你本機的調色盤應用。(譯者擴充:)這是一個雙向的功能,你既可以在調色盤中選擇一個顏色,然後按“確定”按鈕把該值填寫到 SublimeText 中活動文件的當前位置,也可以在活動文件中選擇一個顏色的值,按此外掛的