都会需要有用到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提交时也可以一眼看出来这次提交是修改了业务还是布局,减少出错。