Android 面向单Activity开发之移花接木

3,384 阅读4分钟

      在古早的 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修饰的方法,是调用不到的(目前只能反射),这些都是一个值得优化的地方。