NO IMAGE

1.本文通用約定

該約定參考自 Effective java 第二版

  • 服務端: 並不是指執行在伺服器端的程式,而是在工程中,提供基本方法的部分。
  • 客戶端: 並不是指執行在使用者手機中的程式,而是在工程中,呼叫提供基本方法的部分。

編碼系列

2.異常管理

程式碼如下

public class ExceptionUtils {
/**
* 物件的非空校驗
*
* @param object  代校驗的引數
* @param message 如果引數為空時的異常堆疊資訊
* @param <T>
* @return
*/
public static <T> T checkoutNotNull(T object, String message) {
if (object == null) {
throw new NullPointerException(message);
}
return object;
}
}

參考自 Retrofit原始碼 v2.0.0

優點

  • 空指標這樣的異常,通過這種方法檢測。在服務端程式碼段中使用。
  • 保證了服務工具的封裝性(檢測的程式碼放在服務端,符合物件導向的程式設計風格)
  • 保證了客戶端程式碼的簡潔性(引數校驗不應該由客戶端呼叫)
  • 保證了整體程式碼的可調式性。
  • 如果引數合法,返回輸入引數,方便客戶端處理

3.Builder模式建立可選引數物件

程式碼如下

public class ZMDialog {
public static class Builder {
private Context context;
private String title;
private String message;
private String positiveMessage;
private String negativeMessage;
private ZMSubscriber positiveClickEvent;
private ZMSubscriber negativeClickEvent;
private boolean cancelable;
public Builder(Context context) {
this.context = context;
}
public Context getContext() {
return context;
}
public String getTitle() {
return title;
}
public Builder setTitle(String title) {
this.title = title;
return this;
}
public String getMessage() {
return message;
}
public Builder setMessage(String message) {
this.message = message;
return this;
}
public String getPositiveMessage() {
return positiveMessage;
}
public Builder setPositiveMessage(String positiveMessage) {
this.positiveMessage = positiveMessage;
return this;
}
public String getNegativeMessage() {
return negativeMessage;
}
/**
* 單選對話方塊的話,需要用這個
*
* @param negativeMessage
* @return
*/
public Builder setNegativeMessage(String negativeMessage) {
this.negativeMessage = negativeMessage;
return this;
}
public ZMSubscriber getPositiveClickEvent() {
return positiveClickEvent;
}
public Builder setPositiveClickEvent(ZMSubscriber positiveClickEvent) {
this.positiveClickEvent = positiveClickEvent;
return this;
}
public ZMSubscriber getNegativeClickEvent() {
return negativeClickEvent;
}
/**
* 單選對話方塊的話,需要用這個
*
* @param negativeClickEvent
* @return
*/
public Builder setNegativeClickEvent(ZMSubscriber negativeClickEvent) {
this.negativeClickEvent = negativeClickEvent;
return this;
}
public boolean isCancelable() {
return cancelable;
}
public Builder setCancelable(boolean cancelable) {
this.cancelable = cancelable;
return this;
}
public ZMDialog builder() {
return new ZMDialog(this);
}
}
private ZMDialog(final Builder zmDialogBuilder) {
MaterialDialog.Builder materialBuilder = new MaterialDialog.Builder(zmDialogBuilder.getContext());
materialBuilder.canceledOnTouchOutside(zmDialogBuilder.isCancelable())
.title(zmDialogBuilder.getTitle())
.content(zmDialogBuilder.getMessage())
.positiveText(zmDialogBuilder.getPositiveMessage())
.negativeText(zmDialogBuilder.getNegativeMessage())
.onPositive(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
zmDialogBuilder.getPositiveClickEvent().onNext(null);
}
})
.onNegative(new MaterialDialog.SingleButtonCallback() {
@Override
public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) {
zmDialogBuilder.getNegativeClickEvent().onNext(null);
}
})
.build()
.show();
}
} 

參考自 Effective java 第二版

優點

  • 可選項引數採用了鏈式引入,程式碼優雅
  • 避免了構造方法傳參不便於維護的缺陷

4.第三方的封裝

程式碼如下

@Override
public void dealWithCustomAction(Context context, UMessage uMessage) {
super.dealWithCustomAction(context, uMessage);
PushMessage pushMessage = new PushMessage();
......//此處為公司機密        PushMessageDispatcher.getInstance().onClick(context, pushMessage);
}

參考自 朋友交流經驗,朋友參考自哪裡,就不知道了

優點

  • umeng收到訊息後,並不直接處理,而是將訊息直接推出到處理的地方,這樣可以避免,更換其他推送渠道時的程式碼邏輯的更改
  • 用第三方的sdk時,要注意,不要在和第三方直接相關的類中處理,這樣能避免更換第三方庫時的邏輯重構

5.Intent、Bundle傳遞資料

程式碼如下

public class BusActivity extends BaseActivityWithToolBar   {
private static final String KEY_TASK_ID = "TASK_ID";
public static Intent getCallingIntent(Context context, String taskId) {
Intent intent = new Intent();
intent.putExtra(KEY_TASK_ID, taskId);
intent.setClass(context, BusActivity.class);
return intent;
}
}

參考自idea自動生成的fragment程式碼,第三方開源專案(具體哪個專案忘了,不好意思)

  • intent中的key存放在服務端,客戶端只關心傳遞的資料,降低key的耦合
  • 不再需要客戶端與服務端約定key了
  • intent,bundle,map等傳遞資料,都可以用這種方式
  • 用了靜態方法,增加記憶體的消耗,但,這帶來了程式碼的簡潔性和可程式的維護性,記憶體的犧牲是值得的

