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工作,為了能夠讓自己能夠好好完成工作,并且能夠快速得到技術提升。是以打算以公衆号的方式去敦促自己學習,我會把自己日常的學習筆記釋出到公衆号上,如果可以,共同進步!~ 個人公衆号