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,自己项目源码
- 抽象编程,有据可依(此例中,范型声明与返回值一致)
- 编写程序时执行类型检查,让错误出现在编译阶段