在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