回想起刚开始做Android开发工作时,有一个烦恼:当拿到一个新项目的时候,想从界面的跳转来梳理整个App的功能,结果发现根本没有对应的产品文档,然后代码中启动Activity的代码散落在Activity以外的各种地方,例如自定义view中、Fragment中、各种Adapter中…这样就造成了如下麻烦:
而启动方法直接new Intent(...)
,然后startActivity
也有以下问题:
onCreate
方法及以后;可能还有其他问题,鉴于上面提出的几点,后来在开发项目中对启动Activity作出了如下约束:
下面分享下添加这两点约束条件的理由。
第一点是在限制启动Activity的范围,只有特定组件有权利去启动Activity,这样使得职责更为集中、单一,查找功能的时候也更为方便。而且这两个组件本身就可以直接拿到当前Activity:Activity自身可以通过this
来取得,Fragment则可以通过getActivity
方法。
但是这样也会给编程带来一些麻烦,有些点击事件的处理需要额外的步骤,最常见是自定义View以及RecyclerView的Adapter和ViewHolder。
禁止自定义View中去启动Activity的理由是:自定义View应当聚焦控件自身的功能,不应该涉及跳转业务逻辑。
处理方法就是设置监听回调:
public class CustomView extends View {
private Runnable mOnXXXClickListener;
private View mXXX;
public CustomView(Context context) {
super(context);
//....
mXXX.setOnClickListener(v -> {
if(mOnXXXClickListener != null){
mOnXXXClickListener.run();
}
});
}
public void setOnXXXClickListener(Runnable onXXXClickListener) {
this.mOnXXXClickListener = onXXXClickListener;
}
}
如果之前是在Adapter中处理点击的,那么可以考虑用上述监听的方式,或者像下面这样在Activity中使用匿名类的方式(这里的XXXAdapter
已经实现了对应抽象方法):
mRecyclerView.setAdapter(new XXXAdapter() {
@Override
public void onBindViewHolder(@NonNull @NotNull RecyclerView.ViewHolder holder, int position) {
super.onBindViewHolder(holder, position);
holder.itemView.setOnClickListener(v -> {
//start activity
});
}
});
如果之前是在ViewHolder中处理点击的,那么就得通过监听,或者直接把ViewHolder作为内部类放到Activity中。
如果启动Activity的方法没有限制,那么检查Activity的启动点就不太方便,并且new Intent
有上面所说的问题。
一种简单的方法是在每个Activity添加静态的启动方法,例如:
public class XXXActivity extends AppCompatActivity {
public static void start(Activity activity) {
Intent intent = new Intent(activity, XXXActivity.class);
activity.startActivity(intent);
}
//......
}
如果有参数且要检查有效性就是:
public class XXXActivity extends AppCompatActivity {
private static final String KEY_ARG_1 = "key_arg_1";
public static void start(Activity activity, String arg) {
Intent intent = new Intent(activity, XXXActivity.class);
if(!TextUtils.isEmpty()){
intent.putExtra(KEY_ARG_1, arg);
} else {
throw new IllegalArgumentException();
}
activity.startActivity(intent);
}
//......
}
需要处理返回参数时,可以添加一个startForResult
版本的启动方法:
public class XXXActivity extends AppCompatActivity {
public static void start(Activity activity) {
Intent intent = new Intent(activity, XXXActivity.class);
activity.startActivity(intent);
}
public static void startForResult(Activity activity, int requestCode) {
Intent intent = new Intent(activity, XXXActivity.class);
activity.startActivityForResult(intent, requestCode);
}
//......
}
有些情况只需要Intent
,而不用start
的时候可以提供一个getIntentForStart
方法:
public class XXXActivity extends AppCompatActivity {
private static final String KEY_ARG_1 = "key_arg_1";
public static Intent getIntentForStart(Activity activity, String arg) {
Intent intent = new Intent(activity, XXXActivity.class);
if(!TextUtils.isEmpty()){
intent.putExtra(KEY_ARG_1, arg);
} else {
throw new IllegalArgumentException();
}
return intent;
}
//......
}