现在都使用过支付宝吧,支付宝里边有好多功能就是采用插件化开发的,例如支付宝里边的小黄车,小蓝车等功能,都是采用的插件化做的。一开始我认为是WebView做的,
后来打开手机里的显示边框布局发现不是WebView,是原生的。他采用的是插件化加载第三方应用。
采用插件化开发的好处有宿主app与插件进行分开编译;并发开发节约时间和成本;按需要下载模块,但是第一次加载比较慢。
下边我们来讲解一下这个插件化开发:在进行插件化开发的时候要,有个桥梁将宿主APP和插件进行互相连接,这个连接也就是一个标准,那么这个标准是什么呢,就是一个
接口,让宿主app和插件都使用这套标准接口,这个接口就写有关activity的生命周期的方法。
/*** 两端都使用的标准* Created by yzl on 2017/10/10.*/public interfacePayInterface {
public voidonCreate(Bundle savedInstance);
public voidonStart();
public voidonResume();
public voidonPause();
public voidonStop();
public voidonDestroy();
public voidonSaveInstanceState(Bundle outState);
public booleanonTouchEvent(MotionEvent event);
public voidonBackPressed();
public voidattach(Activity activity);
}
标准接口写好了,然后进行写插件,我们这里写简单的插件,两个activity的展示。
在插件里我们要抽出一个基类activity,BaseActivity。让她继承activity并实现PayInterface. 插件中的主activity(mainactivity)继承BaseActivity。
现在mainactivity中要查找控件,使用this是不可以的,因为MainActivity.this是系统注入如入的上下文,插件只可以使用宿主注入的上下文也就是这里的that(所以
在接口中写里attach(Activity,activity),这个拿到的上下文就是宿主APP注入的上下文。
**** 重写关于上下文的父方法* Created by yzl on 2017/10/10.*/public classBaseActivty extendsActivity implementsPayInterface{
protectedActivity that;
@Overridepublic voidsetContentView(@LayoutResintlayoutResID) {
if(that== null){
super.setContentView(layoutResID);
}else{
that.setContentView(layoutResID);
}
}
@Overridepublic TfindViewById(intid) {
if(that== null){
return super.findViewById(id);
}else{
returnthat.findViewById(id);
}
}
@OverridepublicClassLoader getClassLoader() {
if(that== null){
return super.getClassLoader();
}else{
returnthat.getClassLoader();
}
}
@NonNull@OverridepublicLayoutInflater getLayoutInflater() {
if(that== null){
return super.getLayoutInflater();
}else{
returnthat.getLayoutInflater();
}
}
@OverridepublicWindow getWindow() {
if(that== null){
return super.getWindow();
}else{
returnthat.getWindow();
}
}
@OverridepublicWindowManager getWindowManager() {
if(that== null){
return super.getWindowManager();
}else{
returnthat.getWindowManager();
}
}
@Overridepublic voidonCreate(Bundle savedInstance) {
}
@Overridepublic voidonStart() {
}
@Overridepublic voidonResume() {
}
@Overridepublic voidonPause() {
}
@Overridepublic voidonStop() {
}
@Overridepublic voidonDestroy() {
}
@Overridepublic voidonSaveInstanceState(Bundle outState) {
}
@Overridepublic voidattach(Activity activity) {
this.that= activity;
}}public classMainActivity extendsBaseActivty implementsView.OnClickListener {
privateTextView tv_chajian;
@Overridepublic voidonCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_main);
tv_chajian= that.findViewById(R.id.tv_chajian);
}
}现在插件写好了,在说说宿主App。宿主app就是先加载在内存卡里存放的pak,然后找到要加载的activity的classname然后加载对应的class文件,在加载对应资源文件。
这里加载很明显分为四步,apk文件,classname,class文件,资源文件。加载apk我们简单现在不说明,现在说说其余三个。现在我们写一个插件管理器pluginManger
工具类,将类设计为单利的,保证对象的唯一性。找加载的第一个activity的classname,
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
dexClassLoader= newDexClassLoader(path,dexOutFile.getAbsolutePath(),null,context.getClassLoader());
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,packageManager.GET_ACTIVITIES);
entryActivityName= packageInfo.activities[0].name;接下来在准备资源,利用反射的方法zhao daoAssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath",String.class);
addAssetPath.invoke(assetManager,path);
resources= newResources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());pluginManger工具类的代码如下:/*** Created by yzl on 2017/10/10.*/public classpluginManger {
privateContext context;
privateDexClassLoader dexClassLoader;
privateResources resources;
privateString entryActivityName;
private static finalpluginManger ourInstance= newpluginManger();
public staticpluginManger getInstance() {
returnourInstance;
}
privatepluginManger() {
}
public voidloadpath(String path){
File dexOutFile = context.getDir("dex", Context.MODE_PRIVATE);
dexClassLoader= newDexClassLoader(path,dexOutFile.getAbsolutePath(),null,context.getClassLoader());
PackageManager packageManager = context.getPackageManager();
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,packageManager.GET_ACTIVITIES);
entryActivityName= packageInfo.activities[0].name;
Log.i("插件activity的classname",entryActivityName);
try{
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = AssetManager.class.getMethod("addAssetPath",String.class);
addAssetPath.invoke(assetManager,path);
resources= newResources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());
} catch(Exception e) {
e.printStackTrace();
}
}
publicDexClassLoader getDexClassLoader() {
returndexClassLoader;
}
publicResources getResources() {
returnresources;
}
publicString getEntryActivityName() {
returnentryActivityName;
}
public voidsetContext(Context context) {
this.context= context.getApplicationContext();
}
}现在要加载的class文件的名字与资源都准备好了,接下来去加载对应的class文件,并显示。name宿主APP显示一个界面,那就必须的有容器去放置这个内容,那么我们去插件一个宿主activity(ProxyActivity)className= getIntent().getStringExtra("classname");//得到要加载的全类名
Class activityClazz = getClassLoader().loadClass(className);
Constructor constructor = activityClazz.getConstructor(newClass[]{});//创建构造方法
Object instance = constructor.newInstance(newObject[]{});
payInterface= (PayInterface) instance;//将得到的实力转化为标准的接口类型
payInterface.attach(this);
Bundle bundle = newBundle();//创建bundle对象便于传递参数
payInterface.onCreate(bundle);//创建这样界面还是空白的因为她还没有生命周期,我们要将对应的生命周期加上就完美了。/*** Created by yzl on 2017/10/10.*/public classProxyActivity extendsActivity {
privateString className;//要跳转的activityprivatePayInterface payInterface;
@Overrideprotected voidonCreate(@NullableBundle savedInstanceState) {
super.onCreate(savedInstanceState);
className= getIntent().getStringExtra("classname");//得到要加载的全类名Log.i("传递过来的class名称为",className);
try{
Class activityClazz = getClassLoader().loadClass(className);
Constructor constructor = activityClazz.getConstructor(newClass[]{});
Object instance = constructor.newInstance(newObject[]{});
payInterface= (PayInterface) instance;
payInterface.attach(this);
Bundle bundle = newBundle();
payInterface.onCreate(bundle);
} catch(Exception e) {
e.printStackTrace();
}
//利用反射加载class对应的class文件//因为插件不在art虚拟机里安装的,所以直接使用凡事是找不到class文件的,//这个类所加载的class都是插件中的class,不能是宿主中的class,所以要重写getclassloader();}
@Overrideprotected voidonStart() {
super.onStart();
payInterface.onStart();
}
@Overrideprotected voidonResume() {
super.onResume();
payInterface.onResume();
}
@Overrideprotected voidonPause() {
super.onPause();
payInterface.onPause();
}
@Overrideprotected voidonStop() {
super.onStop();
payInterface.onStop();
}
@Overrideprotected voidonDestroy() {
super.onDestroy();
payInterface.onDestroy();
}
@OverridepublicClassLoader getClassLoader() {
returnpluginManger.getInstance().getDexClassLoader();
//由于super是系统注入的上下文(也就是本宿主的上下文)xi}
@OverridepublicResources getResources() {
returnpluginManger.getInstance().getResources();
}
@Overridepublic voidstartActivity(Intent intent) {
String twoActivityClass = intent.getStringExtra("className");
Intent intent1 = newIntent(this,ProxyActivity.class);
intent1.putExtra("classname",twoActivityClass);
super.startActivity(intent1);
}
}