android动态加载(二)

来源:互联网 发布:ocr日文识别软件 编辑:程序博客网 时间:2024/05/16 18:17

         上一篇说了android中的动态加载,即在android工程中动态加载经过dx操作以后的jar文件和没有安装的apk文件,今天我们来看看怎么执行已经安装的apk中的类中的方法。

所以,我们会需要两个工程,一个是plugone,这个是我们暴露给外面的方法的一个android工程。另外一个我们暂且给他起名为useplugone吧。

      

         先来看看plugone工程,我们在plugone工程中有这样一个类,用来暴露给调用者一个方法:

package com.example.plugone;public class Plugin1 {public int add(int a,int b) {return a + b;}}

    另外,还需要在清单文件中为MainActivity中添加一个action,这样做是为了在useplugone工程中,找到该apk中的资源,代码如下如下:

<activity            android:name="com.example.plugone.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>


接下来就是我们的useplugone工程了,主要的代码如下:

 //创建一个意图,用来找到指定的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;  //  /data/app/com.example.plugone-1.apk                  //native代码的目录          String libPath = actInfo.applicationInfo.nativeLibraryDir;  //  /data/app-lib/com.example.plugone-1        //创建类加载器,把dex加载到虚拟机中          DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath,                  this.getClass().getClassLoader());                    //利用反射调用插件包内的类的方法                    try {              Class<?> clazz = calssLoader.loadClass(pacageName+".Plugin1");                            Object obj = clazz.newInstance();              Class[] param = new Class[2];              param[0] = Integer.TYPE;              param[1] = Integer.TYPE;                            Method method = clazz.getMethod("add", param);                            Integer ret = (Integer)method.invoke(obj, 1,12);              int result = ret.intValue();            System.out.println(result);                      } catch (Exception e) {              e.printStackTrace();          }

             源码下载


          下面我们在重新看看在android工程中动态加载jar文件,同样该jar文件是需要经过dx处理的,不懂得可以看看上一篇文章android动态加载,在上一篇中我们导出jar文件时用的java工程,并且当时还指明导出jar文件时不要选择将接口导出,这是因为在这个android工程中同样声明了该接口,所以如果将接口导出,会出现接口冲突,这次我们改为将android工程导出为jar文件,并且可以导出接口,这次我们创建一个android工程plugtwo,然后导出jar文件,经过dx处理以后,将其放入另一个useplugtwo工程的asset文件夹下,来让程序自动运行。下面分别来看看plugtwo和useplugtwo工程的代码:

   
        首先是plugtwo:

首先我们声明一个接口文件:

package com.example.plugtwo;public interface Iinterface {      public void call();      public String getData();  }  
然后创建一个实体类实现该接口,这次我们让该实体类可以弹出toast,所以需要创建一个context成员变量

package com.example.plugtwo;import android.content.Context;  import android.widget.Toast;    public class IClass implements Iinterface {        private Context context;        public IClass(Context context) {          super();          this.context = context;      }        @Override      public void call() {          Toast.makeText(context, "call method", 0).show();      }        @Override      public String getData() {          return "hello,i am from IClass";      }    } 
         程序比较简单,我就不细说了。然后将该程序导出为jar文件,这里我们同样导出为load.jar,然后将该load.jar文件拷贝到具有dx命令的sdk的文件夹下,执行dx --dex --output=testdex.jar load.jar  ,成功以后会发现在该文件夹下生成了一个testdex.jar文件,这个jar文件就是我们的android工程可以直接调用的jar文件。

   现在我们新建一个名为useplugtwo的android工程,并且将该testdex.jar文件拷贝到useplugtwo的asset工程中,接下来看看我们的useplugtwo中的MainActivity代码:

    首先我们调用copyFromAsset方法将asset文件夹下的testdex.jar文件拷贝到sdcard的指定目录,代码如下:

             public void copyFromAsset() {InputStream ins = null;FileOutputStream fos = null;try {ins = getAssets().open("testdex.jar");//String getStr = "/storage/sdcard0/liuhang/";File file = new File(dir+"/testjar/");if (!file.exists()) {file.mkdirs();}file = new File(file,"testdex.jar");fos = new FileOutputStream(file);int count = 0;byte[]b = new byte[1024];while ((count = ins.read(b)) != -1) {  fos.write(b,0,count);}} catch (Exception e) {e.printStackTrace();}finally{try {if (fos != null) {fos.flush();fos.close();}} catch (Exception e) {e.printStackTrace();}}}

   然后就是利用反射动态执行生成的testdex.jar文件中的方法,如下:

 File file = new File("/storage/sdcard0/testjar/testdex.jar");          final File optimizedDexOutputPath = getDir("temp", Context.MODE_PRIVATE);        String filePath = file.getAbsolutePath();        String optimizedPath = optimizedDexOutputPath.getAbsolutePath();        //optimizedPath == data/data/com.example.useplugtwo/app_temp        DexClassLoader classLoader = new DexClassLoader(file.getAbsolutePath(),                  optimizedDexOutputPath.getAbsolutePath(), null,                  getClassLoader());          try {              Class<?> iclass = classLoader.loadClass("com.example.plugtwo.IClass");              Constructor<?> istructor = iclass.getConstructor(Context.class);              //利用反射原理去调用               Method method = iclass.getMethod("call", null);              String data = (String) method.invoke(istructor.newInstance(this), null);              System.out.println(data);          } catch (Exception e) {              // TODO Auto-generated catch block               e.printStackTrace();          }  

    今天就到这里,该休息了。
    源码连接


        

0 0
原创粉丝点击