美文网首页Android学习
Android项目基类封装ViewBinding、MVP、Vie

Android项目基类封装ViewBinding、MVP、Vie

作者: 碧桃鹦鹉 | 来源:发表于2020-11-27 17:15 被阅读0次

    都会需要有用到BaseActivity,从最开始的initData、initView,到后来需要承载监听推送、监听网络变化的广播、延迟锁定等等各种需求,BaseActivity的功能越来越杂,越来越密集。相对实际的页面上的功能需求,基类的封装经过这样长时间的无脑堆砌,到最后看起来会更匪夷所思。所以从一开始,Base的封装就要足够清晰、稳健、可扩展。
    AndroidBase
    我的思路是分层继承,每一层只做和这一层功能相关的事,变动修改单一功能都是清晰的,同时在最后的一层功能又是完整的。
    AppCompatActivity
                 |
    BaseViewActivity
                 |
    BaseFunctionsActivity
                 |
    BaseViewModelActivity
                 |
    BaseMVPActivity
                 |
    BaseToolbarActivity

    1.BaseViewActivity(View层)

    主要代码:

      public abstract class BaseViewActivity<VB extends ViewBinding> extends AppCompatActivity
    {
        private View rootView;
        protected VB vBinding;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setRequestedOrientation(getScreenOrientation());//竖屏
            vBinding = ViewBindingCreator.createViewBinding(getClass(), getLayoutInflater());
            rootView = generateContentView(vBinding == null ? getContentView() : vBinding.getRoot());
            setContentView(rootView);
        }
    
        protected View getContentView()
        {
            return null;
        }
    
        protected View generateContentView(View contentView)
        {
            return contentView;
        }
    
        protected int getScreenOrientation()
        {
            return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        }
        public void showLoadingDialog()
        {
        }
        public void hideLoadingDialog()
        {
        }
    }
    
    

    ViewBindingCreator主要代码:

    @SuppressWarnings("unchecked")
    public static <VB extends ViewBinding> VB createViewBinding(Class targetClass,
                                                                    LayoutInflater layoutInflater)
        {
            Type type = targetClass.getGenericSuperclass();
    
            if (type instanceof ParameterizedType)
            {
                try
                {
                    Type[] types = ((ParameterizedType) type).getActualTypeArguments();
    
                    for (Type type1 : types)
                    {
                        if (type1.getTypeName()
                                 .endsWith("Binding"))
                        {
                            Method method = ((Class<VB>) type1).getMethod("inflate",
                                                                          LayoutInflater.class);
                            return (VB) method.invoke(null, layoutInflater);
                        }
                    }
    
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
            } return null;
        }
    
    public abstract class BaseViewFragment<VB extends ViewBinding> extends Fragment
    {
    
        protected VB vBinding;
    
        @Nullable
        @Override
        public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
                                 @Nullable Bundle savedInstanceState)
        {
            if (vBinding == null)
            {
                vBinding = ViewBindingCreator.createViewBinding(getClass(), inflater);
                View rootView = generateContentView(
                        vBinding == null ? getContentView() : vBinding.getRoot());
                rootView.setBackgroundColor(getResources().getColor(R.color.colorBackgroundTint));
                onCreateView(rootView);
            }
            return vBinding.getRoot();
        }
    
        protected abstract void onCreateView(View rootView);
    
        protected View getContentView()
        {
            return null;
        }
    
        protected View generateContentView(View contentView)
        {
            return contentView;
        }
    
        protected void setStatusBarTextDark(boolean isStatusBarTextDark)
        {
            Activity activity = getActivity();
            if (activity instanceof BaseViewActivity)
            {
                ((BaseViewActivity) activity).setStatusBarTextDark(isStatusBarTextDark);
            }
        }
    
        protected void showLoadingDialog()
        {
            Activity activity = getActivity();
            if (activity instanceof BaseViewActivity)
            {
                ((BaseViewActivity) activity).showLoadingDialog();
            }
        }
    
        protected void hideLoadingDialog()
        {
            Activity activity = getActivity();
            if (activity instanceof BaseViewActivity)
            {
                ((BaseViewActivity) activity).hideLoadingDialog();
            }
        }
    }
    

    继承自AppCompatActivity不用说什么了。用ViewModel也是因为之前用ButterKnife,更新了Android Studio4.0之后提示R.id.xxxxx不再是静态常量,所以推荐使用ViewBinding的方式,ButterKnife官方也说了不再维护,转用ViewBinding。相对ButterKnife就是少了那个对输入框 点击事件的注解方式写法。我在另一个文章吐槽过。也还行吧,官方都推荐了 ,应该不会有太大问题。BaseViewActivity内主要通过ViewBinding或者getContentView获取页面内容View,优先看有没有ViewBinding,没有的话再用getContentView。ViewBinding这块用了反射去调inflate方法来创建对象。反射是不好,但是为了可以偷懒避免在每个具体xxxActivity里都写一遍xxxViewBinding.inflate,只需要在泛型声明一下xxxViewBinding就可以了。还提供了一个generateContentView方法,便于子类重载后扩充contentView,例如BaseToolbarActivity就是实现了这个方法,对contentView进一步包装后再返回。
    在这个类内也可以封装loadingDialog,Toast什么有关View的基础功能。
    同时在写具体业务的Activity或者Fragment时,不需要重载什么新的方法,只需要传一个泛型ViewBinding就可以了,减少写代码的负担。

    2.BaseFunctionsActivity(基础功能层)

    主要代码:

    public abstract class BaseFunctionsActivity<VB extends ViewBinding> extends BaseViewActivity<VB>
    {
        public static ArrayList<Class<? extends BaseActivityFunction>> functionsClasses = new ArrayList<>();
    
        private LinkedHashMap<String, BaseActivityFunction> functions = new LinkedHashMap<>();
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            for (Class<? extends BaseActivityFunction> functionClass : functionsClasses)
            {
                try
                {
                    functions.put(functionClass.getName(), functionClass.newInstance());
                }
                catch (IllegalAccessException | InstantiationException e)
                {
                    e.printStackTrace();
                }
            }
    
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivityCreated(this, savedInstanceState);
            }
        }
    
        @Override
        protected void onNewIntent(Intent intent)
        {
            super.onNewIntent(intent);
        }
    
        @Override
        protected void onSaveInstanceState(@NonNull Bundle outState)
        {
            super.onSaveInstanceState(outState);
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivitySaveInstanceState(this, outState);
            }
        }
    
        @Override
        protected void onStart()
        {
            super.onStart();
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivityStarted(this);
            }
        }
    
        @Override
        protected void onRestart()
        {
            super.onRestart();
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivityRestarted(this);
            }
        }
    
        @Override
        protected void onResume()
        {
            super.onResume();
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivityResumed(this);
            }
        }
    
        @Override
        protected void onPause()
        {
            super.onPause();
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivityPaused(this);
            }
        }
    
        @Override
        protected void onStop()
        {
            super.onStop();
            for (BaseActivityFunction baseActivityFunction : functions.values())
            {
                baseActivityFunction.onActivityStopped(this);
            }
        }
    
        @Override
        protected void onDestroy()
        {
            super.onDestroy();
            Iterator<BaseActivityFunction> iterator = functions.values()
                                                               .iterator();
            while (iterator.hasNext())
            {
                BaseActivityFunction baseActivityFunction = iterator.next();
                baseActivityFunction.onActivityDestroyed(this);
                baseActivityFunction = null;
                iterator.remove();
            }
            functions = null;
        }
    
        @Override
        public void onBackPressed()
        {
            boolean canBackPressed = true;
            for (BaseActivityFunction value : functions.values())
            {
                if (!value.onBeforeBackPressed(this))
                {
                    canBackPressed = false;
                }
            }
            if (canBackPressed)
            {
                super.onBackPressed();
            }
        }
    
        @Override
        public void finish()
        {
            super.finish();
            for (BaseActivityFunction value : functions.values())
            {
                value.onFinish(this);
            }
        }
    
        public BaseActivityFunction getFunction(Class<? extends BaseActivityFunction> fClass)
        {
            return functions.get(fClass.getName());
        }
    }
    
    public abstract class BaseActivityFunction implements Application.ActivityLifecycleCallbacks
    {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState)
        {
        }
    
        @Override
        public void onActivityStarted(Activity activity)
        {
        }
    
        public void onActivityRestarted(Activity activity)
        {
        }
    
        @Override
        public void onActivityResumed(Activity activity)
        {
        }
    
        @Override
        public void onActivityPaused(Activity activity)
        {
        }
    
        @Override
        public void onActivityStopped(Activity activity)
        {
        }
    
        @Override
        public void onActivitySaveInstanceState(Activity activity, Bundle outState)
        {
        }
    
        @Override
        public void onActivityDestroyed(Activity activity)
        {
        }
    
        public void onFinish(Activity activity)
        {
        }
    
        public boolean onBeforeBackPressed(Activity activity)
        {
            return true;
        }
    }
    

    因为在很多场景,都需要基类Activity具有一些功能,检查是否登录,监听网络变化,嵌入友盟推送等等。本基类封装库又是一个完整的个体,所以就会出现尴尬的情况,基类是A->B->C->D->XXXXX这样的继承关系,但是我又需要在B和C之间插入一个继承B1,所以基于此需求,BaseFunctionsActivity通过实现类似ActivityCallback的方式,提供给外部嵌入内部的机会,通过继承BaseActivityFunction来实现不同功能模块。
    比如我想让整个应用的所有Activity能监听网络变化,并Toast提示到界面上,我就只需要创建一个NetChangeActivityFunction,实现监听网络变化的广播,把NetChangeActivityFunction.class添加到BaseFunctionsActivity的静态functionsClasses中,这样每个Activity就具有了NetChangeActivityFunction的功能。如果某一个具体Activity想调用NetChangeActivityFunction中的方法时,用getFunction方法可以获取到当前Activity里已经实现的BaseActivityFunction具体对象,就可以调用到方法,变量同理。
    同时也提供了对onBackPressed的拦截,因为有很多场景,需要在onBackPressed时判断一些逻辑,再决定是否继续执行。这块的重点设计的逻辑是,如果一个Activity有多个Function,只要其中有一个Function判定为false就不会执行super.onBackPressed();但是每一个Function的onBackPressed都会执行一次。暂时考虑对每个Function都公平一点,避免出现到某一个Function的onBackPressed就终止了后面的执行。
    这样的在一个Activity里注入多个Function的方式有一个缺点就是每一个Activity都承载了多个的Function对象,如果BaseFunctions很多的话,相对直接继承多层Activity的方式,会占用更多内存。

    3.BaseViewModelActivity(ViewModel层)

    主要代码:

    public abstract class BaseViewModelActivity<VB extends ViewBinding> extends BaseFunctionsActivity<VB>
    {
        protected final void setViewModelHolder(@NonNull IViewModelHolder iViewModelHolder)
        {
            iViewModelHolder.setIViewModelOwners(new IViewModelOwners()
            {
                @Override
                public ViewModelStoreOwner getActivityViewModelStoreOwner()
                {
                    return BaseViewModelActivity.this;
                }
    
                @Override
                public ViewModelStoreOwner getFragmentViewModelStoreOwner()
                {
                    return null;
                }
    
                @Override
                public LifecycleOwner getActivityLifecycleOwner()
                {
                    return BaseViewModelActivity.this;
                }
    
                @Override
                public LifecycleOwner getFragmentLifecycleOwner()
                {
                    return null;
                }
            });
        }
    } 
    
    public interface IViewModelHolder
    {
        void setIViewModelOwners(IViewModelOwners iViewModel);
    
        void onViewModelLoaded();
    } 
    
    public interface IViewModelOwners
    {
        ViewModelStoreOwner getActivityViewModelStoreOwner();
    
        ViewModelStoreOwner getFragmentViewModelStoreOwner();
    
        LifecycleOwner getActivityLifecycleOwner();
    
        LifecycleOwner getFragmentLifecycleOwner();
    } 
    
    public class BaseViewModelHolder implements IViewModelHolder
    {
        private IViewModelOwners iViewModelOwners;
    
        private ViewModelProvider activityViewModelProvider;
        private ViewModelProvider fragmentViewModelProvider;
    
        @Override
        public final void setIViewModelOwners(IViewModelOwners iViewModel)
        {
            this.iViewModelOwners = iViewModel;
            onViewModelLoaded();
        }
    
        @Override
        public void onViewModelLoaded()
        {
    
        }
    
        protected final <VM extends ViewModel> VM getActivityViewModel(Class<VM> vmClass)
        {
            if (activityViewModelProvider == null)
            {
                activityViewModelProvider = new ViewModelProvider(
                        iViewModelOwners.getActivityViewModelStoreOwner(),
                        new ViewModelProvider.NewInstanceFactory());
            }
            return activityViewModelProvider.get(vmClass);
        }
    
        protected final <VM extends ViewModel> VM getFragmentViewModel(Class<VM> vmClass)
        {
            if (fragmentViewModelProvider == null)
            {
                fragmentViewModelProvider = new ViewModelProvider(
                        iViewModelOwners.getFragmentViewModelStoreOwner(),
                        new ViewModelProvider.NewInstanceFactory());
            }
            return fragmentViewModelProvider.get(vmClass);
        }
    
        protected final <T> void observeActivityLiveData(LiveData<T> liveData, Observer<T> observer)
        {
            liveData.observe(iViewModelOwners.getActivityLifecycleOwner(), observer);
        }
    
        protected final <T> void observeFragmentLiveData(LiveData<T> liveData, Observer<T> observer)
        {
            liveData.observe(iViewModelOwners.getFragmentLifecycleOwner(), observer);
        }
    }
    

    ViewModel这块也是尝试了很多种方式,目前保持一个还算合适的状态,但是总感觉也不是最好的状态。因为对ViewModel和LiveData的学习也是近期才开始的,没有太多的应用场景可能理解的不是完全到位。
    ViewModel层整体的思路是,因为ViewModel需要ViewModelStore才能创建,LiveData又需要LifeCycle,这两个需要都集中在ComponentActivity和Fragment这两个宿主中,但是ViewModel的创建又不能直接持有宿主的引用,ViewModel的使用场景绝大部分又应该都是在Presenter的内部。在一个宿主中可能存在多个ViewModel,ViewModel就要发源于宿主,在Presenter中被引用和使用。还有个限制是不希望ViewModel层高于MVP层,因为希望MVP层是具体使用场景中比较主要的,整体的架构还是围绕MVP的,ViewModel相关的只是附加提供的功能。
    因为Activity和Fragment都具有ViewModelStoreOwner和LifeCycleOwner的属性,所以把他们两个类里总计四个属性:ActivityViewModelStoreOwner、ActivityLifeCycleOwner、FragmentViewModelStoreOwner和FragmentLifeCycleOwner抽象为一个新的对象IViewModelOwners(这个名字不太妥,之后得改)。这样的新对象就是脱离了Activity和Fragment的限制的,是超越了他们的。为什么一定要拆分成四个方法而不是两个,是因为有的时候需要在Fragment里用到Fragment所附着的Activity的ViewModel,所以只能拆分开。
    在BaseViewModelHolder中,通过IViewModelOwners接口在Activity和Fragment中获取到ViewModelStoreOwner和LifeCycleOwner,可以创建出ViewModelProvider,再通过对应的activityViewModelProvider或者fragmentViewModelProvider创建出需要的ViewModel。LiveData的观测也是发在BaseViewModelHolder中。之后让BasePresenter继承自BaseViewModelHolder就可以获得到对应的创建ViewModel和观测LiveData功能了。

    BaseMVPActivity(MVP层)

    主要代码:

    public abstract class BaseMVPActivity<Presenter extends BaseContract.Presenter, VB extends ViewBinding> extends BaseViewModelActivity<VB> implements BaseContract.View
    
    {
        protected Presenter presenter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            presenter = createPresenter();
            if (presenter != null)
            {
                setViewModelHolder(presenter);
            }
        }
    
        @Override
        public void showLoading()
        {
            showLoadingDialog();
        }
    
        @Override
        public void hideLoading()
        {
            hideLoadingDialog();
        }
    
        @Override
        public void message(String message)
        {
            ToastUtil.toast(message);
        }
    
        protected Presenter createPresenter()
        {
            return null;
        }
    
        @Override
        protected void onDestroy()
        {
            super.onDestroy();
            if (presenter != null)
            {
                presenter.detachView();
            }
        }
    }
    
    public interface BaseContract
    {
        interface View
        {
            void showLoading();
    
            void hideLoading();
    
            void message(String message);
        }
    
        interface Presenter extends IViewModelHolder
        {
            boolean viewNotNull();
    
            void detachView();
        }
    }
    
    public abstract class BasePresenter<View extends BaseContract.View> extends BaseViewModelHolder implements BaseContract.Presenter
    {
        protected View view;
    
        protected BasePresenter(View view)
        {
            this.view = view;
        }
    
        @Override
        public boolean viewNotNull()
        {
            return this.view != null;
        }
    
        @Override
        public void detachView()
        {
            this.view = null;
        }
    
        protected <VM extends ViewModel> VM getViewModel(Class<VM> vmClass)
        {
            if (view instanceof Fragment)
            {
                return getFragmentViewModel(vmClass);
            }
            return getActivityViewModel(vmClass);
        }
    
        protected <T> void observeLiveData(LiveData<T> liveData, Observer<T> observer)
        {
            if (view instanceof Fragment)
            {
                observeFragmentLiveData(liveData, getMVPObserver(observer));
            }
            else
            {
                observeActivityLiveData(liveData, getMVPObserver(observer));
            }
        }
    
        private <T> Observer<T> getMVPObserver(Observer<T> observer)
        {
            return t -> {
                if (viewNotNull())
                {
                    observer.onChanged(t);
                }
            };
        }
    }
    

    MVP的封装还是比较基础的,BaseContract内含View和Presenter的接口,Activity持有Presenter的引用,Presenter持有View的引用,同时BasePresenter继承了BaseViewModelHolder,也就有了M层的功能,但是又相对BaseContract的这一套是独立的,ViewModel相关并不与MVP强相关。同时对获取ViewModel做了进一步封装,减少BasePresenter子类使用ViewModel时的判断逻辑。BaseMVPActivity还是和BaseViewActivity一样,在继承没有强制需要实现的方法,增加了Presenter的泛型,自主去实现createPresenter方法,实现了之后自然就需要让Activity实现XXXContract.View的接口。
    举例创建一个Activity:通过在文件夹右键单击 New->Activity->Empty Activity创建一个TestActivity,自动创建好后只需要把继承的AppCompatActivity修改为BaseMVPActivity(也可以根据需要继承自BaseToolbarActivity)。这时都不需要实现任何抽象方法,只需要在BaseMVPActivity上配置泛型就可以,但是Presenter的创建还是要主动实现一个父类的createPresenter()方法。
    这样写起代码来都是顺畅的,比如要新建一个页面,就是先新建一个XXXContract类,想好这个页面可能会有那些功能和回调,去定义View和Presenter的方法,然后新建一个XXXPresenter类实现XXXContract.Presenter接口,可以暂时不写具体功能,然后再创建一个XXXActivity,把XXXViewBinding和XXXPresenter泛型配置好。这时候可以先去写XXXViewBinding的布局页面,也可以先写XXXPresenter中的业务逻辑。脑袋里想好要改页面的东西,那就去关注xml布局,想改业务逻辑就去XXXModel或者XXXPresenter,相互之间都不干扰。git提交时也可以一眼看出来这次提交是修改了业务还是布局,减少出错。

    相关文章

      网友评论

        本文标题:Android项目基类封装ViewBinding、MVP、Vie

        本文链接:https://www.haomeiwen.com/subject/coixwktx.html