通用Bean
data class WxData(var `data`: List<Data>,var errorCode: Int,var errorMsg: String)data class Data(var children: List<Any>,var courseId: Int,var id: Int,var name: String,var order: Int,var parentChapterId: Int,var userControlSetTop: Boolean,var visible: Int)
Retrofit + Gson
导入最新版
//retrofitapi 'com.squareup.retrofit2:retrofit:2.9.0'//retrofit - gsonapi 'com.squareup.retrofit2:converter-gson:2.9.0'
Api ``` interface GsonApi {
companion object{
val BASE_URL = "https://www.wanandroid.com/"fun create(): GsonApi {return Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build().create(GsonApi::class.java)}
}
@GET(“wxarticle/chapters/json”) fun getWxData(): Call
}
- 使用
private fun testGson() {
GsonApi.create().getWxData().enqueue(object : Callback
override fun onFailure(call: Call<WxData>, t: Throwable) {Log.e(TAG, "onFailure: $t")}})
}
<a name="63eedece"></a>### Retrofit + Gson + Rxjava2 + rxlife[RxLife地址](https://github.com/liujingxing/rxlife)<br />导包:
//retrofit api ‘com.squareup.retrofit2:retrofit:2.9.0’ //retrofit - gson api ‘com.squareup.retrofit2:converter-gson:2.9.0’ //rxjava 相关 api ‘com.squareup.retrofit2:adapter-rxjava2:2.9.0’ //rxjava2 api ‘io.reactivex.rxjava2:rxandroid:2.1.1’ api “io.reactivex.rxjava2:rxjava:2.2.6” //rxlife api ‘com.rxjava.rxlife:rxlife:1.0.8’ //cookie implementation ‘com.github.franmontiel:PersistentCookieJar:v1.0.1’ implementation ‘com.squareup.okhttp3:logging-interceptor:3.8.1’
- BaseBean2```kotlinpublic class BaseBean2<T> {/*** result : 1* message : 请求参数为空或不合法!*/private int result;// 0成功,其他失败private int errorCode;private String message;private T data;public boolean isSuccess() {return result == 0;}public T getData() {return data;}public void setData(T data) {this.data = data;}public int getResult() {return result;}public void setResult(int result) {this.result = result;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public int getErrorCode() {return errorCode;}public void setErrorCode(int errorCode) {this.errorCode = errorCode;}}
RetrofitHelp ``` public class RetrofitHelp {
private Retrofit retrofit; private String mBaseUrl = HttpConfig.sBaseUrl;
private RetrofitHelp() {
initRetrofit();
}
private void initRetrofit() {
// 指定缓存路径,缓存大小 50MbCache cache = new Cache(new File(HttpConfig.sContext.getCacheDir(), "HttpCache"),1024 * 1024 * 50);// Cookie 持久化ClearableCookieJar cookieJar =new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(HttpConfig.sContext));OkHttpClient.Builder builder = new OkHttpClient.Builder().cookieJar(cookieJar)//.sslSocketFactory(SSLHelper.getSSLCertifcation(context)).cache(cache)
// .addInterceptor(cacheControlInterceptor)//有网策略
//.addNetworkInterceptor(cacheControlInterceptor)//无网策略.connectTimeout(60, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).writeTimeout(30, TimeUnit.SECONDS)// .addNetworkInterceptor(new StethoInterceptor()).retryOnConnectionFailure(true);if (BuildConfig.DEBUG) {SdkManager.initInterceptor(builder);}Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ").serializeNulls().registerTypeAdapter(String.class, new StringConverter())//对为null的字段进行转换.create();retrofit = new Retrofit.Builder().baseUrl(mBaseUrl).client(builder.build()).addConverterFactory(GsonConverterFactory.create(gson))
// .addConverterFactory(GsonDConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()).build();
}
/**
实现了序列化接口 对为null的字段进行转换 */ public static class StringConverter implements JsonSerializer
, JsonDeserializer { //字符串为null 转换成””,否则为字符串类型 @Override public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { return json.getAsJsonPrimitive().getAsString();
}
@Override public JsonElement serialize(String src, Type typeOfSrc, JsonSerializationContext context) {
return src == null || src.equals("null") ? new JsonPrimitive("") : new JsonPrimitive(src);
} }
private static class SingletonInstance { private static final RetrofitHelp INSTANCE = new RetrofitHelp(); }
public static RetrofitHelp getInstance() { return SingletonInstance.INSTANCE; }
public <T> T create(Class<T> service) {if (retrofit == null){mBaseUrl = HttpConfig.sBaseUrl;initRetrofit();}return retrofit.create(service);}
// public
}
- RxSubUtils
public abstract class RxSubUtils
* 弱引用*/private WeakReference<Context> mContext;//private Context mContext;private String msg;//提示内容private boolean isShow;
// private LoadingDialog loadingDialog;
// public RxSubUtils(CompositeDisposable mCompositeSubscription) { // this.compositeDisposable = mCompositeSubscription; // }
public RxSubUtils(){}//无参数,一般不用提示对话框public RxSubUtils(Context context, String hint) {//自定义提示对话框内容this(context, hint, true);}public RxSubUtils(Context context) {//默认对话框内容//mContext=context;//this.mContext=new WeakReference<Context>(context);this(context, "初始化...");}public RxSubUtils(Context context, boolean isShow) {this(context, "初始化...", isShow);}public RxSubUtils(Context context, String hint, boolean isShow) {this.mContext = new WeakReference<Context>(context);this.msg = hint;this.isShow = isShow;}/*** 这个一定要有 Presenter的逻辑在这里处理** @param t*/@Overridepublic void onNext(T t) {_onNext(t);}@Overridepublic void onError(Throwable e) {e.printStackTrace();Log.e(TAG, "onError: " + e.toString());if (isShow) {hintDialog();}//判断e内容String message = "";int result = 0;if (e instanceof ServerException){//后台返回错误相关result = ((ServerException) e).getResult();}else {//其他错误原因result = RESULT_ERROR;}message = e.getMessage();Log.e(TAG, "onError: reuslt = " + result + "msg = " + message);_onError(e,result, message);}@Overridepublic void onComplete() {
// if (compositeDisposable != null) // compositeDisposable.clear(); // Log.e(TAG, “onComplete: “ ); //LoadingDialogManager.getLoadingDialog().hideDialog(); if (isShow) { hintDialog(); } }
private void hintDialog() {Context context = mContext.get();if (context == null) {return;}
// if (loadingDialog != null) { // loadingDialog.close(); // } }
@Overridepublic void onStart() {super.onStart();/*if (mContext != null) {LoadingDialogManager.getLoadingDialog().showDialog(mContext);}*///获取contextif (isShow) {initDialog();}}private void initDialog() {Context context = mContext.get();if (context == null) {return;}
// loadingDialog = new LoadingDialog(context, false); // loadingDialog.setLoadingText(msg) // .setInterceptBack(true) // .setLoadStyle(LoadingDialog.STYLE_LINE) // .show(); }
protected abstract void _onNext(T t);protected abstract void _onError(Throwable e, int result, String msg);
// /* // 错误处理,需要的话重写这个方法 // */ // protected void _onError(String err, int status) { // if (!TextUtils.isEmpty(err)) { //// ToastUtils.showShort(err); // } // }
}
- RxUtils
public class RxUtils { private static FlowableTransformer ioToMainThreadSchedulerTransformer; private static FlowableTransformer newThreadToMainThreadSchedulerTransformer;
static {ioToMainThreadSchedulerTransformer = createIOToMainThreadScheduler();newThreadToMainThreadSchedulerTransformer = createNewThreadToMainThreadScheduler();}private static <T> FlowableTransformer<T, T> createIOToMainThreadScheduler() {return tObservable -> tObservable.subscribeOn(Schedulers.io()).unsubscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread());}/*** 从IO线程切换到主线程** @param <T>* @return*/public static <T> FlowableTransformer<T, T> applyIOToMainThreadSchedulers() {return ioToMainThreadSchedulerTransformer;}private static <T> FlowableTransformer<T, T> createNewThreadToMainThreadScheduler() {return tObservable -> tObservable.subscribeOn(Schedulers.newThread()).unsubscribeOn(Schedulers.computation()).unsubscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());}public static <T> FlowableTransformer<T, T> applyNewThreadToMainThreadSchedulers() {return newThreadToMainThreadSchedulerTransformer;}/*** io -----> main* @param <T>* @return*/public static <T> FlowableTransformer<T, T> ioToMain() {return upstream -> upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());}/*** 简单处理一下数据** @param <T>* @return*/public static <T> FlowableTransformer<BaseBean2<T>, T> handleBaseBean() {return upstream -> upstream.flatMap(new Function<BaseBean2<T>, Publisher<T>>() {@Overridepublic Publisher<T> apply(@io.reactivex.annotations.NonNull BaseBean2<T> tBaseBean2) throws Exception {if (tBaseBean2.isSuccess()) {return createData(tBaseBean2.getData());} else {return Flowable.error(new ServerException(tBaseBean2.getResult(), tBaseBean2.getErrorCode(), tBaseBean2.getMessage()));}}}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());}/*** 创建成功的数据** @param data* @param <T>* @return*/private static <T> Flowable<T> createData(T data) {return Flowable.create(new FlowableOnSubscribe<T>() {@Overridepublic void subscribe(@NonNull FlowableEmitter<T> flowableEmitter) throws Exception {try {flowableEmitter.onNext(data);flowableEmitter.onComplete();} catch (Exception e) {flowableEmitter.onError(e);}}}, BackpressureStrategy.BUFFER);}
}
- SdkManager
public class SdkManager { // public static void initStetho(Context context) { // Stetho.initializeWithDefaults(context); // }
public static OkHttpClient.Builder initInterceptor(OkHttpClient.Builder builder) {HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {@Overridepublic void log(String message) {Log.e("Okhttp", "log: " + message );}});interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);builder.addInterceptor(interceptor);//.addNetworkInterceptor(new StethoInterceptor());return builder;}
}
- ServerException
public class ServerException extends Exception { private int result;// 0成功,其他失败 private int errorCode; private String message;
public ServerException(int result, int errorCode, String message) {this.result = result;this.errorCode = errorCode;this.message = message;}public int getResult() {return result;}public void setResult(int result) {this.result = result;}public int getErrorCode() {return errorCode;}public void setErrorCode(int errorCode) {this.errorCode = errorCode;}@Overridepublic String getMessage() {return message;}public void setMessage(String message) {this.message = message;}
}
- 使用- ApiService```kotlinpublic interface ApiService {@GET("v3/api/bluetoothkey/getCalibrateParams")Flowable<BaseBean2<PhoneModelBean>> getPhoneModel(@QueryMap Map<String, String> map);static ApiService create(){return RetrofitHelp.getInstance().create(ApiService.class);}}
Activity中使用
ApiService.create().getPhoneModel(requestMap).compose(RxUtils.handleBaseBean()).as(RxLife.as(this)).subscribe(new RxSubUtils<PhoneModelBean>() {@Overrideprotected void _onNext(PhoneModelBean phoneModelBean) {}@Overrideprotected void _onError(Throwable e, int result, String msg) {}});RetrofitHelp.getInstance().create(ApiService.class).getPhoneModel(requestMap).compose(RxUtils.handleBaseBean()).as(RxLife.as(this)).subscribe(new RxSubUtils<PhoneModelBean>() {@Overrideprotected void _onNext(PhoneModelBean phoneModelBean) {Log.e("MMM", "_onNext: "+ phoneModelBean.getParams() );}@Overrideprotected void _onError(Throwable e, int result, String msg) {Log.e("MMM", "_onError: " + result + "--" + msg + "--" + e);}});
对错误统一处理封装
HttpCallBack ``` /**
- @Author: 13009
- @CreateDate: 2021/6/23 10:25
- https://blog.csdn.net/qq_30889373/article/details/73499576
- @Description: http请求回调处理
*/
public abstract class HttpCallBack
{
public abstract void onSuccess(T t);
public void onFail(Throwable e, int result, String msg, Activity activity) { ALog.i(“HttpCallBack”, result + “——-“ + msg + “—e—“ + e.toString()); //统一处理异常 if (result == RxSubUtils.RESULT_ERROR) {
//其他错误Throwable throwable = HttpHelp.unifiedError(e);CommonToast.showSettledResult(activity, false, throwable.getMessage());
// ExceptionHandle.ResponeThrowable responeThrowable = ExceptionHandle.handleException(e); // CommonToast.showSettledResult(activity, false, responeThrowable.message); } else {
//服务器返回错误if (!TextUtils.isEmpty(msg)) {CommonToast.showSettledResult(activity, false, msg);}
} }
}
- httpHelp
public class HttpHelp {
public static ApiService sApiService = RetrofitHelp.getInstance().create(ApiService.class);
/**
获取蓝牙标定数据 *
- @param map
@param rxSubUtils /*/ public static void getPhoneModel(Map
map, LifecycleOwner lifecycleOwner, RxSubUtils rxSubUtils) { //将提交的参数统一加密 setSubscribe(sApiService. getPhoneModel(MD5Util.getRequestMap(map,PhoneUtils.getToken(PublicUtils.getContext()),PhoneUtils.getPhoneID())),rxSubUtils, lifecycleOwner);
}
private static
void setSubscribe(Flowable > flowable, RxSubUtils rxSubUtils, LifecycleOwner lifecycleOwner) { if (lifecycleOwner == null) { flowable.compose(RxUtils.handleBaseBean()).subscribe(rxSubUtils);
} else {
flowable.compose(RxUtils.handleBaseBean()).as(RxLife.as(lifecycleOwner)).subscribe(rxSubUtils);
} }
public static void getPhoneModel(Map<String, String> map, FragmentActivity activity, HttpCallBack<PhoneModelBean> httpCallBack) {//将提交的参数统一加密setSubscribe(sApiService.getPhoneModel(MD5Util.getRequestMap(map,PhoneUtils.getToken(PublicUtils.getContext()),PhoneUtils.getPhoneID())),activity, httpCallBack);}private static <T> void setSubscribe(Flowable<BaseBean2<T>> flowable, FragmentActivity activity, HttpCallBack<T> httpCallBack) {RxSubUtils<T> rxSubUtils = new RxSubUtils<T>() {@Overrideprotected void _onNext(T t) {if (httpCallBack != null) {httpCallBack.onSuccess(t);}}@Overrideprotected void _onError(Throwable e, int result, String msg) {if (httpCallBack != null) {httpCallBack.onFail(e, result, msg, activity);}}};if (activity == null) {flowable.compose(RxUtils.handleBaseBean()).subscribe(rxSubUtils);} else {flowable.compose(RxUtils.handleBaseBean()).as(RxLife.as(activity)).subscribe(rxSubUtils);}}
/ /*
* 统一错误处理 -> 汉化了提示,以下错误出现的情况 (ps:不一定百分百按我注释的情况,可能其他情况)/*/public static Throwable unifiedError(Throwable e) {Throwable throwable = null;Context context = PublicUtils.getContext();if (!NetworkUtil.isNetworkAvailable(context)) {//无网络throwable = new Throwable(context.getString(R.string.exception_network_exception_please_check_online), e == null ? null : e.getCause());} else {if (e instanceof UnknownHostException) {//无网络的情况,或者主机挂掉了。返回,对应消息 Unable to resolve host "m.app.haosou.com": No address associated with hostname//主机挂了,也就是你服务器关了throwable = new Throwable(context.getString(R.string.exception_service_error_please_try_again), e.getCause());} else if (e instanceof SocketTimeoutException || e instanceof SocketException) {//连接超时等throwable = new Throwable(context.getString(R.string.exception_network_timeout_please_check_online), e.getCause());} else if (e instanceof IllegalArgumentException || e instanceof JsonSyntaxException) {//也就是后台返回的数据,与你本地定义的Gson类,不一致,导致解析异常 (ps:当然这不能跟客户这么说)throwable = new Throwable(context.getString(R.string.exception_failed_to_get_the_data_please_try_again_later), e.getCause());} else {//其他 未知throwable = new Throwable(context.getString(R.string.exception_unknown), e == null ? null : e.getCause());}}return throwable;}
}
- 使用示例
//retrofit使用示例,带生命周期处理HttpHelp.getPhoneModel(requestMap, this, new RxSubUtils<PhoneModelBean>() {@Overrideprotected void _onNext(PhoneModelBean phoneModelBean) {ALog.i(getClass().getSimpleName(), JsonUtils.toJson(phoneModelBean));}@Overrideprotected void _onError(Throwable e, int result, String msg) {ALog.i(getClass().getSimpleName(), result + "-----" + msg + "--e--" + e.toString());}});//retrofit使用示例,带生命周期处理HttpHelp.getPhoneModel(requestMap, this, new HttpCallBack<PhoneModelBean>() {@Overridepublic void onSuccess(PhoneModelBean phoneModelBean) {ALog.i(getClass().getSimpleName(), JsonUtils.toJson(phoneModelBean));}//想要单独处理异常,重写此方法即可
// @Override // public void onFail(Throwable e, int result, String msg, Activity activity) { // super.onFail(e, result, msg, activity); // } });
<a name="d11feb98"></a>### Retrofit + Gson + Rxjava3 + rxlife3- 导入
//retrofit api ‘com.squareup.retrofit2:retrofit:2.9.0’ //retrofit - gson api ‘com.squareup.retrofit2:converter-gson:2.9.0’ //rxjava 相关 api ‘com.squareup.retrofit2:adapter-rxjava3:2.9.0’ //rxjava3最新版 api “io.reactivex.rxjava3:rxjava:3.0.13” api ‘io.reactivex.rxjava3:rxandroid:3.0.0’ //rxlife3 implementation ‘com.github.liujingxing.rxlife:rxlife-rxjava3:2.1.0’
- 使用示例- GsonApi
interface GsonApi {
companion object{val BASE_URL = "https://www.wanandroid.com/"fun create(): GsonApi {return Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create())
// .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .build() .create(GsonApi::class.java) } }
@GET("wxarticle/chapters/json")fun getWxData(): Call<WxData>@GET("wxarticle/chapters/json")fun getWxData2(): Flowable<WxData>
}
- Activity
GsonApi.create().getWxData2() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .life(this) .subscribe({ Log.e(TAG, “testGson: $it”) }, { Log.e(TAG, “testGson: $it”) })
<a name="b54ffc07"></a>### Retrofit + MoShi + LiveData + 协程- 导包:```kotlindef fragment_version = "1.3.4"implementation "androidx.fragment:fragment-ktx:$fragment_version"/* implementation 'androidx.lifecycle:lifecycle-process:2.2.0'implementation 'androidx.lifecycle:lifecycle-livedata:2.3.1'*/implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'// implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'//retrofitapi 'com.squareup.retrofit2:retrofit:2.9.0'//retrofit-moshiimplementation 'com.squareup.retrofit2:converter-moshi:2.9.0'//moshiimplementation 'com.squareup.moshi:moshi:1.12.0'kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.12.0'
Bean
WxData
@JsonClass(generateAdapter = true)data class WxData(var `data`: List<Data>,var errorCode: Int,var errorMsg: String)@JsonClass(generateAdapter = true)data class Data(var children: List<Any>,var courseId: Int,var id: Int,var name: String,var order: Int,var parentChapterId: Int,var userControlSetTop: Boolean,var visible: Int)
WxInfo
`` @JsonClass(generateAdapter = true) data class WxInfo( @Json(name = "data") vardata`: List, @Json(name = “errorCode”) var errorCode: Int, @Json(name = “errorMsg”) var errorMsg: String )
@JsonClass(generateAdapter = true)
data class Data2(
@Json(name = “children”)
var children: List
- ViewModel
class GsonViewModel: ViewModel() {
fun getWxData() = liveData {val response = withContext(Dispatchers.IO){GsonApi.create().getWxData()}emit(response)}fun getWxInfo() = liveData {val response = withContext(Dispatchers.IO){GsonApi.create().getWxInfo()}emit(response)}
}
- Activity中使用
private fun liveTest() { viewModel.getWxData().observe(this){ Log.e(TAG, “liveTest: $it”) } viewModel.getWxInfo().observe(this){ Log.e(TAG, “liveTest—: $it”) } }
<a name="79ffe87d"></a>### Retrofit + Flow + MoShi + 协程- 导包```kotlin//retrofitapi 'com.squareup.retrofit2:retrofit:2.9.0'//retrofit-moshiimplementation 'com.squareup.retrofit2:converter-moshi:2.9.0'//moshiimplementation 'com.squareup.moshi:moshi:1.12.0'kapt 'com.squareup.moshi:moshi-kotlin-codegen:1.12.0'//retrofit - adapter - flowimplementation 'com.github.chenxyu:retrofit-adapter-flow:1.2.0'
- Bean
`` @JsonClass(generateAdapter = true) data class WxInfo( @Json(name = "data") vardata`: List, @Json(name = “errorCode”) var errorCode: Int, @Json(name = “errorMsg”) var errorMsg: String )
@JsonClass(generateAdapter = true)
data class Data2(
@Json(name = “children”)
var children: List
- api
interface GsonApi {
companion object{val BASE_URL = "https://www.wanandroid.com/"fun create(): GsonApi {return Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(MoshiConverterFactory.create())
// .addConverterFactory(GsonConverterFactory.create()) // .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) // .addCallAdapterFactory(RxJava3CallAdapterFactory.create()) .addCallAdapterFactory(FlowCallAdapterFactory.create()) .build() .create(GsonApi::class.java) } }
@GET("wxarticle/chapters/json")fun getWxInfoFlow(): Flow<WxInfo>
}
- ViewModel
fun getWxInfoFlow() = GsonApi.create().getWxInfoFlow() .flowOn(Dispatchers.IO) .onStart { Log.e(TAG, “getWxInfoFlow: onStart”) }.onCompletion { Log.e(TAG, “getWxInfoFlow: onCompletion”) }.catch { e -> e.printStackTrace() Log.e(TAG, “getWxInfoFlow: ${e.message}”) }.asLiveData() /.collect { Log.e(TAG, “getWxInfoFlow: $it”) emit(it) }/
fun getWxTest() = handlerFlow(GsonApi.create().getWxInfoFlow())private fun <T> handlerFlow(flow: Flow<T>): LiveData<T> {return flow.flowOn(Dispatchers.IO).onStart {}.onCompletion {}.onEach {//可以在这里统一处理返回数据if (it is WxInfo){
// it.errorMsg = “this is wxinfo” //错误情况,截断,后续回到catch中 error(“this is error”) } }.catch { e -> Log.e(TAG, “handlerFlow: ${e.message}”) }.asLiveData() }
- Activity使用
private fun flowTest() { / viewModel.getWxInfoFlow() .observe(this){ Log.e(TAG, “flowTest: $it”) }/ //统一处理过程测试 viewModel.getWxTest() .observe(this){ Log.e(TAG, “flowTest: $it”) } } ```
结语
最终来看,Kotlin 和 Jetpack 才是主流,最适合的还是协程和 LiveData 或 Flow,不过 Flow 这个 adapter 不确定会不会有问题,比较不是官方做的,有待检验,最稳妥的做法还是使用 LiveData。
