当前位置: 首页 > 面试题库 >

用于HTTP请求的AsyncTaskLoader,用于使用泛型,继承来处理方向更改

东方旭东
2023-03-14
问题内容

目前,我正在使用异步http库对我们的服务器执行http请求。但这带来了一个问题,即如果在屏幕旋转期间正在进行http调用,则在调用结束时我们将引用旧上下文。我通过保留对onCreate中捕获的最新实例的静态引用来解决此问题,并使用该引用调用方法(并在onDestroy中将其清空)。它工作正常,但看起来像是黑客。我见过有人建议使用片段来处理此问题,例如:

http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-
changes.html

这似乎是个好主意,但我想我可以通过简单地让我的Activity扩展FragmentActivity并使用AsyncTaskLoader子类来实现此目的,该子类专门用于我的工作。

这是我的想法:用ApiRequest实现AsyncTaskLoader并返回ApiResponse。但是,我希望能够继承HttpAsyncTask的子类并重写解析响应的方法,以便我可以解析响应并将其转换为扩展ApiResponse的另一种对象。我不确定如何指定类型参数来实现此目的。

这是我的代码:

public class HttpAsyncTaskLoader</*not sure what to put here*/> extends AsyncTaskLoader<? not sure ?> {
    private ApiClient mClient ;
    private ApiRequest mRequest;
    private volatile boolean isExecuting = false;
    public HttpAsyncTaskLoader(Context context, ApiClient client, ApiRequest request) {
        super(context);
        mClient = client;
        mRequest = request;
    }

    /**
     * Subclasses should override this method to do additional parsing
     * @param response
     * @return
     */
    protected /*subclass of ApiResponse (or ApiResponse itself)*/ onResponse(ApiResponse response) 
    {
        //base implementation just returns the value, subclasses would 
        //do additional processing and turn it into some base class of ApiResponse  
        return response; 
    }

    @Override
    public /** not sure ***/ loadInBackground() {
        HttpResponse response = null;
        ResponseError error = null;
        JSONObject responseJson = null;
        ApiResponse apiResponse = null;
        try {
            isExecuting = true;
            //synchronous call
            response  =  mClient.execute(mRequest);
            isExecuting = false;
            responseJson = new JSONObject(EntityUtils.toString(response.getEntity()));
        } catch (IOException e) {
            error = new ResponseError(e);
        } catch (URISyntaxException e) {
            error = new ResponseError(e);
        } catch (JSONException e) {
            error = new ResponseError(e);
        } finally {
            mClient.getConnectionManager().closeExpiredConnections();
            isExecuting = false;
            apiResponse = new ApiResponse(getContext().getResources(), response, responseJson, error);
        }
        return onResponse(apiResponse);
    }

    @Override
    public void onCanceled(ApiResponse response) {
        if (isExecuting) {
            mClient.getConnectionManager().shutdown();
        }
    }

}

有人知道我该如何做到吗?我不确定如何指定类型参数?我希望该类可以原样使用,并能够对其进行子类化。关键是我不想在上面的loadInBackground方法中重新实现功能。我确定我可以只使用ApiResponse作为我的通用参数,然后将onLoadFinished中返回的ApiResponse对象强制转换为期望的特定基类,但我希望以更类型安全的方式执行此操作。我也对那些本质上可以完成相同事情但又以另一种方式完成的想法持开放态度。


问题答案:

好的,这就是我想出的,它实际上似乎工作得很好,并且可以在后台工作期间处理屏幕方向更改。这是我更新的HttpAsyncTaskLoader。

public class HttpAsyncTaskLoader<T extends ApiResponse> extends AsyncTaskLoader {
    private ApiClient mClient ;
    protected ApiRequest mRequest;
    private ApiResponse mResponse;
    private volatile boolean isExecuting = false;
    public HttpAsyncTaskLoader(Context context, ApiClient client, ApiRequest request) {
        super(context);
        mClient = client;
        mRequest = request;
    }

    /** Subclasses should call this from loadInBackground   */
    protected ApiResponse executeRequest(ApiRequest request) {
        HttpResponse response = null;
        ResponseError error = null;
        JSONObject responseJson = null;
        try {
            isExecuting = true;
            Log.d(TAG, "executing api");
            response  =  mClient.execute(request);
            Log.d(TAG, "got a response");
            isExecuting = false;
            responseJson = new JSONObject(EntityUtils.toString(response.getEntity()));
            Log.d(TAG, "parsed response to json");
        } catch (IOException e) {
            error = new ResponseError(e);
        } catch (URISyntaxException e) {
            error = new ResponseError(e);
        } catch (JSONException e) {
            error = new ResponseError(e);
        } finally {
            mClient.getConnectionManager().closeExpiredConnections();
            isExecuting = false;
            mResponse = new ApiResponse(getContext().getResources(), response, responseJson, error);
        }
        return mResponse;
    }

    protected void onStartLoading() {
        super.onStartLoading();
        if (takeContentChanged() ||  mResponse == null) {
            forceLoad();
        }
        if (getResponse() != null) {
            deliverResult(getResponse());
        }
    }

    /** 
    * Subclasses should also override this so the correct object 
    * gets delivered in all cases (see onStartLoading above)
    */
    public ApiResponse getResponse() {
        return mResponse;
    }

    @Override
    public void onCanceled(Object data) {
        super.onCanceled(data);
        if (isExecuting) {
            mClient.getConnectionManager().shutdown();
        }
    }

