diff --git a/app/src/main/java/com/aoleyun/sn/activity/debug/DebugActivity.java b/app/src/main/java/com/aoleyun/sn/activity/debug/DebugActivity.java index 0d96b32..bdfa585 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/debug/DebugActivity.java +++ b/app/src/main/java/com/aoleyun/sn/activity/debug/DebugActivity.java @@ -71,9 +71,6 @@ public class DebugActivity extends BaseMvvmActivity { - - @Override - public ActivityDebugBinding getVDBinding() { - return binding; - } - - @Override - public void onDestroy() { - - } +public class DebugViewModel extends BaseViewModel { } diff --git a/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java b/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java index 3262604..e0a9064 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java +++ b/app/src/main/java/com/aoleyun/sn/activity/main/MainActivity.java @@ -167,7 +167,7 @@ public class MainActivity extends BaseMvvmActivity { +public class MainViewModel extends BaseViewModel { private static final String TAG = "MainViewModel"; - @Override - public ActivityMainBinding getVDBinding() { - return binding; - } - - @Override - public void onDestroy() { - - } - public void init() { checkUpdateInfo(); checkAoleyunUpdate(); diff --git a/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogActivity.java b/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogActivity.java index 86e9b6b..6778f33 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogActivity.java +++ b/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogActivity.java @@ -19,7 +19,7 @@ public class RequestLogActivity extends BaseMvvmActivity>() { @Override public void onChanged(List logBeans) { diff --git a/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogViewModel.java b/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogViewModel.java index b23baf7..3118252 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogViewModel.java +++ b/app/src/main/java/com/aoleyun/sn/activity/requestlog/RequestLogViewModel.java @@ -6,29 +6,18 @@ import com.aoleyun.sn.base.mvvm.BaseViewModel; import com.aoleyun.sn.databinding.ActivityRequestLogBinding; import com.aoleyun.sn.rlog.LogBean; import com.aoleyun.sn.rlog.LogDBManager; -import com.trello.rxlifecycle4.android.ActivityEvent; import java.util.List; -public class RequestLogViewModel extends BaseViewModel { +public class RequestLogViewModel extends BaseViewModel { - @Override - public ActivityRequestLogBinding getVDBinding() { - return binding; - } - - @Override - public void onDestroy() { - - } - - private MutableLiveData> mLogBeanListData =new MutableLiveData<>(); + private final MutableLiveData> mLogBeanListData = new MutableLiveData<>(); public MutableLiveData> getLogBeanListData() { return mLogBeanListData; } - public void getDBList(){ + public void getDBList() { mLogBeanListData.setValue(LogDBManager.getInstance().getLogList()); } } diff --git a/app/src/main/java/com/aoleyun/sn/activity/update/AppUpdateActivity.java b/app/src/main/java/com/aoleyun/sn/activity/update/AppUpdateActivity.java index bf6a553..06d2ce0 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/update/AppUpdateActivity.java +++ b/app/src/main/java/com/aoleyun/sn/activity/update/AppUpdateActivity.java @@ -1,5 +1,7 @@ package com.aoleyun.sn.activity.update; +import static com.arialyy.aria.core.inf.IEntity.STATE_RUNNING; + import android.content.Intent; import android.view.View; @@ -17,8 +19,6 @@ import com.google.gson.Gson; import com.google.gson.JsonObject; import com.hjq.toast.Toaster; -import static com.arialyy.aria.core.inf.IEntity.STATE_RUNNING; - public class AppUpdateActivity extends BaseMvvmActivity { private AppUpdateInfo mAppInfoData; @@ -31,9 +31,6 @@ public class AppUpdateActivity extends BaseMvvmActivity { - - @Override - public ActivityAppUpdateBinding getVDBinding() { - return binding; - } - - @Override - public void onDestroy() { - - } +public class AppUpdateViewModel extends BaseViewModel { } diff --git a/app/src/main/java/com/aoleyun/sn/activity/update/UpdateActivity.java b/app/src/main/java/com/aoleyun/sn/activity/update/UpdateActivity.java index 28e1d5b..5a3fcf5 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/update/UpdateActivity.java +++ b/app/src/main/java/com/aoleyun/sn/activity/update/UpdateActivity.java @@ -25,7 +25,7 @@ public class UpdateActivity extends BaseMvvmActivity>() { @Override public void onChanged(List appUpdateInfos) { diff --git a/app/src/main/java/com/aoleyun/sn/activity/update/UpdateViewModel.java b/app/src/main/java/com/aoleyun/sn/activity/update/UpdateViewModel.java index f4336e0..a47b125 100644 --- a/app/src/main/java/com/aoleyun/sn/activity/update/UpdateViewModel.java +++ b/app/src/main/java/com/aoleyun/sn/activity/update/UpdateViewModel.java @@ -6,23 +6,12 @@ import com.aoleyun.sn.base.mvvm.BaseViewModel; import com.aoleyun.sn.bean.AppUpdateInfo; import com.aoleyun.sn.databinding.ActivityUpdateBinding; import com.aoleyun.sn.network.NetInterfaceManager; -import com.trello.rxlifecycle4.android.ActivityEvent; import java.util.List; -public class UpdateViewModel extends BaseViewModel { +public class UpdateViewModel extends BaseViewModel { - @Override - public ActivityUpdateBinding getVDBinding() { - return binding; - } - - @Override - public void onDestroy() { - - } - - private MutableLiveData> mAppUpdateInfoListData = new MutableLiveData<>(); + private final MutableLiveData> mAppUpdateInfoListData = new MutableLiveData<>(); public MutableLiveData> getAppUpdateInfoListData() { return mAppUpdateInfoListData; diff --git a/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseMvvmActivity.java b/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseMvvmActivity.java index a23c9f8..41e4b10 100644 --- a/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseMvvmActivity.java +++ b/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseMvvmActivity.java @@ -1,45 +1,91 @@ package com.aoleyun.sn.base.mvvm; import android.os.Bundle; -import android.util.Log; +import androidx.annotation.CallSuper; import androidx.annotation.Nullable; import androidx.databinding.DataBindingUtil; import androidx.databinding.ViewDataBinding; -import androidx.lifecycle.ViewModel; import androidx.lifecycle.ViewModelProvider; import com.aoleyun.sn.base.BaseTransparentActivity; -import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; -public abstract class BaseMvvmActivity extends BaseTransparentActivity { - private static final String TAG = "BaseMvvmActivity"; +/** + * MVVM Activity 基类。 + * + *

子类按 {@code BaseMvvmActivity} 声明泛型后,基类会在 onCreate 中自动完成:

+ *
    + *
  • 加载布局并创建 ViewDataBinding,设置 lifecycleOwner;
  • + *
  • 通过 ViewModelProvider 创建并复用 ViewModel;
  • + *
  • 为 ViewModel 注入 Binding / Application Context / 页面生命周期。
  • + *
+ * + * @param 页面 ViewModel,必须继承 {@link BaseViewModel} + * @param 页面 ViewDataBinding + */ +public abstract class BaseMvvmActivity, VDB extends ViewDataBinding> + extends BaseTransparentActivity { protected VM mViewModel; protected VDB mViewDataBinding; - protected Class vmClass; @Override + @CallSuper protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); - //ViewDataBinding + mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId()); mViewDataBinding.setLifecycleOwner(this); - //ViewModel - vmClass = (Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]; - boolean isAbstract = Modifier.isAbstract(vmClass.getModifiers()); - Log.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract); - if (!isAbstract) {//不是一个抽象类 - mViewModel = new ViewModelProvider(this).get(vmClass); - } + + mViewModel = new ViewModelProvider(this).get(resolveViewModelClass()); + mViewModel.attach(mViewDataBinding, this, getLifecycleSubject()); + initDataBinding(); initView(); initData(); } - protected abstract void initDataBinding(); + @Override + @CallSuper + protected void onDestroy() { + if (mViewModel != null) { + mViewModel.detach(mViewDataBinding); + } + mViewDataBinding = null; + super.onDestroy(); + } + + /** + * 从泛型声明中解析 ViewModel 类型,支持子类之间存在多层继承。 + */ + @SuppressWarnings("unchecked") + private Class resolveViewModelClass() { + Class clazz = getClass(); + while (clazz != null && clazz != BaseMvvmActivity.class) { + Type genericSuperclass = clazz.getGenericSuperclass(); + if (genericSuperclass instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; + if (parameterizedType.getRawType() == BaseMvvmActivity.class) { + Type[] typeArguments = parameterizedType.getActualTypeArguments(); + if (typeArguments[0] instanceof Class) { + return (Class) typeArguments[0]; + } + } + } + clazz = clazz.getSuperclass(); + } + throw new IllegalStateException("无法解析 ViewModel 类型,请检查 " + + getClass().getName() + " 是否声明为 BaseMvvmActivity"); + } + + /** + * binding 相关初始化(如 setClick / setXxx),此时 ViewModel 已完成注入 + */ + protected void initDataBinding() { + } /** * 初始化视图 diff --git a/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseViewModel.java b/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseViewModel.java index 2002d22..44568ba 100644 --- a/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseViewModel.java +++ b/app/src/main/java/com/aoleyun/sn/base/mvvm/BaseViewModel.java @@ -2,70 +2,92 @@ package com.aoleyun.sn.base.mvvm; import android.content.Context; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.databinding.ViewDataBinding; import androidx.lifecycle.ViewModel; -import java.lang.ref.WeakReference; +import com.trello.rxlifecycle4.android.ActivityEvent; import io.reactivex.rxjava3.subjects.BehaviorSubject; /** - * 所有viewmodel的基类 + * 所有 ViewModel 的基类。 + * + *

Binding、Application Context、页面生命周期均由 {@link BaseMvvmActivity} + * 在 onCreate 时自动注入,子类无需(也不应该)手动设置这些依赖。

+ * + * @param 该 ViewModel 对应的 ViewDataBinding 类型 */ -public abstract class BaseViewModel extends ViewModel implements ViewDataBindingCallback { +public abstract class BaseViewModel extends ViewModel { /** - * 当前viewmodel对应的页面binding + * 当前页面 binding,页面销毁时会被置空 */ - protected VDB binding; + @Nullable + private VDB binding; - @Override - public void setVDBinding(ViewDataBinding vdBinding) { + /** + * Application Context,避免持有 Activity 造成内存泄漏 + */ + @Nullable + private Context appContext; - binding = (VDB) vdBinding; + /** + * 页面生命周期(RxLifecycle) + */ + @Nullable + private BehaviorSubject lifecycleSubject; + + /** + * 由 {@link BaseMvvmActivity} 调用,完成依赖注入。 + */ + final void attach(@NonNull VDB binding, + @NonNull Context context, + @NonNull BehaviorSubject lifecycleSubject) { + this.binding = binding; + this.appContext = context.getApplicationContext(); + this.lifecycleSubject = lifecycleSubject; } - @Override - public VDB getVDBinding() { - if (binding == null) { - throw new NullPointerException("BaseViewModel >> getVDBinding >> null!!!"); + /** + * 页面销毁时解除 binding 引用,避免 ViewModel 在配置变更后持有已销毁的视图。 + */ + final void detach(@Nullable VDB vdBinding) { + if (this.binding == vdBinding) { + this.binding = null; } + } + + /** + * @return 当前页面 binding,页面销毁后返回 null + */ + @Nullable + public final VDB getBinding() { return binding; } + /** + * @return Application Context,ViewModel 被回收后返回 null + */ + @Nullable + public final Context getCtx() { + return appContext; + } /** - * 上下文 + * @return 页面生命周期,用于 RxLifecycle 绑定 */ - private WeakReference ctx; - - @Override - public void setCtx(Context context) { - if (ctx == null) { - ctx = new WeakReference<>(context); - } + @Nullable + public final BehaviorSubject getLifecycle() { + return lifecycleSubject; } @Override - public Context getCtx() { - if (ctx == null) { - throw new NullPointerException("BaseViewModel >> getCtx >> null!!!"); - } - return ctx.get(); - } - - - public abstract void onDestroy(); - - private BehaviorSubject mBehaviorSubject; - - @Override - public void setLifecycle(BehaviorSubject subject) { - this.mBehaviorSubject = (BehaviorSubject) subject; - } - - @Override - public BehaviorSubject getLifecycle() { - return mBehaviorSubject; + protected void onCleared() { + binding = null; + appContext = null; + lifecycleSubject = null; + super.onCleared(); } } diff --git a/app/src/main/java/com/aoleyun/sn/base/mvvm/ViewDataBindingCallback.java b/app/src/main/java/com/aoleyun/sn/base/mvvm/ViewDataBindingCallback.java deleted file mode 100644 index e373bd7..0000000 --- a/app/src/main/java/com/aoleyun/sn/base/mvvm/ViewDataBindingCallback.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.aoleyun.sn.base.mvvm; - -import android.content.Context; - -import androidx.databinding.ViewDataBinding; - -import io.reactivex.rxjava3.subjects.BehaviorSubject; - -public interface ViewDataBindingCallback { - - void setVDBinding(VDB binding); - - VDB getVDBinding() throws NullPointerException; - - void setCtx(Context context); - - Context getCtx() throws NullPointerException; - - void setLifecycle(BehaviorSubject subject); - - BehaviorSubject getLifecycle(); -}