2018年技术周期计划:周期计划-5(2018/1/29-2018/2/4)
写在前面
今天在做一个关于Notification业务的时候,突然想到了自己以前看公司代码时候遇到的一个问题:本来很简单的问题为什么要写的很复杂?
加之自己正要写Notification,所以又回过头来好好的看了一番公司的代码。才感觉前辈们的代码的确是有它们存在的价值和意义。
开始
在正式开始之前,我们先思考一下我们写Notification的套路:无论我们是自定义Notification还是用系统的布局。只要涉及到点击事件,我们就要包装一个PendingIntent。OK,那么问题来了。
如果我们业务简单,我们可能这么写:
Intent intent=new Intent(this,MainActivity.class);
PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
在添加setContentIntent的时候把这个PendingIntent传进去。这样当然没有什么毛病。
不过,我们思考一个问题:
如果这个Intent,我们需要传递参数该怎么办?我们当然可能顺其自然的在intent对象中,开始putExtra,setAction之类的,这样也没有什么毛病。
此时,我们再思考一个问题:
如果这个MainActivity是别人(比如是我的Leader,W哥)维护的,W哥为了更方便的协同开发,他在他的MainActivity中暴露一个用于启动当前Activity的接口,比如是这样的:
public static void start(@NonNull Context context,String action) {
Intent intent = new Intent(context, HomeActivity.class);
intent.putExtra(EXTRA_NAV_ACTION_KEY, action);
if (!(context instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
}
context.startActivity(intent);
}
按照W哥的想法,一切需要启动MainActivity的外部context只需要调用这个静态的start,传入对应的action即可,至于怎么启动外部不需要管。
OK,那么我们的问题来了:
回想一下我们刚才生成PendingIntent的过程....既然要生成PendingIntent,就需要我们自己实例化一个Intent,但是W哥的做法显然是封装了Intent,外部没办法获取。那么怎么办?难道让我的顶头上司去改代码?
公司的做法是这样的:自己获取一个用于Broadcast的Intent,然后对应写一个BroadcastReceiver,在这个接受者中,再去调用MainActivity中的start()。
让我们来看代码
public class NotificationReceiver extends BroadcastReceiver{
// @NotificationClickAction / @NotificationFromType这个注解是自定义的,
//非常简单,但又非常有效的一个自定义注解。一会儿会针对这个注解进行简单的展开
publicstatic PendingIntent createClickIntent(@NonNull Context context, @NotificationClickAction String action, @NotificationFromType int fromType, int requestCode) {
Intent contentIntent = null;
switch (action) {
//随意编写了一个ACTION
case NOTIFICATION_ACTION_CLICK_1:
contentIntent = new Intent(action);
break;
}
if (contentIntent != null) {
contentIntent.setComponent(new ComponentName(context, NotificationReceiver.class));
contentIntent.setPackage(context.getPackageName());
//传递的额外参数,这里是业务需要
contentIntent.putExtra(NOTIFICATION_FROM_TYPE_EXTRA_KEY, fromType);
return PendingIntent.getBroadcast(context, requestCode, contentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}
throw new IllegalArgumentException("createDeleteIntent UnSupport actionType");
}
//省略部分代码
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
//省略部分代码
switch (action) {
//在这里接受我们的Broadcast的ACTION,以此来调用MainActivity暴露的方法
case NOTIFICATION_ACTION_CLICK_1: {
MainActivituy.start(context, action);
break;
}
default://no support action
return;
}
//省略部分代码
}
}
看完代码,我猜大概大家已经能清楚的看出思路,我们外部需要PendingIntent的时候,调用NotificationReceiver中的createClickIntent()方法传入对应一个ACTION,然后在onRecive()中接收这个ACTION,做自己的逻辑即可。
自定义的小注解
上述的代码中,出现了@NotificationClickAction这个注解。其实它的声明很简单,就是这样的:
//我们可以看到这个注解类型是String,有俩个默认变量(它的作用请继续往下看)
@StringDef(value = {NOTIFICATION_ACTION_CLICK_1, NOTIFICATION_ACTION_2})
@Retention(RetentionPolicy.SOURCE)
public @interface NotificationClickAction {
}
public static final String NOTIFICATION_ACTION_CLICK_1 = "我就是一个常量1";
public static final String NOTIFICATION_ACTION_CLICK_2 = "我就是一个常量2";
通过上边的代码,我们可以看出一个问题,那就是我们声明了俩个常量NOTIFICATION_ACTION_CLICK_1 以及NOTIFICATION_ACTION_CLICK_2,并且它俩是注解NotificationClickAction 的初始值。
这样有什么用呢?
还记得我们在createClickIntent()方法里出入的参数么?
@NotificationClickAction String action
用此注解标注的参数,有一个作用就是:如果我们传递了一个这个注解没有包含的变量,比如NOTIFICATION_ACTION_CLICK_3,那么编辑器便会提示你这是错误的。这样的好处便是告诉我们此处需要传什么值。
那么我们再回归到W哥的那个关于MainActivity的start封装,他的start中也包含了对应注解参数,那么我完全可以只需要按照注解的标注传参,即可。极大的提高了协同开发的效率。
尾声
个人认为这是一篇注重代码质量和团队合作规范的一篇博客,重点不是为了去记录知识点,而是为了让后来的伙伴们看到我写的代码,由衷的说一句:这代码看起来真舒服。
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect
2018年7月2号,我正式开始了自己的Android工作,为了能够让自己能够好好完成工作,并且能够快速得到技术提升。所以打算以公众号的方式去敦促自己学习,我会把自己日常的学习笔记发布到公众号上,如果可以,共同进步!~