背景与主要方针
背景介绍
商业侧广告,广告的完结当然是在ad组件完结。 但都需求将其它事务方的界面作为载体去呈现。
例如一个彩蛋广告(完结在ad组件),是悬浮在主页信息流之上(feed组件),因而不可避免的,会侵入feed组件的代码。
因而一般的组件化开发模式下,需求feed组件依靠ad_api,由ad_api供给相关广告的接口,在feed侧调用,完结交互。
如下伪代码:
/**
* 主页信息流 (位于feed组件)
*/
public classMainPageFragmentextendsBaseFeedFragment {
//彩蛋接口,位于ad组件
privateIAdEggiAdEgg;
@Override
public voidonCreate(@NullableBundle savedInstanceState) {
super.onCreate(savedInstanceState);
//经过组件化路由,取得IAdEgg的完结类
InstanceProvider.optional(IAdEgg.class).ifPresent(it -> {
iAdEgg= it;
});
}
@Override
public voidonResume() {
super.onResume();
//展现彩蛋
iAdEgg.show();
}
@Override
public voidonStop() {
super.onStop();
//彩蛋消失
iAdEgg.dismiss();
}
@Override
public voidonDestroy() {
super.onDestroy();
//彩蛋毁掉
iAdEgg.destroy();
}
}
这样做,在事务上,没有啥问题,实际上,初期咱们也是这样处理的。
当然了,这样做的坏处,也很明显。
比如,我需求对这个广告的调用进行修正,要将原来 onStop()处履行的iAdEgg.dismiss() 办法,调整到 onPause()里边去,不可避免的,我需求改动feed组件。
再比如,咱们需求新增一个广告,比如增一个浮层广告吧,那会怎么写呢? 伪代码如下:
/**
* 主页信息流
*/
public classMainPageFragmentextendsBaseFeedFragment {
//彩蛋接口,位于ad组件
privateIAdEggiAdEgg;
//浮层广告接口,位于ad组件
privateIAdFloatiAdFloat;
@Override
public voidonCreate(@NullableBundle savedInstanceState) {
super.onCreate(savedInstanceState);
//经过组件化路由,取得IAdEgg的完结类
InstanceProvider.optional(IAdEgg.class).ifPresent(it -> {
iAdEgg= it;
});
//经过组件化路由,取得IAdFloat的完结类
InstanceProvider.optional(IAdFloat.class).ifPresent(it -> {
iAdFloat= it;
});
}
@Override
public voidonResume() {
super.onResume();
//展现彩蛋
iAdEgg.show();
//展现浮层广告
iAdFloat.show();
}
@Override
public voidonStop() {
super.onStop();
//彩蛋消失
iAdEgg.dismiss();
//浮层广告消失
iAdFloat.dismiss();
}
@Override
public voidonDestroy() {
super.onDestroy();
//彩蛋毁掉
iAdEgg.destroy();
//浮层广告毁掉
iAdFloat.destroy();
}
}
跟着商业化的特形广告越来越多,会将ad的代码不断的在feed组件进行叠加。
到现在停止,一个宿主界面最多会时承载5种特形广告。而且任何的调用机遇的修正,亦或许新增特形广告,本质上都是对feed侧进行修正。
且实际上的广告事务不会向上述伪代码一样简单明了,我方的侵入逻辑甚至增加了主页的保护成本。
定论简而言之,ad组件过度的侵入了feed组件。解耦已经火烧眉毛。
方针&完结效果
针对上述的坏处,设计解耦模型 「Geoffrey」,该模型以APT+动态代理为基础,采用接口注解的计划,必定程度 上完结了 ad组件与其它组件的解耦。
依然以彩蛋广告为例,仅需求在feed侧进行一个注册,就能监听到生命周期:
/**
* 主页信息流 位于feed组件
*/
public classMainPageFragmentextendsBaseFeedFragment {
@Override
public voidonCreate(@NullableBundle savedInstanceState) {
super.onCreate(savedInstanceState);
//经过组件化路由,取得商业行为搜集器
InstanceProvider.optional(IAdBehaviorDispatcher.class).ifPresent(it -> {
it.register(this);
});
}
}
在ad组件内部完结彩蛋广告的编写
/**
* 主页彩蛋 位于ad组件
*/
@AddServiceHost({"com.zhihu.android.app.feed.ui.fragment.MainPageFragment"})
@ClassImplementation(type = "AdEgg",relativeInterface = RelativeClassPath.AD_RELATIVE_PATH)
public classAdEggDemoimplementsIAdEggObserver {
@Override
public voidonHostDestroy(String fromPage) {
//...毁掉逻辑
}
@Override
public voidonHostStop(String fromPage) {
//...dismiss逻辑
}
@Override
public voidonHostResume(String fromPage) {
//...show逻辑
}
}
因为在AdEggDemo的类注解中,运用@AddServiceHost添加了需求监听的宿主界面的全类名:MainPageFragment
因而,当MainPageFragment在其onCreate中调用it.register(this)办法,就会动态创立一个AdEggDemo的实例。
当MainPageFragment依次调用了 onResume()、onStop()、onDestroy() 时,相应的,会调用AdEggDemo的实例的 onHostResume(String fromPage)、onHostStop(String fromPage)、onHostDestroy(String fromPage),很明显,参数fromPage便是MainPageFragment的全类名,用于某些事务下的判断。
而且在宿主调用了onDestroy时,也会自动毁掉创立的AdEggDemo实例。
那么,现在咱们需求在MainPageFragment 新增一个浮层广告呢?
不需求再改动feed组件。
只需求在ad组件中新增一个AddFloat完结类,其注解同彩蛋,监听MainPageFragment。
@AddServiceHost({"com.zhihu.android.app.feed.ui.fragment.MainPageFragment"})
@ClassImplementation(type = "AdFloat", relativeInterface = RelativeClassPath.AD_RELATIVE_PATH)
public class AdFloat implements IAdFloatObserver {
@Override
public void onHostDestroy(String fromPage) {
//...毁掉逻辑
}
@Override
public void onHostStop(String fromPage) {
//...dismiss逻辑
}
@Override
public void onHostResume(String fromPage) {
//...show逻辑
}
}
也便是说,当MainPageFragment 的实例在履行生命周期办法时,会既回调 AdFloat实例里的生命周期,也回调AdEggDemo实例里的生命周期。 因为这两个类,都在类注解上参加了对MainPageFragment的监听。
从上面的例子能够看出,其实「Geoffrey」所做的事情,便是建立终究的履行类与宿主的联系,将你需求关怀的,宿主的行为,如生命周期,传递到履行类中去!**
有同学又要问了,诶诶诶,你这个计划里,如同只能传递生命周期办法,假如要传递其它行为呢?比如我要监听这个界面的有一个改写操作,我需求监听这个动作,该怎么做?
当然能够,只是feed需求这么写:
/**
* 主页信息流
*/
public classMainPageFragmentextendsBaseFeedFragment {
//商业侧行为搜集器
privateIBehaviorReceiverbehaviorReceiver;
@Override
public voidonCreate(@NullableBundle savedInstanceState) {
super.onCreate(savedInstanceState);
//经过组件化路由,取得商业行为搜集器
InstanceProvider.optional(IAdBehaviorDispatcher.class).ifPresent(it -> {
behaviorReceiver= it.register(this);
});
}
@Override
protected voidonRefresh(booleanfromUser) {
super.onRefresh(fromUser);
//调一下onRefresh
behaviorReceiver.onRefresh(fromUser);
}
}
到这里停止,又多了一个类 IBehaviorReceiver ,这又个啥?
这是个接口类,里边界说了,商业侧广告所需求搜集的全部行为!其间当然就包含了生命周期,只是你只要调用了注册办法,就默许会监听生命周期,不再需求自己去写。
看一下已经存在的一些办法:
/**
* 接口界说类
* 在这里界说了商业侧需求搜集的行为
*/
@BehaviorCreator
public interfaceIBehaviorReceiverextendsIHostLifecycle {
@AddBehaviorObserver({"AdFloat"})
voidonRefresh(booleanfromUser);
@Override
voidonHostCreate();
@AddBehaviorObserver({"AdFloat"})
@Override
voidonHostDestroy();
@Override
voidonHostPause();
@AddBehaviorObserver({"AdFloat"})
@Override
voidonHostResume();
@Override
voidonHostStart();
@Override
voidonHostStop();
}
某一个类型的广告,比如浮层广告, 需求重视的宿主界面行为有:onResume、onDestroy、onRefresh 三个办法,只需求在这三个办法上的AddBehaviorObserver注解中,参加自己的一个String类型的type即可! 如上。
这个注解有啥含义呢? 含义在于,经过apt,会在编译期生成一个新接口,而且这个接口,只含有这三个办法。生成的接口如下:
@ServiceType(
value ="AdFloat",
relativeInterface ="com.zhihu.android.behavior.IBehaviorReceiver"
)
public interfaceIAdFloatObserverextendsIObserver {
voidonRefresh(booleanfromUser,String fromPage);
voidonHostDestroy(String fromPage);
voidonHostResume(String fromPage);
}
咱们终究真实的履行类,完结这个接口即可!能够往上面翻翻看看用法,是不是浮层广告的履行类完结它了?然后在完结类上打好注解,那就能完结监听了。 或许咱们这里再看一遍:
@AddServiceHost({"com.zhihu.android.app.feed.ui.fragment.MainPageFragment"})
@ClassImplementation(type = "AdFloat", relativeInterface = RelativeClassPath.AD_RELATIVE_PATH)
public class AdFloat implements IAdFloatObserver {
@Override
public void onHostDestroy(String fromPage) {
//...毁掉逻辑
}
@Override
public void onRefresh(boolean fromUser,String fromPage) {
//...改写逻辑
}
@Override
public void onHostResume(String fromPage) {
//...show逻辑
}
}
总结一下,这套模型,是一个 「行为搜集分发器」,其实是将需求观察到的feed的行为,收敛到位于ad组件的「Geoffrey」中,再由「Geoffrey」进行分发到对应的ad完结中去!
优点在于,我只需求搜集一次行为,就能够屡次分发,满足商业广告中的事务特色。
行为是怎么搜集呢? 是经过在feed中运用IBehaviorReceiver这个接口去搜集。
行为怎么分发呢? 履行类中的注解上,添加了相应的宿主界面全类名。 当宿主调用了该履行为需求重视的办法时(生命周期办法 or 指定的办法),就会进行相应的回调。
到此,根本用法全部讲完。
架构描述
关键:
1.经过在接口上打注解,生成了新的接口,经过apt完结,具体运用 javapoet 辅助完结该事务,有兴趣的同学能够自行了解该部份完结,这里暂且不表:
git.in.zhihu.com/yanfang/beh…
这部份代码,只与 生成接口 这项事务有关。
2.如何分发。
运用时已经讲到了,ad组件实际上,只会供给一个接口给其它组件运用,便是:IBehaviorReceiver ,其它组件经过持有该接口的引用,去自动调用ad组件需求观察的办法。
但是该接口没有任何的直接完结类! 有没有觉得很眼熟?
是的,聪明的你必定发现了,这不便是retrofit中对动态代理的运用吗? 在接口办法上参加注解,在动态代理中去让真实的履行类履行,真实的履行类便是完结了apt生成的接口的类。
先看一张运用了「Geoffrey」的类图,这是商业侧现在特形广告运用「Geoffrey」模型的类图,其间「Geoffrey」的内部完结在下面类图中为黑盒。
能够比较明显的看到,feed组件现在只依靠ad_api组件,而且只持有经过IAdBehaviorDispatcher中返回的IBehaviorReceiver接口。
当feed组件经过IBehaviorReceiver调用具体办法时,「Geoffrey」会将该动作,传递到关怀此动作的履行类中去!