私はレトロフィットに切り替えて、非同期コールバックでそれを使用するための適切なアーキテクチャを理解しようとしています。
たとえば、インターフェイスがあります:
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呼び出しは最初のアクティビティの寿命よりも長く実行されると思います)。たぶん、コールバックの静的インスタンスを使用する必要がありますか?正しい方法を教えてください...
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 ほとんどすべての質問に答えます
潜在的に長時間実行されるサーバー呼び出しには、 AsyncTaskLoader を使用します。私にとって、ローダーの主な利点は、アクティビティライフサイクルの処理です。 onLoadFinishedは、アクティビティがユーザーに表示される場合にのみ呼び出されます。ローダーは、アクティビティ/フラグメントと向きの変更間でも共有されます。
そこで、loadInBackgroundでレトロフィット同期呼び出しを使用する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) {}
});
データを複数回要求する場合は、initLoaderの代わりにrestartLoaderを使用します。
私はAndroidアプリで一種のMVP(ModelViewPresenter)実装を使用しています。レトロフィットリクエストについては、それぞれのプレゼンターのアクティビティコールを作成し、それがレトロフィットリクエストを作成し、パラメータとしてカスタムリスナーがアタッチされたコールバックを送信します(プレゼンターによって実装されます)。コールバックがonSuccess
またはonFailure
メソッドに到達したら、リスナーのそれぞれのメソッドを呼び出します。これらのメソッドは、PresenterメソッドとActivityメソッドを呼び出します:P
これで、画面がオンになった場合、アクティビティが再作成されると、プレゼンターにアタッチされます。これは、プレゼンターのインスタンスを保持するAndroidのアプリケーションのカスタム実装を使用して行われ、アクティビティのクラスに従って正しいプレゼンターを回復するためのマップを使用します。
それが最良の方法であるかどうかわからない、おそらく@pareshgoelの答えが優れていますが、それは私のために働いています:D
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を呼び出すプレゼンターのメソッドを呼び出します。そして、プレゼンター上のすべての場所を初期化するには、Pを呼び出します。
Request rsqt = restAdapter.create(Request.class);
rsqt.get(new RequestCallback<YourExpectedObject>(listener));
お役に立てば幸いです。
まず、この行:api.getUserName(userId、new Callback {...})がMainActivityへの強力な参照を保持する匿名のCallbackクラスを作成するため、アクティビティがここでリークします。 Callbackが呼び出される前にデバイスがローテーションされると、MainActivityはガベージコレクションされません。 Callback.call()で行う内容によっては、アプリで未定義の動作が発生する場合があります。
このようなシナリオを処理する一般的な考え方は次のとおりです。
上記はリークを防ぐだけです。それでも、アクティビティにRetrofitコールを返すのに役立ちません。
ここで、構成の変更後でも結果をコンポーネント(この場合はアクティビティ)に戻すには、アクティビティにアタッチされたヘッドレスの保持フラグメントを使用して、Retrofitを呼び出します。 Retained fragmentについて詳しくはこちらをご覧ください- http://developer.Android.com/reference/Android/app/Fragment.html#setRetainInstance(boolean)
一般的な考え方は、フラグメントが構成変更時にアクティビティに自動的にアタッチされるということです。
Google I/Oでこのビデオを見る を強くお勧めします。
RESTリクエストをサービス(ほとんど殺されない)に委任して作成する方法について説明します。リクエストが完了すると、すぐにAndroidの組み込みデータベースに保存されるため、アクティビティの準備が整うとすぐにデータが利用可能になります。
このアプローチを使用すると、アクティビティのライフサイクルを心配する必要がなくなり、リクエストがはるかに分離された方法で処理されます。
ビデオでは特にレトロフィットについて説明していませんが、このパラダイムにレトロフィットを簡単に適合させることができます。
Retrofit2を使用して方向の変更を処理します。私は就職の面接でこれを尋ねられ、その時にそれを知らなかったために拒否されましたが、ここでは今です。
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();
}
}
}
}
Robospice を使用します
データを必要とするアプリ内のすべてのコンポーネントは、spiceサービスに登録します。サービスは、リクエストをサーバーに送信します(必要に応じて後付けを使用)。応答が戻ると、登録したすべてのコンポーネントが通知されます。それらのいずれかがもう使用できない場合(ローテーションのためにキックされたアクティビティなど)、通知されません。
利点:デバイスを回転したり、新しいダイアログ/フラグメントを開いたりするかどうかに関係なく、失われない単一の要求...