Android項(xiàng)目基類封裝ViewBinding、MVP、ViewModel

都會(huì)需要有用到BaseActivity,從最開始的initData、initView,到后來需要承載監(jiān)聽推送、監(jiān)聽網(wǎng)絡(luò)變化的廣播、延遲鎖定等等各種需求,BaseActivity的功能越來越雜,越來越密集。相對(duì)實(shí)際的頁(yè)面上的功能需求,基類的封裝經(jīng)過這樣長(zhǎng)時(shí)間的無腦堆砌,到最后看起來會(huì)更匪夷所思。所以從一開始,Base的封裝就要足夠清晰、穩(wěn)健、可擴(kuò)展。
AndroidBase
我的思路是分層繼承,每一層只做和這一層功能相關(guān)的事,變動(dòng)修改單一功能都是清晰的,同時(shí)在最后的一層功能又是完整的。
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也是因?yàn)橹坝肂utterKnife,更新了Android Studio4.0之后提示R.id.xxxxx不再是靜態(tài)常量,所以推薦使用ViewBinding的方式,ButterKnife官方也說了不再維護(hù),轉(zhuǎn)用ViewBinding。相對(duì)ButterKnife就是少了那個(gè)對(duì)輸入框 點(diǎn)擊事件的注解方式寫法。我在另一個(gè)文章吐槽過。也還行吧,官方都推薦了 ,應(yīng)該不會(huì)有太大問題。BaseViewActivity內(nèi)主要通過ViewBinding或者getContentView獲取頁(yè)面內(nèi)容View,優(yōu)先看有沒有ViewBinding,沒有的話再用getContentView。ViewBinding這塊用了反射去調(diào)inflate方法來創(chuàng)建對(duì)象。反射是不好,但是為了可以偷懶避免在每個(gè)具體xxxActivity里都寫一遍xxxViewBinding.inflate,只需要在泛型聲明一下xxxViewBinding就可以了。還提供了一個(gè)generateContentView方法,便于子類重載后擴(kuò)充contentView,例如BaseToolbarActivity就是實(shí)現(xiàn)了這個(gè)方法,對(duì)contentView進(jìn)一步包裝后再返回。
在這個(gè)類內(nèi)也可以封裝loadingDialog,Toast什么有關(guān)View的基礎(chǔ)功能。
同時(shí)在寫具體業(yè)務(wù)的Activity或者Fragment時(shí),不需要重載什么新的方法,只需要傳一個(gè)泛型ViewBinding就可以了,減少寫代碼的負(fù)擔(dān)。

2.BaseFunctionsActivity(基礎(chǔ)功能層)

主要代碼:

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;
    }
}

