基于插件开发的Android实现流程

转载请注明地址:http://blog.****.net/droyon/article/details/20951797

本文记述“柯元旦”Android内核剖析中基于类装载器的“插件”架构。

插件的概念:

1、插件不能独立运行,而必须运行于一个宿主程序中,即由宿主程序去调用插件程序。

2、插件一般可以独安装。

3、宿主程序中可以管理不同的插件,包括查看插件的数目,禁用或者使用某个插件。

4、宿主程序应该保证插件的向下兼容性,即新版本的宿主程序可以运行较老版本的插件。

下面详细看一下这种架构:


案例代码下载


1、宿主程序:

新建Android项目PluginDevAndroid

基于插件开发的Android实现流程


2、插件项目1:Plugin1

基于插件开发的Android实现流程

3、插件项目2:

基于插件开发的Android实现流程

4、综述:

4.1、接口类一般定义在宿主项目中,比如本例中的IPluginDev.java

4.2、插件项目需要应用IPluginDev时,则必须通过一个外部的jar包,并且该jar包是以Library的形式被添加到Plugin项目的build Path,而不是以“外部的”jar方式添加。

如图:

基于插件开发的Android实现流程

4.3、宿主程序想要知道系统中有哪些插件,可以定义一个Action,本例中使用的是如下action。

基于插件开发的Android实现流程

这样的话,宿主程序就可以通过PackageManager类的queryIntentActivities函数查询相关的插件的列表了。

得到了插件的PackageName,就可以访问插件的资源内容。例如:

  1. Resources res = pm.getResourcesForApplication(packageName);  
  2. int id = 0;  
  3. id = res.getIdentifier("version""string", packageName);  
  4. String version = res.getString(id);  
  5. Log.d("hlwang""MainActivity test version is:"+version);  
这段代码中,首先获取插件的Resource对象,接着得到名称为version字段的字符串id值,然后再调用getString获得该变量的值,于是宿主程序就知道插件程序中的资源内容了。


关键代码:

  1. private void test(){  
  2.         Intent intent = new Intent("com.example.plugindevandroid.plugin",null);  
  3.         final PackageManager pm = getPackageManager();  
  4.         final List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);  
  5.           
  6.         for(ResolveInfo r:plugins){  
  7.             ActivityInfo activityInfo = r.activityInfo;  
  8.               
  9.             String div = System.getProperty("path.separator");  
  10.             String packageName = activityInfo.packageName;  
  11.             String packageName0 = getPackageName();  
  12.             String dexPath = activityInfo.applicationInfo.sourceDir;  
  13.             String dexOutputDir1 = activityInfo.applicationInfo.dataDir;  
  14.             String dexOutputDir2 = getApplicationInfo().dataDir;  
  15.             String libPath = activityInfo.applicationInfo.nativeLibraryDir;  
  16.               
  17.             Log.d("hlwang""MainActivity test div is:"+div  
  18.                     +",packageName is:"+packageName  
  19.                     +",packageName0 is:"+packageName0  
  20.                     +",dexPath is:"+dexPath  
  21.                     +",dexOutputDir1 is:"+dexOutputDir1  
  22.                     +",dexOutputDir2 is:"+dexOutputDir2  
  23.                     +",libPath is:"+libPath);  
  24.               
  25.             DexClassLoader dexCl = new DexClassLoader(dexPath, dexOutputDir2, libPath, getClassLoader());  
  26.             Log.d("hlwang""MainActivity test clazzName is:"+packageName+".PluginVersion");  
  27.             try{  
  28.                 Class clazz = dexCl.loadClass(packageName+".PluginVersion");  
  29.                 IPluginDev plugin = (IPluginDev) clazz.newInstance();  
  30.                 String name = plugin.getName();  
  31.                 Log.d("hlwang""MainActivity test name is:"+name);  
  32.                   
  33.                 Resources res = pm.getResourcesForApplication(packageName);  
  34.                 int id = 0;  
  35.                 id = res.getIdentifier("version""string", packageName);  
  36.                 String version = res.getString(id);  
  37.                 Log.d("hlwang""MainActivity test version is:"+version);  
  38.                   
  39.                   
  40.                 PluginObject p = new PluginObject();  
  41.                 p.name = name;  
  42.                 p.version = version;  
  43.                 Log.d("hlwang""MainActivity test p is:"+p);  
  44.                 mList.add(p);  
  45.             }catch(Exception e){  
  46.                 Log.d("hlwang""MainActivity exception eeeeeeeeeeeee");  
  47.                 e.printStackTrace();  
  48.             }  
  49.               
  50.         }  
  51.         Log.d("hlwang""MainActivity test list size is:"+mList.size());  
  52.         setListAdapter(new ArrayAdapter<PluginObject>(this,  
  53.                 android.R.layout.simple_list_item_1, mList));  
  54.     }  
这段代码中,首先得到插件的List<ResolverInfo>列表。

然后得到插件的packageName,以及插件的dexPath目录。

再次,得到dexOutputDir目录。

libPath一般只c/c++使用的库文件。


DexClassLoader的参数意义:

dexPath:插件apk或者jar包文件的路径

dexOutputDir:将目标apk或者jar包解压的文件的存放目录。因为宿主程序只对本应用程序所在的目录由存取权限。


运行截图:

基于插件开发的Android实现流程