基於插件開發的Android實現流程

 

本文記述“柯元旦”Android內核剖析中基於類裝載器的“插件”架構。

插件的概念:

1、插件不能獨立運行,而必須運行於一個宿主程序中,即由宿主程序去調用插件程序。

2、插件一般可以獨安裝。

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

4、宿主程序應該保證插件的向下兼容性,即新版本的宿主程序可以運行較老版本的插件。

下面詳細看一下這種架構:

1、宿主程序:

新建Android項目PluginDevAndroid

 

2、插件項目1:Plugin1

3、插件項目2:

4、綜述:

4.1、接口類一般定義在宿主項目中,比如本例中的IPluginDev.java

4.2、插件項目需要應用IPluginDev時,則必須通過一個外部的jar包,並且該jar包是以Library的形式被添加到Plugin項目的build Path,而不是以“外部的”jar方式添加。

如圖:

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

這樣的話,宿主程序就可以通過PackageManager類的queryIntentActivities函數查詢相關的插件的列表瞭。

得到瞭插件的PackageName,就可以訪問插件的資源內容。例如:

 

				Resources res = pm.getResourcesForApplication(packageName);
				int id = 0;
				id = res.getIdentifier(version, string, packageName);
				String version = res.getString(id);
				Log.d(hlwang, MainActivity test version is:+version);

這段代碼中,首先獲取插件的Resource對象,接著得到名稱為version字段的字符串id值,然後再調用getString獲得該變量的值,於是宿主程序就知道插件程序中的資源內容瞭。

 

 

關鍵代碼:

 

private void test(){
		Intent intent = new Intent(com.example.plugindevandroid.plugin,null);
		final PackageManager pm = getPackageManager();
		final List plugins = pm.queryIntentActivities(intent, 0);
		
		for(ResolveInfo r:plugins){
			ActivityInfo activityInfo = r.activityInfo;
			
			String p = System.getProperty(path.separator);
			String packageName = activityInfo.packageName;
			String packageName0 = getPackageName();
			String dexPath = activityInfo.applicationInfo.sourceDir;
			String dexOutputDir1 = activityInfo.applicationInfo.dataDir;
			String dexOutputDir2 = getApplicationInfo().dataDir;
			String libPath = activityInfo.applicationInfo.nativeLibraryDir;
			
			Log.d(hlwang, MainActivity test p is:+p
					+,packageName is:+packageName
					+,packageName0 is:+packageName0
					+,dexPath is:+dexPath
					+,dexOutputDir1 is:+dexOutputDir1
					+,dexOutputDir2 is:+dexOutputDir2
					+,libPath is:+libPath);
			
			DexClassLoader dexCl = new DexClassLoader(dexPath, dexOutputDir2, libPath, getClassLoader());
			Log.d(hlwang, MainActivity test clazzName is:+packageName+.PluginVersion);
			try{
				Class clazz = dexCl.loadClass(packageName+.PluginVersion);
				IPluginDev plugin = (IPluginDev) clazz.newInstance();
				String name = plugin.getName();
				Log.d(hlwang, MainActivity test name is:+name);
				
				Resources res = pm.getResourcesForApplication(packageName);
				int id = 0;
				id = res.getIdentifier(version, string, packageName);
				String version = res.getString(id);
				Log.d(hlwang, MainActivity test version is:+version);
				
				
				PluginObject p = new PluginObject();
				p.name = name;
				p.version = version;
				Log.d(hlwang, MainActivity test p is:+p);
				mList.add(p);
			}catch(Exception e){
				Log.d(hlwang, MainActivity exception eeeeeeeeeeeee);
				e.printStackTrace();
			}
			
		}
		Log.d(hlwang, MainActivity test list size is:+mList.size());
		setListAdapter(new ArrayAdapter(this,
                android.R.layout.simple_list_item_1, mList));
	}

這段代碼中,首先得到插件的List列表。

 

然後得到插件的packageName,以及插件的dexPath目錄。

再次,得到dexOutputDir目錄。

libPath一般隻c/c++使用的庫文件。

 

DexClassLoader的參數意義:

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

dexOutputDir:將目標apk或者jar包解壓的文件的存放目錄。因為宿主程序隻對本應用程序所在的目錄由存取權限。

運行截圖:

 

發佈留言