实现对重新创建的活动进行回调的最佳实践?

我正在切换到 Requfit,并试图理解将其与异步回调一起使用的适当架构。

例如,我有一个接口:

interface RESTService{
@GET("/api/getusername")
void getUserName(@Query("user_id") String userId,
Callback<Response> callback);
}

我在主要活动中运行这个:

RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("WEBSITE_URL")
.build();
RESTService api = restAdapter.create(RESTService.class);
api.getUserName(userId, new Callback<Response> {...});

然后用户旋转设备,我有新创建的活动... 这里发生了什么?如何获得对新活动的响应(我假设后台的 api 调用的执行时间将长于第一个活动生命周期)。也许我必须使用静态实例的回调或什么?请告诉我正确的方向。

70899 次浏览

首先,您的活动在这里泄露,因为这行: Api.getUserName (userId,new Callback { ... }) 创建一个匿名 Callback 类,该类保存对 MainActivity 的强引用。当设备在调用 Callback 之前旋转时,MainActivity 将不会被垃圾回收。根据您在 Callback.call ()中的操作,您的应用程序可能会产生未定义的行为。

处理这种情况的总体思路是:

  1. 永远不要创建非静态的内部类(或者问题中提到的匿名类)。
  2. 相反,应该创建一个静态类,该类保存 Activity/Fragment 的 WeakReference < > 。

以上只是防止泄漏。它仍然不能帮助您获得翻新调用回到您的活动。

现在,为了在配置更改之后将结果返回到组件(在您的情况下是 Activity) ,您可能希望使用附加到 Activity 的无头保留片段,该片段调用 Afterfit。在这里阅读更多关于保留片段 -http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)的内容

一般的想法是,片段在配置更改时自动附加到活动。

我一直在我的 Android 应用程序上使用一种 MVP (ModelViewPresenter)实现。对于便携式检测请求,我让 Activity 调用它各自的 Presenter,然后由它生成便携式检测请求,作为一个参数,我发送一个回调函数,并附带一个自定义监听器(由便携式检测器实现)。当 Callback 到达 onSuccessonFailure方法时,我调用监听器的各个方法,它调用 Presenter,然后调用 Activity 方法: P

现在,为了防止屏幕被翻转,当我的活动被重新创建时,它将自己附加到演示者上。这是使用 Android 的 Application 的一个自定义实现来实现的,它在其中保存演示者的实例,并使用一个映射来根据 Activity 的类恢复正确的演示者。

我不知道这是不是最好的方法,也许@pareshgoel 的回答更好,但它一直在为我工作。

例子:

public abstract interface RequestListener<T> {


void onSuccess(T response);
    

void onFailure(RetrofitError error);
}

...

public class RequestCallback<T> implements Callback<T> {


protected RequestListener<T> listener;
    

public RequestCallback(RequestListener<T> listener){
this.listener = listener;
}
    

@Override
public void failure(RetrofitError arg0){
this.listener.onFailure(arg0);
}


@Override
public void success(T arg0, Response arg1){
this.listener.onSuccess(arg0);
}


}

在演示者上的某个地方实现侦听器,在覆盖方法上调用演示者的方法,该方法将调用 Activity。并且在演示者身上打电话到任何你想要的地方初始化所有东西

Request rsqt = restAdapter.create(Request.class);
rsqt.get(new RequestCallback<YourExpectedObject>(listener));

对于可能长时间运行的服务器调用,我使用 AsyncTaskLoader。对我来说,Loader 的主要优点是活动生命周期处理。仅当用户可以看到活动时才调用 OnLoadFinish。加载程序还在活动/片段和方向更改之间共享。

所以我创建了一个在 加载背景中使用同步调用的 ApiLoader。

abstract public class ApiLoader<Type> extends AsyncTaskLoader<ApiResponse<Type>> {


protected ApiService service;
protected ApiResponse<Type> response;


public ApiLoader(Context context) {
super(context);
Vibes app = (Vibes) context.getApplicationContext();
service = app.getApiService();
}


@Override
public ApiResponse<Type> loadInBackground() {
ApiResponse<Type> localResponse = new ApiResponse<Type>();


try {
localResponse.setResult(callServerInBackground(service));
} catch(Exception e) {
localResponse.setError(e);
}


response = localResponse;
return response;
}


@Override
protected void onStartLoading() {
super.onStartLoading();
if(response != null) {
deliverResult(response);
}


if(takeContentChanged() || response == null) {
forceLoad();
}
}


@Override
protected void onReset() {
super.onReset();
response = null;
}




abstract protected Type callServerInBackground(SecondLevelApiService api) throws Exception;


}

在您的活动中,您可以像这样初始化这个加载程序:

getSupportLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<ApiResponse<DAO>>() {
@Override
public Loader<ApiResponse<DAO>> onCreateLoader(int id, Bundle args) {
spbProgress.setVisibility(View.VISIBLE);


return new ApiLoader<DAO>(getApplicationContext()) {
@Override
protected DAO callServerInBackground(ApiService api) throws Exception {
return api.requestDAO();
}
};
}


@Override
public void onLoadFinished(Loader<ApiResponse<DAO>> loader, ApiResponse<DAO> data) {
if (!data.hasError()) {
DAO dao = data.getResult();
//handle data
} else {
Exception error = data.getError();
//handle error
}
}


@Override
public void onLoaderReset(Loader<ApiResponse<DAO>> loader) {}
});

如果要多次请求数据,请使用 RestartLoader而不是 InitLoader

使用 Otto。 有很多样本可以混合 otto 和翻新,例如: < a href = “ https://github.com/Pat-dalberg/ImageNom/blob/master/src/com/dalberg/android/imagenom/ FlickrClient.java”> https://github.com/pat-dalberg/imagenom/blob/master/src/com/dalberg/android/imagenom/async/flickrclient.java

或者阅读这篇文章 < a href = “ http://www.mdswanson.com/blog/2014/04/07/final-android-rest-clients.html”> http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html 它回答了几乎所有的问题

使用 机器人香料

所有组件在您的应用程序中需要数据,注册香料服务。该服务负责将您的请求发送到服务器(如果需要,可以通过改造)。当响应返回时,所有注册的组件都会得到通知。如果其中有一个不再可用(比如一个因为轮换而被踢掉的活动) ,它就不会被通知。

好处: 一个单一的请求不会丢失,无论你是否旋转你的设备,打开新的对话框/片段等。

我强烈推荐你 观看谷歌 I/O 提供的视频

它讨论了如何通过将 REST 请求委托给服务来创建 REST 请求(这几乎从来不会被终止)。当请求完成后,它会立即存储到 Android 的内置数据库中,这样当您的活动准备好时,数据就可以立即使用了。

使用这种方法,您永远不必担心活动的生命周期,并且您的请求以一种更加分离的方式得到处理。

这个视频并没有特别提到翻新,但是你可以很容易地为这个范例进行翻新。

使用 Revifit2处理方向改变。我在一次工作面试中被问到这个问题,结果被拒绝了,因为当时我并不知道,但是现在我知道了。

public class TestActivity extends AppCompatActivity {
Call<Object> mCall;
@Override
public void onDestroy() {
super.onDestroy();
if (mCall != null) {
if (mCall.isExecuted()) {
//An attempt will be made to cancel in-flight calls, and
// if the call has not yet been executed it never will be.
mCall.cancel();
}
}
}
}