在Android开发中,有时,会需要在应用中额外下载一些其他的插件,进行额外的功能。比如QQ的皮肤,掌阅的语音包,需要在使用过程中下载额外的apk,但是额外的apk无需安装我们即可使用里面的资源和方法。这样的功能就是用ClassLoader实现的。在此记录一些ClassLoader的使用方法与经验用以备忘。

  这里要实现的例子很简单。我在一个额外的工程B中实现一个Toast的方法,然后用工程A中的类去调用这个方法。

  首先来看看工程B:

  工程B中我先实现了一个简单的类

public class ToastClass {

    public void alertMessage(Context context , String str){
        Toast.makeText(context , str , Toast.LENGTH_SHORT ).show();
    }

}

 

  等会在工程A中传入上下文和要显示的内容即可弹出一个toast窗。

  但是,并不是任何apk中的内我们都能随意调用,我们首先要做一个协定。协定的方式是action,我们在AndroidManifest中的MainActivity的Intent-Filter中加入以下一行

<action android:name="com.dream.fishbonelsy.blurtestdemo"/>

  这样,工程A就以此为凭据调用工程B中的类和方法。

  下面在工程A中,使用ClassLoader来获取这个apk

  

        // 获取APK
        Intent intent = new Intent("com.dream.fishbonelsy.blurtestdemo" , null);
        // 获取包管理器
        PackageManager packageManager = context.getPackageManager();
        List<ResolveInfo> resolveInfos = packageManager.queryIntentActivities(intent, 0);
        // 获得指定的activity的信息
        ActivityInfo activityInfo = resolveInfos.get(0).activityInfo;
        // 获得包名
        String packageName = activityInfo.packageName;
        // 获得apk目录
        String apkPath = activityInfo.applicationInfo.sourceDir;
        //dex解压后的目录,注意,这个用宿主程序的目录,android中只允许程序读取写自己
        //目录下的文件
        String dexOutputDir = context.getApplicationInfo().dataDir;
        //native代码的目录
        String libPath = activityInfo.applicationInfo.nativeLibraryDir;
        //创建类加载器,把dex加载到虚拟机中
        DexClassLoader calssLoader = new DexClassLoader(apkPath, dexOutputDir, libPath,
                this.getClass().getClassLoader());

 

  这里通过包管理器和工程B的action获得一系列需要的信息:apk目录,文件目录,native代码录目,包名。

  获取了包名之后,如果我们知道类名和方法名就可以直接调用了。

  

//利用反射调用插件包内的类的方法

        try {
            Class<?> clazz = calssLoader.loadClass(packageName+".ToastClass");

            Object obj = clazz.newInstance();
            Class[] param = new Class[2];
            param[0] = Context.class;
            param[1] = String.class;
            Method method = clazz.getMethod("alertMessage", param);
            method.invoke(obj,context , "hello android");

            Log.i("Host", "return result is " );

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

  通过反射,我们即可调用工程B中的方法。

 

Done

 

posted on 2016-01-24 14:10  Fishbonell  阅读(237)  评论(0编辑  收藏  举报