动态加载apk文件并调用其代码
2016-06-07 21:03
295 查看
转载:文中思路来自链接:http://blog.csdn.net/jiangwei0910410003/article/details/17679823
一、编写动态功能类
编写一个apk,用于导出方法,供其他apk调用
IDynamic是一个接口,用于外界可以使用接口进行调用
此外,在manifest.xml中,需要声明一个Action,用于其他apk能通过Intent检索到此apk
,名字无所谓,只要其他apk通过Intent能检索到此apk就可以
二、调用动态apk
根据目标apk包名,获取其包路径、catche路径等信息,创建DexClassLoader对象,并通过反射机制来调用动态apk中的功能函数代码
这里是此处通过反射机制来调用代码,还没有完成通过接口来调用的功能,待继续调试
这里是从已经安装的apk获取路径等信息,如果是目录下静态的apk(并没有安装到手机上),直接把构建DexClassLoader的第一个参数改为静态apk的绝对路径,其他不需要变化,即可正常运行;
不过如果使用PathClassLoader,来加载,必须通过intent查找目标apk,同时目标apk必须已经安装,因为它只能加载固定目录下的apk,不能随意路径的apk
使用了IDynamic后,需要把对应的jar包引入到工程里的lib中,通过“Build Path”->”Configure Build Path”里进行添加;只把jar拷贝到这个目录是没有用的
一、编写动态功能类
编写一个apk,用于导出方法,供其他apk调用
public class Dynamic implements IDynamic{ private Activity mActivity; public Dynamic() { // TODO Auto-generated constructor stub } @Override public void init(Activity activity) { mActivity = activity; Log.i("TestDynamic", "In Dynamic::Init"); } @Override public void showBanner() { Toast.makeText(mActivity, "我是ShowBannber方法", 1500).show(); } @Override public void showDialog() { Toast.makeText(mActivity, "我是ShowDialog方法", 1500).show(); } @Override public void showFullScreen() { Toast.makeText(mActivity, "我是ShowFullScreen方法", 1500).show(); } @Override public void showAppWall() { Toast.makeText(mActivity, "我是ShowAppWall方法", 1500).show(); } @Override public void destory() { } }
IDynamic是一个接口,用于外界可以使用接口进行调用
public interface IDynamic { /**初始化方法*/ public void init(Activity activity); /**自定义方法*/ public void showBanner(); public void showDialog(); public void showFullScreen(); public void showAppWall(); /**销毁方法*/ public void destory(); }
此外,在manifest.xml中,需要声明一个Action,用于其他apk能通过Intent检索到此apk
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <action android:name="com.example.dynamiclib"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
,名字无所谓,只要其他apk通过Intent能检索到此apk就可以
二、调用动态apk
根据目标apk包名,获取其包路径、catche路径等信息,创建DexClassLoader对象,并通过反射机制来调用动态apk中的功能函数代码
public class MainActivity extends Activity implements OnClickListener{ private Button btn_show_banner; private Button btn_show_dialog; private Button btn_show_fullscreen; private Button btn_show_appwall; private final static String TAG = "TestDynamic"; private IDynamic libInterface = null; private Method method_init = null; private Method method_showBanner = null; private Method method_showDialog = null; private Method method_showFullScreen = null; private Method method_showAppWall = null; private Object object = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_show_banner = (Button) findViewById(R.id.button1); btn_show_dialog = (Button) findViewById(R.id.button2); btn_show_fullscreen = (Button) findViewById(R.id.button3); btn_show_appwall = (Button) findViewById(R.id.button4); /** * 调用静态apk时,需要有一个解压路径 * **/ File dexOutputDir = this.getDir("dex", 0); /** * 获取apk存放的路径,此apk未安装,在磁盘上,也可能是网络临时拉下来的 * 这里测试,放在SD卡根目录 * 主要是用于DexClassLoader的第一个参数使用 * **/ String dexPath = Environment.getExternalStorageDirectory().toString() + File.separator + "DynamicLib.apk"; Log.i(TAG, "ApkPath = "+dexPath); Log.i(TAG, "dexOutPath = "+dexOutputDir.getAbsolutePath()); /** * 与被调用的apk里名称约定要一致 * 用来找到指定apk,系统中多个时,下面get要注意取哪一个 * **/ Intent intent = new Intent("com.example.dynamiclib"); /** * 获取包管理器 * **/ PackageManager pm = getPackageManager(); List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0); /** * 获得指定Activity信息 * 如果有两个名字都是“com.example.dynamiclib”的Intent, * 则可能需要get(1),因为它会返回两个activity * **/ ActivityInfo activityInfo = resolveInfos.get(0).activityInfo; /** * 获得apk的目录或者jar的目录 * **/ String apkPath = activityInfo.applicationInfo.sourceDir; /** * native代码的目录 * **/ String libPath = activityInfo.applicationInfo.nativeLibraryDir; /** * 第一种方法 * 创建DexClassLoader * 把apk加载到虚拟机中 * 注意:这里的路径可以使用两种 * 即可以是dexPath(磁盘上的静态文件) * 也可以是通过pm获取的已经安装的apk的路径 * **/ DexClassLoader dcl = new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, this.getClass().getClassLoader()); /** * 第二种方法 * 使用PathClassLoader * 这里只能找到已经安装的apk * 并且路径,必须通过Intent、PM来联合获取,否则不能成功 * 此外,Intent里的名字,如果系统里存在两个名字相同的apk,name可能还需要试试第二个 * 因为不一定哪个在前,哪个在后 * **/ //PathClassLoader dcl = new PathClassLoader(apkPath, libPath, this.getClass().getClassLoader()); Log.i(TAG, "DexClassLoader ="+dcl); try { Log.i(TAG, "before dcl.loadClass"); /** * 加载目标class,名称为目标类名 * **/ Class<?> orgClass = dcl.loadClass("com.example.dynamiclib.Dynamic"); Log.i(TAG, "orgClass load is " + orgClass.getClassLoader().toString()); object = orgClass.newInstance(); Class[] param = new Class[1]; param[0] = Activity.class; method_init = orgClass.getMethod("init", param); method_showBanner = orgClass.getMethod("showBanner"); method_showDialog = orgClass.getMethod("showDialog"); method_showFullScreen = orgClass.getMethod("showFullScreen"); method_showAppWall = orgClass.getMethod("showAppWall"); if (method_init != null) { method_init.invoke(object, this); } Log.i(TAG, "libInterFace is "+libInterface); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block Log.i(TAG, "Not Found Class"); e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } btn_show_banner.setOnClickListener(this); btn_show_dialog.setOnClickListener(this); btn_show_fullscreen.setOnClickListener(this); btn_show_appwall.setOnClickListener(this); } void ShowTips(String tips) { Toast.makeText(this, tips, Toast.LENGTH_LONG).show(); } @Override public void onClick(View v) { // TODO Auto-generated method stub switch (v.getId()) { case R.id.button1: if (method_showBanner != null) { try { method_showBanner.invoke(object); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { ShowTips("类加载失败"); } break; case R.id.button2: if (method_showDialog != null) { try { method_showDialog.invoke(object); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { ShowTips("类加载失败"); } break; case R.id.button3: if (method_showFullScreen != null) { try { method_showFullScreen.invoke(object); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { ShowTips("类加载失败"); } break; case R.id.button4: if (method_showAppWall != null) { try { method_showAppWall.invoke(object); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } else { ShowTips("类加载失败"); } break; default: break; } } }
这里是此处通过反射机制来调用代码,还没有完成通过接口来调用的功能,待继续调试
这里是从已经安装的apk获取路径等信息,如果是目录下静态的apk(并没有安装到手机上),直接把构建DexClassLoader的第一个参数改为静态apk的绝对路径,其他不需要变化,即可正常运行;
不过如果使用PathClassLoader,来加载,必须通过intent查找目标apk,同时目标apk必须已经安装,因为它只能加载固定目录下的apk,不能随意路径的apk
使用了IDynamic后,需要把对应的jar包引入到工程里的lib中,通过“Build Path”->”Configure Build Path”里进行添加;只把jar拷贝到这个目录是没有用的
相关文章推荐
- Leetcode 349. Intersection of Two Arrays
- Java并发编程-Executor框架之Callable和Future接口
- Java中Fail-Fast机制、ConcurrentModificationException异常
- SpringMVC知识点梳理
- C/C++卡布列克运算验证
- RxJava 与 Retrofit 结合的最佳实践
- C++ STL中Map的按Key排序和按Value排序
- JDK常用的包
- Python(2)-第二天
- Python(1)-第一天
- Spring JdbcTemplate方法详解
- Python 装饰器 学习笔记
- php数组概述
- 武器类属性
- 主动轮廓线模型Snake模型简介&openCV中cvSnakeImage()函数代码分析
- Java 内存区域和GC机制
- 跟着小白学~如何使用二维数组打印杨辉三角
- C++智能指针(auro_ptr...)
- 实战 virtualenv 搭建多版本 Python 环境
- laravel随记(3)