2013-11-02 3 views
2

현재 비동기 http 라이브러리를 사용하여 Google 서버에 대해 HTTP 요청을 실행하고 있습니다. 그러나 이것은 화면 회전 중에 http 호출이 진행 중일 때 호출이 완료되면 이전 컨텍스트에 대한 참조를 갖게되는 문제를 낳습니다. 나는 일종의 onCreate에서 캡처 된 최신 인스턴스에 대한 정적 참조를 유지함으로써이 문제를 해결했으며 해당 참조가있는 메소드를 호출합니다 (onDestroy에서는 null). 괜찮 았지만 해킹처럼 보였습니다. 좋은 생각하지만 난 단순히 내 활동을함으로써이 작업을 수행 할 수있는 생각처럼 보일http 요청에 대한 http 요청에 대해 generics, 상속을 사용하여 방향 변경을 처리합니다.

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

연장 FragmentActivity 사용 : 나는 어떤 사람들은이 다루는 여기처럼하는 조각의 사용을 권장 본 적이 내가하고있는 일을 위해 특별히 의도 된 AsyncTaskLoader 서브 클래스.

내 아이디어는 다음과 같습니다. AsyncTaskLoader with AsiRequest를 구현하고 ApiResponse를 반환합니다. 그러나 나는 응답을 구문 분석하고 ApiResponse를 확장하는 다른 종류의 개체로 변환 할 수 있도록 HttpAsyncTask의 하위 클래스를 만들고 응답을 구문 분석하는 메서드를 재정의 할 수 있기를 원합니다. 이 형식 인수를 지정하는 방법을 잘 모르겠습니다.

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 개체를 캐스팅 할 수 있지만,보다 안전한 형식으로이 작업을 수행하고 싶습니다. 또한 나는 본질적으로 동일한 것을 성취하지만 다른 방식으로 성취하는 아이디어에 열려 있습니다.

+0

? 그것은 기본적으로 당신이 원하는 것과 비슷한 방식으로합니다. –

+0

아니요 화면 방향 변경을 처리하지 않습니다. 실제로 위의 작업을 수행 할 방법을 찾았지만 여전히 화면 방향에서 작동하지 않습니다. 토론 할 시간이 조금 있으면 업데이트 할 것입니다. –

+0

실례로 저는 발리가 문제를 해결하는 방법을 모르겠다 고 말하면됩니다. 공유하시기 바랍니다 그리고 나는 당신에게 upvote 줄거야 또는 좋은 해결책이 있다면 받아들이십시오! –

답변

7

좋아,이게 내가 실제로 잘 작동하는 것 같아서 배경 작업 중에 화면 방향 변경을 처리한다. 여기 내 업데이트 된 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를 사용하려고하면 컴파일 오류가 발생합니다. 유형으로.

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

: (결과 객체가 null의 경우 호출 forceLoad) 제가 위에서처럼 또한,이 onStartLoading 구현해야합니다 그렇지 않으면 loadInBackground가 여기에

를 호출되지 않습니다 것은 HttpAsyncTaskLoader의 서브 클래스의 예입니다 사용자가 로그인 버튼을 누를 때까지이 로더가 시작되지 않는 동안, 당신은 그것을 이미 진행했다 경우에서 onCreate에 initLoader를 사용하여 로더에 다시 연결해야

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 

    } 
} 

주 : 다음은이 로더를 사용하는 활동이다 , 그렇지 않으면 whe n 오리 엔테이션을 플립하면 작업이 완료되었다는 통지를받지 못합니다.

재미있는 점은 TaskFragment를 사용하지 않아도된다는 점입니다. 난 정말 http를 물건에 대한 다른 사람이 이렇게 어쩌면 거기에 몇 가지 측면을 보지 못했지만 그것은 잘 작동하는 것.

+0

위대한 작품 매트, 정말로 정말로 도움이된다! :) – Alessandro

0

이런 종류의 문제에 전념하는 라이브러리를 구현하는 데 관심이 없습니까?You Have Volley는 Google과 Robospice가 합작했습니다.

귀하의 질문에 대답하지만 왜 발리를 사용하지 않습니다

http://arnab.ch/blog/2013/08/asynchronous-http-requests-in-android-using-volley/

https://github.com/octo-online/robospice

+1

나는 위의 두 가지에 관심이있어 어떤 사람들보다 그들을 보았다. Robospice가 그렇게 많이하는 것처럼 보입니다. 어디서부터 시작해야할지 모르겠습니다. 나는 아직 발리리가 활동 라이프 사이클 이벤트를 관리하는 것에 대해 무엇을하지 않았는가를 파악하지 못했다. 이것에 대한 쉬운 메커니즘이 있다면 나는 그것을 고려할 것이다. 또한 현재 클라이언트에는 대형 업로드 (RAM의 모든 데이터를 미리 버퍼링하지 않은)를 수행하기 위해 빌드 한 추가 기능이 있습니다. 나는 발리가 큰 요청에 좋지 않다는 것을 들었다. 그래서 나는 아직도 그것을 위해 나의 오래된 도서관을 유지해야 할 것이다. –

+0

Robospice를 사용해 보았지만 너무 어려워서 Volley로 전환했습니다. 실제로 당신 말이 맞습니다. 발리는 제품 수명주기를 관리하지 않습니다. 네트워크 연결을보다 잘 처리 할 수있는 방법 일뿐입니다. 다음은 Google I/O에 대한 이야기입니다. https://developers.google.com/events/io/sessions/325304728 – Climbatize

관련 문제