fix(settings): 重构SN上报并清理失效启动页
- SettingsViewModel改用SnRepository进行设备注册 - 移除RxLifecycle手动绑定基类,改用AndroidX Lifecycle - 新增系统、硬件、网络、安全、定位等SN信息上报接口 - 修复ContactInfo equals逻辑错误 - 应用列表变化后自动清理失效启动页
This commit is contained in:
@@ -288,12 +288,6 @@ dependencies {
|
||||
//Gson
|
||||
implementation 'com.google.code.gson:gson:2.14.0'
|
||||
implementation 'com.google.zxing:core:3.5.4'
|
||||
//生命周期管理
|
||||
implementation 'com.trello.rxlifecycle4:rxlifecycle:4.0.2'
|
||||
implementation 'com.trello.rxlifecycle4:rxlifecycle-android:4.0.2'
|
||||
implementation 'com.trello.rxlifecycle4:rxlifecycle-components:4.0.2'
|
||||
implementation 'com.trello.rxlifecycle4:rxlifecycle-components-preference:4.0.2'
|
||||
implementation 'com.trello.rxlifecycle4:rxlifecycle-android-lifecycle:4.0.2'
|
||||
|
||||
|
||||
implementation 'com.jakewharton.rxbinding4:rxbinding:4.0.0'
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
package com.ttstd.dialer.activity.settings.home;
|
||||
|
||||
import com.ttstd.dialer.base.mvvm.BaseViewModel;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import android.app.Application;
|
||||
import android.os.Build;
|
||||
|
||||
import com.ttstd.dialer.base.mvvm.BaseAndroidViewModel;
|
||||
import com.ttstd.dialer.data.repository.SnRepository;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
public class SettingsViewModel extends BaseViewModel {
|
||||
public class SettingsViewModel extends BaseAndroidViewModel {
|
||||
|
||||
private final SnRepository mRepository;
|
||||
|
||||
public SettingsViewModel(Application application) {
|
||||
super(application);
|
||||
mRepository = SnRepository.getInstance(application);
|
||||
}
|
||||
|
||||
public void snRegister() {
|
||||
addDisposable(OkHttpManager.getInstance().getSnRegisterObservable()
|
||||
addDisposable(mRepository.register(
|
||||
DeviceManagerService.getInstance().getSerial(), Build.MODEL)
|
||||
.subscribe(baseResponse -> Logger.e("snRegister", "onNext: " + baseResponse),
|
||||
throwable -> Logger.e("snRegister", "onError: " + throwable.getMessage())));
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@ import android.view.View;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.OnApplyWindowInsetsListener;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.rx.BaseRxActivity;
|
||||
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
|
||||
|
||||
public abstract class BaseDataBindingActivity extends BaseRxActivity {
|
||||
public abstract class BaseDataBindingActivity extends AppCompatActivity {
|
||||
|
||||
public BaseDataBindingActivity() {
|
||||
super();
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.ttstd.dialer.base;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.ttstd.dialer.base.rx.BaseRxDialogFragment;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
public abstract class BaseDialogFragment extends BaseRxDialogFragment {
|
||||
public abstract class BaseDialogFragment extends DialogFragment {
|
||||
|
||||
protected boolean isViewInitiated;
|
||||
protected boolean isVisibleToUser;
|
||||
|
||||
@@ -4,8 +4,7 @@ import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.ttstd.dialer.base.rx.BaseRxFragment;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
/**
|
||||
* Fragment 基类,提供标准的懒加载框架
|
||||
@@ -20,7 +19,7 @@ import com.ttstd.dialer.base.rx.BaseRxFragment;
|
||||
* 2. {@link #fetchData()} 每次可见都会回调,用于需要刷新的场景。
|
||||
* 3. View 销毁重建后自动重置懒加载状态,保证重建后的页面能重新渲染。
|
||||
*/
|
||||
public abstract class BaseFragment extends BaseRxFragment {
|
||||
public abstract class BaseFragment extends Fragment {
|
||||
|
||||
private static final String TAG = "BaseFragment";
|
||||
|
||||
|
||||
@@ -1,43 +1,18 @@
|
||||
package com.ttstd.dialer.base;
|
||||
|
||||
import android.content.Intent;
|
||||
import androidx.lifecycle.LifecycleService;
|
||||
|
||||
import androidx.lifecycle.Lifecycle;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.LifecycleRegistry;
|
||||
/**
|
||||
* Service 基类(基于 AndroidX {@link LifecycleService})。
|
||||
* <p>
|
||||
* 通过继承 {@link LifecycleService} 自动获得:
|
||||
* - {@link androidx.lifecycle.LifecycleOwner} 能力
|
||||
* - 生命周期事件自动派发:onCreate → ON_CREATE,onStartCommand → ON_START / ON_RESUME,onDestroy → ON_DESTROY
|
||||
* (其中 ON_START / ON_RESUME 满足 Service 中 LiveData 的触发条件,无需再手动修补)
|
||||
* - 不再需要手动维护 {@link androidx.lifecycle.LifecycleRegistry} 或覆写生命周期方法派发事件
|
||||
* <p>
|
||||
* 子类如需在生命周期回调中做处理,可直接覆写 onStartCommand / onDestroy 并调用 super。
|
||||
*/
|
||||
public abstract class BaseService extends LifecycleService {
|
||||
|
||||
import com.ttstd.dialer.base.rx.BaseRxService;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class BaseService extends BaseRxService implements LifecycleOwner {
|
||||
|
||||
private final LifecycleRegistry lifecycleRegistry = new LifecycleRegistry(this);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Lifecycle getLifecycle() {
|
||||
return lifecycleRegistry;
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,16 +6,16 @@ import android.view.View;
|
||||
|
||||
import androidx.annotation.CallSuper;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.OnApplyWindowInsetsListener;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.ttstd.dialer.R;
|
||||
import com.ttstd.dialer.base.rx.BaseRxActivity;
|
||||
import com.zackratos.ultimatebarx.ultimatebarx.java.UltimateBarX;
|
||||
|
||||
public abstract class BaseTransparentActivity extends BaseRxActivity {
|
||||
public abstract class BaseTransparentActivity extends AppCompatActivity {
|
||||
|
||||
public BaseTransparentActivity() {
|
||||
super();
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
package com.ttstd.dialer.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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
package com.ttstd.dialer.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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
package com.ttstd.dialer.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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.ttstd.dialer.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);
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,28 @@ package com.ttstd.dialer.data.remote;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.bean.req.SnNetworkInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnOtherInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSecurityInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSystemInfoReq;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import com.ttstd.dialer.network.api.SnApi;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
/**
|
||||
* 设备(SN)远程数据源(Data Layer - Remote DataSource)。
|
||||
* <p>
|
||||
* 只负责发起网络请求并做基础解包,不做线程调度、不碰缓存。
|
||||
* 所有线程调度与缓存策略由上层 {@link com.ttstd.dialer.data.repository.SnRepository} 统一处理。
|
||||
*/
|
||||
public class SnRemoteDataSource {
|
||||
|
||||
@@ -23,4 +37,89 @@ public class SnRemoteDataSource {
|
||||
public Observable<DeveloperOptions> getDeveloperOptions() {
|
||||
return ApiResponseMapper.unwrap(mApi.getDeveloperOptions());
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备注册(sn + model)
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> register(@NonNull String sn, @NonNull String model) {
|
||||
return mApi.register(sn, model);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备基本信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateSystemInfo(@NonNull SnSystemInfoReq req) {
|
||||
return mApi.updateSystemInfo(OkHttpManager.convertToRequestBodyjson(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备硬件信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateHardwareInfo(@NonNull SnHardwareInfoReq req) {
|
||||
return mApi.updateHardwareInfo(OkHttpManager.convertToRequestBodyjson(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备网络信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateNetworkInfo(@NonNull SnNetworkInfoReq req) {
|
||||
return mApi.updateNetworkInfo(OkHttpManager.convertToRequestBodyjson(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备安全信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateSecurityInfo(@NonNull SnSecurityInfoReq req) {
|
||||
return mApi.updateSecurityInfo(OkHttpManager.convertToRequestBodyjson(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备其他信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateOtherInfo(@NonNull SnOtherInfoReq req) {
|
||||
return mApi.updateOtherInfo(OkHttpManager.convertToRequestBodyjson(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传定位信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> uploadLocation(@NonNull SnLocationReq req) {
|
||||
return mApi.uploadLocation(OkHttpManager.convertToRequestBodyjson(req));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传截图(或拍照文件)
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> uploadScreenshot(@NonNull MultipartBody.Part part) {
|
||||
return mApi.uploadScreenshot(part);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传已安装应用列表
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> uploadInstallApks(@NonNull RequestBody body) {
|
||||
return mApi.uploadInstallApks(body);
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传应用图标(全局图标库)
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> uploadAppIcon(@NonNull String packageName,
|
||||
String appName,
|
||||
@NonNull MultipartBody.Part part) {
|
||||
return mApi.uploadAppIcon(
|
||||
OkHttpManager.convertToRequestBody(packageName),
|
||||
OkHttpManager.convertToRequestBody(appName == null ? "" : appName),
|
||||
part);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,14 @@ import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.bean.req.SnNetworkInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnOtherInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSecurityInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSystemInfoReq;
|
||||
import com.ttstd.dialer.data.NetworkBoundResource;
|
||||
import com.ttstd.dialer.data.Resource;
|
||||
import com.ttstd.dialer.data.cache.CacheManager;
|
||||
@@ -13,6 +20,9 @@ import com.ttstd.dialer.data.remote.SnRemoteDataSource;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.RequestBody;
|
||||
|
||||
/**
|
||||
* 设备(SN)配置仓库(Data Layer - Repository)。
|
||||
@@ -86,4 +96,98 @@ public class SnRepository {
|
||||
}
|
||||
}.asObservable();
|
||||
}
|
||||
|
||||
// ==================== 设备上报类(写操作,fire-and-forget)====================
|
||||
|
||||
/**
|
||||
* 设备注册(sn + model)。调用方按需 subscribe。
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> register(@NonNull String sn, @NonNull String model) {
|
||||
return mRemoteDataSource.register(sn, model)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备基本信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateSystemInfo(@NonNull SnSystemInfoReq req) {
|
||||
return mRemoteDataSource.updateSystemInfo(req)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备硬件信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateHardwareInfo(@NonNull SnHardwareInfoReq req) {
|
||||
return mRemoteDataSource.updateHardwareInfo(req)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备网络信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateNetworkInfo(@NonNull SnNetworkInfoReq req) {
|
||||
return mRemoteDataSource.updateNetworkInfo(req)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备安全信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateSecurityInfo(@NonNull SnSecurityInfoReq req) {
|
||||
return mRemoteDataSource.updateSecurityInfo(req)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备其他信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> updateOtherInfo(@NonNull SnOtherInfoReq req) {
|
||||
return mRemoteDataSource.updateOtherInfo(req)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传定位信息
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse<Void>> uploadLocation(@NonNull SnLocationReq req) {
|
||||
return mRemoteDataSource.uploadLocation(req)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传截图 / 拍照文件
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> uploadScreenshot(@NonNull MultipartBody.Part part) {
|
||||
return mRemoteDataSource.uploadScreenshot(part)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传已安装应用列表(body 由调用方构建为 JSON RequestBody)
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> uploadInstallApks(@NonNull RequestBody body) {
|
||||
return mRemoteDataSource.uploadInstallApks(body)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传应用图标(全局图标库)
|
||||
*/
|
||||
@NonNull
|
||||
public Observable<BaseResponse> uploadAppIcon(@NonNull String packageName,
|
||||
String appName,
|
||||
@NonNull MultipartBody.Part part) {
|
||||
return mRemoteDataSource.uploadAppIcon(packageName, appName, part)
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,7 +203,7 @@ public class ContactInfo implements Serializable {
|
||||
public boolean equals(@Nullable Object obj) {
|
||||
if (obj instanceof ContactInfo) {
|
||||
return Objects.equals(((ContactInfo) obj).phoneNumber, phoneNumber)
|
||||
|| ((ContactInfo) obj).id == id;
|
||||
&& ((ContactInfo) obj).id == id;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -200,6 +200,8 @@ public class AppManager {
|
||||
updatePositionSync();
|
||||
}
|
||||
reorderAppPositionsSync();
|
||||
// 初始化时清理已失效的启动页
|
||||
clearInvalidSplash();
|
||||
notifyCompleted(true, "应用列表处理完成");
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "异步处理失败", e);
|
||||
@@ -490,9 +492,63 @@ public class AppManager {
|
||||
}
|
||||
}
|
||||
|
||||
// app 安装信息变化后,清理已失效的启动页
|
||||
clearInvalidSplash();
|
||||
|
||||
LiveDataBus.get().send(LiveDataAction.ACTION_UPDATE_APPS, TAG);
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 启动页(开屏页)清理
|
||||
// ============================================================
|
||||
|
||||
/**
|
||||
* 清除数据库中不存在的启动页。
|
||||
* <p>
|
||||
* 遍历数据库中的所有 AppInfo(每条记录对应一个启动组件 ComponentName),
|
||||
* 通过 PackageManager 校验组件是否仍可解析。当应用被卸载、更新导致 Activity
|
||||
* 类名变化或被禁用时,对应的 ComponentName 将失效,此时从数据库删除该记录。
|
||||
* <p>
|
||||
* 在每次初始化以及 app 安装信息变化(updateApp)时调用。
|
||||
*
|
||||
* @return 本次删除的失效启动页数量
|
||||
*/
|
||||
public int clearInvalidSplash() {
|
||||
if (mAppRepository == null) return 0;
|
||||
try {
|
||||
List<AppInfo> allApps = mAppRepository.getAllApp();
|
||||
if (allApps == null || allApps.isEmpty()) return 0;
|
||||
|
||||
List<AppInfo> toDelete = new ArrayList<>();
|
||||
for (AppInfo app : allApps) {
|
||||
if (app == null) continue;
|
||||
ComponentName componentName = app.getComponentName();
|
||||
// 组件为空、或组件已不存在(Activity 被卸载/重命名/禁用)则标记为删除
|
||||
if (componentName == null || !ApkUtils.isComponentExist(mContext, componentName)) {
|
||||
toDelete.add(app);
|
||||
}
|
||||
}
|
||||
|
||||
if (toDelete.isEmpty()) return 0;
|
||||
|
||||
for (AppInfo app : toDelete) {
|
||||
try {
|
||||
mAppRepository.delete(app);
|
||||
Logger.i(TAG, "清除失效启动页: " + (app.getComponentName() != null
|
||||
? app.getComponentName().flattenToShortString() : "null"));
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "删除失效启动页失败: " + app.getPackageName(), e);
|
||||
}
|
||||
}
|
||||
Logger.i(TAG, "共清除 " + toDelete.size() + " 条失效启动页");
|
||||
LiveDataBus.get().send(LiveDataAction.ACTION_UPDATE_APPS, TAG);
|
||||
return toDelete.size();
|
||||
} catch (Exception e) {
|
||||
Logger.e(TAG, "清除失效启动页异常", e);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 重构getAllApp方法,复用现有处理逻辑
|
||||
/**
|
||||
@@ -831,6 +887,8 @@ public class AppManager {
|
||||
@Override
|
||||
public void run() {
|
||||
manager.reorderAppPositionsSync();
|
||||
// 初始化时清理已失效的启动页
|
||||
manager.clearInvalidSplash();
|
||||
manager.notifyCompleted(true, "应用列表处理完成");
|
||||
}
|
||||
}).exceptionally(new java.util.function.Function<Throwable, Void>() {
|
||||
|
||||
@@ -3,13 +3,10 @@ package com.ttstd.dialer.manager;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.data.Resource;
|
||||
import com.ttstd.dialer.data.repository.ContactDataRepository;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.db.contact.ContactRepository;
|
||||
import com.ttstd.dialer.network.BaseObserver;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -25,7 +22,6 @@ import io.reactivex.rxjava3.core.ObservableOnSubscribe;
|
||||
import io.reactivex.rxjava3.core.Observer;
|
||||
import io.reactivex.rxjava3.disposables.Disposable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
|
||||
public class ContactManager {
|
||||
private static final String TAG = "ContactManager";
|
||||
@@ -57,84 +53,51 @@ public class ContactManager {
|
||||
|
||||
/**
|
||||
* 获取联系人列表(优先从网络获取,失败则从本地数据库获取)
|
||||
* <p>
|
||||
* 统一走 {@link ContactDataRepository} 的多级缓存 + SWR:命中缓存秒出,
|
||||
* 网络失败自动降级 Room 本地数据。旧逻辑在 Manager 层直连 OkHttpManager,
|
||||
* 现收敛到 Data Layer(Repository),Manager 仅做回调转发。
|
||||
*
|
||||
* @param lifecycle RxLifecycle 生命周期绑定
|
||||
* @param callback 回调接口
|
||||
* @param callback 回调接口
|
||||
*/
|
||||
public void getContacts(BehaviorSubject<ActivityEvent> lifecycle, ContactCallback callback) {
|
||||
public void getContacts(ContactCallback callback) {
|
||||
Logger.e(TAG, "开始获取联系人列表");
|
||||
|
||||
OkHttpManager.getInstance().getContactListObservable(lifecycle)
|
||||
.compose(RxLifecycle.bindUntilEvent(lifecycle, ActivityEvent.DESTROY))
|
||||
.subscribe(new BaseObserver<BaseResponse<List<ContactInfo>>>() {
|
||||
ContactDataRepository.getInstance(mContext).getContactList(false)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<Resource<List<ContactInfo>>>() {
|
||||
@Override
|
||||
public void onSuccess(BaseResponse<List<ContactInfo>> listBaseResponse) {
|
||||
Log.e(TAG, "网络请求成功: " + listBaseResponse);
|
||||
if (listBaseResponse.isSuccess()) {
|
||||
List<ContactInfo> networkContacts = listBaseResponse.getData();
|
||||
if (networkContacts != null && !networkContacts.isEmpty()) {
|
||||
// 在子线程中处理数据库同步
|
||||
Observable.create(new ObservableOnSubscribe<List<ContactInfo>>() {
|
||||
@Override
|
||||
public void subscribe(@NonNull ObservableEmitter<List<ContactInfo>> emitter) throws Throwable {
|
||||
try {
|
||||
// 同步网络和本地数据
|
||||
syncContacts(networkContacts);
|
||||
// 返回排序后的联系人列表
|
||||
List<ContactInfo> sortedContacts = networkContacts.stream()
|
||||
.sorted((c1, c2) -> Integer.compare(c1.getPosition(), c2.getPosition()))
|
||||
.collect(Collectors.toList());
|
||||
emitter.onNext(sortedContacts);
|
||||
emitter.onComplete();
|
||||
} catch (Exception e) {
|
||||
emitter.onError(e);
|
||||
}
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Observer<List<ContactInfo>>() {
|
||||
@Override
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Logger.e(TAG, "开始处理联系人数据");
|
||||
}
|
||||
public void onSubscribe(@NonNull Disposable d) {
|
||||
Logger.e(TAG, "开始获取联系人");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNext(@NonNull List<ContactInfo> contactInfos) {
|
||||
Logger.e(TAG, "联系人数据处理完成,数量: " + contactInfos.size());
|
||||
if (callback != null) {
|
||||
callback.onSuccess(contactInfos);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Logger.e(TAG, "处理联系人数据失败: " + e.getMessage());
|
||||
if (callback != null) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Logger.e(TAG, "联系人数据处理完成");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 网络返回空数据,从本地获取
|
||||
getLocalContacts(callback);
|
||||
@Override
|
||||
public void onNext(@NonNull Resource<List<ContactInfo>> resource) {
|
||||
if (resource.data != null) {
|
||||
Logger.e(TAG, "联系人数据获取成功,数量: " + resource.data.size());
|
||||
if (callback != null) {
|
||||
callback.onSuccess(resource.data);
|
||||
}
|
||||
} else if (resource.isError()) {
|
||||
Logger.e(TAG, "联系人获取失败: " + resource.message);
|
||||
if (callback != null) {
|
||||
callback.onFailure(new Exception(resource.message));
|
||||
}
|
||||
} else {
|
||||
// 网络请求业务失败,从本地获取
|
||||
getLocalContacts(callback);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Log.e(TAG, "网络请求失败: " + e.getMessage());
|
||||
// 网络请求失败,从本地获取
|
||||
getLocalContacts(callback);
|
||||
public void onError(@NonNull Throwable e) {
|
||||
Logger.e(TAG, "联系人获取异常: " + e.getMessage());
|
||||
if (callback != null) {
|
||||
callback.onFailure(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Logger.e(TAG, "联系人获取完成");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@ package com.ttstd.dialer.manager;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.ttstd.dialer.data.repository.ContactDataRepository;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.db.contact.ContactRepository;
|
||||
import com.ttstd.dialer.db.contact.SyncStatus;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
|
||||
import java.util.List;
|
||||
@@ -19,11 +19,13 @@ public class ContactSyncManager {
|
||||
private static final String TAG = "ContactSyncManager";
|
||||
private static volatile ContactSyncManager INSTANCE;
|
||||
|
||||
private ContactRepository mRepository;
|
||||
private CompositeDisposable mDisposable;
|
||||
private final ContactRepository mRepository;
|
||||
private final ContactDataRepository mDataRepository;
|
||||
private final CompositeDisposable mDisposable;
|
||||
|
||||
private ContactSyncManager(Context context) {
|
||||
mRepository = new ContactRepository(context);
|
||||
mDataRepository = ContactDataRepository.getInstance(context);
|
||||
mDisposable = new CompositeDisposable();
|
||||
}
|
||||
|
||||
@@ -39,7 +41,8 @@ public class ContactSyncManager {
|
||||
}
|
||||
|
||||
public void syncPendingContacts() {
|
||||
Observable.fromCallable(() -> {
|
||||
// 订阅结果加入 CompositeDisposable,dispose() 时可统一取消
|
||||
mDisposable.add(Observable.fromCallable(() -> {
|
||||
List<ContactInfo> pendingContacts = mRepository.getUnsyncedContacts();
|
||||
Logger.d(TAG, "查询到 " + pendingContacts.size() + " 个待同步联系人");
|
||||
return pendingContacts;
|
||||
@@ -53,21 +56,22 @@ public class ContactSyncManager {
|
||||
.subscribe(
|
||||
result -> Logger.d(TAG, "同步完成: " + result),
|
||||
throwable -> Logger.e(TAG, "同步流程异常: " + throwable.getMessage())
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
private Observable<String> syncSingleContactObservable(ContactInfo contactInfo) {
|
||||
return OkHttpManager.getInstance().getContactInsertObservable(contactInfo)
|
||||
// 统一走 Data Layer(Repository → RemoteDataSource),不再直连 OkHttpManager
|
||||
return mDataRepository.addContact(contactInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(baseResponse -> {
|
||||
if (baseResponse.isSuccess()) {
|
||||
Long onlineId = baseResponse.getData();
|
||||
return saveSuccessSyncStatus(contactInfo.getId(), onlineId)
|
||||
.flatMap(result -> {
|
||||
if (result.online) {
|
||||
// 服务端插入成功,更新同步状态并记录在线 id
|
||||
return saveSuccessSyncStatus(contactInfo.getId(), result.id)
|
||||
.map(v -> "成功: " + contactInfo.getName());
|
||||
} else {
|
||||
return saveFailedSyncStatus(contactInfo.getId())
|
||||
.flatMap(v -> Observable.error(new Exception("业务失败: " + baseResponse.getMsg())));
|
||||
}
|
||||
// 网络不可用降级到本地保存,标记为失败待后续重试
|
||||
return saveFailedSyncStatus(contactInfo.getId())
|
||||
.flatMap(v -> Observable.error(new Exception("离线降级保存: " + contactInfo.getName())));
|
||||
})
|
||||
.onErrorResumeNext(throwable -> {
|
||||
return saveFailedSyncStatus(contactInfo.getId())
|
||||
|
||||
@@ -1,26 +1,11 @@
|
||||
package com.ttstd.dialer.network;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.DeveloperOptions;
|
||||
import com.ttstd.dialer.bean.req.SnHardwareInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.bean.req.SnNetworkInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnOtherInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSecurityInfoReq;
|
||||
import com.ttstd.dialer.bean.req.SnSystemInfoReq;
|
||||
import com.ttstd.dialer.db.contact.ContactInfo;
|
||||
import com.ttstd.dialer.mdm.DeviceManagerService;
|
||||
import com.ttstd.dialer.network.api.ContactApi;
|
||||
import com.ttstd.dialer.network.api.SnApi;
|
||||
import com.ttstd.dialer.network.interceptor.AuthInterceptor;
|
||||
import com.ttstd.dialer.utils.Logger;
|
||||
import com.ttstd.iconloader.utils.FileUtils;
|
||||
@@ -28,13 +13,8 @@ import com.ttstd.iconloader.utils.FileUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.rxjava3.core.Observable;
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers;
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
import okhttp3.Cache;
|
||||
import okhttp3.ConnectionPool;
|
||||
import okhttp3.Dispatcher;
|
||||
@@ -50,6 +30,12 @@ import retrofit2.converter.gson.GsonConverterFactory;
|
||||
/**
|
||||
* OkHttp + Retrofit 管理类
|
||||
* 单例模式 + 避免内存泄漏 + 增强异常处理 + 提升扩展性
|
||||
*
|
||||
* <p>职责收敛为「网络基础设施」:
|
||||
* - 创建并持有 OkHttpClient / Retrofit
|
||||
* - 通过 {@link #getApiService(Class)} 提供 Retrofit 接口实例
|
||||
* - 提供 RequestBody / MultipartBody 转换工具
|
||||
* <p>线程调度由 Data Layer(Repository / RemoteDataSource)统一负责,不再在此层处理。
|
||||
*/
|
||||
public class OkHttpManager {
|
||||
private static final String TAG = "OkHttpManager";
|
||||
@@ -121,8 +107,8 @@ public class OkHttpManager {
|
||||
|
||||
// 1. 构建调度器(控制并发)
|
||||
Dispatcher dispatcher = new Dispatcher();
|
||||
dispatcher.setMaxRequests(1); // 全局最大并发
|
||||
dispatcher.setMaxRequestsPerHost(1); // 单Host最大并发
|
||||
// dispatcher.setMaxRequests(1); // 全局最大并发
|
||||
// dispatcher.setMaxRequestsPerHost(1); // 单Host最大并发
|
||||
|
||||
// 2. 构建缓存(增强异常处理)
|
||||
Cache cache = createCache();
|
||||
@@ -179,8 +165,6 @@ public class OkHttpManager {
|
||||
}
|
||||
|
||||
Cache cache = new Cache(cacheDir, CACHE_SIZE_BYTES);
|
||||
// 验证缓存是否可用(避免创建了但不可用的情况)
|
||||
// cache.getCacheDirectory().getFreeSpace();
|
||||
return cache;
|
||||
} catch (SecurityException e) {
|
||||
Log.e(TAG, "No permission to create cache directory", e);
|
||||
@@ -248,6 +232,13 @@ public class OkHttpManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 Retrofit API 接口实例(Data Layer 通过它创建 RemoteDataSource)
|
||||
*/
|
||||
public <T> T getApiService(Class<T> tClass) {
|
||||
return mRetrofit.create(tClass);
|
||||
}
|
||||
|
||||
public static MultipartBody.Part getBodyPart(String type, String name, File file) {
|
||||
MediaType mediaType = MediaType.Companion.parse(type);
|
||||
RequestBody fileBody = RequestBody.Companion.create(file, mediaType);
|
||||
@@ -284,148 +275,4 @@ public class OkHttpManager {
|
||||
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), String.valueOf(param));
|
||||
return requestBody;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 统一线程调度 + 生命周期管理
|
||||
*
|
||||
* @param observable 接口Observable
|
||||
* @param provider 生命周期管理者(Activity/Fragment)
|
||||
* @return 处理后的Observable
|
||||
*/
|
||||
public <T> Observable<T> schedule(Observable<T> observable, BehaviorSubject<ActivityEvent> provider) {
|
||||
return observable
|
||||
// 子线程执行请求
|
||||
.subscribeOn(Schedulers.io())
|
||||
// 主线程回调
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
// 生命周期管理:页面销毁自动取消请求,防止内存泄漏
|
||||
.compose(RxLifecycle.bindUntilEvent(provider, ActivityEvent.DESTROY));
|
||||
}
|
||||
|
||||
public <T> T getApiService(Class<T> tClass) {
|
||||
return mRetrofit.create(tClass);
|
||||
}
|
||||
|
||||
public <T> Observable<T> schedule(Observable<T> observable) {
|
||||
return observable
|
||||
// 子线程执行请求
|
||||
.subscribeOn(Schedulers.io())
|
||||
// 主线程回调
|
||||
.observeOn(AndroidSchedulers.mainThread());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getSnRegisterObservable(BehaviorSubject<ActivityEvent> provider) {
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备基本信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateSystemInfoObservable(SnSystemInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateSystemInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备硬件信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateHardwareInfoObservable(SnHardwareInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateHardwareInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备网络信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateNetworkInfoObservable(SnNetworkInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateNetworkInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备安全信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateSecurityInfoObservable(SnSecurityInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateSecurityInfo(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传设备其他信息
|
||||
*/
|
||||
public Observable<BaseResponse<Void>> getUpdateOtherInfoObservable(SnOtherInfoReq req) {
|
||||
RequestBody body = convertToRequestBodyjson(req);
|
||||
return schedule(getApiService(SnApi.class).updateOtherInfo(body));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadScreenshotObservable(MultipartBody.Part part) {
|
||||
return schedule(getApiService(SnApi.class).uploadScreenshot(part));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadLocationObservable(SnLocationReq locationReq, BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(SnApi.class).uploadLocation(convertToRequestBodyjson(locationReq)), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadLocationObservable(SnLocationReq locationReq) {
|
||||
return schedule(getApiService(SnApi.class).uploadLocation(convertToRequestBodyjson(locationReq)));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<DeveloperOptions>> getDeveloperOptionsObservable(BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(SnApi.class).getDeveloperOptions(), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<DeveloperOptions>> getDeveloperOptionsObservable() {
|
||||
return schedule(getApiService(SnApi.class).getDeveloperOptions());
|
||||
}
|
||||
|
||||
public Observable<BaseResponse> getUploadInstallApksObservable(RequestBody body) {
|
||||
return schedule(getApiService(SnApi.class).uploadInstallApks(body));
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传应用图标(全局图标库,packageName/appName 作为 form 字段,file 作为文件 part)
|
||||
* <p>
|
||||
* 注意:Retrofit 的 @Part 不允许传 null,appName 为空时统一转为空串,
|
||||
* 由服务端按「未上报」处理(沿用已有名称)。
|
||||
*/
|
||||
public Observable<BaseResponse> getUploadAppIconObservable(String packageName, String appName, MultipartBody.Part part) {
|
||||
MediaType textPlain = MediaType.Companion.parse("text/plain");
|
||||
RequestBody packageNameBody = RequestBody.Companion.create(packageName, textPlain);
|
||||
RequestBody appNameBody = RequestBody.Companion.create(appName == null ? "" : appName, textPlain);
|
||||
return schedule(getApiService(SnApi.class).uploadAppIcon(packageNameBody, appNameBody, part));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<List<ContactInfo>>> getContactListObservable(BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(ContactApi.class).getContactList(), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<Long>> getContactInsertObservable(ContactInfo contactInfo, BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(ContactApi.class).insertContact(convertToRequestBodyjson(contactInfo)), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<Long>> getContactInsertObservable(ContactInfo contactInfo) {
|
||||
return schedule(getApiService(ContactApi.class).insertContact(convertToRequestBodyjson(contactInfo)));
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<ContactInfo>> getContactFormObservable(long id, BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(ContactApi.class).getContactForm(id), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<Void>> getContactUpdateObservable(long id, ContactInfo contactInfo, BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(ContactApi.class).updateContact(id, convertToRequestBodyjson(contactInfo)), provider);
|
||||
}
|
||||
|
||||
public Observable<BaseResponse<Void>> getContactDeleteObservable(long id, BehaviorSubject<ActivityEvent> provider) {
|
||||
return schedule(getApiService(ContactApi.class).deleteContact(id), provider);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ public interface SnApi {
|
||||
);
|
||||
|
||||
@POST(UrlConstants.UPLOAD_LOCATION)
|
||||
Observable<BaseResponse> uploadLocation(
|
||||
Observable<BaseResponse<Void>> uploadLocation(
|
||||
@Body RequestBody body
|
||||
);
|
||||
|
||||
|
||||
@@ -87,12 +87,16 @@ public class PushExecutor {
|
||||
*/
|
||||
private List<ApkInstalledInfo> mApkInstalledInfos;
|
||||
|
||||
private SnRepository mSnRepository;
|
||||
|
||||
private PushExecutor(Context context) {
|
||||
if (context == null) {
|
||||
throw new RuntimeException("Context is NULL");
|
||||
}
|
||||
this.mContext = context;
|
||||
this.mPackageManager = mContext.getPackageManager();
|
||||
// 数据访问统一走 Data Layer(Repository),避免直连 OkHttpManager
|
||||
this.mSnRepository = SnRepository.getInstance(mContext);
|
||||
}
|
||||
|
||||
public static void init(Context context) {
|
||||
@@ -203,7 +207,7 @@ public class PushExecutor {
|
||||
public Observable<BaseResponse> apply(List<ApkInstalledInfo> apkInstalledInfos) throws Throwable {
|
||||
mApkInstalledInfos = apkInstalledInfos;
|
||||
RequestBody body = OkHttpManager.convertToRequestBodyjson(apkInstalledInfos);
|
||||
return OkHttpManager.getInstance().getUploadInstallApksObservable(body);
|
||||
return mSnRepository.uploadInstallApks(body);
|
||||
}
|
||||
})
|
||||
.subscribeOn(Schedulers.io())
|
||||
@@ -279,8 +283,7 @@ public class PushExecutor {
|
||||
return Observable.empty();
|
||||
}
|
||||
MultipartBody.Part part = OkHttpManager.getBodyPart("image/png", "file", iconFile);
|
||||
return OkHttpManager.getInstance()
|
||||
.getUploadAppIconObservable(packageName, apkInfo.getAppName(), part)
|
||||
return mSnRepository.uploadAppIcon(packageName, apkInfo.getAppName(), part)
|
||||
// getUploadAppIconObservable 内部已 observeOn(mainThread),
|
||||
// 这里必须切回 IO 线程,否则下面的 Room 写入会发生在主线程
|
||||
.observeOn(Schedulers.io())
|
||||
@@ -323,33 +326,33 @@ public class PushExecutor {
|
||||
*/
|
||||
private void uploadAllDeviceInfo() {
|
||||
DeviceInfoCollector collector = new DeviceInfoCollector(mContext);
|
||||
OkHttpManager http = OkHttpManager.getInstance();
|
||||
|
||||
// defer + fromCallable 保证采集动作发生在订阅时的 IO 线程,而非当前线程
|
||||
// 上报统一走 Data Layer(SnRepository → SnRemoteDataSource),不再直连 OkHttpManager
|
||||
Observable<BaseResponse<Void>> system = Observable
|
||||
.fromCallable(collector::collectSystemInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateSystemInfoObservable);
|
||||
.flatMap(mSnRepository::updateSystemInfo);
|
||||
|
||||
Observable<BaseResponse<Void>> hardware = Observable
|
||||
.fromCallable(collector::collectHardwareInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateHardwareInfoObservable);
|
||||
.flatMap(mSnRepository::updateHardwareInfo);
|
||||
|
||||
Observable<BaseResponse<Void>> network = Observable
|
||||
.fromCallable(collector::collectNetworkInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateNetworkInfoObservable);
|
||||
.flatMap(mSnRepository::updateNetworkInfo);
|
||||
|
||||
Observable<BaseResponse<Void>> security = Observable
|
||||
.fromCallable(collector::collectSecurityInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateSecurityInfoObservable);
|
||||
.flatMap(mSnRepository::updateSecurityInfo);
|
||||
|
||||
Observable<BaseResponse<Void>> other = Observable
|
||||
.fromCallable(collector::collectOtherInfo)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.flatMap(http::getUpdateOtherInfoObservable);
|
||||
.flatMap(mSnRepository::updateOtherInfo);
|
||||
|
||||
Observable.mergeDelayError(
|
||||
Observable.just(system, hardware, network, security, other))
|
||||
@@ -395,7 +398,7 @@ public class PushExecutor {
|
||||
if (integer == 0) {
|
||||
// 截图成功,上传文件
|
||||
MultipartBody.Part part = OkHttpManager.getBodyPart("image/png", "file", new File(filepath));
|
||||
return OkHttpManager.getInstance().getUploadScreenshotObservable(part)
|
||||
return mSnRepository.uploadScreenshot(part)
|
||||
.subscribeOn(Schedulers.io()); // 确保上传操作在子线程执行
|
||||
} else {
|
||||
// 截图失败,返回一个空的Observable
|
||||
@@ -436,7 +439,7 @@ public class PushExecutor {
|
||||
.flatMap(integer -> {
|
||||
if (integer == 0) {
|
||||
MultipartBody.Part part = OkHttpManager.getBodyPart("image/jpeg", "file", file);
|
||||
return OkHttpManager.getInstance().getUploadScreenshotObservable(part)
|
||||
return mSnRepository.uploadScreenshot(part)
|
||||
.subscribeOn(Schedulers.io());
|
||||
} else {
|
||||
return Observable.empty();
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.ttstd.dialer.activity.main.MainActivity;
|
||||
import com.ttstd.dialer.base.BaseService;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.config.CommonConfig;
|
||||
import com.ttstd.dialer.data.repository.SnRepository;
|
||||
import com.ttstd.dialer.manager.ContactSyncManager;
|
||||
import com.ttstd.dialer.manager.MqttManager;
|
||||
import com.ttstd.dialer.utils.ApkUtils;
|
||||
@@ -69,6 +70,7 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
super.onBind(intent);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -81,8 +83,8 @@ public class MainService extends BaseService implements NetworkUtils.OnNetworkSt
|
||||
|
||||
// 初始化ViewModel(绑定上下文+View回调)
|
||||
mViewModel = new MainServiceModel();
|
||||
// 绑定Service生命周期(关键:让请求跟随生命周期)
|
||||
mViewModel.setLifecycleSubject(getLifecycleSubject());
|
||||
// 注入 Repository(数据访问统一走 Data 层,避免 ViewModel 直接触网)
|
||||
mViewModel.setSnRepository(SnRepository.getInstance(this));
|
||||
|
||||
LiveEventBus.get(CommonConfig.LOCATION_CHANGED_KEY, SnLocationReq.class)
|
||||
.observeSticky(this, new Observer<SnLocationReq>() {
|
||||
|
||||
@@ -1,47 +1,36 @@
|
||||
package com.ttstd.dialer.service.main;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.trello.rxlifecycle4.LifecycleTransformer;
|
||||
import com.trello.rxlifecycle4.RxLifecycle;
|
||||
import com.trello.rxlifecycle4.android.ActivityEvent;
|
||||
import com.ttstd.dialer.bean.BaseResponse;
|
||||
import com.ttstd.dialer.bean.req.SnLocationReq;
|
||||
import com.ttstd.dialer.data.repository.SnRepository;
|
||||
import com.ttstd.dialer.gson.GsonUtils;
|
||||
import com.ttstd.dialer.network.BaseObserver;
|
||||
import com.ttstd.dialer.network.OkHttpManager;
|
||||
|
||||
import io.reactivex.rxjava3.subjects.BehaviorSubject;
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable;
|
||||
import io.reactivex.rxjava3.observers.DisposableObserver;
|
||||
|
||||
public class MainServiceModel extends ViewModel {
|
||||
private static final String TAG = "MainServiceModel";
|
||||
|
||||
private BehaviorSubject<ActivityEvent> lifecycleSubject;
|
||||
private final CompositeDisposable mDisposables = new CompositeDisposable();
|
||||
private SnRepository mSnRepository;
|
||||
|
||||
// 设置Service生命周期Subject
|
||||
public void setLifecycleSubject(BehaviorSubject<ActivityEvent> lifecycleSubject) {
|
||||
this.lifecycleSubject = lifecycleSubject;
|
||||
}
|
||||
|
||||
// 绑定请求到Service销毁事件(自动取消请求)
|
||||
private <T> LifecycleTransformer<T> bindToLifecycle() {
|
||||
if (lifecycleSubject == null) {
|
||||
throw new IllegalStateException("请先设置LifecycleSubject!");
|
||||
}
|
||||
return RxLifecycle.bindUntilEvent(lifecycleSubject, ActivityEvent.DESTROY);
|
||||
public void setSnRepository(SnRepository snRepository) {
|
||||
this.mSnRepository = snRepository;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCleared() {
|
||||
// ViewModel 销毁(Service 停止)时自动取消进行中的上报,避免内存泄漏
|
||||
if (!mDisposables.isDisposed()) {
|
||||
mDisposables.clear();
|
||||
}
|
||||
super.onCleared();
|
||||
// 清空生命周期引用,防止内存泄漏
|
||||
this.lifecycleSubject = null;
|
||||
}
|
||||
|
||||
|
||||
public void uploadLocation(SnLocationReq locationReq) {
|
||||
// 空校验:位置信息缺失时不发起请求,避免后端收到无效数据
|
||||
if (locationReq == null) {
|
||||
@@ -49,19 +38,26 @@ public class MainServiceModel extends ViewModel {
|
||||
return;
|
||||
}
|
||||
Log.e(TAG, "uploadLocation: " + GsonUtils.toJSONString(locationReq));
|
||||
OkHttpManager.getInstance().getUploadLocationObservable(locationReq)
|
||||
.compose(bindToLifecycle())
|
||||
.subscribe(new BaseObserver<BaseResponse>() {
|
||||
if (mSnRepository == null) {
|
||||
Log.e(TAG, "uploadLocation: SnRepository 未注入,跳过");
|
||||
return;
|
||||
}
|
||||
mDisposables.add(mSnRepository.uploadLocation(locationReq)
|
||||
.subscribeWith(new DisposableObserver<BaseResponse<Void>>() {
|
||||
@Override
|
||||
public void onSuccess(BaseResponse baseResponse) {
|
||||
Log.e("uploadLocation", "onSuccess: " + baseResponse);
|
||||
public void onNext(BaseResponse<Void> baseResponse) {
|
||||
Log.e("uploadLocation", "onNext: " + baseResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable e) {
|
||||
Log.e("uploadLocation", "onFailure: " + e.getMessage());
|
||||
public void onError(Throwable e) {
|
||||
Log.e("uploadLocation", "onError: " + e.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void onComplete() {
|
||||
Log.e("uploadLocation", "onComplete");
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
|
||||
import com.kongzue.dialogx.dialogs.PopTip;
|
||||
@@ -37,6 +38,24 @@ public class ApkUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断指定组件(Activity)是否仍然存在且可被解析。
|
||||
* 应用卸载、更新导致 Activity 类名变化或被禁用时,均会返回 false。
|
||||
*
|
||||
* @param context 上下文
|
||||
* @param componentName 待校验的组件
|
||||
* @return true 表示组件仍存在
|
||||
*/
|
||||
public static boolean isComponentExist(Context context, @Nullable ComponentName componentName) {
|
||||
if (context == null || componentName == null) return false;
|
||||
try {
|
||||
ActivityInfo info = context.getPackageManager().getActivityInfo(componentName, 0);
|
||||
return info != null;
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否为系统应用
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user