android动态加载(二)

         上一篇说了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();  
        }  

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


        

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。