    @Override
    public ApiResponse loadInBackground() {
        return executeRequest(mRequest);
    }
}

请注意,在上面的示例中,onCanceled方法采用一个Object。如果尝试使用ApiResponse,则会出现编译错误。作为类型。另外,您必须像上面一样实现onStartLoading(如果结果对象为null,则调用forceLoad),否则不会调用loadInBackground

接下来是HttpAsyncTaskLoader子类的示例:

public class LoginAsyncTaskLoader extends HttpAsyncTaskLoader {
    private LoginResponse mLoginResponse;
    public LoginAsyncTaskLoader(Context context, ApiClient client, ApiRequest request) {
        super(context, client, request);
    }

    @Override
    public LoginResponse loadInBackground() {
        ApiResponse apiResponse = executeRequest(mRequest);
        mLoginResponse = new LoginResponse(apiResponse.getResources(), apiResponse.response, apiResponse.responseJson, apiResponse.getError());
        return mLoginResponse;
    }

    @Override
    public ApiResponse getResponse() {
        return mLoginResponse;
    }
}

这是使用此加载程序的活动:

public class LoginActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<LoginResponse> {

    private String username,password;       
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login);
        Loader loader = getSupportLoaderManager().getLoader(0);
        if (loader != null) {
            getSupportLoaderManager().initLoader(0, null, this);
        }
    }

    public void loginSubmit(View button) {
            Bundle data = new Bundle();
            data.putString("username", getUsername());
            data.putString("password", getPassword());  
            getSupportLoaderManager().restartLoader(0, data, this);
    }


    @Override
    public Loader<LoginResponse> onCreateLoader(int i, Bundle bundle) {
    //might want to start a progress bar
        ApiClient client = new ApiClient();
        LoginApi loginApi = new LoginApi(bundle.getString("username"), bundle.getString("password"));
        return new LoginAsyncTaskLoader(this, apiClient, loginApi);
    }


    @Override
    public void onLoadFinished(Loader<LoginResponse> loginLoader,
                               LoginResponse loginResponse)
    {
        //handle result, maybe send to a new activity if response doesn't have an error

    }

    @Override
    public void onLoaderReset(Loader<LoginResponse> responseAndJsonHolderLoader)
    {
        //not sure if anything needs to be done here to do

    }
}

请注意,虽然该加载器直到用户按下“登录”按钮才启动,但是如果已经进行了,则必须使用onCreate中的initLoader重新连接到加载器,以防它已经在进行中;否则,当您翻转方向时,您将不会收到任务完成的通知。

有趣的是,这似乎很好用,不需要使用TaskFragment。我还没有真正看到其他人为http做这些事情,所以也许有一些缺点,但似乎工作得很好。



 类似资料:
  • 具有以下类别: 和 和 和 和 和 因此,应用程序需要动态实例化步骤子类对象,相应地设置配置并运行步骤,如下所示。 错误消息: "方法setConfig(捕获#8-of?扩展Config)在类型Step中 查看文档,在这种情况下,Java看起来不友好:https://docs.oracle.com/javase/tutorial/java/generics/wildcardGuidelines.h

  • 问题内容: 我正在尝试构建一个简单的Golang / Appengine应用程序,该应用程序使用通道来处理每个http请求。原因是我希望每个请求都执行合理的大内存计算,并且以线程安全的方式执行每个请求(即,并发请求的计算不会混淆)非常重要。 本质上,我需要一个同步队列,该队列一次只能处理一个请求,并且通道看起来很自然。 是否可以将Go的缓冲通道用作线程安全队列? 但是,我无法让我简单的hello

  • 我在Spring Batch中遇到了一个问题,这通常很简单。 我有多个步骤,面向块处理。对于每个步骤,我都想使用相同的ItemReader。 这是我的步骤宣言: 我的请求有一些参数,我想用准备状态设置器替换它们。 所以我的意图是为每个bean创建多个bean(对于每个步骤),以便为每个bean调用特定的准备语句。 我有以下错误: 我做错了什么?如果你需要更多的细节,请告诉我。 谢谢你的帮助。

  • 问题内容: 我正在尝试为我的角度应用程序启用oauth2令牌获取。我的配置运行正常(身份验证对于所有请求均正常运行,令牌提取也正常运行),但是有一个问题。 CORS请求要求在将GET OPTIONS请求发送到服务器之前。更糟糕的是,该请求不包含任何身份验证标头。我希望此请求始终返回200状态,而无需在服务器上进行任何身份验证。可能吗?也许我想念一些东西 我的Spring安全配置: angular

  • 问题内容: 因此,这些年来,我终于停止拖延脚步,决定“适当”学习JavaScript。语言设计中最令人头疼的元素之一是继承的实现。拥有Ruby的经验,我很高兴看到闭包和动态类型。但是对于我一生来说,无法弄清楚使用其他实例进行继承的对象实例将带来什么好处。 问题答案: 首先,让我们看一下JavaScript程序员在捍卫原型继承中所陈述的最常见的参数(我从当前的答案池中获取这些参数): 这很简单。 功

  • 问题内容: 在JavaScript中,每个对象同时是一个实例和一个类。要进行继承,可以将任何对象实例用作原型。 在Python,C ++等中,有类和实例作为单独的概念。为了进行继承,您必须使用基类创建一个新类,然后可以使用该新类来生成派生实例。 为什么JavaScript朝这个方向发展(基于原型的面向对象)?与传统的基于类的OO相比,基于原型的OO有哪些优点和缺点? 问题答案: 这里大约有一百个术