每个android应用程序中都有一个唯一的上下文Application对象,这个Application一般我们不需要去创建,应用启动时,系统会自动创建一个默认的Application实例。由于Application在整个应用中是唯一的,它是一个单例。所以,有的应用就可能希望利用Application来完成一些工作。
在android中,实现一个自定义的Application是很简单的。直接自定义一个类继承Application,然后在AndroidManifest.xml的application节点属性里将android:name设置为你自定义的这个application类即可。
如果在集成sdk时,涉及到子应用也有自己的application。那么主应用如何将多个子应用的application一起合并呢?
我们先来尝试一下,如果子应用有自己的application ,如果不将主应用与子应用的application进行聚合,会出现什么情况呢?
答:子应用会注册到主应用的application 上,会将主应用的application覆盖掉。如下是主应用的清单文件,其中application组件下的android:name 属性的值是子应用的application。 此时会将主应用的application给覆盖掉。导致打开主应用后,直接加载的是子应用的application。如果一个主应用集成多个子aar时,就会出现混乱。所以这里应该使用多继承策略。
合并application的思想
下面我会提供一个案例:一主两从
一主: 即主应用。下面的项目成为 H应用 二从: 两个子应用,也就是将要接入到主应用的两个子应用 分别为 A应用和B应用
合并application无非就是在加载主应用的application时也要动态的去加载子应用的application,使得主子应用的application具备各自的生命周期。那这里有两种备用方案: 1、需要使用子应用的Application,就把子应用的application实现的方法拷贝到主应用中,这种无疑就是太麻烦,方法太笨,如果需要集成多个子应用就会有更多的工作量。 2、或者自定义一个Application去继承子应用的Application,在Application对应的接口里调用他们的方法。这种也是比较麻烦。
但是,现在问题来了,因为主应用整套框架的核心思想就是,兼顾所有渠道。不可能直接在主应用的AndroidManifest.xml中配置上某个子应用的Application或者自定义一个Application,去继承某一个子应用的Application。我们必须想办法越过去。
幸运的是,方法总是有的。在这里,我在主应用抽象层中定义了一个Application监听器IApplicationListener,同时定义一个继承了Application类的HostApplication。在HostApplication类中,维护了一个IApplicationListener实例。这样在HostApplication的onCreate,attackBaseContext等方法中,会间接的调用IApplicationListener中相应的接口。
这样,在具体接入子SDK的时候,我们就定一个适配器模式的类来继承渠道自己的Application,同时实现主应用抽象层的IApplicationListener接口。然后在IApplicationListener的接口实现中,直接调用基类(子SDK的Application)的对应方法。
如果子应用没有自己的application 。那么就直接将HostApplication配置到AndroidManifest.xml的application节点的android:name属性中。这样在打包时会自动注册到主应用的清单文件中。
上述思想很明确:就是静态代理
静态代理模式 1、必须要求我们定一个一个父类或接口 这个接口定义了application类的公共方法。 2、必须要定义代理对象,主应用通过代理对象去访问目标对象。 3、多个子应用需要定义多个代理对象。
在这里我们再统一一下合并思路:
主应用配置
(1)首先在主应用中定义一个接口
public interface IApplicationListener {
void onProxyCreate();
void onProxyAttachBaseContext(Context base);
void onProxyConfigurationChanged(Configuration newConfig);
}
(2)定义各个子sdk的代理application对象
如果A应用的本身的sdk中定义的application如下:
public class MyApplication extends Application {
public MyApplication() {
}
public void onCreate() {
super.onCreate();
Toast.makeText(this, "this is A plugin application", 0).show();
}
public void onConfigurationChanged(@NonNull Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
}
}
为当前的MyApplication设置代理对象 AProxyApplication,让此代理对象继承MyApplication,并实现IApplicationListener接口
public class AProxyApplication extends MyApplication implements IApplicationListener {
@Override
public void onProxyCreate() {
super.onCreate();
}
@Override
public void onProxyAttachBaseContext(Context base) {
super.attachBaseContext(base);
}
@Override
public void onProxyConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
}
(3)定义主应用中的HostApplication。这里是关键,这里主要实现的是: 通过接口来聚合目标对象 简单来说就是定义接口的引用,获取子应用的代理对象(通过反射的方式,因为application具备完整的生命周期,aar包被依赖打包到主应用后所有的对象实例都已经初始化,所以在运行时需要通过反射方式获取,这里也不知道理解的对不对),通过代理对象去调用目标对象中的方法,目标对象为子应用的application。
package com.hzs.plguin;
import android.app.Application;
import android.content.Context;
import android.content.res.Configuration;
import android.util.Log;
import android.widget.Toast;
public class HostApplication extends Application {
private IApplicationListener listener;
private static final String PROXY_NAME = "com.plugin.sdk.FirstStandPluginApplication";
@Override
public void onCreate() {
super.onCreate();
Log.i("Host","加载主应用的application");
if( listener != null){
listener.onProxyCreate();
}
}
@Override
public void attachBaseContext(Context base) {
super.attachBaseContext(base);
this.listener = initProxyApplication();
if( this.listener != null){
this.listener.onProxyAttachBaseContext(base);
}
}
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
if( this.listener != null){
this.listener.onProxyConfigurationChanged(newConfig);
}
}
private IApplicationListener initProxyApplication() {
String proxyAppName = SDKTools.getAppMetaData(this, PROXY_NAME);
if(proxyAppName == null ){
return null;
}
try {
Class clazz = Class.forName(proxyAppName);
return (IApplicationListener)clazz.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
在上述(3)步骤以前还漏下关键的一步,就是需要在主应用的清单文件中进行声明子sdk 的代理application
SDKTools 代码
package com.hzs.plguin;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
public class SDKTools {
private String getMetaDataStringApplication(Context context ,String key) {
Bundle bundle = getAppMetaDataBundle(context.getPackageManager(), context.getPackageName());
if (bundle != null && bundle.containsKey(key)) {
return bundle.getString(key);
}
return null;
}
private Bundle getAppMetaDataBundle(PackageManager packageManager,String packageName) {
Bundle bundle = null;
try {
ApplicationInfo ai = packageManager.getApplicationInfo(packageName,
PackageManager.GET_META_DATA);
bundle = ai.metaData;
} catch (PackageManager.NameNotFoundException e) {
Log.e("getMetaDataBundle", e.getMessage(), e);
}
return bundle;
}
public static String getAppMetaData(Context context, String key) {
if (context == null || TextUtils.isEmpty(key)) {
return null;
}
String channelNumber = null;
try {
PackageManager packageManager = context.getPackageManager();
if (packageManager != null) {
ApplicationInfo applicationInfo = packageManager.getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
if (applicationInfo != null) {
if (applicationInfo.metaData != null) {
channelNumber = applicationInfo.metaData.getString(key);
}
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return channelNumber;
}
}
子应用配置
无需任何配置
总结
至此application合并完毕。如果有其他的业务驱动,也有可能在此基础上进行改造,主要要理解其中的解决思想,为了让大家更好的理解静态代理,下面贴一个类图:对角线的为聚合关系
科普一下meta-data:
|