实现接口的代码为:
public classPlugAppActivityextendsActivityimplementsMyInterface{
/**Calledwhentheactivityisfirstcreated.*/
@Override
public voidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
public voidtest(){
System.out.println(getApplicationInfo().sourceDir);
}
}
public classPlugAppActivityextendsActivityimplementsMyInterface{
/**Calledwhentheactivityisfirstcreated.*/
@Override
public voidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
public voidtest(){
System.out.println(getApplicationInfo().sourceDir);
}
}
为什么这里要继承Activity呢?这个在下一步说明,这里的Activity可以替代成service、receiver或provider。
在AndroidManifest加入这个Activity(其他组件同理)。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.intsig.plugApp"
android:versionCode="1"
android:versionName="1.0" android:sharedUserId="com.main">
<uses-sdk android:minSdkVersion="7" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".PlugAppActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="com.intsig.appMain.PLUGIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.intsig.plugApp"
android:versionCode="1"
android:versionName="1.0" android:sharedUserId="com.main">
<uses-sdk android:minSdkVersion="7" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".PlugAppActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="com.intsig.appMain.PLUGIN" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
</application>
</manifest>
这里的sharedUserId是指插件与主程序共用一个Uid,这样就消除了权限的壁垒。Android系统继承了Linux系统管理文件的方法,为每一个应用程序分配一个独立的用户ID和用户组ID,而由这个应用程序创建出来的数据文件就赋予相应的用户以及用户组读写的权限,其余用户则无权对该文件进行读写。例如,如果我们进入到Android系统日历应用程序数据目录com.android.providers.calendar下的databases文件中,会看到一个用来保存日历数据的数据库文件calendar.db,它的权限设置如下所示:
root@android:/data/data/com.android.providers.calendar/databases#ls-l
-rw-rw----app_17app_17337922011-11-0715:50calendar.db
root@android:/data/data/com.android.providers.calendar/databases#ls-l
-rw-rw----app_17app_17337922011-11-0715:50calendar.db
这里的app_17就是系统自动分配的Uid。
至于给activity添加的intent-filter中的action也会在后面解释。
Step3:在主程序中获取插件,并调用接口方法。
<SPAN style="FONT-SIZE:18px">publicclassMainActivityextendsActivity{
//</SPAN><SPAN style="FONT-SIZE:12px">预定义的action</SPAN><SPAN style="FONT-SIZE:18px">
publicstaticfinalStringACTION_PLUGIN="com.intsig.mainApp.PLUGIN";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try{
//</SPAN><SPAN style="FONT-SIZE:12px">查找符合这个action的所有activity即插件,若插件使用的是其他组件换成对应的方法</SPAN><SPAN style="FONT-SIZE:18px">
List<ResolveInfo> infos=getPackageManager().queryIntentActivities(
newIntent(ACTION_PLUGIN),PackageManager.MATCH_DEFAULT_ONLY);
ActivityInfopluginInfo;
for(ResolveInfoinfo:infos){
<SPAN style="WHITE-SPACE:pre"> </SPAN>pluginInfo=info.activityInfo;
//</SPAN><SPAN style="FONT-SIZE:12px">根据插件的安装路径获得ClassLoader</SPAN><SPAN style="FONT-SIZE:18px">
ClassLoadercl=newPathClassLoader(pluginInfo.applicationInfo.sourceDir,getClassLoader());
//</SPAN><SPAN style="FONT-SIZE:12px">获得插件类的实例</SPAN><SPAN style="FONT-SIZE:18px">
MyInterfaceplugin=(MyInterface)cl.loadClass(pluginInfo.name).newInstance();
plugin.test();
}
}catch(Exceptione){
e.printStackTrace();
}
}
}</SPAN>
<span style="font-size:18px;">publicclassMainActivityextendsActivity{
//</span><span style="font-size:12px;">预定义的action</span><span style="font-size:18px;">
publicstaticfinalStringACTION_PLUGIN="com.intsig.mainApp.PLUGIN";
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
try{
//</span><span style="font-size:12px;">查找符合这个action的所有activity即插件,若插件使用的是其他组件换成对应的方法</span><span style="font-size:18px;">
List<ResolveInfo> infos=getPackageManager().queryIntentActivities(
newIntent(ACTION_PLUGIN),PackageManager.MATCH_DEFAULT_ONLY);
ActivityInfopluginInfo;
for(ResolveInfoinfo:infos){
<span style="WHITE-SPACE:pre"> </span>pluginInfo=info.activityInfo;
//</span><span style="font-size:12px;">根据插件的安装路径获得ClassLoader</span><span style="font-size:18px;">
ClassLoadercl=newPathClassLoader(pluginInfo.applicationInfo.sourceDir,getClassLoader());
//</span><span style="font-size:12px;">获得插件类的实例</span><span style="font-size:18px;">
MyInterfaceplugin=(MyInterface)cl.loadClass(pluginInfo.name).newInstance();
plugin.test();
}
}catch(Exceptione){
e.printStackTrace();
}
}
}</span>
这里通过intent来找到所有符合条件的activity,即我们之前实现的插件,通过动态的加载类来获得插件实例。主程序的AndroidManifest如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.intsig.mainApp"
android:versionCode="1"
android:versionName="1.0" android:sharedUserId="com.main">
<uses-sdk android:minSdkVersion="7" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.intsig.mainApp"
android:versionCode="1"
android:versionName="1.0" android:sharedUserId="com.main">
<uses-sdk android:minSdkVersion="7" />
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
插件中的sharedUserId要与这里的保持一致。
上面三步描述了用android的四大组件来实现插件,但除此之外还有另一种方式。从上面的demo可以发现所有的插件与主程序的sharedUserId都是一致的,那么就可以通过检索所有安装程序的sharedUserId,只要与主程序的一致便可当做是它的插件。在上面的方法中我们获得了插件的路径以及实现接口类的类名,从而能够动态的加载这个类,而通过检索sharedUserId能够获得到路径却无法获得到类名,那么可以在插件中加入一个xml文件来说明插件中包含的实现类,通过读取这个xml来获取出类名和其他一些可能需要的描述信息,这个就会比第一种要复杂一些。总结一下,当插件的功能比较简单,选择第一种方法比较容易实现;当插件功能较多,逻辑复杂时,可以将插件再细分成模块,同时xml文件可以表现出插件的组织结构,那么第二种方法更好一些。
上面所讲的两种方法都是适用于将安装的apk作为插件,实现插件开发还可以通过在sd卡中的指定目录放入插件的jar包或apk文件,原理与上述类似,只是将PathClassLoader换成DexClassLoader,换成它的原因是DexClassLoader的文档描述有一句:“A class loader that loads classes from.jar
and.apk
files containing aclasses.dex
entry. This can be used to execute code not installed as part of an application.”二者的区别我还没来得及研究,希望有兴趣的同学去研究下。
优质内容筛选与推荐>>
1、C# Excel 行高,列宽,合并单元格,单元格边框线,冻结2、nginx反向代理+nginx缓存功能及squid缓存架构实现3、学车路程4、js replace 如何替换字符串中的最后一个匹配项5、学习笔记