对重新创建的活动实施Retrofit回调的最佳实践?

lor*_*max 72 rest android web-services retrofit

我正在转向Retrofit并尝试理解使用异步回调的正确架构.

例如,我有一个界面:

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

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

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

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

小智 42

使用otto.有很多样本要混合otto和改造,例如https://github.com/pat-dalberg/ImageNom/blob/master/src/com/dalberg/android/imagenom/async/FlickrClient.java

或者阅读这篇文章http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html 它回答了几乎所有问题

  • 这绝不是OP的问题的答案,即"我如何得到对新活动的回应".博客文章明确指出"如果一个活动启动了一个API调用,然后被破坏或落后,我们仍然会发布结果响应事件,但没有人会在那里听....我们总是在每次重新查询数据我们的活动恢复了" (14认同)

小智 34

对于潜在的长时间运行的服务器调用,我使用AsyncTaskLoader.对我来说,Loaders的主要优点是活动 - 生命周期处理.仅当您的活动对用户可见时,才会调用onLoadFinished.活动/片段和方向更改之间也共享加载器.

所以,我创建了使用改造中同步调用的ApiLoader loadInBackground.

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;

}
Run Code Online (Sandbox Code Playgroud)

在您的活动中,您可以像这样启动此加载器:

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) {}
    });
Run Code Online (Sandbox Code Playgroud)

如果要多次请求数据,请使用restartLoader而不是initLoader.

  • 我根据您的想法创建了一个完整的示例:https://github.com/rciovati/retrofit-loaders-example (5认同)

Leo*_*age 21

我一直在我的Android应用程序上使用一种MVP(ModelViewPresenter)实现.对于Retrofit请求,我使Activity调用它的各自的Presenter,它反过来生成Retrofit Request,并作为参数我发送一个附加了自定义Listener的Callback(由演示者实现).当Callback到达onSuccessonFailure方法时,我调用Listener各自的方法,调用Presenter然后调用Activity方法:P

现在,如果屏幕被转动,当重新创建我的活动时,它会将自己附加到Presenter.这是使用Android应用程序的自定义实现进行的,它保留了演示者的实例,并使用地图根据Activity的类恢复正确的演示者.

我不知道如果这是最好的方式,也许@pareshgoel答案更好,但它一直在为我工作:D

例子:

public abstract interface RequestListener<T> {

    void onSuccess(T response);

    void onFailure(RetrofitError error);
}
Run Code Online (Sandbox Code Playgroud)

...

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);
    }

}
Run Code Online (Sandbox Code Playgroud)

在演示者的某处实现侦听器,并在overrode方法上调用演示者的方法,该方法将调用Activity.并在演示者的任何地方打电话来启动所有内容:P

Request rsqt = restAdapter.create(Request.class);
rsqt.get(new RequestCallback<YourExpectedObject>(listener));
Run Code Online (Sandbox Code Playgroud)

希望它能帮到你.

  • 类似于观察者对我而言.只需使用RxJava (5认同)
  • 回调是丑陋的.您可以使用RxJava(RxAndroid)或Otto(EventBus)代替. (2认同)

par*_*oel 10

首先,您的活动在此处泄漏,因为此行:api.getUserName(userId,new Callback {...})创建一个匿名Callback类,该类包含对MainActivity的强引用.在调用Callback之前旋转设备时,MainActivity不会被垃圾回收.根据您在Callback.call()中执行的操作,您的应用可能会产生未定义的行为.

处理此类方案的一般想法是:

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

以上只是防止泄漏.它仍然无法帮助您将Retrofit回调给您的活动.

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

一般的想法是Fragment自动将自身附加到Activity上的配置更改.


Mar*_*cny 5

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

它讨论了如何通过将REST请求委托给服务来创建REST请求(该服务几乎从未被杀死)。请求完成后,它会立即存储到Android的内置数据库中,因此当您的“活动”准备就绪时,数据立即可用。

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

该视频没有专门讨论改造,但是您可以轻松地将改造应用于此范例。

  • 这是5年前,我的猜测是此后技术已得到改进,使用服务可能不是当前的最佳方法。 (5认同)