动态化加载未安装的apk

在没有安装apk的情况下提供资源给宿主app使用

使用DexClassLoader这个类来加载未安装的apk,提供资源供宿主app使用

首先的思路肯定也是下载到我们手机的某个目录里。然后分别得到插件apk的信息(名称、包名等),然后显示可用的插件,最后动态加载apk获得资源

然后要想到的问题就是要怎么获取到apk的包信息,因为现在要的是获取未安装apk的资源,无法通过createPackageContext(…);方法来构建出一个context,所以这时只有在Resource上下功夫

1,在PackageManager类中有这样的一个方法getPackageArchiveInfo(),是用来获取未安装apk的信息

`public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags)` 

这个方法的参数刚好是传入一个FilePath,然后返回apk文件的PackageInfo信息:

`/**
 * 获取未安装apk的信息
 * @param context
 * @param archiveFilePath apk文件的path
 * @return
 */
private String[] getUninstallApkInfo(Context context, String archiveFilePath) {
    String[] info = new String[2];
    PackageManager pm = context.getPackageManager();
    PackageInfo pkgInfo = pm.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);
    if (pkgInfo != null) {
        ApplicationInfo appInfo = pkgInfo.applicationInfo;
        String versionName = pkgInfo.versionName;//版本号
        Drawable icon = pm.getApplicationIcon(appInfo);//图标
        String appName = pm.getApplicationLabel(appInfo).toString();//app名称
        String pkgName = appInfo.packageName;//包名
        info[0] = appName;
        info[1] = pkgName;
    }
    return info;
}`

2、得到对应未安装apk的Resource对象,我们需要通过反射来获得:

`/**
 * @param apkName
 * @return 得到对应插件的Resource对象
 */
private Resources getPluginResources(String apkName) {
    try {
        AssetManager assetManager = AssetManager.class.newInstance();
        Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);//反射调用方法addAssetPath(String path)
        //第二个参数是apk的路径:Environment.getExternalStorageDirectory().getPath()+File.separator+"plugin"+File.separator+"apkplugin.apk"
        addAssetPath.invoke(assetManager, apkDir+File.separator+apkName);//将未安装的Apk文件的添加进AssetManager中,第二个参数为apk文件的路径带apk名
        Resources superRes = this.getResources();
        Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(),
                superRes.getConfiguration());
        return mResources;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}`

通过得到AssetManager中的内部的方法addAssetPath,将未安装的apk路径传入从而添加进assetManager中,然后通过new Resource把assetManager传入构造方法中,进而得到未安装apk对应的Resource对象。

解决了上面的两个问题,接下来就是加载未安装的apk获得它的内部资源。

/**
 * 加载apk获得内部资源
 * @param apkDir apk目录
 * @param apkName apk名字,带.apk
 * @throws Exception
 */
private void dynamicLoadApk(String apkDir, String apkName, String apkPackageName) throws Exception {
    File optimizedDirectoryFile = getDir("dex", Context.MODE_PRIVATE);//在应用安装目录下创建一个名为app_dex文件夹目录,如果已经存在则不创建
    Log.v("zxy", optimizedDirectoryFile.getPath().toString());// /data/data/com.example.dynamicloadapk/app_dex
    //参数:1、包含dex的apk文件或jar文件的路径,2、apk、jar解压缩生成dex存储的目录,3、本地library库目录,一般为null,4、父ClassLoader
    DexClassLoader dexClassLoader = new DexClassLoader(apkDir+File.separator+apkName, optimizedDirectoryFile.getPath(), null, ClassLoader.getSystemClassLoader());
    Class<?> clazz = dexClassLoader.loadClass(apkPackageName + ".R$mipmap");//通过使用apk自己的类加载器,反射出R类中相应的内部类进而获取我们需要的资源id
    Field field = clazz.getDeclaredField("one");//得到名为one的这张图片字段
    int resId = field.getInt(R.id.class);//得到图片id
    Resources mResources = getPluginResources(apkName);//得到插件apk中的Resource
    if (mResources != null) {
        //通过插件apk中的Resource得到resId对应的资源
        findViewById(R.id.background).setBackgroundDrawable(mResources.getDrawable(resId));
    }
}

其中通过new DexClassLoader()来创建未安装apk的类加载器,我们来看看它的参数:

    public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)  

dexPath就是apk文件的路径,
optimizedDirectory就是apk解压缩后的存放dex的目录,要注意的是,在4.1以后该目录不允许在sd卡上,所以我们用getDir()方法在应用内部创建一个dexOutputDir。
libraryPath为本地的library,一般为null。 parent为 父加载器

