feat(base): 添加MVVM、Rx生命周期和WebRTC控制端基础架构

引入BaseApplication、BaseService及MVVM基类(BaseViewModel/BaseMvvmActivity),
集成RxLifecycle管理生命周期,新增WebRTC控制端MainActivity与信令客户端,
建立屏幕捕获、推送、JPush等模块初始化流程,完善项目核心框架。
This commit is contained in:
TongTongStudio
2026-08-03 00:25:25 +08:00
parent 6eb2c7321a
commit 61398ff4ff
21 changed files with 1445 additions and 16 deletions

View File

@@ -1,4 +1,4 @@
package com.ttstd.controller;
package com.ttstd.controller.activity.main;
import android.Manifest;
import android.content.pm.PackageManager;
@@ -14,7 +14,6 @@ import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
@@ -38,6 +37,7 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.ttstd.control.Action;
import com.ttstd.control.ControlMessage;
import com.ttstd.controller.R;
import com.ttstd.controller.signaling.ApiClient;
import com.ttstd.controller.signaling.SignalMessage;
import com.ttstd.controller.signaling.WebSocketClient;
@@ -52,8 +52,6 @@ import org.webrtc.IceCandidate;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.Executors;
@@ -156,7 +154,7 @@ public class MainActivity extends AppCompatActivity {
btnDisconnectControl = findViewById(R.id.btn_disconnect_control);
// 默认服务器地址
etServerUrl.setText("wss://www.ttstd.com/signal");
etServerUrl.setText("ws://192.168.5.224:8080/ws/signal");
// 设备ID 由服务端注册后下发REGISTER_SUCCESS.fromDeviceId用户无需填写
etDeviceId.setEnabled(false);
etDeviceId.setHint("连接后由服务端下发");

View File

@@ -0,0 +1,6 @@
package com.ttstd.controlled.activity.main;
import com.ttstd.controlled.base.mvvm.BaseViewModel;
public class MainViewModel extends BaseViewModel {
}

View File

@@ -0,0 +1,61 @@
package com.ttstd.controller.base;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.util.Log;
import androidx.multidex.MultiDex;
import com.tencent.mmkv.MMKV;
public class BaseApplication extends Application {
private static final String TAG = "BaseApplication";
/**
* ViewModel中因为经常旋转导致弱引用为空
*/
@SuppressLint("StaticFieldLeak")
private static Context mAppContext;
public static Context getContext() {
return mAppContext;
}
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
@Override
public void onCreate() {
super.onCreate();
Log.e(TAG, "onCreate: ");
mAppContext = getApplicationContext();
init();
}
@Override
public void onTerminate() {
super.onTerminate();
}
@Override
public void onLowMemory() {
super.onLowMemory();
}
@Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
}
private void init() {
Log.e(TAG, "init: ");
String rootDir = MMKV.initialize(this);
Log.e(TAG, "mmkv root: " + rootDir);
}
}

View File

@@ -0,0 +1,61 @@
package com.ttstd.controller.base;
import android.content.Intent;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import com.ttstd.controlled.base.rx.BaseRxService;
import org.jetbrains.annotations.NotNull;
/**
* Service 基类。
*
* 同时实现 LifecycleOwner 与 ViewModelStoreOwner
* - LifecycleOwner 让 LiveData.observe(this, ...) 可用;
* - ViewModelStoreOwner 让 ViewModelProvider 创建的 ViewModel 在 onDestroy
* 时收到 onCleared 回调,从而自动 dispose 其中的网络请求。
*/
public abstract class BaseService extends BaseRxService implements LifecycleOwner, ViewModelStoreOwner {
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
private final ViewModelStore viewModelStore = new ViewModelStore();
@NotNull
@Override
public Lifecycle getLifecycle() {
return lifecycleRegistry;
}
@NotNull
@Override
public ViewModelStore getViewModelStore() {
return viewModelStore;
}
@Override
public void onCreate() {
super.onCreate();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
//修补在Service中LiveData不能触发
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
// 触发 ViewModel.onCleared(),取消所有在途请求
viewModelStore.clear();
}
}

View File

@@ -0,0 +1,45 @@
package com.ttstd.controller.base.mvvm;
import android.os.Bundle;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModelProvider;
import java.lang.reflect.ParameterizedType;
public abstract class BaseMvvmActivity<VM extends BaseViewModel, DB extends ViewDataBinding> extends AppCompatActivity {
protected VM viewModel;
protected DB binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, getLayoutId());
binding.setLifecycleOwner(this);
initViewModel();
initView();
initData();
}
@LayoutRes
protected abstract int getLayoutId();
protected abstract void initView();
protected abstract void initData();
@SuppressWarnings("unchecked")
private void initViewModel() {
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
if (type != null) {
Class<VM> viewModelClass = (Class<VM>) type.getActualTypeArguments()[0];
viewModel = new ViewModelProvider(this).get(viewModelClass);
}
}
}

View File