因?yàn)樵诤芏鄨?chǎng)景,都需要基類Activity具有一些功能,檢查是否登錄,監(jiān)聽網(wǎng)絡(luò)變化,嵌入友盟推送等等。本基類封裝庫(kù)又是一個(gè)完整的個(gè)體,所以就會(huì)出現(xiàn)尷尬的情況,基類是A->B->C->D->XXXXX這樣的繼承關(guān)系,但是我又需要在B和C之間插入一個(gè)繼承B1,所以基于此需求,BaseFunctionsActivity通過實(shí)現(xiàn)類似ActivityCallback的方式,提供給外部嵌入內(nèi)部的機(jī)會(huì),通過繼承BaseActivityFunction來實(shí)現(xiàn)不同功能模塊。
比如我想讓整個(gè)應(yīng)用的所有Activity能監(jiān)聽網(wǎng)絡(luò)變化,并Toast提示到界面上,我就只需要?jiǎng)?chuàng)建一個(gè)NetChangeActivityFunction,實(shí)現(xiàn)監(jiān)聽網(wǎng)絡(luò)變化的廣播,把NetChangeActivityFunction.class添加到BaseFunctionsActivity的靜態(tài)functionsClasses中,這樣每個(gè)Activity就具有了NetChangeActivityFunction的功能。如果某一個(gè)具體Activity想調(diào)用NetChangeActivityFunction中的方法時(shí),用getFunction方法可以獲取到當(dāng)前Activity里已經(jīng)實(shí)現(xiàn)的BaseActivityFunction具體對(duì)象,就可以調(diào)用到方法,變量同理。
同時(shí)也提供了對(duì)onBackPressed的攔截,因?yàn)橛泻芏鄨?chǎng)景,需要在onBackPressed時(shí)判斷一些邏輯,再?zèng)Q定是否繼續(xù)執(zhí)行。這塊的重點(diǎn)設(shè)計(jì)的邏輯是,如果一個(gè)Activity有多個(gè)Function,只要其中有一個(gè)Function判定為false就不會(huì)執(zhí)行super.onBackPressed();但是每一個(gè)Function的onBackPressed都會(huì)執(zhí)行一次。暫時(shí)考慮對(duì)每個(gè)Function都公平一點(diǎn),避免出現(xiàn)到某一個(gè)Function的onBackPressed就終止了后面的執(zhí)行。
這樣的在一個(gè)Activity里注入多個(gè)Function的方式有一個(gè)缺點(diǎn)就是每一個(gè)Activity都承載了多個(gè)的Function對(duì)象,如果BaseFunctions很多的話,相對(duì)直接繼承多層Activity的方式,會(huì)占用更多內(nèi)存。

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這塊也是嘗試了很多種方式,目前保持一個(gè)還算合適的狀態(tài),但是總感覺也不是最好的狀態(tài)。因?yàn)閷?duì)ViewModel和LiveData的學(xué)習(xí)也是近期才開始的,沒有太多的應(yīng)用場(chǎng)景可能理解的不是完全到位。
ViewModel層整體的思路是,因?yàn)閂iewModel需要ViewModelStore才能創(chuàng)建,LiveData又需要LifeCycle,這兩個(gè)需要都集中在ComponentActivity和Fragment這兩個(gè)宿主中,但是ViewModel的創(chuàng)建又不能直接持有宿主的引用,ViewModel的使用場(chǎng)景絕大部分又應(yīng)該都是在Presenter的內(nèi)部。在一個(gè)宿主中可能存在多個(gè)ViewModel,ViewModel就要發(fā)源于宿主,在Presenter中被引用和使用。還有個(gè)限制是不希望ViewModel層高于MVP層,因?yàn)橄M鸐VP層是具體使用場(chǎng)景中比較主要的,整體的架構(gòu)還是圍繞MVP的,ViewModel相關(guān)的只是附加提供的功能。
因?yàn)锳ctivity和Fragment都具有ViewModelStoreOwner和LifeCycleOwner的屬性,所以把他們兩個(gè)類里總計(jì)四個(gè)屬性:ActivityViewModelStoreOwner、ActivityLifeCycleOwner、FragmentViewModelStoreOwner和FragmentLifeCycleOwner抽象為一個(gè)新的對(duì)象IViewModelOwners(這個(gè)名字不太妥,之后得改)。這樣的新對(duì)象就是脫離了Activity和Fragment的限制的,是超越了他們的。為什么一定要拆分成四個(gè)方法而不是兩個(gè),是因?yàn)橛械臅r(shí)候需要在Fragment里用到Fragment所附著的Activity的ViewModel,所以只能拆分開。
在BaseViewModelHolder中,通過IViewModelOwners接口在Activity和Fragment中獲取到ViewModelStoreOwner和LifeCycleOwner,可以創(chuàng)建出ViewModelProvider,再通過對(duì)應(yīng)的activityViewModelProvider或者fragmentViewModelProvider創(chuàng)建出需要的ViewModel。LiveData的觀測(cè)也是發(fā)在BaseViewModelHolder中。之后讓BasePresenter繼承自BaseViewModelHolder就可以獲得到對(duì)應(yīng)的創(chuàng)建ViewModel和觀測(cè)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的封裝還是比較基礎(chǔ)的,BaseContract內(nèi)含View和Presenter的接口,Activity持有Presenter的引用,Presenter持有View的引用,同時(shí)BasePresenter繼承了BaseViewModelHolder,也就有了M層的功能,但是又相對(duì)BaseContract的這一套是獨(dú)立的,ViewModel相關(guān)并不與MVP強(qiáng)相關(guān)。同時(shí)對(duì)獲取ViewModel做了進(jìn)一步封裝,減少BasePresenter子類使用ViewModel時(shí)的判斷邏輯。BaseMVPActivity還是和BaseViewActivity一樣,在繼承沒有強(qiáng)制需要實(shí)現(xiàn)的方法,增加了Presenter的泛型,自主去實(shí)現(xiàn)createPresenter方法,實(shí)現(xiàn)了之后自然就需要讓Activity實(shí)現(xiàn)XXXContract.View的接口。
舉例創(chuàng)建一個(gè)Activity:通過在文件夾右鍵單擊 New->Activity->Empty Activity創(chuàng)建一個(gè)TestActivity,自動(dòng)創(chuàng)建好后只需要把繼承的AppCompatActivity修改為BaseMVPActivity(也可以根據(jù)需要繼承自BaseToolbarActivity)。這時(shí)都不需要實(shí)現(xiàn)任何抽象方法,只需要在BaseMVPActivity上配置泛型就可以,但是Presenter的創(chuàng)建還是要主動(dòng)實(shí)現(xiàn)一個(gè)父類的createPresenter()方法。
這樣寫起代碼來都是順暢的,比如要新建一個(gè)頁(yè)面,就是先新建一個(gè)XXXContract類,想好這個(gè)頁(yè)面可能會(huì)有那些功能和回調(diào),去定義View和Presenter的方法,然后新建一個(gè)XXXPresenter類實(shí)現(xiàn)XXXContract.Presenter接口,可以暫時(shí)不寫具體功能,然后再創(chuàng)建一個(gè)XXXActivity,把XXXViewBinding和XXXPresenter泛型配置好。這時(shí)候可以先去寫XXXViewBinding的布局頁(yè)面,也可以先寫XXXPresenter中的業(yè)務(wù)邏輯。腦袋里想好要改頁(yè)面的東西,那就去關(guān)注xml布局,想改業(yè)務(wù)邏輯就去XXXModel或者XXXPresenter,相互之間都不干擾。git提交時(shí)也可以一眼看出來這次提交是修改了業(yè)務(wù)還是布局,減少出錯(cuò)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容