日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤

發(fā)布時間:2023/12/4 Android 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

第一步、先制做一個有我們需要的圖片資源的APK

如下圖,這里有個about_log.png,我們需要生成apk文件。

生成的apk文件如果你不到項目的文件夾里面去取apk,想通過命令放到手機里面去可以快速用下面命令

1)、在手機里面通過包名找到apk路徑,一定不要忘記有 -f

?

adb shell pm list package -f | grep com.example.testclassloader

得到如下結(jié)果

?

?

package:/data/app/com.example.testclassloader-2/base.apk=com.example.testclassloader

2)、把base.apk拉到本地然后改名字,命令如下

?

adb shell pull /data/app/com.example.testclassloader-2/base.apk testClassLoader.apk

?

3)、把testClassLoader.apk放到手機里面去,命令如下

?

?

adb shell push testClassLoader.apk /sdcard/

?

?

?

4)、去手機文件管理器里面找看是否有testClassLoader.apk文件

?

第二步、獲取為安裝apk包名的信息(假設(shè)前提不知道)

我們可以通過這個方法得到

?

public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags)

?

具體方法如下

?

/** * 獲取未安裝apk的信息 * @param context * @param apkPath apk文件的path * @return */ private Map<String,String> getUninstallApkInfo(Context context, String apkPath) { Map hashMap = new HashMap<String,String>();PackageManager pm = context.getPackageManager(); PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES); if (null != pkgInfo) { ApplicationInfo appInfo = pkgInfo.applicationInfo; String pkgName = appInfo.packageName;//包名 hashMap.put(PKG_NAME, pkgName);} else {Log.d(TAG, "program don't get apk package information");} return hashMap; }

?


第三步、獲取未安裝apk(插件)的Resource

?

因為沒有安裝,所以不能得到context,所以我們需要未安裝apk的Resource,我們可以通過反射來獲取,代碼如下

?

/** * @param apkPath * @return 得到對應(yīng)插件的Resource對象 */ private Resources getPluginResources(String apkPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); //反射調(diào)用方法addAssetPath(String path)Method addAssetPath = assetManager.getClass().getMethod(ADDSSETPATH, String.class); //將未安裝的Apk文件的添加進AssetManager中,第二個參數(shù)是apk的路徑 addAssetPath.invoke(assetManager, apkPath);Resources superRes = this.getResources(); Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); return mResources; } catch (Exception e) { e.printStackTrace(); } return null; }

?

?

?

第四步、用DexClassLoader加載apk資源文件替換背景

如果你多DexClassLoader用法和原理不熟悉,可以參考我之前的博客 Android插件化開發(fā)之DexClassLoader動態(tài)加載dex、jar小Demo ?http://blog.csdn.net/u011068702/article/details/53263442 Android插件化開發(fā)之動態(tài)加載基礎(chǔ)之ClassLoader工作機制 ?http://blog.csdn.net/u011068702/article/details/53248960 代碼如下: /** * 加載apk獲得內(nèi)部資源,并且替換背景* @param apkDir apk目錄 * @param apkName apk名字,帶.apk * @throws Exception */ private void dynamicLoadApk(String apkPath, String apkPackageName) throws Exception {//在應(yīng)用安裝目錄下創(chuàng)建一個名為app_dex文件夾目錄,如果已經(jīng)存在則不創(chuàng)建,這個目錄主要是最優(yōu)化目錄,用于緩存dex文件File optimizedDirectoryFile = getDir(DEX, Context.MODE_PRIVATE); //打印路徑 理論上是/data/data/package/app_dexLog.v(TAG, optimizedDirectoryFile.getPath().toString()); //構(gòu)建DexClassLoaderDexClassLoader dexClassLoader = new DexClassLoader(apkPath, optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader()); //通過使用apk自己的類加載器,反射出R類中相應(yīng)的內(nèi)部類進而獲取我們需要的資源idClass<?> clazz = dexClassLoader.loadClass(apkPackageName + DRAWABLE); //得到名為about_log的這張圖片字段,這個圖片是為安裝apk里面的圖片F(xiàn)ield field = clazz.getDeclaredField(IMAGE_ID); //得到圖片id int resId = field.getInt(R.id.class);//得到插件apk中的Resource Resources mResources = getPluginResources(apkPath);if (mResources != null) { //通過插件apk中的Resource得到resId對應(yīng)的資源 Drawable btnDrawable = mResources.getDrawable(resId);mLayout.setBackgroundDrawable(btnDrawable); } else {Log.d(TAG, "mResources is null");}} ?

第五步、爆出所有代碼(為了詳細點)

package com.chenyu.dexclassloaderapk;import java.io.File; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.os.Environment; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView;import com.example.dexclassloaderapk.R;import dalvik.system.DexClassLoader;public class MainActivity extends ActionBarActivity {public static final String TAG = "DexClassLoaderApk";public static final String PKG_NAME = "pkgName";public static final String APK_PATH = "testClassLoader.apk";public static final String ADDSSETPATH = "addAssetPath";public static final String DEX = "dex";//這個IMAGE_ID是只我放入手機里面APK 在代碼里面這個圖片的ID,這里我們拿到之后,然后去替換北京圖片public static final String IMAGE_ID = "about_log";public static final String DRAWABLE = ".R$drawable";public TextView mTextView;//背景的布局public RelativeLayout mLayout;public Map<String, String> apkInfo;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final String apkPath = Environment.getExternalStorageDirectory().toString() + File.separator + APK_PATH;mTextView = (TextView)findViewById(R.id.text);mLayout = (RelativeLayout)findViewById(R.id.re_Layout);mTextView.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v) {//一定要記得加上android.permission.READ_EXTERNAL_STORAGE權(quán)限,不然死活都拿不到數(shù)據(jù)//我就換了一個這個錯誤,如果發(fā)現(xiàn)代碼沒問題,網(wǎng)上找也沒問題,這個時候應(yīng)該思考是不是沒有加權(quán)限apkInfo = getUninstallApkInfo(MainActivity.this, apkPath);String packageName = apkInfo.get(PKG_NAME);if (null != packageName) {try {dynamicLoadApk(apkPath, packageName);} catch (Exception e) {e.printStackTrace();Log.i(TAG, "change image fail");}} else {Log.i(TAG, "package is null");}}});}/** * 獲取未安裝apk的信息 * @param context * @param apkPath apk文件的path * @return */ private Map<String,String> getUninstallApkInfo(Context context, String apkPath) { Map hashMap = new HashMap<String,String>();PackageManager pm = context.getPackageManager(); PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES); if (null != pkgInfo) { ApplicationInfo appInfo = pkgInfo.applicationInfo; String pkgName = appInfo.packageName;//包名 hashMap.put(PKG_NAME, pkgName);} else {Log.d(TAG, "program don't get apk package information");} return hashMap; } /** * @param apkPath * @return 得到對應(yīng)插件的Resource對象 */ private Resources getPluginResources(String apkPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); //反射調(diào)用方法addAssetPath(String path)Method addAssetPath = assetManager.getClass().getMethod(ADDSSETPATH, String.class); //將未安裝的Apk文件的添加進AssetManager中,第二個參數(shù)是apk的路徑 addAssetPath.invoke(assetManager, apkPath);Resources superRes = this.getResources(); Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); return mResources; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 加載apk獲得內(nèi)部資源,并且替換背景* @param apkDir apk目錄 * @param apkName apk名字,帶.apk * @throws Exception */ private void dynamicLoadApk(String apkPath, String apkPackageName) throws Exception {//在應(yīng)用安裝目錄下創(chuàng)建一個名為app_dex文件夾目錄,如果已經(jīng)存在則不創(chuàng)建,這個目錄主要是最優(yōu)化目錄,用于緩存dex文件File optimizedDirectoryFile = getDir(DEX, Context.MODE_PRIVATE); //打印路徑 理論上是/data/data/package/app_dexLog.v(TAG, optimizedDirectoryFile.getPath().toString()); //構(gòu)建DexClassLoaderDexClassLoader dexClassLoader = new DexClassLoader(apkPath, optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader()); //通過使用apk自己的類加載器,反射出R類中相應(yīng)的內(nèi)部類進而獲取我們需要的資源idClass<?> clazz = dexClassLoader.loadClass(apkPackageName + DRAWABLE); //得到名為about_log的這張圖片字段,這個圖片是為安裝apk里面的圖片F(xiàn)ield field = clazz.getDeclaredField(IMAGE_ID); //得到圖片id int resId = field.getInt(R.id.class);//得到插件apk中的Resource Resources mResources = getPluginResources(apkPath);if (mResources != null) { //通過插件apk中的Resource得到resId對應(yīng)的資源 Drawable btnDrawable = mResources.getDrawable(resId);mLayout.setBackgroundDrawable(btnDrawable); } else {Log.d(TAG, "mResources is null");}} } dynamicLoadApk(apkPath, packageName);} catch (Exception e) {e.printStackTrace();Log.i(TAG, "change image fail");}} else {Log.i(TAG, "package is null");}}});}/** * 獲取未安裝apk的信息 * @param context * @param apkPath apk文件的path * @return */ private Map<String,String> getUninstallApkInfo(Context context, String apkPath) { Map hashMap = new HashMap<String,String>();PackageManager pm = context.getPackageManager(); PackageInfo pkgInfo = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES); if (null != pkgInfo) { ApplicationInfo appInfo = pkgInfo.applicationInfo; String pkgName = appInfo.packageName;//包名 hashMap.put(PKG_NAME, pkgName);} else {Log.d(TAG, "program don't get apk package information");} return hashMap; } /** * @param apkPath * @return 得到對應(yīng)插件的Resource對象 */ private Resources getPluginResources(String apkPath) { try { AssetManager assetManager = AssetManager.class.newInstance(); //反射調(diào)用方法addAssetPath(String path)Method addAssetPath = assetManager.getClass().getMethod(ADDSSETPATH, String.class); //將未安裝的Apk文件的添加進AssetManager中,第二個參數(shù)是apk的路徑 addAssetPath.invoke(assetManager, apkPath);Resources superRes = this.getResources(); Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration()); return mResources; } catch (Exception e) { e.printStackTrace(); } return null; } /** * 加載apk獲得內(nèi)部資源,并且替換背景* @param apkDir apk目錄 * @param apkName apk名字,帶.apk * @throws Exception */ private void dynamicLoadApk(String apkPath, String apkPackageName) throws Exception {//在應(yīng)用安裝目錄下創(chuàng)建一個名為app_dex文件夾目錄,如果已經(jīng)存在則不創(chuàng)建,這個目錄主要是最優(yōu)化目錄,用于緩存dex文件File optimizedDirectoryFile = getDir(DEX, Context.MODE_PRIVATE); //打印路徑 理論上是/data/data/package/app_dexLog.v(TAG, optimizedDirectoryFile.getPath().toString()); //構(gòu)建DexClassLoaderDexClassLoader dexClassLoader = new DexClassLoader(apkPath, optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader()); //通過使用apk自己的類加載器,反射出R類中相應(yīng)的內(nèi)部類進而獲取我們需要的資源idClass<?> clazz = dexClassLoader.loadClass(apkPackageName + DRAWABLE); //得到名為about_log的這張圖片字段,這個圖片是為安裝apk里面的圖片F(xiàn)ield field = clazz.getDeclaredField(IMAGE_ID); //得到圖片id int resId = field.getInt(R.id.class);//得到插件apk中的Resource Resources mResources = getPluginResources(apkPath);if (mResources != null) { //通過插件apk中的Resource得到resId對應(yīng)的資源 Drawable btnDrawable = mResources.getDrawable(resId);mLayout.setBackgroundDrawable(btnDrawable); } else {Log.d(TAG, "mResources is null");}} }
點擊TextView內(nèi)容“換皮膚”來觸發(fā)的,當(dāng)初背景是設(shè)置的一個機器人。

第六步:運行項目爆結(jié)果照片

點擊換皮護之前背景圖片如下 點擊換圖片之后背景圖片如下 ok,說明獲取到了這種圖片資源,換皮膚成功,這里只是代表換皮膚意思,效果比較丑,不要噴哈。

第七步、總結(jié)

這樣做資源和宿主分離了,減輕了apk負擔(dān),同時也有解耦和作用,我們手機一些瀏覽器換模式(日和夜)、QQ換皮膚、表情包、線上下載線下維護、是項目更加靈活,可擴展性更好,同時也復(fù)習(xí)了DexClassLoader和反射相關(guān)知識。

?

?


?

?

總結(jié)

以上是生活随笔為你收集整理的Android插件化开发之用DexClassLoader加载未安装的APK资源文件来实现app切换背景皮肤的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产噜噜噜噜噜久久久久久久久 | 久久加勒比| 国内成人精品 | 亚洲Av无码成人精品区伊人 | 在线不卡国产 | 国产东北女人做受av | 俄罗斯porn | 亚洲色图av在线 | 偷拍老头老太高潮抽搐 | 一级久久| 男女互操在线观看 | 51成人精品网站 | 国产少女免费观看高清 | 精品国产免费av | 亚洲玖玖爱 | 91亚洲精品久久久久久久久久久久 | 无码人妻一区二区三区免费n鬼沢 | 91视频成人免费 | 亚洲国产高清在线 | 亚洲一区二区三区免费视频 | 久久精品99| 王者后宫yin肉h文催眠 | 亚洲日本中文字幕在线 | 久久高清毛片 | 国产欧美精品一区 | 淫视频网站 | 国产亚洲精品成人无码精品网站 | 亚洲区综合 | 国产熟妇一区二区三区aⅴ网站 | 中文字幕一区二区三区视频 | 午夜国产在线观看 | 五月婷婷色| 少妇性l交大片免潘金莲 | 熟妇人妻av无码一区二区三区 | 欧美日韩国产精品成人 | 欧美综合视频在线观看 | 中文字幕福利视频 | 成人片在线播放 | 91精品一区二区三区四区 | 天天干天天搞天天射 | 无码人妻一区二区三区在线 | 国产专区欧美专区 | 亚洲30p| 国产欧美一区二区精品性色超碰 | 精品中文视频 | 国产电影一区二区三区爱妃记 | a级黄色片 | av2014天堂网| 亚洲综合色一区 | 亚洲高清视频在线观看 | 亚洲日b视频 | 污视频导航 | 色射网| 欧美精品免费在线观看 | 前任攻略在线观看免费完整版 | 久久99精品国产91久久来源 | 激情五月色播五月 | 国产精品无码av在线有声小说 | 欧美一区二不卡视频 | 人妖av在线| 婷婷四房播播 | 先锋资源av在线 | 黑丝美女啪啪 | 精品一卡二卡三卡 | 亚洲国产免费 | 91毛片视频| 男生脱女生衣服 | 男人把女人捅爽 | 探花系列在线观看 | 色综合久久88色综合天天 | 黑人粗进入欧美aaaaa | 99久久人妻无码精品系列 | 五月婷婷爱爱 | 久久精品无码毛片 | 国产精品免费精品一区 | 中文av网站 | 激情婷 | 天堂av8| 在线理论视频 | 欧美成人国产 | 一级女性全黄久久生活片免费 | 欧美三级视频在线观看 | 美女福利在线观看 | 日韩乱码人妻无码中文字幕久久 | 91黄瓜视频 | 都市激情校园春色亚洲 | 91丝袜呻吟高潮美腿白嫩 | 奇米精品一区二区三区在线观看 | av网址在线 | 免费荫蒂添的好舒服视频 | 午夜一级黄色片 | 免费a v网站| 久久伊人操 | 亚洲视频在线一区 | 欧美高大丰满少妇xxxx | 亚洲天堂av在线免费观看 | 神马午夜视频 | 国产一区欧美日韩 | 色婷婷av一区二区三区软件 |