后面就是用反射来获取出需要的资源。

下面是demo演示的效果,我是把三个apk插件先放在assets目录下,然后copy到sd上来模仿下载过程,然后加载出相应插件的资源:

copyApkFile("apkthemeplugin-1.apk"); 
copyApkFile("apkthemeplugin-2.apk"); 
copyApkFile("apkthemeplugin-3.apk"); 

显示前
这里写图片描述

显示后
这里写图片描述

X-plore是具有树视图,LAN / FTP / Root / Clouds等的双窗格文件管理器。 描述 亮点: ●双窗格树视图 ●Root,FTP,SMB,Sqlite,Picasa,Zip,Rar,7zip explorer ●云存储访问:Google Drive™,Dropbox,Box.net,Mega.co.nz ***,SkyDrive,Webdav ,Yandex.disk,Ubuntu One,SugarSync,自卸车,Copy.com ●SSH文件传输(SFTP)和SSH Shell - _http://bit.ly/xp-ssh *** ●应用程序管理器 ●WiFi文件共享** * - _http://bit.ly/xp-wifi● 从PC网络浏览器管理文件*** - _http://bit.ly/xp-web●常用 文件夹 ●内置图像,视频,音频查看器,文本 ●十六进制查看器 ●快速图像查看器,可缩放并滑动到上一个/下一个图像 ●图像和视频的缩略图以及各种文件类型(取决于相关应用程序) ●多选 - 始终可用,但不会令人不安 ●将APK文件视为ZIP ●共享 - 通过蓝牙,电子邮件或任何设备支持的任何位置发送文件 ●可配置按钮和快捷键 ●与Zip无缝协作(如同这是正常的文件夹) ***标记的功能是支付 - 他们需要捐赠 X-plore允许您查看Android设备内部。还在外面。 这是一个双窗格浏览器,同时显示两个文件夹,并且从一个窗格到另一个窗格完成常用操作,如复制文件。 X-plore在树状视图中显示文件夹层次结构,以便清晰定位并快速切换到其他位置。 您可以浏览设备的内部,如果您是高级用户并且设备已植根,则可以更改系统数据 - 备份文件,删除不需要的应用程序等。 如果您是标准用户,您可以选择隐藏内部存储器,并确保不要弄乱系统。 您可以在设备上轻松查看大容量存储器的内容,或者可能连接的USB记忆棒。 简单的应用程序管理器允许查看,运行,复制,共享,卸载和进一步探索已安装的应用程序 WiFi文件共享 通过WiFi从其他Android设备访问Android设备上的文件。 从PC Web浏览器访问从PC 管理Android设备上的文件。 支持访问FTP和FTPS(安全FTP)服务器。 可以配置多个服务器。 X-plore可以在LAN(局域网)中的其他计算机上显示共享文件夹。 系统允许访问连接到LAN的计算机上的共享文件夹,如果它支持SMB协议,则甚至可以访问远程服务器。 X-plore可以访问各种Web存储“云”服务器,并访问其文件。 您需要在支持的Web服务中拥有帐户,然后您可以通过X-plore访问在线存储的文件。 支持的还有SSH文件传输(SFTP)和终端shell模拟器。 主要操作与管理文件和文件夹有关 - 查看,复制,移动,删除,压缩到Zip,提取,重命名,共享等。 Picasa相册 X-plore可以列出Picasa相册中的图片,下载它们,创建相册,上传和删除照片,编辑字幕。 SQLite数据库查看器 X-plore可以将SQLite数据库文件(具有.db扩展名的文件)显示为可扩展的表列表,每个表包含具有数据库条目的行和列的列表。 主要交互通过触摸屏完成,单击文件夹或文件以打开文件,或长按打开上下文菜单,其中包含可在特定单击项目或多个选定项目上执行的选项。 多选允许一次对更多文件进行操作。单击复选框可以选择文件。通过单击父文件夹的复选框,也可以选择文件夹中的所有文件或清除选择。 打开文件可能意味着对最流行的文件类型使用内置查看器之一:图像,音频,视频和文本。 或者您可以将X-plore配置为使用系统应用程序打开文件,在这种情况下,可以启动可以打开特定文件的系统预定义应用程序。 档案(目前支持Zip,Rar和7zip)显示为其他文件夹。 按钮栏可以在两个窗格之间进行额外的交互,并且是完全可配置的。 什么是新的 3.88.00: ●重新设计的图标 ●几乎没有其他修复 3.88.01: ●修复在BlueStacks模拟器上运行 3.88.02: ●修复WiFi共享图标
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值