refactor(mvvm): 重构闹钟与应用列表ViewModel为AndroidViewModel

将ViewModel改为继承BaseAndroidViewModel,通过构造器注入Application,使用addDisposable管理RxJava订阅,LiveData改为私有并提供getter,移除RxLifecycle依赖。
This commit is contained in:
TongTongStudio
2026-08-21 19:10:09 +08:00
parent 7e8877a0db
commit bbf6cc6679
57 changed files with 2236 additions and 780 deletions

View File

@@ -45,8 +45,8 @@ public class AlarmAddActivity extends BaseMvvmActivity<AlarmAddViewModel, Activi
}
@Override
protected void initData() {
mViewModel.mSaveResult.observe(this, success -> {
protected void observeViewModel() {
mViewModel.getSaveResult().observe(this, success -> {
if (success) {
TipDialog.show("添加成功", WaitDialog.TYPE.SUCCESS);
finish();

View File

@@ -1,40 +1,38 @@
package com.ttstd.dialer.activity.alarm.add;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.alarmclock.AlarmManagerHelper;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityAlarmAddBinding;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.db.alarm.AlarmInfo;
import com.ttstd.dialer.db.alarm.AlarmRepository;
import com.ttstd.dialer.utils.Logger;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleObserver;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AlarmAddViewModel extends BaseViewModel<ActivityAlarmAddBinding, ActivityEvent> {
public class AlarmAddViewModel extends BaseAndroidViewModel {
private static final String TAG = "AlarmAddViewModel";
private AlarmRepository mRepository;
private final AlarmRepository mRepository;
private int mVolume = 80;
private int mSnoozeInterval = 5;
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = new AlarmRepository(context);
private final MutableLiveData<Boolean> mSaveResult = new MutableLiveData<>();
public AlarmAddViewModel(Application application) {
super(application);
mRepository = new AlarmRepository(application);
}
public MutableLiveData<Boolean> mSaveResult = new MutableLiveData<>();
public LiveData<Boolean> getSaveResult() {
return mSaveResult;
}
public void saveAlarm(int hour, int minute, String label, boolean vibration, int repeatType) {
AlarmInfo alarmInfo = new AlarmInfo();
@@ -47,43 +45,31 @@ public class AlarmAddViewModel extends BaseViewModel<ActivityAlarmAddBinding, Ac
alarmInfo.setVolume(mVolume);
alarmInfo.setSnoozeInterval(mSnoozeInterval);
Single.fromCallable(() -> mRepository.insertAlarm(alarmInfo))
addDisposable(Single.fromCallable(() -> mRepository.insertAlarm(alarmInfo))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribe(new SingleObserver<Long>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.d(TAG, "开始保存闹钟");
}
.subscribe(aLong -> {
Logger.d(TAG, "闹钟保存成功,ID: " + aLong);
alarmInfo.setId(aLong.intValue());
@Override
public void onSuccess(Long aLong) {
Logger.d(TAG, "闹钟保存成功,ID: " + aLong);
alarmInfo.setId(aLong.intValue());
// 计算并设置系统闹钟
long nextTime = AlarmManagerHelper.calculateNextTime(alarmInfo);
alarmInfo.setNextTriggerTime(nextTime);
// 计算并设置系统闹钟
long nextTime = AlarmManagerHelper.calculateNextTime(alarmInfo);
alarmInfo.setNextTriggerTime(nextTime);
// 更新数据库中的下次触发时间
Completable.fromAction(() -> mRepository.updateNextTriggerTime(alarmInfo.getId(), nextTime))
.subscribeOn(Schedulers.io())
.subscribe(() -> {
AlarmManagerHelper.setExactAlarm(getSafeContext(), alarmInfo.getId(), nextTime);
mSaveResult.postValue(true);
}, throwable -> {
Logger.e(TAG, "更新下次触发时间失败: " + throwable.getMessage());
mSaveResult.postValue(true); // 依然认为保存成功,只是系统闹钟可能没设好
});
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e(TAG, "闹钟保存失败: " + e.getMessage());
mSaveResult.setValue(false);
}
});
// 更新数据库中的下次触发时间
addDisposable(Completable.fromAction(() -> mRepository.updateNextTriggerTime(alarmInfo.getId(), nextTime))
.subscribeOn(Schedulers.io())
.subscribe(() -> {
AlarmManagerHelper.setExactAlarm(getAppContext(), alarmInfo.getId(), nextTime);
mSaveResult.postValue(true);
}, throwable -> {
Logger.e(TAG, "更新下次触发时间失败: " + throwable.getMessage());
mSaveResult.postValue(true); // 依然认为保存成功,只是系统闹钟可能没设好
}));
},
throwable -> {
Logger.e(TAG, "闹钟保存失败: " + throwable.getMessage());
mSaveResult.setValue(false);
}));
}
}

View File

@@ -51,8 +51,8 @@ public class AlarmEditActivity extends BaseMvvmActivity<AlarmEditViewModel, Acti
}
@Override
protected void initData() {
mViewModel.mUpdateResult.observe(this, success -> {
protected void observeViewModel() {
mViewModel.getUpdateResult().observe(this, success -> {
if (success) {
TipDialog.show("保存成功", WaitDialog.TYPE.SUCCESS);
finish();
@@ -61,7 +61,7 @@ public class AlarmEditActivity extends BaseMvvmActivity<AlarmEditViewModel, Acti
}
});
mViewModel.mDeleteResult.observe(this, success -> {
mViewModel.getDeleteResult().observe(this, success -> {
if (success) {
TipDialog.show("删除成功", WaitDialog.TYPE.SUCCESS);
finish();

View File

@@ -1,40 +1,42 @@
package com.ttstd.dialer.activity.alarm.edit;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.alarmclock.AlarmManagerHelper;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityAlarmEditBinding;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.db.alarm.AlarmInfo;
import com.ttstd.dialer.db.alarm.AlarmRepository;
import com.ttstd.dialer.utils.Logger;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.CompletableObserver;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AlarmEditViewModel extends BaseViewModel<ActivityAlarmEditBinding, ActivityEvent> {
public class AlarmEditViewModel extends BaseAndroidViewModel {
private static final String TAG = "AlarmEditViewModel";
private AlarmRepository mRepository;
private final AlarmRepository mRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = new AlarmRepository(context);
}
public MutableLiveData<Boolean> mUpdateResult = new MutableLiveData<>();
public MutableLiveData<Boolean> mDeleteResult = new MutableLiveData<>();
private final MutableLiveData<Boolean> mUpdateResult = new MutableLiveData<>();
private final MutableLiveData<Boolean> mDeleteResult = new MutableLiveData<>();
private AlarmInfo mOriginalAlarm;
public AlarmEditViewModel(Application application) {
super(application);
mRepository = new AlarmRepository(application);
}
public LiveData<Boolean> getUpdateResult() {
return mUpdateResult;
}
public LiveData<Boolean> getDeleteResult() {
return mDeleteResult;
}
public void loadAlarmInfo(AlarmInfo alarmInfo) {
this.mOriginalAlarm = alarmInfo;
}
@@ -57,58 +59,36 @@ public class AlarmEditViewModel extends BaseViewModel<ActivityAlarmEditBinding,
alarmInfo.setNextTriggerTime(nextTime);
}
Completable.fromAction(() -> mRepository.updateAlarm(alarmInfo))
addDisposable(Completable.fromAction(() -> mRepository.updateAlarm(alarmInfo))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.d(TAG, "开始更新闹钟");
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e(TAG, "闹钟更新失败: " + e.getMessage());
mUpdateResult.setValue(false);
}
@Override
public void onComplete() {
Logger.d(TAG, "更新闹钟完成");
if (alarmInfo.isEnabled()) {
AlarmManagerHelper.setExactAlarm(getSafeContext(), alarmInfo.getId(), alarmInfo.getNextTriggerTime());
} else {
AlarmManagerHelper.cancelAlarm(getSafeContext(), alarmInfo.getId());
}
mUpdateResult.setValue(true);
}
});
.subscribe(() -> {
Logger.d(TAG, "更新闹钟完成");
if (alarmInfo.isEnabled()) {
AlarmManagerHelper.setExactAlarm(getAppContext(), alarmInfo.getId(), alarmInfo.getNextTriggerTime());
} else {
AlarmManagerHelper.cancelAlarm(getAppContext(), alarmInfo.getId());
}
mUpdateResult.setValue(true);
},
throwable -> {
Logger.e(TAG, "闹钟更新失败: " + throwable.getMessage());
mUpdateResult.setValue(false);
}));
}
public void deleteAlarm(AlarmInfo alarmInfo) {
Completable.fromAction(() -> mRepository.deleteAlarm(alarmInfo))
addDisposable(Completable.fromAction(() -> mRepository.deleteAlarm(alarmInfo))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.d(TAG, "开始删除闹钟");
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e(TAG, "闹钟删除失败: " + e.getMessage());
mDeleteResult.setValue(false);
}
@Override
public void onComplete() {
Logger.d(TAG, "删除闹钟完成");
AlarmManagerHelper.cancelAlarm(getSafeContext(), alarmInfo.getId());
mDeleteResult.setValue(true);
}
});
.subscribe(() -> {
Logger.d(TAG, "删除闹钟完成");
AlarmManagerHelper.cancelAlarm(getAppContext(), alarmInfo.getId());
mDeleteResult.setValue(true);
},
throwable -> {
Logger.e(TAG, "闹钟删除失败: " + throwable.getMessage());
mDeleteResult.setValue(false);
}));
}
}

View File

@@ -109,8 +109,8 @@ public class AlarmListActivity extends BaseMvvmActivity<AlarmListViewModel, Acti
}
@Override
protected void initData() {
mViewModel.mAlarmListData.observe(this, new Observer<List<AlarmInfo>>() {
protected void observeViewModel() {
mViewModel.getAlarmListData().observe(this, new Observer<List<AlarmInfo>>() {
@Override
public void onChanged(List<AlarmInfo> alarmInfos) {
mViewDataBinding.swipeRefreshLayout.setRefreshing(false);
@@ -121,7 +121,7 @@ public class AlarmListActivity extends BaseMvvmActivity<AlarmListViewModel, Acti
}
});
mViewModel.mDeleteLiveData.observe(this, new Observer<Boolean>() {
mViewModel.getDeleteLiveData().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean success) {
if (success) {

View File

@@ -1,14 +1,12 @@
package com.ttstd.dialer.activity.alarm.list;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.alarmclock.AlarmManagerHelper;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityAlarmListBinding;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.db.alarm.AlarmInfo;
import com.ttstd.dialer.db.alarm.AlarmRepository;
import com.ttstd.dialer.utils.Logger;
@@ -16,111 +14,76 @@ import com.ttstd.dialer.utils.Logger;
import java.util.List;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Completable;
import io.reactivex.rxjava3.core.CompletableObserver;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.core.SingleObserver;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AlarmListViewModel extends BaseViewModel<ActivityAlarmListBinding, ActivityEvent> {
public class AlarmListViewModel extends BaseAndroidViewModel {
private static final String TAG = "AlarmListViewModel";
private AlarmRepository mRepository;
private final AlarmRepository mRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = new AlarmRepository(context);
private final MutableLiveData<List<AlarmInfo>> mAlarmListData = new MutableLiveData<>();
private final MutableLiveData<Boolean> mDeleteLiveData = new MutableLiveData<>();
public AlarmListViewModel(Application application) {
super(application);
mRepository = new AlarmRepository(application);
}
public MutableLiveData<List<AlarmInfo>> mAlarmListData = new MutableLiveData<>();
public LiveData<List<AlarmInfo>> getAlarmListData() {
return mAlarmListData;
}
public LiveData<Boolean> getDeleteLiveData() {
return mDeleteLiveData;
}
public void getAllAlarms() {
Single.fromCallable(() -> mRepository.getAllAlarms())
addDisposable(Single.fromCallable(() -> mRepository.getAllAlarms())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribe(new SingleObserver<List<AlarmInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.d(TAG, "开始加载闹钟列表");
}
@Override
public void onSuccess(List<AlarmInfo> alarmInfos) {
Logger.d(TAG, "加载闹钟列表成功,数量: " + alarmInfos.size());
mAlarmListData.setValue(alarmInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e(TAG, "加载闹钟列表失败: " + e.getMessage());
mAlarmListData.setValue(null);
}
});
.subscribe(alarmInfos -> {
Logger.d(TAG, "加载闹钟列表成功,数量: " + alarmInfos.size());
mAlarmListData.setValue(alarmInfos);
},
throwable -> {
Logger.e(TAG, "加载闹钟列表失败: " + throwable.getMessage());
mAlarmListData.setValue(null);
}));
}
public void updateAlarmEnabled(AlarmInfo alarmInfo) {
Completable.fromAction(() -> {
addDisposable(Completable.fromAction(() -> {
mRepository.updateAlarmEnabled(alarmInfo.getId(), alarmInfo.isEnabled());
if (alarmInfo.isEnabled()) {
long nextTime = AlarmManagerHelper.calculateNextTime(alarmInfo);
alarmInfo.setNextTriggerTime(nextTime);
mRepository.updateNextTriggerTime(alarmInfo.getId(), nextTime);
AlarmManagerHelper.setExactAlarm(getSafeContext(), alarmInfo.getId(), nextTime);
AlarmManagerHelper.setExactAlarm(getAppContext(), alarmInfo.getId(), nextTime);
Logger.d(TAG, "updateAlarmEnabled: 闹钟状态开启");
} else {
AlarmManagerHelper.cancelAlarm(getSafeContext(), alarmInfo.getId());
AlarmManagerHelper.cancelAlarm(getAppContext(), alarmInfo.getId());
Logger.d(TAG, "updateAlarmEnabled: 闹钟状态关闭");
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onComplete() {
Logger.d(TAG, "updateAlarmEnabled: 更新闹钟状态成功");
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e(TAG, "updateAlarmEnabled: 更新闹钟状态失败: " + e.getMessage());
}
});
.subscribe(() -> Logger.d(TAG, "updateAlarmEnabled: 更新闹钟状态成功"),
throwable -> Logger.e(TAG, "updateAlarmEnabled: 更新闹钟状态失败: " + throwable.getMessage())));
}
public MutableLiveData<Boolean> mDeleteLiveData = new MutableLiveData<>();
public void deleteAlarm(AlarmInfo alarmInfo) {
Completable.fromAction(() -> mRepository.deleteAlarm(alarmInfo))
addDisposable(Completable.fromAction(() -> mRepository.deleteAlarm(alarmInfo))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
.subscribe(new CompletableObserver() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.d(TAG, "开始删除闹钟");
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e(TAG, "删除闹钟失败: " + e.getMessage());
mDeleteLiveData.setValue(false);
}
@Override
public void onComplete() {
Logger.d(TAG, "删除闹钟完成");
AlarmManagerHelper.cancelAlarm(getSafeContext(), alarmInfo.getId());
mDeleteLiveData.setValue(true);
}
});
.subscribe(() -> {
Logger.d(TAG, "删除闹钟完成");
AlarmManagerHelper.cancelAlarm(getAppContext(), alarmInfo.getId());
mDeleteLiveData.setValue(true);
},
throwable -> {
Logger.e(TAG, "删除闹钟失败: " + throwable.getMessage());
mDeleteLiveData.setValue(false);
}));
}
}

View File

@@ -159,13 +159,13 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
@Override
protected void initData() {
protected void observeViewModel() {
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
.observe(this, event -> {
mViewModel.getDbAppList();
mViewModel.getHotseatApp();
});
mViewModel.mHotseatAppData.observe(this, new Observer<List<AppInfo>>() {
mViewModel.getHotseatAppData().observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
if (appInfos != null && !appInfos.isEmpty()) {
@@ -177,18 +177,15 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
mAppGridAdapter.setAppInfos(appInfos);
}
});
mViewModel.getHotseatApp();
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
mViewModel.getDesktopSortAppData().observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mMoreAppAdapter.setAppInfos(appInfos);
}
});
mViewModel.getDbAppList();
mViewModel.mAppUpdateData.observe(this, new Observer<Integer>() {
mViewModel.getAppUpdateData().observe(this, new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
if (integer > 0) {
@@ -198,7 +195,12 @@ public class AppListActivity extends BaseMvvmActivity<AppListViewModel, Activity
mViewModel.getAllApp();
}
});
}
@Override
protected void onLoadData() {
mViewModel.getHotseatApp();
mViewModel.getDbAppList();
mViewModel.getAllApp();
}

View File

@@ -1,13 +1,11 @@
package com.ttstd.dialer.activity.app;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityAppListBinding;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.db.app.AppRepository;
import com.ttstd.dialer.manager.AppManager;
@@ -17,132 +15,90 @@ import java.util.List;
import java.util.concurrent.Callable;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, ActivityEvent> {
public class AppListViewModel extends BaseAndroidViewModel {
private static final String TAG = "AppListViewModel";
private AppRepository mAppRepository;
private final AppRepository mAppRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mAppRepository = new AppRepository(context);
private final MutableLiveData<List<AppInfo>> mAllAppData = new MutableLiveData<>();
private final MutableLiveData<List<AppInfo>> mHotseatAppData = new MutableLiveData<>();
private final MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
private final MutableLiveData<Integer> mAppUpdateData = new MutableLiveData<>();
public AppListViewModel(Application application) {
super(application);
mAppRepository = new AppRepository(application);
}
public MutableLiveData<List<AppInfo>> mAllAppData = new MutableLiveData<>();
public LiveData<List<AppInfo>> getAllAppData() {
return mAllAppData;
}
public LiveData<List<AppInfo>> getHotseatAppData() {
return mHotseatAppData;
}
public LiveData<List<AppInfo>> getDesktopSortAppData() {
return mDesktopSortAppData;
}
public LiveData<Integer> getAppUpdateData() {
return mAppUpdateData;
}
public void getAllApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
addDisposable(Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getAllApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("getAllApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getAllApp", "onNext: " + appInfos);
mAllAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getAllApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getAllApp", "onComplete: ");
}
});
.subscribe(appInfos -> {
Logger.e("getAllApp", "onNext: " + appInfos);
mAllAppData.setValue(appInfos);
},
throwable -> Logger.e("getAllApp", "onError: " + throwable.getMessage())));
}
public MutableLiveData<List<AppInfo>> mHotseatAppData = new MutableLiveData<>();
public void getHotseatApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
addDisposable(Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getHotseatApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("getHotseatApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getHotseatApp", "onNext: " + appInfos);
mHotseatAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getHotseatApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getHotseatApp", "onComplete: ");
}
});
.subscribe(appInfos -> {
Logger.e("getHotseatApp", "onNext: " + appInfos);
mHotseatAppData.setValue(appInfos);
},
throwable -> Logger.e("getHotseatApp", "onError: " + throwable.getMessage())));
}
public MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
public void getDbAppList() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
addDisposable(Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getInsideApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("getDbAppList", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getDbAppList", "onNext: " + appInfos);
mDesktopSortAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getDbAppList", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getDbAppList", "onComplete: ");
}
});
.subscribe(appInfos -> {
Logger.e("getDbAppList", "onNext: " + appInfos);
mDesktopSortAppData.setValue(appInfos);
},
throwable -> Logger.e("getDbAppList", "onError: " + throwable.getMessage())));
}
public MutableLiveData<Integer> mAppUpdateData = new MutableLiveData<>();
public void updateAppInfo(AppInfo appInfo) {
Observable.fromCallable(new Callable<Integer>() {
addDisposable(Observable.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
if (appInfo.getOutside() == 0) {
@@ -166,32 +122,15 @@ public class AppListViewModel extends BaseViewModel<ActivityAppListBinding, Acti
return mAppRepository.update(appInfo);
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("updateAppInfo", "onSubscribe: ");
}
@Override
public void onNext(@NonNull Integer row) {
Logger.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
AppManager.getInstance().reorderAppPositions();
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("updateAppInfo", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("updateAppInfo", "onComplete: ");
}
});
.subscribe(row -> {
Logger.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
AppManager.getInstance().reorderAppPositions();
},
throwable -> Logger.e("updateAppInfo", "onError: " + throwable.getMessage())));
}
}

View File

@@ -80,8 +80,8 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
}
@Override
protected void initData() {
mViewModel.mOnlineIdData.observe(this, new Observer<Long>() {
protected void observeViewModel() {
mViewModel.getOnlineIdData().observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
if (aLong > 0) {
@@ -92,7 +92,7 @@ public class ContactAddActivity extends BaseMvvmActivity<ContactAddViewModel, Ac
}
}
});
mViewModel.mDbIdData.observe(this, new Observer<Long>() {
mViewModel.getDbIdData().observe(this, new Observer<Long>() {
@Override
public void onChanged(Long aLong) {
PopTip.show("添加成功").iconSuccess();

View File

@@ -1,13 +1,12 @@
package com.ttstd.dialer.activity.contact.add;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.data.repository.ContactDataRepository;
import com.ttstd.dialer.databinding.ActivityContactAddBinding;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.utils.Logger;
@@ -17,22 +16,27 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
* 新增联系人 ViewModel(Google 标准 MVVM):
* 网络优先 / 本地降级的策略已下沉到 Repository,ViewModel 只负责状态分发。
*/
public class ContactAddViewModel extends BaseViewModel<ActivityContactAddBinding, ActivityEvent> {
public class ContactAddViewModel extends BaseAndroidViewModel {
private static final String TAG = "ContactAddViewModel";
private ContactDataRepository mRepository;
private final ContactDataRepository mRepository;
/**
* 服务端保存成功返回的 id
*/
public MutableLiveData<Long> mOnlineIdData = new MutableLiveData<>();
/** 服务端保存成功返回的 id */
private final MutableLiveData<Long> mOnlineIdData = new MutableLiveData<>();
/** 网络失败降级到本地 Room 保存返回的 id */
public MutableLiveData<Long> mDbIdData = new MutableLiveData<>();
private final MutableLiveData<Long> mDbIdData = new MutableLiveData<>();
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = ContactDataRepository.getInstance(context);
public ContactAddViewModel(Application application) {
super(application);
mRepository = ContactDataRepository.getInstance(application);
}
public LiveData<Long> getOnlineIdData() {
return mOnlineIdData;
}
public LiveData<Long> getDbIdData() {
return mDbIdData;
}
/**

View File

@@ -35,15 +35,7 @@ public class ContactEditActivity extends BaseMvvmActivity<ContactEditViewModel,
@Override
protected void initDataBinding() {
mViewDataBinding.setClick(new BtnClick());
}
@Override
protected void initView() {
}
@Override
protected void initData() {
Intent intent = getIntent();
mContactInfo = (ContactInfo) intent.getSerializableExtra("ContactInfo");
if (mContactInfo == null) {
@@ -52,7 +44,16 @@ public class ContactEditActivity extends BaseMvvmActivity<ContactEditViewModel,
}
mViewDataBinding.setContactInfo(mContactInfo);
GlideUtils.loadImageSafe(ContactEditActivity.this, mContactInfo.getAvatar(), mViewDataBinding.nvAvatar, AvatarCacheUtil.getAvatar(ContactEditActivity.this, mContactInfo.getName()));
mViewModel.mBooleanMutableLiveData.observe(this, new Observer<Boolean>() {
}
@Override
protected void initView() {
}
@Override
protected void observeViewModel() {
mViewModel.getBooleanMutableLiveData().observe(this, new Observer<Boolean>() {
@Override
public void onChanged(Boolean aBoolean) {
if (aBoolean) {

View File

@@ -1,15 +1,14 @@
package com.ttstd.dialer.activity.contact.edit;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.kongzue.dialogx.dialogs.PopTip;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.data.ApiException;
import com.ttstd.dialer.data.repository.ContactDataRepository;
import com.ttstd.dialer.databinding.ActivityContactEditBinding;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.utils.Logger;
@@ -18,17 +17,20 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
/**
* 编辑联系人 ViewModel(Google 标准 MVVM):只依赖 Repository。
*/
public class ContactEditViewModel extends BaseViewModel<ActivityContactEditBinding, ActivityEvent> {
public class ContactEditViewModel extends BaseAndroidViewModel {
private static final String TAG = "ContactEditViewModel";
private ContactDataRepository mRepository;
private final ContactDataRepository mRepository;
public MutableLiveData<Boolean> mBooleanMutableLiveData = new MutableLiveData<>();
private final MutableLiveData<Boolean> mBooleanMutableLiveData = new MutableLiveData<>();
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = ContactDataRepository.getInstance(context);
public ContactEditViewModel(Application application) {
super(application);
mRepository = ContactDataRepository.getInstance(application);
}
public LiveData<Boolean> getBooleanMutableLiveData() {
return mBooleanMutableLiveData;
}
/**

View File

@@ -159,7 +159,7 @@ public class ContactListActivity extends BaseMvvmActivity<ContactListViewModel,
}
@Override
protected void initData() {
protected void observeViewModel() {
// 观察 Resource 状态:LOADING / SUCCESS(含缓存 stale 数据) / ERROR(含兜底数据)
mViewModel.getContactListData().observe(this, new Observer<Resource<List<ContactInfo>>>() {
@Override

View File

@@ -1,15 +1,13 @@
package com.ttstd.dialer.activity.contact.list;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.data.Resource;
import com.ttstd.dialer.data.repository.ContactDataRepository;
import com.ttstd.dialer.databinding.ActivityContactListBinding;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.utils.Logger;
@@ -21,18 +19,17 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
* 联系人列表 ViewModel(Google 标准 MVVM):
* 只依赖 Repository,向 UI 暴露 LiveData&lt;Resource&lt;T&gt;&gt;,不感知网络/缓存/数据库细节。
*/
public class ContactListViewModel extends BaseViewModel<ActivityContactListBinding, ActivityEvent> {
public class ContactListViewModel extends BaseAndroidViewModel {
private static final String TAG = "ContactListViewModel";
private ContactDataRepository mRepository;
private final ContactDataRepository mRepository;
private final MutableLiveData<Resource<List<ContactInfo>>> mContactList = new MutableLiveData<>();
private final MutableLiveData<Boolean> mDeleteResult = new MutableLiveData<>();
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = ContactDataRepository.getInstance(context);
public ContactListViewModel(Application application) {
super(application);
mRepository = ContactDataRepository.getInstance(application);
}
public LiveData<Resource<List<ContactInfo>>> getContactListData() {

View File

@@ -162,8 +162,8 @@ public class ContactTestActivity extends BaseMvvmActivity<ContactTestViewModel,
}
@Override
protected void initData() {
mViewModel.mOnlineContactListData.observe(this, new Observer<List<ContactInfo>>() {
protected void observeViewModel() {
mViewModel.getOnlineContactListData().observe(this, new Observer<List<ContactInfo>>() {
@Override
public void onChanged(List<ContactInfo> contactInfos) {
if (contactInfos == null) {
@@ -177,7 +177,7 @@ public class ContactTestActivity extends BaseMvvmActivity<ContactTestViewModel,
}
});
mViewModel.mLocalContactListData.observe(this, new Observer<List<ContactInfo>>() {
mViewModel.getLocalContactListData().observe(this, new Observer<List<ContactInfo>>() {
@Override
public void onChanged(List<ContactInfo> contactInfos) {
if (contactInfos == null) {

View File

@@ -1,13 +1,12 @@
package com.ttstd.dialer.activity.contact.test;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.data.repository.ContactDataRepository;
import com.ttstd.dialer.databinding.ActivityContactTestBinding;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.utils.Logger;
@@ -18,19 +17,26 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
/**
* 联系人测试页 ViewModel(Google 标准 MVVM):只依赖 Repository。
*/
public class ContactTestViewModel extends BaseViewModel<ActivityContactTestBinding, ActivityEvent> {
public class ContactTestViewModel extends BaseAndroidViewModel {
private static final String TAG = "ContactTestViewModel";
private ContactDataRepository mRepository;
private final ContactDataRepository mRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = ContactDataRepository.getInstance(context);
private final MutableLiveData<List<ContactInfo>> mOnlineContactListData = new MutableLiveData<>();
private final MutableLiveData<List<ContactInfo>> mLocalContactListData = new MutableLiveData<>();
public ContactTestViewModel(Application application) {
super(application);
mRepository = ContactDataRepository.getInstance(application);
}
public MutableLiveData<List<ContactInfo>> mOnlineContactListData = new MutableLiveData<>();
public MutableLiveData<List<ContactInfo>> mLocalContactListData = new MutableLiveData<>();
public LiveData<List<ContactInfo>> getOnlineContactListData() {
return mOnlineContactListData;
}
public LiveData<List<ContactInfo>> getLocalContactListData() {
return mLocalContactListData;
}
/**
* 网络联系人(走多级缓存 + SWR,数据已按 position 排序)

View File

@@ -205,8 +205,39 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
}
@Override
protected void initData() {
Log.e(TAG, "initData: ");
protected void observeViewModel() {
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
.observe(this, event -> {
mViewModel.getOutsideApp();
mViewModel.getHotseatApp();
});
mViewModel.getDesktopSortAppData().observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mAppInfos = appInfos;
Logger.e(TAG, "onChanged: mAppInfos size = " + mAppInfos.size());
setAppList();
}
});
mViewModel.getHotseatAppData().observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
if (appInfos != null && !appInfos.isEmpty()) {
GridLayoutManager layoutManager = (GridLayoutManager) mViewDataBinding.gvApp.getLayoutManager();
if (layoutManager != null) {
layoutManager.setSpanCount(Math.max(1, appInfos.size()));
}
}
mAppGridAdapter.setAppInfos(appInfos);
}
});
}
@Override
protected void onLoadData() {
Log.e(TAG, "onLoadData: ");
registerReceivers();
AppManager.getInstance().addProgressCallback(new AppManager.ProgressCallback() {
@Override
@@ -219,37 +250,8 @@ public class MainActivity extends BaseMvvmActivity<MainViewModel, ActivityMainBi
mViewModel.getOutsideApp();
}
});
LiveDataBus.get().<String>on(LiveDataAction.ACTION_UPDATE_APPS)
.observe(this, event -> {
mViewModel.getOutsideApp();
mViewModel.getHotseatApp();
});
mViewModel.mDesktopSortAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
mAppInfos = appInfos;
Logger.e(TAG, "onChanged: mAppInfos size = " + mAppInfos.size());
setAppList();
}
});
mViewModel.getOutsideApp();
mViewModel.mHotseatAppData.observe(this, new Observer<List<AppInfo>>() {
@Override
public void onChanged(List<AppInfo> appInfos) {
if (appInfos != null && !appInfos.isEmpty()) {
GridLayoutManager layoutManager = (GridLayoutManager) mViewDataBinding.gvApp.getLayoutManager();
if (layoutManager != null) {
layoutManager.setSpanCount(Math.max(1, appInfos.size()));
}
}
mAppGridAdapter.setAppInfos(appInfos);
}
});
mViewModel.getHotseatApp();
}
@Override

View File

@@ -1,13 +1,11 @@
package com.ttstd.dialer.activity.main;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityMainBinding;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.db.app.AppRepository;
import com.ttstd.dialer.utils.Logger;
@@ -16,91 +14,60 @@ import java.util.List;
import java.util.concurrent.Callable;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class MainViewModel extends BaseViewModel<ActivityMainBinding, ActivityEvent> {
public class MainViewModel extends BaseAndroidViewModel {
private static final String TAG = "MainViewModel";
private AppRepository mAppRepository;
private final AppRepository mAppRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mAppRepository = new AppRepository(context);
private final MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
private final MutableLiveData<List<AppInfo>> mHotseatAppData = new MutableLiveData<>();
public MainViewModel(Application application) {
super(application);
mAppRepository = new AppRepository(application);
}
public MutableLiveData<List<AppInfo>> mDesktopSortAppData = new MutableLiveData<>();
public LiveData<List<AppInfo>> getDesktopSortAppData() {
return mDesktopSortAppData;
}
public LiveData<List<AppInfo>> getHotseatAppData() {
return mHotseatAppData;
}
public void getOutsideApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
addDisposable(Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getOutsideAppWithoutHotseat();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("getOutsideApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getOutsideApp", "onNext: " + appInfos);
mDesktopSortAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getOutsideApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getOutsideApp", "onComplete: ");
}
});
.subscribe(appInfos -> {
Logger.e("getOutsideApp", "onNext: " + appInfos);
mDesktopSortAppData.setValue(appInfos);
},
throwable -> Logger.e("getOutsideApp", "onError: " + throwable.getMessage())));
}
public MutableLiveData<List<AppInfo>> mHotseatAppData = new MutableLiveData<>();
public void getHotseatApp() {
Observable.fromCallable(new Callable<List<AppInfo>>() {
addDisposable(Observable.fromCallable(new Callable<List<AppInfo>>() {
@Override
public List<AppInfo> call() throws Exception {
return mAppRepository.getHotseatApp();
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), ActivityEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<List<AppInfo>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("getHotseatApp", "onSubscribe: ");
}
@Override
public void onNext(@NonNull List<AppInfo> appInfos) {
Logger.e("getHotseatApp", "onNext: " + appInfos);
mHotseatAppData.setValue(appInfos);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("getHotseatApp", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("getHotseatApp", "onComplete: ");
}
});
.subscribe(appInfos -> {
Logger.e("getHotseatApp", "onNext: " + appInfos);
mHotseatAppData.setValue(appInfos);
},
throwable -> Logger.e("getHotseatApp", "onError: " + throwable.getMessage())));
}
}

View File

@@ -60,7 +60,7 @@ public class SettingsCallActivity extends BaseMvvmActivity<SettingsCallViewModel
}
@Override
protected void initData() {
protected void observeViewModel() {
}

View File

@@ -1,9 +1,7 @@
package com.ttstd.dialer.activity.settings.call;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivitySettingsCallBinding;
public class SettingsCallViewModel extends BaseViewModel<ActivitySettingsCallBinding, ActivityEvent> {
public class SettingsCallViewModel extends BaseViewModel {
}

View File

@@ -44,11 +44,15 @@ public class SettingsActivity extends BaseMvvmActivity<SettingsViewModel, Activi
}
@Override
protected void initData() {
mViewModel.snRegister();
protected void observeViewModel() {
}
@Override
protected void onLoadData() {
mViewModel.snRegister();
}
public class BtnClick {
public void exit(View view) {
finish();

View File

@@ -1,42 +1,15 @@
package com.ttstd.dialer.activity.settings.home;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.bean.BaseResponse;
import com.ttstd.dialer.databinding.ActivitySettingsBinding;
import com.ttstd.dialer.network.OkHttpManager;
import com.ttstd.dialer.utils.Logger;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
public class SettingsViewModel extends BaseViewModel<ActivitySettingsBinding, ActivityEvent> {
public class SettingsViewModel extends BaseViewModel {
public void snRegister() {
OkHttpManager.getInstance().getSnRegisterObservable(getLifecycle())
.subscribe(new Observer<BaseResponse>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("snRegister", "onSubscribe: ");
}
@Override
public void onNext(@NonNull BaseResponse baseResponse) {
Logger.e("snRegister", "onNext: " + baseResponse);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("snRegister", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("snRegister", "onComplete: ");
}
});
addDisposable(OkHttpManager.getInstance().getSnRegisterObservable()
.subscribe(baseResponse -> Logger.e("snRegister", "onNext: " + baseResponse),
throwable -> Logger.e("snRegister", "onError: " + throwable.getMessage())));
}
}

View File

@@ -140,7 +140,7 @@ public class SettingsUtilsActivity extends BaseMvvmActivity<SettingsUtilsViewMod
}
@Override
protected void initData() {
protected void observeViewModel() {
}

View File

@@ -1,10 +1,7 @@
package com.ttstd.dialer.activity.settings.utils;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivitySettingsCallBinding;
import com.ttstd.dialer.databinding.ActivitySettingsUtilsBinding;
public class SettingsUtilsViewModel extends BaseViewModel<ActivitySettingsUtilsBinding, ActivityEvent> {
public class SettingsUtilsViewModel extends BaseViewModel {
}

View File

@@ -31,7 +31,7 @@ public class TemplateActivity extends BaseMvvmActivity<TemplateViewModel, Activi
}
@Override
protected void initData() {
protected void observeViewModel() {
}

View File

@@ -1,9 +1,7 @@
package com.ttstd.dialer.activity.template;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityTemplateBinding;
public class TemplateViewModel extends BaseViewModel<ActivityTemplateBinding, ActivityEvent> {
public class TemplateViewModel extends BaseViewModel {
}

View File

@@ -67,23 +67,23 @@ public class WeatherMainActivity extends BaseMvvmActivity<WeatherMainViewModel,
}
@Override
protected void initData() {
Logger.e(TAG, "initData: ");
protected void observeViewModel() {
Logger.e(TAG, "observeViewModel: ");
mViewModel.mWeatherNowData.observe(this, new Observer<WeatherNow>() {
mViewModel.getWeatherNowData().observe(this, new Observer<WeatherNow>() {
@Override
public void onChanged(WeatherNow weatherNow) {
mViewDataBinding.setWeatherNow(weatherNow);
}
});
mViewModel.mWeather24hData.observe(this, new Observer<List<WeatherHourly>>() {
mViewModel.getWeather24hData().observe(this, new Observer<List<WeatherHourly>>() {
@Override
public void onChanged(List<WeatherHourly> weatherHourlies) {
mHourlyWeatherAdapter.setWeatherHourlyList(weatherHourlies);
}
});
mViewModel.mWeatherDailyListData.observe(this, new Observer<List<WeatherDaily>>() {
mViewModel.getWeatherDailyListData().observe(this, new Observer<List<WeatherDaily>>() {
@Override
public void onChanged(List<WeatherDaily> weatherDailies) {
Optional<WeatherDaily> dailyOptional = weatherDailies.stream().filter(new Predicate<WeatherDaily>() {
@@ -101,7 +101,10 @@ public class WeatherMainActivity extends BaseMvvmActivity<WeatherMainViewModel,
mWeatherAdapter.setWeatherDailyList(weatherDailies);
}
});
}
@Override
protected void onLoadData() {
boolean manually = mMMKV.decodeBool(CommonConfig.MANUALLY_SELECT_LOCATION, CommonConfig.MANUALLY_SELECT_LOCATION_DEFAULT_VALUE);
if (manually) {

View File

@@ -1,5 +1,6 @@
package com.ttstd.dialer.activity.weather.main;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.qweather.sdk.Callback;
@@ -10,19 +11,31 @@ import com.qweather.sdk.response.weather.WeatherHourly;
import com.qweather.sdk.response.weather.WeatherHourlyResponse;
import com.qweather.sdk.response.weather.WeatherNow;
import com.qweather.sdk.response.weather.WeatherNowResponse;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.ActivityWeatherMainBinding;
import com.ttstd.dialer.gson.GsonUtils;
import com.ttstd.dialer.manager.WeatherManager;
import com.ttstd.dialer.utils.Logger;
import java.util.List;
public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBinding, ActivityEvent> {
public class WeatherMainViewModel extends BaseViewModel {
private static final String TAG = "WeatherMainViewModel";
public MutableLiveData<WeatherNow> mWeatherNowData = new MutableLiveData<>();
private final MutableLiveData<WeatherNow> mWeatherNowData = new MutableLiveData<>();
private final MutableLiveData<List<WeatherHourly>> mWeather24hData = new MutableLiveData<>();
private final MutableLiveData<List<WeatherDaily>> mWeatherDailyListData = new MutableLiveData<>();
public LiveData<WeatherNow> getWeatherNowData() {
return mWeatherNowData;
}
public LiveData<List<WeatherHourly>> getWeather24hData() {
return mWeather24hData;
}
public LiveData<List<WeatherDaily>> getWeatherDailyListData() {
return mWeatherDailyListData;
}
public void getWeatherNow(String adCode) {
Logger.e(TAG, "getWeatherNow: " + adCode);
@@ -48,8 +61,6 @@ public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBindi
});
}
public MutableLiveData<List<WeatherHourly>> mWeather24hData = new MutableLiveData<>();
public void getWeather24h(String adCode) {
Logger.e(TAG, "getWeather24h: " + adCode);
WeatherManager.getInstance().getWeather24HourAdCode(adCode, new Callback<WeatherHourlyResponse>() {
@@ -74,9 +85,6 @@ public class WeatherMainViewModel extends BaseViewModel<ActivityWeatherMainBindi
});
}
public MutableLiveData<List<WeatherDaily>> mWeatherDailyListData = new MutableLiveData<>();
public void getWeather10D(String adCode) {
Logger.e(TAG, "getWeather10D: " + adCode);
WeatherManager.getInstance().getWeather10DayAdCode(adCode, new Callback<WeatherDailyResponse>() {

View File

@@ -1,44 +1,135 @@
package com.ttstd.dialer.base;
import android.os.Bundle;
import android.util.Log;
import androidx.annotation.Nullable;
import com.ttstd.dialer.base.rx.BaseRxFragment;
/**
* Fragment 基类,提供标准的懒加载框架
* <p>
* 兼容两种宿主场景:
* 1. ViewPager(v1) + FragmentPagerAdapter:通过 {@link #setUserVisibleHint} 判断可见性(老接口,deprecated)
* 2. ViewPager2 / FragmentTransaction.setMaxLifecycle:通过 onResume/onPause 判断可见性
* <p>
* 懒加载核心思路:
* 1. 视图创建完成({@code isViewInitiated}) + 当前页可见({@code isVisibleToUser}) 两个条件同时满足时,
* 才触发首次加载({@link #onLazyLoad()}),之后切回该页不再重复触发。
* 2. {@link #fetchData()} 每次可见都会回调,用于需要刷新的场景。
* 3. View 销毁重建后自动重置懒加载状态,保证重建后的页面能重新渲染。
*/
public abstract class BaseFragment extends BaseRxFragment {
private static final String TAG = "BaseFragment";
/** 视图是否已创建(onViewCreated 之后为 true) */
protected boolean isViewInitiated;
/** 当前 Fragment 是否对用户可见 */
protected boolean isVisibleToUser;
/** 首次数据是否已初始化 */
protected boolean isDataInitiated;
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
public void onViewCreated(@androidx.annotation.NonNull android.view.View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
isViewInitiated = true;
// 视图已创建,检查是否满足懒加载条件
prepareFetchData();
}
@Override
public void onResume() {
super.onResume();
// ViewPager2 / setMaxLifecycle 场景下没有 setUserVisibleHint 回调,用 onResume 判断可见性;
// 兼容 ViewPager(v1):off-screen 页 getUserVisibleHint()==false,不会被误判为可见
if (!isViewInitiated || isHidden() || !getUserVisibleHint()) {
return;
}
setVisibleToUser(true);
}
@Override
public void onPause() {
// ViewPager2 / setMaxLifecycle 场景下用 onPause 判断不可见
if (isViewInitiated && !isHidden() && getUserVisibleHint()) {
setVisibleToUser(false);
}
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
// View 销毁重建后,重置懒加载状态,保证重建后的页面能重新渲染控件
// 否则 Fragment 实例保留的 isDataInitiated=true 会导致重建后不再触发 onLazyLoad,控件消失
isDataInitiated = false;
isViewInitiated = false;
isVisibleToUser = false;
}
/**
* ViewPager(v1) + FragmentPagerAdapter 场景下,通过此回调精确判断当前页是否可见
*/
@Override
@Deprecated
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
if (!isViewInitiated) {
return;
}
setVisibleToUser(isVisibleToUser);
}
/**
* 统一更新可见性并触发懒加载判断
*/
private void setVisibleToUser(boolean visible) {
if (this.isVisibleToUser == visible) {
return;
}
this.isVisibleToUser = visible;
prepareFetchData();
}
/**
* 触发真实数据加载,子类重写实现具体加载逻辑
*/
public abstract void fetchData();
/**
* 懒加载入口:视图已创建 且 当前页可见 时触发一次
*/
public boolean prepareFetchData() {
return prepareFetchData(false);
}
/**
* 懒加载入口
*
* @param forceUpdate 为 true 时,即使已加载过也强制重新加载
*/
public boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
if (!isDataInitiated) {
Log.d(TAG, "lazyLoad trigger: " + getClass().getSimpleName());
onLazyLoad();
}
// 只在非强制刷新时标记为已初始化,保证首屏只渲染一次
if (!forceUpdate) {
isDataInitiated = true;
}
fetchData();
//注释掉保证每次都更新数据
// isDataInitiated = true;
return true;
}
return false;
}
/**
* 首次可见时回调,用于做一次性的重操作(如创建控件、加载图片等)
* 默认空实现,子类按需重写
*/
protected void onLazyLoad() {
}
}

View File

@@ -0,0 +1,135 @@
package com.ttstd.dialer.base.mvvm;
import android.app.Application;
import androidx.annotation.NonNull;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
/**
* MVVM ViewModel 基类(带 Context 版本)
*
* 继承 {@link AndroidViewModel},可通过 {@link #getAppContext()} 获取 Application 级别的 Context。
* Application Context 生命周期与进程一致,不会造成内存泄漏。
*
* 职责:
* - 持有 UI 状态(LiveData)
* - 处理业务逻辑
* - 暴露数据给 View 层
* - 通过 {@link #addDisposable(Disposable)} 统一管理 RxJava 异步任务
* - 可通过 {@link #getAppContext()} 获取 Application Context(安全)
*
* 禁止:
* - 持有任何 View 引用(Activity / Fragment / View)
* - 使用 Activity Context 或持有 View 的 Context(只能使用 Application)
* - 直接进行网络请求或数据库操作(必须通过 Repository)
* - 在 ViewModel 中弹 Toast / Dialog / Snackbar
* - 手动创建 Thread / Executor / AsyncTask 执行异步任务(必须使用 RxJava + {@link #addDisposable(Disposable)})
* - 忘记将 Disposable add 到 {@link #mDisposables} 中
*/
public abstract class BaseAndroidViewModel extends AndroidViewModel implements IMvvmUiState {
// ============================================================
// 通用 UI 状态
// ============================================================
/** 全局加载状态 */
private final MutableLiveData<Boolean> mLoadingLiveData = new MutableLiveData<>(false);
/** 全局错误提示 */
private final MutableLiveData<String> mErrorLiveData = new MutableLiveData<>();
/** 全局成功提示 */
private final MutableLiveData<String> mSuccessLiveData = new MutableLiveData<>();
// ============================================================
// RxJava 异步管理(核心)
// ============================================================
/**
* CompositeDisposable 用于统一管理所有 RxJava 订阅。
* 在 onCleared() 中自动 dispose,防止内存泄漏。
*/
protected final CompositeDisposable mDisposables = new CompositeDisposable();
// ============================================================
// 构造方法
// ============================================================
public BaseAndroidViewModel(@NonNull Application application) {
super(application);
}
// ============================================================
// 对外暴露(只读)
// ============================================================
public LiveData<Boolean> getLoadingLiveData() {
return mLoadingLiveData;
}
public LiveData<String> getErrorLiveData() {
return mErrorLiveData;
}
public LiveData<String> getSuccessLiveData() {
return mSuccessLiveData;
}
// ============================================================
// 受保护的方法(子类调用)
// ============================================================
protected void setLoading(boolean loading) {
mLoadingLiveData.postValue(loading);
}
protected void setError(String message) {
mErrorLiveData.postValue(message);
}
protected void setSuccess(String message) {
mSuccessLiveData.postValue(message);
}
/**
* 便捷方法:将 Disposable 添加到 {@link #mDisposables} 中管理。
* 子类在发起 RxJava 请求时调用此方法。
*/
protected void addDisposable(Disposable disposable) {
mDisposables.add(disposable);
}
// ============================================================
// Context 安全访问
// ============================================================
/**
* 获取 Application Context(安全,不会泄漏)。
* 子类可通过此方法访问:
* - getApplication().getSharedPreferences(...)
* - getApplication().getResources().getString(...)
* - getApplication().getSystemService(...)
*/
@NonNull
protected Application getAppContext() {
return getApplication();
}
// ============================================================
// 生命周期
// ============================================================
@Override
protected void onCleared() {
super.onCleared();
// ViewModel 销毁时,自动取消所有正在执行的 RxJava 任务
if (!mDisposables.isDisposed()) {
mDisposables.clear();
}
}
}

View File

@@ -1,6 +1,8 @@
package com.ttstd.dialer.base.mvvm;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
@@ -14,6 +16,33 @@ import com.ttstd.dialer.utils.Logger;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
/**
* MVVM Activity 基类
*
* 职责:
* - 绑定 DataBinding
* - 初始化 ViewModel(默认通过反射自动创建)
* - 订阅 ViewModel 中的 LiveData({@link #observeViewModel()})
* - 订阅全局通用状态({@link #observeCommonState()})
* - 页面创建完成后加载数据({@link #onLoadData()})
* - 将用户操作转发给 ViewModel
*
* 生命周期顺序(onCreate):
* 1. 创建 DataBinding
* 2. 创建 ViewModel({@link #createViewModel()})
* 3. 订阅通用状态({@link #observeCommonState()})
* 4. 订阅业务状态({@link #observeViewModel()})
* 5. 初始化界面({@link #initDataBinding()} / {@link #initView()})
* 6. 加载数据({@link #onLoadData()})
*
* 禁止:
* - 在 Activity 中直接进行网络请求或数据库操作(必须通过 ViewModel → Repository)
* - 持有可变 LiveData 的引用(应通过 LiveData 回调观察)
* - 在 Activity 中写业务逻辑
*
* @param <VM> ViewModel 类型,必须继承 {@link BaseViewModel} 或 {@link BaseAndroidViewModel}
* @param <VDB> ViewDataBinding 类型
*/
public abstract class BaseMvvmActivity<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseTransparentActivity {
private static final String TAG = BaseMvvmActivity.class.getSimpleName();
@@ -22,39 +51,131 @@ public abstract class BaseMvvmActivity<VM extends ViewModel, VDB extends ViewDat
protected VDB mViewDataBinding;
protected Class<VM> vmClass;
// ============================================================
// 生命周期
// ============================================================
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//ViewDataBinding
// 1. 创建 DataBinding
mViewDataBinding = DataBindingUtil.setContentView(this, getLayoutId());
mViewDataBinding.setLifecycleOwner(this);
//ViewModel
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
boolean isAbstract = Modifier.isAbstract(vmClass.getModifiers());
Logger.e(TAG, "isLocalClass:" + vmClass.getSimpleName().equals(ViewModel.class.getSimpleName()) + " isAbstract:" + isAbstract);
if (!isAbstract) {//不是一个抽象类
mViewModel = new ViewModelProvider(this).get(vmClass);
if (mViewModel instanceof BaseViewModel) {
BaseViewModel<?, ?> baseVm = (BaseViewModel<?, ?>) mViewModel;
baseVm.setContext(this);
baseVm.setVDBinding(mViewDataBinding);
baseVm.setLifecycle(getLifecycleSubject());
}
}
// 2. 创建 ViewModel
mViewModel = createViewModel();
// 3. 订阅通用状态
observeCommonState();
// 4. 订阅业务状态
observeViewModel();
// 5. 初始化界面
initDataBinding();
initView();
initData();
// 6. 加载数据
onLoadData();
}
// ============================================================
// 抽象方法(子类必须实现)
// ============================================================
/** 创建 ViewModel 实例(默认通过反射自动创建,子类可按需重写) */
protected VM createViewModel() {
vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
boolean isAbstract = Modifier.isAbstract(vmClass.getModifiers());
if (isAbstract) {
Logger.e(TAG, "ViewModel is abstract: " + vmClass.getSimpleName());
return null;
}
return new ViewModelProvider(this).get(vmClass);
}
/** 初始化 DataBinding(如设置 setClick、绑定表达式等) */
protected abstract void initDataBinding();
/**
* 初始化视图
*/
/** 初始化视图(如设置 Adapter、Listener 等) */
protected abstract void initView();
/** 订阅 ViewModel 中的业务 LiveData */
protected abstract void observeViewModel();
// ============================================================
// 可选重写
// ============================================================
/**
* 初始化数据
* 页面创建完成后加载数据
* 子类可重写此方法触发首次数据加载
*/
protected abstract void initData();
protected void onLoadData() {
// 默认空实现
}
/**
* 订阅全局通用状态(loading / error / success)
* 子类可在 {@link #observeViewModel()} 中调用 super.observeCommonState(),或直接重写
*/
protected void observeCommonState() {
if (!(mViewModel instanceof IMvvmUiState)) return;
IMvvmUiState vm = (IMvvmUiState) mViewModel;
vm.getLoadingLiveData().observe(this, loading -> {
if (loading != null && loading) {
showLoading();
} else {
hideLoading();
}
});
vm.getErrorLiveData().observe(this, error -> {
if (error != null && !error.isEmpty()) {
showError(error);
}
});
vm.getSuccessLiveData().observe(this, msg -> {
if (msg != null && !msg.isEmpty()) {
showSuccess(msg);
}
});
}
// ============================================================
// UI 辅助方法(子类可重写)
// ============================================================
protected void showLoading() {
// 默认实现:可替换为自定义 LoadingDialog
// LoadingDialog.show(this);
}
protected void hideLoading() {
// LoadingDialog.dismiss();
}
protected void showError(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
protected void showSuccess(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
// ============================================================
// 禁止使用的模式(编译期约束)
// ============================================================
/**
* @deprecated 禁止在 MVVM Activity 中直接创建 Handler 做延时操作
* → 应使用 ViewModel + LiveData 驱动
*/
@Deprecated
protected Handler getHandler() {
throw new UnsupportedOperationException("禁止在 MVVM Activity 中使用 Handler");
}
}

View File

@@ -1,75 +1,106 @@
package com.ttstd.dialer.base.mvvm;
import android.content.Context;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;
import java.lang.ref.WeakReference;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseViewModel<VDB extends ViewDataBinding, T> extends ViewModel {
/**
* MVVM ViewModel 基类(无 Context 版本)
*
* 职责:
* - 持有 UI 状态(LiveData)
* - 处理业务逻辑
* - 暴露数据给 View 层
* - 通过 {@link #addDisposable(Disposable)} 统一管理 RxJava 异步任务
*
* 禁止:
* - 持有任何 View 引用(Activity / Fragment / View / Context)
* - 直接进行网络请求或数据库操作(必须通过 Repository)
* - 在 ViewModel 中弹 Toast / Dialog / Snackbar
* - 手动创建 Thread / Executor / AsyncTask 执行异步任务(必须使用 RxJava + {@link #addDisposable(Disposable)})
* - 忘记将 Disposable add 到 {@link #mDisposables} 中
*
* 若需要 Context / Application(如 SharedPreferences、AlarmManager 等),请继承
* {@link BaseAndroidViewModel},通过 {@code getAppContext()} 获取 Application Context。
*/
public abstract class BaseViewModel extends ViewModel implements IMvvmUiState {
// ============================================================
// 通用 UI 状态
// ============================================================
/** 全局加载状态 */
private final MutableLiveData<Boolean> mLoadingLiveData = new MutableLiveData<>(false);
/** 全局错误提示 */
private final MutableLiveData<String> mErrorLiveData = new MutableLiveData<>();
/** 全局成功提示 */
private final MutableLiveData<String> mSuccessLiveData = new MutableLiveData<>();
// ============================================================
// RxJava 异步管理(核心)
// ============================================================
/**
* Google 标准写法:ViewModel 内统一管理 Rx 订阅,
* onCleared 时自动取消,防止内存泄漏。
* CompositeDisposable 用于统一管理所有 RxJava 订阅。
* 在 onCleared() 中自动 dispose,防止内存泄漏。
*/
protected final CompositeDisposable mDisposables = new CompositeDisposable();
// ============================================================
// 对外暴露(只读)
// ============================================================
public LiveData<Boolean> getLoadingLiveData() {
return mLoadingLiveData;
}
public LiveData<String> getErrorLiveData() {
return mErrorLiveData;
}
public LiveData<String> getSuccessLiveData() {
return mSuccessLiveData;
}
// ============================================================
// 受保护的方法(子类调用)
// ============================================================
protected void setLoading(boolean loading) {
mLoadingLiveData.postValue(loading);
}
protected void setError(String message) {
mErrorLiveData.postValue(message);
}
protected void setSuccess(String message) {
mSuccessLiveData.postValue(message);
}
/**
* 将订阅托管给 ViewModel 生命周期
* 便捷方法:将 Disposable 添加到 {@link #mDisposables} 中管理。
* 子类在发起 RxJava 请求时调用此方法,确保 ViewModel 销毁时自动取消订阅。
*/
protected void addDisposable(Disposable disposable) {
mDisposables.add(disposable);
}
// ============================================================
// 生命周期
// ============================================================
@Override
protected void onCleared() {
mDisposables.clear();
super.onCleared();
}
/**
* 当前viewmodel对应的页面binding
*/
protected VDB binding;
public void setVDBinding(ViewDataBinding vdBinding) {
binding = (VDB) vdBinding;
}
public VDB getVDBinding() {
if (binding == null) {
throw new NullPointerException("BaseViewModel >> getVDBinding >> null!!!");
// ViewModel 销毁时,自动取消所有正在执行的 RxJava 任务
if (!mDisposables.isDisposed()) {
mDisposables.clear();
}
return binding;
}
private WeakReference<Context> weakContext;
public void setContext(Context context) {
if (weakContext == null) {
weakContext = new WeakReference<>(context.getApplicationContext());
}
}
public Context getSafeContext() {
if (weakContext == null) {
throw new NullPointerException("BaseViewModel >> getCtx >> null!!!");
}
return weakContext.get();
}
private BehaviorSubject<T> mBehaviorSubject;
public void setLifecycle(BehaviorSubject subject) {
this.mBehaviorSubject = (BehaviorSubject<T>) subject;
}
public BehaviorSubject<T> getLifecycle() {
return mBehaviorSubject;
}
}

View File

@@ -0,0 +1,22 @@
package com.ttstd.dialer.base.mvvm;
import androidx.lifecycle.LiveData;
/**
* MVVM ViewModel 通用 UI 状态接口
*
* {@link BaseViewModel} 与 {@link BaseAndroidViewModel} 均实现本接口,
* 使基类({@link BaseMvvmActivity} / {@link BaseMvvmFragment} / {@link BaseMvvmDialogFragment})
* 能以统一的 {@code VM extends ViewModel} 泛型约束订阅 loading / error / success 状态。
*/
public interface IMvvmUiState {
/** 全局加载状态(只读) */
LiveData<Boolean> getLoadingLiveData();
/** 全局错误提示(只读) */
LiveData<String> getErrorLiveData();
/** 全局成功提示(只读) */
LiveData<String> getSuccessLiveData();
}

View File

@@ -6,6 +6,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
@@ -16,20 +17,35 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.dialer.base.BaseDialogFragment;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.IMvvmUiState;
import com.ttstd.dialer.utils.Logger;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* 优化生命周期管理,去掉冗余的 Bundle 定义
* @param <VM> ViewModel 类型
* MVVM DialogFragment 基类
*
* 职责:
* - 绑定 DataBinding
* - 初始化 ViewModel(默认通过反射自动创建)
* - 订阅 ViewModel 中的 LiveData({@link #observeViewModel()})
* - 订阅全局通用状态({@link #observeCommonState()})
* - 页面创建完成后加载数据({@link #onLoadData()})
* - 将用户操作转发给 ViewModel
*
* 禁止:
* - 在 DialogFragment 中直接进行网络请求或数据库操作(必须通过 ViewModel → Repository)
* - 持有可变 LiveData 的引用(应通过 LiveData 回调观察)
* - 在 DialogFragment 中写业务逻辑
*
* @param <VM> ViewModel 类型,必须继承 {@link BaseViewModel} 或 {@link BaseAndroidViewModel}
* @param <VDB> ViewDataBinding 类型
*/
public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseDialogFragment {
protected final String mTag = this.getClass().getSimpleName();
protected boolean mIsVisible;
protected boolean mHasPrepare;
@@ -48,18 +64,16 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
ctx = new WeakReference<>(context);
}
// ============================================================
// 生命周期
// ============================================================
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ViewModel 初始化移至 onCreate
// ViewModel 初始化
try {
Class<VM> vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
if (mViewModel instanceof BaseViewModel) {
BaseViewModel<?, ?> baseVm = (BaseViewModel<?, ?>) mViewModel;
baseVm.setContext(getContext());
baseVm.setLifecycle(getLifecycleSubject());
}
mViewModel = createViewModel();
} catch (Exception e) {
Logger.e(mTag, "ViewModel initialization failed: " + e.getMessage());
}
@@ -68,10 +82,8 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// DataBinding 初始化
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(mViewDataBinding);
}
return mViewDataBinding.getRoot();
}
@@ -80,9 +92,17 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
super.onViewCreated(view, savedInstanceState);
mViewDataBinding.setLifecycleOwner(getViewLifecycleOwner());
// 订阅通用状态
observeCommonState();
// 订阅业务状态
observeViewModel();
initDataBinding();
initView(getArguments());
initData(savedInstanceState);
initView(savedInstanceState);
// 加载数据
onLoadData();
mHasPrepare = true;
if (mIsVisible || getUserVisibleHint()) {
@@ -96,9 +116,6 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
super.onDestroyView();
mHasPrepare = false;
// 及时解绑 Binding,防止内存泄漏
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(null);
}
mViewDataBinding = null;
}
@@ -122,14 +139,85 @@ public abstract class BaseMvvmDialogFragment<VM extends ViewModel, VDB extends V
public void fetchData() {
}
// ============================================================
// 抽象方法(子类必须实现)
// ============================================================
/** 创建 ViewModel 实例(默认通过反射自动创建,子类可按需重写) */
protected VM createViewModel() {
Class<VM> vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return new ViewModelProvider(this).get(vmClass);
}
@LayoutRes
protected abstract int getLayoutId();
/** 初始化 DataBinding(如设置 setClick、绑定表达式等) */
protected abstract void initDataBinding();
protected abstract void initView(Bundle bundle);
/** 初始化视图(如设置 Adapter、Listener 等) */
protected abstract void initView(Bundle savedInstanceState);
protected abstract void initData(Bundle savedInstanceState);
/** 订阅 ViewModel 中的业务 LiveData */
protected abstract void observeViewModel();
// ============================================================
// 可选重写
// ============================================================
/**
* 页面创建完成后加载数据
* 子类可重写此方法触发首次数据加载
*/
protected void onLoadData() {
// 默认空实现
}
/**
* 订阅全局通用状态(loading / error / success)
*/
protected void observeCommonState() {
if (!(mViewModel instanceof IMvvmUiState)) return;
IMvvmUiState vm = (IMvvmUiState) mViewModel;
vm.getLoadingLiveData().observe(getViewLifecycleOwner(), loading -> {
if (loading != null && loading) {
showLoading();
} else {
hideLoading();
}
});
vm.getErrorLiveData().observe(getViewLifecycleOwner(), error -> {
if (error != null && !error.isEmpty()) {
showError(error);
}
});
vm.getSuccessLiveData().observe(getViewLifecycleOwner(), msg -> {
if (msg != null && !msg.isEmpty()) {
showSuccess(msg);
}
});
}
// ============================================================
// UI 辅助方法(子类可重写)
// ============================================================
protected void showLoading() {
}
protected void hideLoading() {
}
protected void showError(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
protected void showSuccess(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {

View File

@@ -6,6 +6,7 @@ import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
@@ -16,27 +17,41 @@ import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import com.ttstd.dialer.base.BaseFragment;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.IMvvmUiState;
import com.ttstd.dialer.utils.Logger;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
/**
* 优化生命周期管理,去掉冗余的 Bundle 定义
* @param <VM> ViewModel 类型
* MVVM Fragment 基类
*
* 职责:
* - 绑定 DataBinding
* - 初始化 ViewModel(默认通过反射自动创建)
* - 订阅 ViewModel 中的 LiveData({@link #observeViewModel()})
* - 订阅全局通用状态({@link #observeCommonState()})
* - 页面创建完成后加载数据({@link #onLoadData()})
* - 支持懒加载({@link #onEnter()} / {@link #onExit()})
* - 将用户操作转发给 ViewModel
*
* 禁止:
* - 在 Fragment 中直接进行网络请求或数据库操作(必须通过 ViewModel → Repository)
* - 持有可变 LiveData 的引用(应通过 LiveData 回调观察)
* - 在 Fragment 中写业务逻辑
* - 使用 getActivity() 强转后直接操作 Activity(应通过 ViewModel 通信)
*
* @param <VM> ViewModel 类型,必须继承 {@link BaseViewModel} 或 {@link BaseAndroidViewModel}
* @param <VDB> ViewDataBinding 类型
*/
public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDataBinding> extends BaseFragment {
protected final String mTag = this.getClass().getSimpleName();
/**
* 是否可见(用于懒加载逻辑)
*/
/** 是否可见(用于懒加载逻辑) */
protected boolean mIsVisible;
/**
* View 是否准备完毕
*/
/** View 是否准备完毕 */
protected boolean mHasPrepare;
protected VM mViewModel;
@@ -54,18 +69,16 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
ctx = new WeakReference<>(context);
}
// ============================================================
// 生命周期
// ============================================================
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ViewModel 初始化
try {
Class<VM> vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
mViewModel = new ViewModelProvider(this).get(vmClass);
if (mViewModel instanceof BaseViewModel) {
BaseViewModel<?, ?> baseVm = (BaseViewModel<?, ?>) mViewModel;
baseVm.setContext(getContext());
baseVm.setLifecycle(getLifecycleSubject());
}
mViewModel = createViewModel();
} catch (Exception e) {
Logger.e(mTag, "ViewModel initialization failed: " + e.getMessage());
}
@@ -74,11 +87,8 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// ViewDataBinding 初始化
// DataBinding 初始化
mViewDataBinding = DataBindingUtil.inflate(inflater, getLayoutId(), container, false);
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(mViewDataBinding);
}
return mViewDataBinding.getRoot();
}
@@ -86,11 +96,18 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mViewDataBinding.setLifecycleOwner(getViewLifecycleOwner());
// 订阅通用状态
observeCommonState();
// 订阅业务状态
observeViewModel();
initDataBinding();
// 去掉重复的 bundle 和 savedInstanceState 定义,直接从参数/方法获取
initView(getArguments());
initData(savedInstanceState);
initView(savedInstanceState);
// 加载数据
onLoadData();
mHasPrepare = true;
// 检查初始可见性
@@ -105,9 +122,6 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
super.onDestroyView();
mHasPrepare = false;
// 及时解绑 Binding,防止内存泄漏
if (mViewModel instanceof BaseViewModel) {
((BaseViewModel<?, ?>) mViewModel).setVDBinding(null);
}
mViewDataBinding = null;
}
@@ -127,14 +141,91 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
}
}
// ============================================================
// 抽象方法(子类必须实现)
// ============================================================
/** 创建 ViewModel 实例(默认通过反射自动创建,子类可按需重写) */
protected VM createViewModel() {
Class<VM> vmClass = (Class<VM>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[0];
return new ViewModelProvider(this).get(vmClass);
}
@LayoutRes
protected abstract int getLayoutId();
/** 初始化 DataBinding(如设置 setClick、绑定表达式等) */
protected abstract void initDataBinding();
protected abstract void initView(Bundle bundle);
/** 初始化视图(如设置 Adapter、Listener 等) */
protected abstract void initView(Bundle savedInstanceState);
protected abstract void initData(Bundle savedInstanceState);
/** 订阅 ViewModel 中的业务 LiveData */
protected abstract void observeViewModel();
// ============================================================
// 可选重写
// ============================================================
/**
* 页面创建完成后加载数据
* 子类可重写此方法触发首次数据加载
*/
protected void onLoadData() {
// 默认空实现
}
/**
* 订阅全局通用状态(loading / error / success)
* 子类可在 {@link #observeViewModel()} 中调用 super.observeCommonState(),或直接重写
*/
protected void observeCommonState() {
if (!(mViewModel instanceof IMvvmUiState)) return;
IMvvmUiState vm = (IMvvmUiState) mViewModel;
vm.getLoadingLiveData().observe(getViewLifecycleOwner(), loading -> {
if (loading != null && loading) {
showLoading();
} else {
hideLoading();
}
});
vm.getErrorLiveData().observe(getViewLifecycleOwner(), error -> {
if (error != null && !error.isEmpty()) {
showError(error);
}
});
vm.getSuccessLiveData().observe(getViewLifecycleOwner(), msg -> {
if (msg != null && !msg.isEmpty()) {
showSuccess(msg);
}
});
}
// ============================================================
// UI 辅助方法(子类可重写)
// ============================================================
protected void showLoading() {
// 默认实现:可替换为自定义 LoadingDialog
}
protected void hideLoading() {
}
protected void showError(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
protected void showSuccess(String message) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
// ============================================================
// 懒加载回调(子类可重写)
// ============================================================
@Override
public void onConfigurationChanged(Configuration newConfig) {
@@ -150,4 +241,17 @@ public abstract class BaseMvvmFragment<VM extends ViewModel, VDB extends ViewDat
public boolean isAttached() {
return getCtx() != null && isAdded();
}
// ============================================================
// 禁止使用的模式(编译期约束)
// ============================================================
/**
* @deprecated 禁止在 MVVM Fragment 中通过 getActivity() 强转后直接操作 Activity
* → 应通过 ViewModel 通信
*/
@Deprecated
protected android.app.Activity getActivityAs() {
throw new UnsupportedOperationException("禁止在 MVVM Fragment 中使用 getActivity() 强转");
}
}

View File

@@ -104,10 +104,10 @@ public class AppFragment extends BaseMvvmFragment<AppViewModel, FragmentAppBindi
}
@Override
protected void initData(Bundle savedInstanceState) {
Logger.e(TAG, "initData: ");
Logger.e(TAG, "initData: " + mViewModel.mAppUpdateData);
mViewModel.mAppUpdateData.observe(getViewLifecycleOwner(), new Observer<Integer>() {
protected void observeViewModel() {
Logger.e(TAG, "observeViewModel: ");
Logger.e(TAG, "observeViewModel: " + mViewModel.getAppUpdateData());
mViewModel.getAppUpdateData().observe(getViewLifecycleOwner(), new Observer<Integer>() {
@Override
public void onChanged(Integer integer) {
if (integer > 0) {

View File

@@ -1,11 +1,8 @@
package com.ttstd.dialer.fragment.app;
import android.content.Context;
import android.app.Application;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentAppBinding;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.db.app.AppInfo;
import com.ttstd.dialer.db.app.AppRepository;
import com.ttstd.dialer.livedata.SingleLiveEvent;
@@ -14,31 +11,27 @@ import com.ttstd.dialer.utils.Logger;
import java.util.concurrent.Callable;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.annotations.NonNull;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Observer;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
public class AppViewModel extends BaseViewModel<FragmentAppBinding, FragmentEvent> {
public class AppViewModel extends BaseAndroidViewModel {
private static final String TAG = "AppViewModel";
private AppRepository mAppRepository;
private final AppRepository mAppRepository;
@Override
public void setContext(Context context) {
super.setContext(context);
mAppRepository = new AppRepository(context);
private final SingleLiveEvent<Integer> mAppUpdateData = new SingleLiveEvent<>();
public AppViewModel(Application application) {
super(application);
mAppRepository = new AppRepository(application);
}
public SingleLiveEvent<Integer> mAppUpdateData = new SingleLiveEvent<>();
// public void resetLiveData() {
// mAppUpdateData = new MutableLiveData<>();
// }
public SingleLiveEvent<Integer> getAppUpdateData() {
return mAppUpdateData;
}
public void updateAppInfo(AppInfo appInfo) {
Observable.fromCallable(new Callable<Integer>() {
addDisposable(Observable.fromCallable(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
// 根据状态自动设置位置为最大值
@@ -53,30 +46,13 @@ public class AppViewModel extends BaseViewModel<FragmentAppBinding, FragmentEven
}
return mAppRepository.update(appInfo);
}
}).compose(RxLifecycle.bindUntilEvent(getLifecycle(), FragmentEvent.DESTROY))
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Integer>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
Logger.e("updateAppInfo", "onSubscribe: ");
}
@Override
public void onNext(@NonNull Integer row) {
Logger.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
}
@Override
public void onError(@NonNull Throwable e) {
Logger.e("updateAppInfo", "onError: " + e.getMessage());
}
@Override
public void onComplete() {
Logger.e("updateAppInfo", "onComplete: ");
}
});
.subscribe(row -> {
Logger.e("updateAppInfo", "onNext: " + row);
mAppUpdateData.setValue(row);
},
throwable -> Logger.e("updateAppInfo", "onError: " + throwable.getMessage())));
}
}

View File

@@ -56,9 +56,8 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
}
@Override
protected void initData(Bundle savedInstanceState) {
mViewModel.preloadData();
mViewModel.mContactListData.observe(getViewLifecycleOwner(), new Observer<List<ContactInfo>>() {
protected void observeViewModel() {
mViewModel.getContactListData().observe(getViewLifecycleOwner(), new Observer<List<ContactInfo>>() {
@Override
public void onChanged(List<ContactInfo> contactInfos) {
Logger.e(TAG, "mContactListData: " + contactInfos);
@@ -68,6 +67,11 @@ public class ContactFragment extends BaseMvvmFragment<ContactViewModel, Fragment
});
}
@Override
protected void onLoadData() {
mViewModel.preloadData();
}
@Override
public void fetchData() {

View File

@@ -1,13 +1,12 @@
package com.ttstd.dialer.fragment.contact;
import android.content.Context;
import android.app.Application;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
import com.ttstd.dialer.data.repository.ContactDataRepository;
import com.ttstd.dialer.databinding.FragmentContactBinding;
import com.ttstd.dialer.db.contact.ContactInfo;
import com.ttstd.dialer.utils.Logger;
@@ -18,19 +17,25 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
/**
* 主页联系人 ViewModel(Google 标准 MVVM):
* 统一走 Repository 多级缓存 + SWR,内存/磁盘缓存秒出,后台自动 revalidate。
*
* 需通过 {@link #getAppContext()} 获取 Application Context 初始化 Repository,
* 不持有任何 View / Activity 引用。
*/
public class ContactViewModel extends BaseViewModel<FragmentContactBinding, FragmentEvent> {
public class ContactViewModel extends BaseAndroidViewModel {
private static final String TAG = "ContactViewModel";
private ContactDataRepository mRepository;
private final ContactDataRepository mRepository;
private boolean isDataLoaded = false;
public MutableLiveData<List<ContactInfo>> mContactListData = new MutableLiveData<>();
private final MutableLiveData<List<ContactInfo>> mContactListData = new MutableLiveData<>();
@Override
public void setContext(Context context) {
super.setContext(context);
mRepository = ContactDataRepository.getInstance(context);
public ContactViewModel(Application application) {
super(application);
mRepository = ContactDataRepository.getInstance(application);
}
public LiveData<List<ContactInfo>> getContactListData() {
return mContactListData;
}
public void preloadData() {

View File

@@ -59,7 +59,7 @@ public class CallFragment extends BaseMvvmDialogFragment<CallViewModel, DialogFr
}
@Override
protected void initData(Bundle savedInstanceState) {
protected void observeViewModel() {
}

View File

@@ -1,10 +1,8 @@
package com.ttstd.dialer.fragment.dialog.contact.call;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.DialogFragmentContactCallBinding;
public class CallViewModel extends BaseViewModel<DialogFragmentContactCallBinding, FragmentEvent> {
public class CallViewModel extends BaseViewModel {
}

View File

@@ -66,7 +66,7 @@ public class ContactDeleteDialogFragment extends BaseMvvmDialogFragment<ContactD
}
@Override
protected void initData(Bundle savedInstanceState) {
protected void observeViewModel() {
}

View File

@@ -1,8 +1,6 @@
package com.ttstd.dialer.fragment.dialog.contact.delete;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.DialogFragmentDeleteContactBinding;
public class ContactDeleteViewModel extends BaseViewModel<DialogFragmentDeleteContactBinding, FragmentEvent> {
public class ContactDeleteViewModel extends BaseViewModel {
}

View File

@@ -66,7 +66,7 @@ public class EditDialogFragment extends BaseMvvmDialogFragment<EditViewModel, Di
}
@Override
protected void initData(Bundle savedInstanceState) {
protected void observeViewModel() {
}

View File

@@ -1,8 +1,6 @@
package com.ttstd.dialer.fragment.dialog.contact.edit;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.DialogFragmentEditContactBinding;
public class EditViewModel extends BaseViewModel<DialogFragmentEditContactBinding, FragmentEvent> {
public class EditViewModel extends BaseViewModel {
}

View File

@@ -87,11 +87,6 @@ public class DockAppDialogFagment extends BaseMvvmDialogFragment<DockAppViewMode
@Override
protected void initView(Bundle bundle) {
}
@Override
protected void initData(Bundle savedInstanceState) {
if (TextUtils.isEmpty(mTitile)) {
mViewDataBinding.tvTitle.setText("提示");
} else {
@@ -130,6 +125,11 @@ public class DockAppDialogFagment extends BaseMvvmDialogFragment<DockAppViewMode
}
}
@Override
protected void observeViewModel() {
}
@Override
public void onStart() {
super.onStart();

View File

@@ -1,10 +1,7 @@
package com.ttstd.dialer.fragment.dialog.dock;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentDialogDockAppBinding;
import com.ttstd.dialer.databinding.FragmentDialogShortcutBinding;
public class DockAppViewModel extends BaseViewModel <FragmentDialogDockAppBinding, FragmentEvent>{
public class DockAppViewModel extends BaseViewModel {
}

View File

@@ -82,11 +82,6 @@ public class MoveAppDialogFagment extends BaseMvvmDialogFragment<MoveAppViewMode
@Override
protected void initView(Bundle bundle) {
}
@Override
protected void initData(Bundle savedInstanceState) {
if (TextUtils.isEmpty(mTitile)) {
mViewDataBinding.tvTitle.setText("提示");
} else {
@@ -125,6 +120,11 @@ public class MoveAppDialogFagment extends BaseMvvmDialogFragment<MoveAppViewMode
}
}
@Override
protected void observeViewModel() {
}
@Override
public void onStart() {
super.onStart();

View File

@@ -1,9 +1,7 @@
package com.ttstd.dialer.fragment.dialog.move;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentDialogMoveAppBinding;
public class MoveAppViewModel extends BaseViewModel <FragmentDialogMoveAppBinding, FragmentEvent>{
public class MoveAppViewModel extends BaseViewModel {
}

View File

@@ -87,11 +87,6 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
@Override
protected void initView(Bundle bundle) {
}
@Override
protected void initData(Bundle savedInstanceState) {
if (TextUtils.isEmpty(mTitile)) {
mViewDataBinding.tvTitle.setText("提示");
} else {
@@ -130,6 +125,11 @@ public class ShortcutDialogFagment extends BaseMvvmDialogFragment<ShortcutViewMo
}
}
@Override
protected void observeViewModel() {
}
@Override
public void onStart() {
super.onStart();

View File

@@ -1,9 +1,7 @@
package com.ttstd.dialer.fragment.dialog.shortcut;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentDialogShortcutBinding;
public class ShortcutViewModel extends BaseViewModel <FragmentDialogShortcutBinding, FragmentEvent>{
public class ShortcutViewModel extends BaseViewModel {
}

View File

@@ -116,7 +116,7 @@ public class HomeFragment extends BaseMvvmFragment<HomeViewModel, FragmentHomeBi
}
@Override
protected void initData(Bundle savedInstanceState) {
protected void observeViewModel() {
LiveEventBus.get(CommonConfig.LOCATION_CHANGED_KEY, SnLocationReq.class)
.observeSticky(this, new Observer<SnLocationReq>() {
@Override

View File

@@ -1,10 +1,8 @@
package com.ttstd.dialer.fragment.home;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentHomeBinding;
public class HomeViewModel extends BaseViewModel<FragmentHomeBinding, FragmentEvent> {
public class HomeViewModel extends BaseViewModel {
}

View File

@@ -37,7 +37,7 @@ public class SettingsFragment extends BaseMvvmFragment<SettingsViewModel, Fragme
}
@Override
protected void initData(Bundle savedInstanceState) {
protected void observeViewModel() {
}

View File

@@ -1,9 +1,7 @@
package com.ttstd.dialer.fragment.settings;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.ttstd.dialer.base.mvvm.BaseViewModel;
import com.ttstd.dialer.databinding.FragmentSettingsBinding;
public class SettingsViewModel extends BaseViewModel<FragmentSettingsBinding, FragmentEvent> {
public class SettingsViewModel extends BaseViewModel {
}

View File

@@ -319,6 +319,13 @@ public class OkHttpManager {
return schedule(getApiService(SnApi.class).register(DeviceManagerService.getInstance().getSerial(), Build.MODEL), provider);
}
/**
* 设备注册(由 ViewModel 通过 addDisposable 管理订阅生命周期)
*/
public Observable<BaseResponse> getSnRegisterObservable() {
return schedule(getApiService(SnApi.class).register(DeviceManagerService.getInstance().getSerial(), Build.MODEL));
}
/**
* 上传设备基本信息
*/