在古早的 Android 开发时期,有一种思想风潮由安卓开发大神 Jake Wharton(JakeWharton)提出:一个应用只需要一个 `Activity`,所有页面都使用 `Fragment` 构建,而 `Activity` 则负责管理这些 `Fragment`。
然而,说实话,安卓的 `Fragment` 并不是特别好用,尤其是在 Support 库时期。虽然单 `Activity` 开发避免了大量使用 `Fragment`,但也不可避免地需要处理它们。当然,你可以使用像 Navigation 和 Fragmentation 这样的库来简化 `Fragment` 的管理,但这会引入外部依赖,增加学习成本,而且 Fragmentation 已经停止维护。
因此,我思考了一种方法,可以在整个应用中只注册一个 `Activity`,既不需要引入外部依赖,也不用添加太多的 `Fragment`。接下来,让我详细解释一下这个方法。
首先,声明一个Activity的接口类,用于定义一些生命周期和必要的功能函数,非强制的都用default修饰了。
/**
* Activity生命周期和功能接口类
*/
public interface IActivity extends Serializable {
//对应生命周期的onStart
void onStart();
//对应生命周期的onCreate
void onCreate(@Nullable Bundle savedInstanceState);
//在onCreate中,执行setContentView之后
default void initView(@Nullable View view){}
//对应生命周期的onResume
void onResume();
//对应生命周期的onDestroy
void onDestroy();
//寄生到一个Activity,获得Activity公开的能力
default void parasitism(Activity activity){}
//获取宿主Activity
default Activity getHostActivity(){return null;}
//对应生命周期的
default void onPause() {}
//对应生命周期的
default void onRestart(){}
//ContentView:布局的id文件
@LayoutRes
default int contentLayout(){ return -1;}
//ContentView: 自己创建的View
default View contentView() { return null;}
}
然后创建一个基类实现此接口,名字就叫BaseActivityImpl,这个类是抽象的,可以根据自身需要修改。
/**
* IActivity接口实现的抽象基类
*/
public abstract class BaseActivityImpl implements IActivity {
//上下文
protected Context context;
//Activity的引用
protected WeakReference<Activity> hostActivity;
//构造方法
public BaseActivityImpl(Context context){
this.context = context;
}
//寄生到Activity实例中
@Override
public void parasitism(Activity activity) {
this.hostActivity = new WeakReference<>(activity);
}
//获取宿主
@Override
public Activity getHostActivity(){
return hostActivity.get();
}
//结束并释放宿主
public void finish(){
if(hostActivity!=null && hostActivity.get()!=null){
hostActivity.get().finish();
}
}
}
最后也是最重要的,所有IActivity最终都由一个Activity实现,名字叫SingleActivity,这个Activity也是唯一在清单(Manifest.xml)注册的Activity。
注意:这个Activity的启动模式(launch mode)不应该被指定为栈顶复用(SingleTop)和栈内复用(SingleTask)!
/**
* 所有IActivity实现类的容器
**/
public class SingleActivity extends Activity {
public static final String EXTRA_IMPL_CLASS = "extra_impl_class";
//IActivity的实现类
private IActivity activity;
//传入类,反射构造器生成
public static void startActivity(Context context,Class<? extends IActivity> activityImplClass){
Intent intent = new Intent(context, SingleActivity.class);
intent.putExtra(EXTRA_IMPL_CLASS,activityImplClass);
context.startActivity(intent);
}
//允许传入intentFlag控制新Activity的launchMode
public static void startActivity(Context context, Class<? extends IActivity> activityImplClass,int intentFlag){
Intent intent = new Intent(context, SingleActivity.class);
intent.putExtra(EXTRA_IMPL_CLASS,activityImplClass);
intent.addFlags(intentFlag);
context.startActivity(intent);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//获取Intent
Intent intent = getIntent();
if(intent!=null){
//获取IActivity实现类
Class<? extends IActivity> clazz = (Class<? extends IActivity>) getIntent().getSerializableExtra(EXTRA_IMPL_CLASS);
//通过IActivity实现类反射构造器创建一个IActivity实例
try {
activity = (IActivity) clazz.getConstructor(Context.class).newInstance(SingleActivity.this);
} catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
e.printStackTrace();
}
}
//如果IActivity不为空,则说明实例构建成功
if(activity!=null){
activity.onCreate(savedInstanceState);
if(activity.contentLayout()!=-1){
setContentView(activity.contentLayout());
}else if(activity.contentView()!=null){
setContentView(activity.contentView());
}
//将SingleActivity作为宿主
activity.parasitism(this);
View rootView = this.findViewById(android.R.id.content).getRootView();
activity.initView(rootView);
}else{
//在不传入IActivity时,说明是第一个启动的Activity,可以作为Splash
//初始化默认的页面
}
}
/**
* 其他都是同步一些生命周期函数
**/
@Override
protected void onResume() {
super.onResume();
if(activity!=null) activity.onResume();
}
@Override
protected void onPause() {
super.onPause();
if(activity!=null) activity.onPause();
}
@Override
protected void onRestart() {
super.onRestart();
if(activity!=null) activity.onRestart();
}
@Override
protected void onDestroy() {
super.onDestroy();
if(activity!=null){
activity.onDestroy();
activity.parasitism(null);
activity = null;
}
}
}
然后在Manifest中注册SingleActivity。
<activity android:name=".onlyone.SingleActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
如果想创建一个Activity实例,可以这么做。
activity_first_impl是一个布局文件,里面只有一个TextView,id为tv_first_activity_impl,实在太简单了,xml我就不放这里了。
public class FirstActivityImpl extends BaseActivityImpl{
private TextView tvTitle;
public FirstActivityImpl(Context context) {
super(context);
//构造函数
}
@Override
public void onStart() {
//在页面可见时,做一些事情
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
//在创建Activity时,做一些事情
}
@Override
public void onResume() {
//在页面恢复时,做一些事情
}
@Override
public void onDestroy() {
//在页面销毁时,做一些事情
}
//可以选择通过LayoutInflater映射去创建ContentView
@Override
public View contentView() {
return null;//LayoutInflater.from(context).inflate(R.layout.activity_first_impl,null,false);
}
//也可以直接返回布局文件的id
@Override
public int contentLayout() {
return R.layout.activity_first_impl;
}
@Override
public void initView(View view) {
tvTitle = view.findViewById(R.id.tv_first_activity_impl);
tvTitle.setText("Hello world!");
}
}
启动此IActivity实例的方法如下:
SingleActivity.startActivity(StartActivity.this,FirstActivityImpl.class)
不足与优化
1.目前不支持ViewBinding和DataBinding,后面也许会加上;
2.拒绝跳转!在SingleActivity管理一个回退栈,每次显示一个新的IActivity实例时复用SingleActivity;
3.可维护性与简化导航?
这样,一个简单的单Activity开发框架就搭建完成了,代码量很少,目前有一些小缺陷,就是IActivity都需要通过BaseActivityImpl创建,构造方法中的上下文(Context)是必要的,因为大部分服务对象都需要用到Context,而且自定义构造参数不太方便,此外BaseActivityImpl只能具备Activity公开的能力,比如被protected和private修饰的方法,是调用不到的(目前只能反射),这些都是一个值得优化的地方。