在android开发中,有很多时候是需要用到动态加载的,今天学习在android中动态加载已安装的apk中的方法。
首先,我们需要新建一个用来被加载的android工程,暂且给他取名叫做:plugproj
在plugproj中新建一个类Dynamic,在这个类中,我们新建一些方法,等会我们会分别在该工程安装和没有安装的情况下加载这些方法,Dynamic.java如下:
package com.example.plugproj; import android.app.Activity; import android.widget.Toast; public class Dynamic { Activity mActivity = null; public void init(Activity activity) { // TODO Auto-generated method stub mActivity = activity; } public void showDialog() { // TODO Auto-generated method stub Toast.makeText(mActivity, "show dialog test gaga",Toast.LENGTH_LONG).show(); } public void showWindow() { // TODO Auto-generated method stub Toast.makeText(mActivity, "show window test gaga",Toast.LENGTH_LONG).show(); } public int addMethod(int a, int b) { // TODO Auto-generated method stub return a + b; } }可以看到init方法中,我们为其mActivity赋值,这是为了得到context对象,好让toast可以执行。
这里还需要注意,我们需要为plugproj工程的启动的activity中配置一个action,好让我们可以通过这个action来加载对应的包,如下:
<activity android:name="com.example.plugproj.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="com.haha.android.plugin"/> </intent-filter> </activity>
安装plugproj工程。
接下来看看另一个主工程,即加载plugproj的工程,主要代码如下:
//创建一个意图,用来找到指定的apk Intent intent = new Intent("com.haha.android.plugin", null); //获得包管理器 PackageManager pm = getPackageManager(); List<ResolveInfo> resolveinfoes = pm.queryIntentActivities(intent, 0); //获得指定的activity的信息 ActivityInfo actInfo = resolveinfoes.get(0).activityInfo; //获得包名 String pacageName = actInfo.packageName; //获得apk的目录或者jar的目录 String apkPath = actInfo.applicationInfo.sourceDir; //dex解压后的目录,注意,这个用宿主程序的目录,android中只允许程序读取写自己 //目录下的文件 String dexOutputDir = getApplicationInfo().dataDir; //native代码的目录 String libPath = actInfo.applicationInfo.nativeLibraryDir; //创建类加载器,把dex加载到虚拟机中 DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath, this.getClass().getClassLoader()); try { clazz = calssLoader.loadClass(pacageName+".Dynamic"); obj = clazz.newInstance(); Method initMethod = clazz.getDeclaredMethod("init",Activity.class); initMethod.invoke(obj,MainActivity.this); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } 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(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); }接下来是执行上面三个方法的onclick事件:
@Override public void onClick(View arg0) { // TODO Auto-generated method stub try { switch (arg0.getId()) { case R.id.id_dialog: Method dialogMethod = clazz.getDeclaredMethod("showDialog",null); dialogMethod.invoke(obj, null); Log.d("Tag","show dialog runs ..."); break; case R.id.id_window: Method windowMethod = clazz.getDeclaredMethod("showWindow",null); windowMethod.invoke(obj,null); Log.d("Tag","show window runs ..."); break; case R.id.id_plus: Class[] param = new Class[2]; param[0] = Integer.TYPE; param[1] = Integer.TYPE; Method method = clazz.getDeclaredMethod("addMethod",param); int result = (Integer) method.invoke(obj, 22,33); Toast.makeText(MainActivity.this,"result is :"+result,Toast.LENGTH_SHORT).show(); Log.d("Tag","add runs ..."); break; default: Toast.makeText(MainActivity.this,"no property id",Toast.LENGTH_LONG).show(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }
这样就实现了加载已经安装的apk中的方法。
接下来看看加载已经打包成为jar文件的方式:
首先需要将plugproj导出为jar文件,我们这里导出为plugproj.jar,注意这时候的jar文件在android手机上是不能直接加载的,我们需要将他转换成dex文件,怎么转换呢,在sdk下有一个dx工具,可以用它来实现,将plugproj.jar拷贝到拥有dx文件的目录下,一般是在platform_tool有时在build_tools文件夹下,然后用命令进入该文件下:
执行如下命令:
dx --dex --output 优化过的jar 没有优化过的jar
此时就会生成一个优化过的jar文件,这个jar文件就是我们的android手机可以直接加载的。将该jar文件拷贝到手机指定的目录下:
以下是关键代码:
String path = "/storage/sdcard0/183/mydynamic_help.jar"; dexPath = getDir("dex", Context.MODE_PRIVATE).getAbsolutePath(); dexClassLoader = new DexClassLoader(path, dexPath, null, getClassLoader()); try { clazz = dexClassLoader.loadClass("com.example.plugproj.Dynamic"); try { obj = clazz.newInstance(); Method method = clazz.getDeclaredMethod("init",Activity.class); method.invoke(obj, MainActivity.this); Log.d("Tag","dynamic init runs ..."); } catch (InstantiationException e1) { e1.printStackTrace(); } catch (IllegalAccessException e1) { e1.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } catch (ClassNotFoundException e) { e.printStackTrace(); }为按钮绑定onclick事件:
public void onClick(View arg0) { // TODO Auto-generated method stub try { switch (arg0.getId()) { case R.id.id_dialog: Method method = clazz.getDeclaredMethod("showDialog",null); method.invoke(obj, null); Log.d("Tag","show dialog runs ..."); break; case R.id.id_window: method = clazz.getDeclaredMethod("showWindow",null); method.invoke(obj, null); Log.d("Tag","show window runs ..."); break; case R.id.id_plus: <span style="white-space:pre"> </span>Class[]param = new Class[2]; param[0] = Integer.TYPE; param[1] = Integer.TYPE; method = clazz.getDeclaredMethod("addMethod",param); int result = (Integer) method.invoke(obj,23,25); <span style="white-space:pre"> </span>Toast.makeText(MainActivity.this,"result is :"+result,Toast.LENGTH_SHORT).show(); Log.d("Tag","add runs ..."); break; <span style="white-space:pre"> </span>default: Toast.makeText(MainActivity.this,"no property id",Toast.LENGTH_LONG).show(); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } }最后千万要记住,添加读写sd卡的权限。
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
原文地址:http://blog.csdn.net/mockingbirds/article/details/44945309