6.跳轉統一管理

程式碼如下

 public class Navigator {
/**
* 跳轉登陸頁
*
* @param context
*/
public void navigateToLogin(@NonNull Context context) {
Intent intent = LoginActivity.getCallingIntent(context);
context.startActivity(intent);
}
}

參考自我任職的公司專案原始碼

  • Navigator必須是單例的
  • 方便管理跳轉,這樣客戶端編寫程式碼的時候,只要在Navigator中找對應的方法就可以了,不用漫山遍野的去找專案工程中對應的服務端檔案,也沒必要從繁雜的服務端檔案中,找尋跳轉方法
  • Navigator的引入,遮蔽了服務端的程式碼細節,符合物件導向的封裝特性

7.android原生註解

幾個常用的原生註解

參考網址 https://developer.android.com/studio/write/annotations.html

  • @Nullable Can be null.
  • @NonNull Cannot be null.
  • @StringRes References a R.string resource.
  • @DrawableRes References a Drawable resource.
  • @ColorRes References a Color resource.
  • @InterpolatorRes References a Interpolator resource.
  • @AnyRes References any type of R. resource.

  • 程式碼中使用這些,可以規範方法入參,問題出在編譯階段,減少執行時的異常

  • 程式碼量少,減少一些不必要的顯示判斷,程式碼優雅可讀

8.關於6.0系統的許可權適配

程式碼如下

許可權檢查

 public class PermissionHelper {
/**
* 檢查App是否有定位許可權
*
* @param activity
* @return true ,已授權,false,未授權
*/
public static boolean checkLocationPermission(Activity activity) {
if (Build.VERSION.SDK_INT >= PERMISSION_LIMIT) {
return PermissionUtils.checkGroupPermissions(activity, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION});
} else {
return true;
}
}
}

fragment中許可權請求

 public class MissionFragment extends BaseFragment {
/**
* fragment中許可權請求方法
*/
requestPermissions(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
/**
* 許可權請求回撥方法
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (locationPermissionGranted(requestCode, permissions, grantResults)) {
navigateToMissionDetail();
}
}
}

參考自

  • fragment中發出請求,在fragment中自己接收,不需要藉助activity中轉
  • 高內聚

9.列舉

程式碼如下

列舉定義

`

/**
* 支付介面入口標示
*/
public enum PaymentEntrance {
/**
* 巴士車票
*/
BUS_TICKET,
/**
* 定製巴士
*/
ZOUME_BUS,
/**
* 當前行程:列表
*/
TRIPS_LIST,
/**
* 當前行程:詳情
*/
TRIP_DETAIL
}

`

列舉使用(服務端)

`

/**
* 獲取攜帶過來的資料
* 用以初始化介面以及傳送網路請求
*/
private void handleExtraParam() {
Bundle bundle = getArguments();
if (bundle != null && bundle.containsKey(EXTRA_PAYMENT_ENTEANCE)) {
paymentEntrance = (PaymentEntrance) bundle.getSerializable(EXTRA_PAYMENT_ENTEANCE);
paymentInfo = bundle.getSerializable(EXTRA_PAYMENT_INFO);
switch (paymentEntrance) {
case BUS_TICKET:
refreshUI((ZMBusTicketOrder) paymentInfo);
break;
case ZOUME_BUS:
refreshUI((ZouMeBusOrderParam) paymentInfo);
break;
case TRIPS_LIST:
case TRIP_DETAIL:
refreshUI((TripOrder) paymentInfo);
break;
}
}
}

`

參考自 android程式設計實戰(第二版)、effectivejava

  • 限定了入參範圍(列舉規定了多少,就只能使用多少),減少客戶端出錯的概率
  • 比謎之數字可讀性更高
  • 列舉型入參就是文件

10.位標記代替多個boolean變數

程式碼如下

`

private static int SELECT_STATE = 0;
private static final int TOGGLE_START_OR = 8;
private static final int TOGGLE_START_AND = 7;
private static final int TOGGLE_DESTINATION_OR = 4;
private static final int TOGGLE_DESTINATION_AND = 11;
private static final int TOGGLE_LINER_OR = 2;
private static final int TOGGLE_LINER_AND = 13;
private static final int TOGGLE_PASSENGER_OR = 1;
private static final int TOGGLE_PASSENGER_AND = 14;
public static final int CODE_SELECT_START = 1;
public static final int CODE_SELECT_DESTINATION = 2;
public static final int CODE_KEY_SOURCE_SEARCH = 5;
public static final int CODE_ADD_PASSENGER = 4;
//標誌位變換控制方法
SELECT_STATE = SELECT_STATE & TOGGLE_START_AND;
SELECT_STATE = SELECT_STATE & TOGGLE_DESTINATION_AND;

`

參考自 android原始碼ApplicationInfo中標誌位使用、自己總結

  • 如果在一個檔案中,控制的標誌位過多,而且標誌位相互獨立,採用boolean型變數將會使判斷邏輯相當繁瑣,使用int進行位運算,則會清爽很多
  • 位運算,控制過程並不會比boolean型變數麻煩太多

11.範型

程式碼如下

`

public interface HasComponent<C> {
C getComponent();
}

`

參考自 effective Java,自己專案原始碼

  • 抽象程式設計,有據可依(此例中,範型宣告與返回值一致)
  • 編寫程式時執行型別檢查,讓錯誤出現在編譯階段