@@ -0,0 +1,57 @@
package com.ttstd.controller.base.mvvm;
import androidx.lifecycle.ViewModel;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.Single;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;
/**
* ViewModel 基类。
*
* 内置 CompositeDisposable所有网络请求通过 {@link #execute} 订阅,
* ViewModel 销毁Activity finish / Service stop时统一 dispose
* 从而实现请求随生命周期自动取消,杜绝内存泄漏与野线程回调。
*/
public class BaseViewModel extends ViewModel {
private final CompositeDisposable disposables = new CompositeDisposable();
/** 注册一个 Disposable交由本 ViewModel 生命周期托管。 */
protected void addDisposable(Disposable disposable) {
if (disposable != null) {
disposables.add(disposable);
}
}
/**
* 订阅一个 Single 并自动切回主线程,结果与异常都在主线程回调。
*/
protected <T> void execute(Single<T> single, Consumer<T> onSuccess, Consumer<Throwable> onError) {
addDisposable(single
.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
.subscribe(onSuccess, onError));
}
/**
* 订阅一个 Observable 并自动切回主线程。
*/
protected <T> void execute(Observable<T> observable, Consumer<T> onNext, Consumer<Throwable> onError) {
addDisposable(observable
.observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread())
.subscribe(onNext, onError));
}
/** 主动清空当前所有订阅(不影响后续再次订阅)。 */
protected void clearDisposables() {
disposables.clear();
}
@Override
protected void onCleared() {
disposables.clear();
super.onCleared();
}
}

View File

@@ -0,0 +1,94 @@
package com.ttstd.controller.base.rx;
import android.os.Bundle;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxActivity}
* copied form RxActivity}
*/
public abstract class BaseRxActivity extends AppCompatActivity implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<ActivityEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
@CallSuper
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
@CallSuper
protected void onStart() {
super.onStart();
lifecycleSubject.onNext(ActivityEvent.START);
}
@Override
@CallSuper
protected void onResume() {
super.onResume();
lifecycleSubject.onNext(ActivityEvent.RESUME);
}
@Override
@CallSuper
protected void onPause() {
lifecycleSubject.onNext(ActivityEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
protected void onStop() {
lifecycleSubject.onNext(ActivityEvent.STOP);
super.onStop();
}
@Override
@CallSuper
protected void onDestroy() {
lifecycleSubject.onNext(ActivityEvent.DESTROY);
super.onDestroy();
}
}

View File

@@ -0,0 +1,123 @@
package com.ttstd.controller.base.rx;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.DialogFragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxFragment}
* copied form RxFragment}
*/
public class BaseRxDialogFragment extends DialogFragment implements LifecycleProvider<FragmentEvent> {
private final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<FragmentEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,123 @@
package com.ttstd.controller.base.rx;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.FragmentEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
/**
* {@link com.trello.rxlifecycle4.components.RxFragment}
* copied form RxFragment}
*/
public class BaseRxFragment extends Fragment implements LifecycleProvider<FragmentEvent> {
private final BehaviorSubject<FragmentEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<FragmentEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<FragmentEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull FragmentEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindFragment(lifecycleSubject);
}
@Override
@CallSuper
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
lifecycleSubject.onNext(FragmentEvent.ATTACH);
}
@Override
@CallSuper
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE);
}
@Override
@CallSuper
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
lifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
}
@Override
@CallSuper
public void onStart() {
super.onStart();
lifecycleSubject.onNext(FragmentEvent.START);
}
@Override
@CallSuper
public void onResume() {
super.onResume();
lifecycleSubject.onNext(FragmentEvent.RESUME);
}
@Override
@CallSuper
public void onPause() {
lifecycleSubject.onNext(FragmentEvent.PAUSE);
super.onPause();
}
@Override
@CallSuper
public void onStop() {
lifecycleSubject.onNext(FragmentEvent.STOP);
super.onStop();
}
@Override
@CallSuper
public void onDestroyView() {
lifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);
super.onDestroyView();
}
@Override
@CallSuper
public void onDestroy() {
lifecycleSubject.onNext(FragmentEvent.DESTROY);
super.onDestroy();
}
@Override
@CallSuper
public void onDetach() {
lifecycleSubject.onNext(FragmentEvent.DETACH);
super.onDetach();
}
}

View File

@@ -0,0 +1,56 @@
package com.ttstd.controller.base.rx;
import android.app.Service;
import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import com.trello.rxlifecycle4.LifecycleProvider;
import com.trello.rxlifecycle4.LifecycleTransformer;
import com.trello.rxlifecycle4.RxLifecycle;
import com.trello.rxlifecycle4.android.ActivityEvent;
import com.trello.rxlifecycle4.android.RxLifecycleAndroid;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.subjects.BehaviorSubject;
public abstract class BaseRxService extends Service implements LifecycleProvider<ActivityEvent> {
private final BehaviorSubject<ActivityEvent> lifecycleSubject = BehaviorSubject.create();
public BehaviorSubject<ActivityEvent> getLifecycleSubject() {
return lifecycleSubject;
}
@Override
@NonNull
@CheckResult
public final Observable<ActivityEvent> lifecycle() {
return lifecycleSubject.hide();
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindUntilEvent(@NonNull ActivityEvent event) {
return RxLifecycle.bindUntilEvent(lifecycleSubject, event);
}
@Override
@NonNull
@CheckResult
public final <T> LifecycleTransformer<T> bindToLifecycle() {
return RxLifecycleAndroid.bindActivity(lifecycleSubject);
}
@Override
public void onCreate() {
super.onCreate();
lifecycleSubject.onNext(ActivityEvent.CREATE);
}
@Override
public void onDestroy() {
super.onDestroy();
lifecycleSubject.onNext(ActivityEvent.DESTROY);
}
}