巧妙运用接口和继承的方式写兼容性框架(请仔细阅读)
1.我们先来谈谈该框架的作用。
本idea纯属自己总结,如有雷同请大牛勿喷。希望对新手们有些帮助。我们的应用,通常会有很多共用的部分,那么将这些部分做成框架的形式。可以使我们的应用更清晰,高效,简洁。
但是框架的兼容性是个非常重要的部分,不能因为框架的升级而导致老应用崩溃,当然以前大部分的框架兼容是通过导入jar包来实现的。也就是只要老的应用使用老的jar包,新的使用新的jar包几乎就没什么问题。但今天小羽要在这里介绍的是多个应用使用同步最新框架的兼容,也就是老的应用也会随时同步新的框架。
场景:比如你们有svn或git上的一系列应用,用到了同一个框架,而且是引入的形式,也就是说所有的应用共享最新的框架。批量打包的时候都是最新的框架,那么我们的新应用新加了一个接口,老应用的框架里面也会加入这个接口,但是老应用又不需要修改代码的情况下从框架入手做到兼容。
2.框架不兼容的情况
接口:接口是需要全部实现的。所以框架不能直接把接口暴露给应用,否则接口方法增加导致老应用必须修改实现。当然一个接口分成多个接口多实现可以解决这个问题,但是多实现导致你的框架越大,需要实现的接口越多,不太推荐。
虚函数:和接口的性质一样。当然你这个方法是任何子类都必须使用的比如创建,绑定,销毁等,那就可以使用。
指定类:因为是框架,所以我们不能和应用的类进行指定转换,如果强行需要指定某个对象的使用,请使用反射,以免属性的改变导致找不到方法或者找不到类的情况。
3.案例展示(说了这么多屁话终于进入正题了)
接下来小羽举个简单的例子来介绍该框架的实现方案。
比如我们要写一个注册广播或者调用aidl的框架(记住是框架不是应用)。怎么使用该框架理念怎么去实现呢?
3.1先写一个注册广播的实列,在实例中注册hello和ok的广播。
public class BroadcastReceverIM {
private Context mContext;
private BroadCastInterface mBroadInterface;
public BroadcastReceverIM(Context context, BroadCastInterface callback){
mContext = context;
this.mBroadInterface = callback;
}
public void registerBroadcastReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction("hello");
filter.addAction("ok");
mContext.registerReceiver(mBroadcastReceiver, filter);
}
public void unRegisterReceiver() {
mContext.unregisterReceiver(mBroadcastReceiver);
}
BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if ("hello".equals(action)) {
onBrHelloCome();
} else if("ok".equals(action)) {
onBrOkCome(intent);
}
}
};
protected void onBrHelloCome() {
mBroadInterface.onBrHelloCome();
}
private void onBrOkCome(Intent intent) {
//TODO解析
mBroadInterface.onBrOkCome("我是ok");
}
}
3.2从上面可以看到有一个接口,这个就是用来传递广播数据的。
public interface BroadCastInterface {
void onBrHelloCome();
void onBrOkCome(String name);
}
3.3然后我们写一个数据封装的仓库,将广播接收到的数据进行封装和保存。
public class DataLib implements BroadCastInterface {
private Context mContext;
private DataFactory mDataFactory;
private BroadcastReceverIM mBroadReceverIM;
private DataInterface mDataInterface;
public DataLib(Context mContext,DataFactory dataFactory, DataInterface dataInterface) {
super();
this.mContext = mContext;
this.mDataInterface = dataInterface;
this.mDataFactory = dataFactory;
initvaliable();
}
private void initvaliable() {
mBroadReceverIM = new BroadcastReceverIM(mContext, this);
mBroadReceverIM.registerBroadcastReceiver();
}
public void destroy() {
mBroadReceverIM.unRegisterReceiver();
}
@Override
public void onBrHelloCome() {
mDataInterface.onBrHelloCome();
}
@Override
public void onBrOkCome(String name) {
//TODO factory 将数据整理到数据工厂。
//虽然案例简单,但是作为框架需要的一些设计模式还是需要注意的。
//所以说小羽这里不是直接让广播返回给基类而不要经过库的处理和工厂模式的封装。
//当然这里的参数可以选择传递或不传递,因为数据已经被存到工厂里了。
//为了效果需要我们选择传递。
mDataInterface.onBrOkCome(name);
}
3.4上面提到了一个数据工厂。这里做一个基于单例的工厂模式,将数据存在里面。
这里不把数据工厂和仓库分开而要嵌在一起,是为了让大家明白他们是一体的。
只要创建了工厂就会生成一个处理的仓库,而且后续工厂在和应用交互的时候可能会和仓库有交互,
所以这里还是有必要在工厂里面留一个仓库的实例。
public class DataFactory {
private static DataFactory instance;
private Context mcontext;
private DataLib mDataLib;
public DataFactory(Context context, DataInterface dataInter) {
super();
instance = this;
mcontext = context;
mDataLib = new DataLib(context, this, dataInter);
}
public void onDestroy() {
mDataLib.destroy();
instance = null;
}
}
3.5大家发现仓库和工厂里面有个DataInterface的接口,没错它就是负责将我们仓库里面拿到的数据通知出去,
告诉我们的基类,说这些数据已经被获取到了。
public interface DataInterface {
void onBrHelloCome();
void onBrOkCome(String name);
}
3.6接下来这个就是最关键的部分,基类。也就是我们做兼容性的地方。同时也是提供接口的地方,
这里提供给外部的接口不是接口的形式,而是继承的形式。至于为什么相信大家看了前面的介绍应该明白了。
public class BaseExampleActivity extends Activity implements DataInterface{
protected DataFactory mDataFactory;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initVariable();
}
private void initVariable() {
mDataFactory = new DataFactory(this, this);
}
@Override
public void onBrHelloCome() {
//父类不做任何实现,不同的子类继承重写自己需要的实现。
//当接口执行了该方法,那么子类只要重写了方法就会在被执行。
//没有重写的方法,子类是看不到的,可以减少子类的代码量。
//也就是说子类可以选择性的去实现框架提供的方案。
//当然广播也可以选择性的去注册,这里不做过多步骤,
//对框架搭建有兴趣童鞋可以尝试去做。
}
@Override
public void onBrOkCome(String name) {
}
}
3.7,好了以上就是我们一个简单的基础框架的架构了,打个jar包或者引入项目等等都可以,也可以直接拷贝进自己的项目去测试了。
a.我们现在第一个应用需求是得到hello的信息,那么我们只需要重写hello的方法就行了。
public class AppTest1 extends BaseExampleActivity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}
@Override
public void onBrHelloCome() {
//super.onBrHelloCome();
//这里的super方法可用可不用,父类是空实现没有什么实际的意义。
Log.i("test", "onBrHelloCome: 我是hello");
}
}
b.我们第二个应用需求是获取hello和ok的信息,同上
public class AppTest2 extends BaseExampleActivity{@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}
@Override
public void onBrHelloCome() {
//super.onBrHelloCome();
//这里的super方法可用可不用,父类是空实现没有什么实际的意义。
Log.i("test", "onBrHelloCome: 我是hello");
}
@Override
public void onBrOkCome(String name) {
//super.onBrOkCome(name);
//这里的super方法可用可不用,父类是空实现没有什么实际的意义。
Log.i("test", "onBrOkCome: 我是OK,我的名字叫" + name);
}
}
3.8接下来我们在框架里面添加一条新的接口,老的应用不去修改代码再运行发现并不会影响老应用。
是不是很神奇?很多新手可能会问,为什么广播来了并没有使用AppTest的实例去调用方法。自己就执行了该方法了?
这就是java的神奇之处,所以我们可以写出很神奇的代码,而去掉了很多繁琐的调用关系。下面3.9总结会介绍到。
3.9总结。
其实这些方案可以运用很多场景。比如我们写一个普通类里面有一系列的接口,当然我们是不知道什么子类会去继承这个类的。所以我们不能直接拿到子类的对象去调用这个接口,我们其实只需要使用父类的this去调用就可以了,子类的实例其实就是父类的一个衍生的实例。换句话说:父类new出来的是父类的对象。而子类new出来的是子类和父类共有的对象。这就是所谓的上转型对象。所以我们在写父类的时候不需要使用子类的对象,一样可以通过调用父类的方法,使得子类也会调用该方法从而在框架里面就能把数据传到未知的子类执行。