Android动态加载jar、apk的实现
前段时间到阿里巴巴参加支付宝技术分享沙龙,看到支付宝在Android使用插件化的技术,挺好奇的。正好这几天看到了农民伯伯的相关文章,因此简单整理了下,有什么错误希望大神指正。
核心类
1.1 DexClassLoader类
可以加载jar/apk/dex,可以从SD卡中加载未安装的apk。
1.2 PathClassLoader类
只能加载已经安装到Android系统中的apk文件。
一、正文
1.1动态加载jar
类似于eclipse的插件化实现, 首先定义好接口, 用户实现接口功能后即可通过动态加载的方式载入jar文件, 以实现具体功能。注意, 这里的jar包需要经过android dx工具的处理 , 否则不能使用。
首先我们定义如下接口 :
package com.example.interf; /** * @Title: ILoader.java * @Package com.example.loadjardemo * @Description: 通用接口, 需要用户实现 * @version V1.0 */public interface ILoader { public String sayHi();}用户需实现,该接口, 并且将工程导出为jar包的形式。
示例如下 :
public class JarLoader implements ILoader {public JarLoader() {}@Overridepublic String sayHi() {return "I am jar loader.";}}
最后, 实现功能的代码打包成jar包 :
首先选中工程, 右键后选择“导出”, 然后选择“java”-----“jar文件”, 然后将你的具体功能实现类导出为jar,文件名为loader.jar,如下图所示 :
将打包好的jar拷贝到SDK安装目录android-sdk-windows\platform-tools下,DOS进入这个目录,执行如下命令:
dx --dex --output=loader_dex.jar loader.jar
然后将loader_dex.jar放到android手机中, 这里我们放到SD卡根目录下。
/** * * @Title: loadJar * @Description: 项目工程中必须定义接口, 而被引入的第三方jar包实现这些接口,然后进行动态加载 。 * 相当于第三方按照接口协议来开发, 使得第三方应用可以以插件的形式动态加载到应用平台中。 * @return void * @throws */private void loadJar(){ final File optimizedDexOutputPath = new File(Environment.getExternalStorageDirectory().toString() + File.separator + "loader_dex.jar"); BaseDexClassLoader cl = new BaseDexClassLoader(Environment.getExternalStorageDirectory().toString(), optimizedDexOutputPath, optimizedDexOutputPath.getAbsolutePath(), getClassLoader()); Class libProviderClazz = null; try { // 载入JarLoader类, 并且通过反射构建JarLoader对象, 然后调用sayHi方法 libProviderClazz = cl.loadClass("com.example.interf.JarLoader"); ILoader loader = (ILoader)libProviderClazz.newInstance(); Toast.makeText(MainActivity.this, loader.sayHi() , Toast.LENGTH_SHORT).show(); } catch (Exception exception) { // Handle exception gracefully here. exception.printStackTrace(); }}
效果如下图所示 :
1.2 加载未安装的apk
首先新建一个Android项目, 定义如下接口 :
public interface ISayHello {public String sayHello() ;}
定义一个Activity实现该接口, 如下:
package com.example.loaduninstallapkdemo;import android.app.Activity;import android.os.Bundle;import android.view.Menu;import android.view.View;import android.view.View.OnClickListener;import android.widget.Toast;/** * * @ClassName: UninstallApkActivity * @Description: 这是被动态加载的Activity类 * */public class UninstallApkActivity extends Activity implements ISayHello{private View mShowView = null ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mShowView = findViewById(R.id.show) ;mShowView.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(UninstallApkActivity.this, "这是已安装的apk被动态加载了", Toast.LENGTH_SHORT).show();}}) ;}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.activity_main, menu);return true;}@Overridepublic String sayHello(){return "Hello, this apk is not installed";}}然后将该编译生apk, 并且将该apk拷贝到SD卡根目录下。
动态加载未安装的apk
/** * * @Title: loadUninstallApk * @Description: 动态加载未安装的apk * @return void * @throws */private void loadUninstallApk(){ String path = Environment.getExternalStorageDirectory() + File.separator; String filename = "LoadUninstallApkDemo.apk"; // 4.1以后不能够将optimizedDirectory设置到sd卡目录, 否则抛出异常. File optimizedDirectoryFile = getDir("dex", 0) ; DexClassLoader classLoader = new DexClassLoader(path + filename, optimizedDirectoryFile.getAbsolutePath(), null, getClassLoader()); try { // 通过反射机制调用, 包名为com.example.loaduninstallapkdemo, 类名为UninstallApkActivity Class mLoadClass = classLoader.loadClass("com.example.loadunstallapkdemo.UninstallApkActivity"); Constructor constructor = mLoadClass.getConstructor(new Class[] {}); Object testActivity = constructor.newInstance(new Object[] {}); // 获取sayHello方法 Method helloMethod = mLoadClass.getMethod("sayHello", null); helloMethod.setAccessible(true); Object content = helloMethod.invoke(testActivity, null); Toast.makeText(MainActivity.this, content.toString(), Toast.LENGTH_LONG).show(); } catch (Exception e) { e.printStackTrace(); }}
A class loader that loads classes from.jar
and.apk
files containing aclasses.dex
entry. This can be used to execute code not installed as part of an application.
This class loader requires an application-private, writable directory to cache optimized classes. UseContext.getDir(String, int)
to create such a directory:
File dexOutputDir = context.getDir("dex", 0);
Do not cache optimized classes on external storage.External storage does not provide access controls necessary to protect your application from code injection attacks.
效果如图 :
1.3 加载已安装的apk
将1.2中的apk安装到手机中,我的例子中,该apk的包名为“com.example.loaduninstallapkdemo”,Activity名为"UninstallApkActivity". 加载代码如下 :
/** * * @Title: loadInstalledApk * @Description: 动态加载已安装的apk * @return void * @throws */private void loadInstalledApk() {try {String pkgName = "com.example.loaduninstallapkdemo";Context context = createPackageContext(pkgName, Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE) ;// 获取动态加载得到的资源Resources resources = context.getResources() ;// 过去该apk中的字符串资源"tips", 并且toast出来,apk换肤的实现就是这种原理String toast = resources.getString(resources.getIdentifier("tips", "string", pkgName) ) ;Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show() ;Class cls = context.getClassLoader().loadClass(pkgName + ".UninstallApkActivity") ;// 跳转到该ActivitystartActivity(new Intent(context, cls)) ;} catch (NameNotFoundException e) {e.printStackTrace();}catch (ClassNotFoundException e) {Log.d("", e.toString()) ;}}</span>
效果如图:
消息被Toast出来, 并且跳转到了目标Activity.
相关资料 :探秘腾讯Android手机游戏平台之不安装游戏apk直接启动法
更多相关文章
- 自己写的一套应用管理系统(包含一套app系统,一套后台web管理系统《
- Android studio 使用NDK 实现串口 动态库
- android imageview显示不全或者gilde加载不全
- [置顶] 如何演示你的App?Android录制Gif动态图教程
- Android 利用addView 动态给Activity添加View组件
- Android 动态加载布局文件
- Android Bitmap的加载和Cache
- webview 笔记二(android和js交互、包括链接跳转常见问题处理,加载
- 第二章 Android动态加载、热更新、热修复、插件化系列文章 之 认