目前市面上的应用商店,不管是 apple 还是 android 平台, 一般只有一家商店。
如果要动态添加商店,允许多家商店共存。搭建一个平台,多家应用商店可以加入。类似于商场与专卖店的关系。每个商店的业务由各自实现,但统一由商场来提供接口供用户选择。下面就来简单做个原型:1 ClassLoadTestMain 商场2 ClassLoadTestPlugin 商店3 PluginInterface 商场提供给商店的接口
接口定义:
- package com.pathfindeng.android.test;
- public interface IPlugin {
- public int add(int a, int b);
- }
package com.pathfindeng.android.test; public interface IPlugin { public int add(int a, int b); }
商店实现接口:
- package com.pathfindeng.android.test.plugin;
- import com.pathfindeng.android.test.IPlugin;
- public class Plugin implements IPlugin{
- public int add(int a, int b){
- return a + b;
- }
- }
package com.pathfindeng.android.test.plugin; import com.pathfindeng.android.test.IPlugin; public class Plugin implements IPlugin{ public int add(int a, int b){ return a + b; } }
商场调用商店的实现:
- package com.pathfindeng.android.test.main;
- import com.pathfindeng.android.test.IPlugin;
- import com.pathfindeng.android.test.main.R;
- import dalvik.system.DexClassLoader;
- import dalvik.system.VMStack;
- import android.app.Activity;
- import android.os.Bundle;
- import android.widget.TextView;
- import android.widget.Toast;
- public class ClassLoadTestMainActivity extends Activity {
- TextView result;
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- result = (TextView)findViewById(R.id.result);
- }
- public void doInPlugin(){
- String dexPath, dexOutputDir, libPath ,className;
- ClassLoader parent;
- dexPath = "/data/app/com.pathfindeng.android.test.plugin-1.apk";
- dexOutputDir = "/data/data/com.pathfindeng.android.test.main";
- libPath = "/data/data/com.pathfindeng.android.test.main/lib";
- parent = ClassLoadTestMainActivity.this.getClassLoader();
- //parent = VMStack.getCallingClassLoader();
- DexClassLoader mDexClassLoader = new DexClassLoader(dexPath, dexOutputDir, libPath, parent);
- className = "com.pathfindeng.android.test.plugin.Plugin";
- try {
- Class mClass = mDexClassLoader.loadClass(className);
- IPlugin mIPlugin = (IPlugin)mClass.newInstance();
- int c;
- c = mIPlugin.add(100, 200);
- result.setText("from plugin : "+c);
- Toast.makeText(ClassLoadTestMainActivity.this, "from plugin : "+c, Toast.LENGTH_LONG);
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /* (non-Javadoc)
- * @see android.app.Activity#onResume()
- */
- @Override
- protected void onResume() {
- // TODO Auto-generated method stub
- super.onResume();
- doInPlugin();
- }
- /* (non-Javadoc)
- * @see android.app.Activity#onDestroy()
- */
- @Override
- protected void onDestroy() {
- // TODO Auto-generated method stub
- super.onDestroy();
- }
- }
package com.pathfindeng.android.test.main; import com.pathfindeng.android.test.IPlugin; import com.pathfindeng.android.test.main.R; import dalvik.system.DexClassLoader; import dalvik.system.VMStack; import android.app.Activity; import android.os.Bundle; import android.widget.TextView; import android.widget.Toast; public class ClassLoadTestMainActivity extends Activity { TextView result; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); result = (TextView)findViewById(R.id.result); } public void doInPlugin(){ String dexPath, dexOutputDir, libPath ,className; ClassLoader parent; dexPath = "/data/app/com.pathfindeng.android.test.plugin-1.apk"; dexOutputDir = "/data/data/com.pathfindeng.android.test.main"; libPath = "/data/data/com.pathfindeng.android.test.main/lib"; parent = ClassLoadTestMainActivity.this.getClassLoader(); //parent = VMStack.getCallingClassLoader(); DexClassLoader mDexClassLoader = new DexClassLoader(dexPath, dexOutputDir, libPath, parent); className = "com.pathfindeng.android.test.plugin.Plugin"; try { Class mClass = mDexClassLoader.loadClass(className); IPlugin mIPlugin = (IPlugin)mClass.newInstance(); int c; c = mIPlugin.add(100, 200); result.setText("from plugin : "+c); Toast.makeText(ClassLoadTestMainActivity.this, "from plugin : "+c, Toast.LENGTH_LONG); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } } /* (non-Javadoc) * @see android.app.Activity#onResume() */ @Override protected void onResume() { // TODO Auto-generated method stub super.onResume(); doInPlugin(); } /* (non-Javadoc) * @see android.app.Activity#onDestroy() */ @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); } }
从上面的代码看,调用插件还是强制定义的,不够人性
接下来做些改进。
插件端 注册 固定 Action Name
- <activity android:name=".商店Activity" android:label="@string/app_name">
- <intent-filter>
- <action android:name="接口指定固定 Action Name" />
- </intent-filter>
- </activity>
<activity android:name=".商店Activity" android:label="@string/app_name"> <intent-filter> <action android:name="接口指定固定 Action Name" /> </intent-filter> </activity>
服务器端 商场
利用 PackageManager 查询所有注册了接口定义的 Action Name 的商店,并获得信息。
- Intent intent = new Intent(Constants.ACTION_PLUGIN, null);
- PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);
Intent intent = new Intent(Constants.ACTION_PLUGIN, null); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> plugins = pm.queryIntentActivities(intent, 0);
这样之前提到的
- DexClassLoader(dexPath, dexOutputDir, libPath, parent)
DexClassLoader(dexPath, dexOutputDir, libPath, parent)
参数就不需要 手动指定了。
待叙……