2016-12-05 2 views
-1

Ich habe dieses sehr einfaches Stück Code mit der Bibliothek retrofit-2.1.0Retrofit2 asynchroner Rückruf ist nicht

public class UserManager { 
    private static final Retrofit retrofit = new Retrofit.Builder() 
      .baseUrl(MY_URL) 
      .addConverterFactory(GsonConverterFactory.create()) 
      .build(); 
    private static final UserService service = retrofit.create(UserService.class); 

    public static User getUserById(Integer userId) { 
     Call<User> call = service.getUser(userId); 
     final User[] user = new User[1]; 
     final boolean[] isCalled = {false}; 

     call.enqueue(new Callback<User>() { 
      @Override 
      public void onResponse(Call<User> call, Response<User> response) { 
       System.err.println("CALLED"); 
       user[0] = response.body(); 
       isCalled[0] = true; 
      } 

      @Override 
      public void onFailure(Call<User> call, Throwable t) { 
       isCalled[0] = true; 
      } 
     }); 
     return user[0]; 
    } 

    interface UserService { 
     @GET("users/{user_id}") 
     Call<User> getUser(@Path("user_id") Integer userId); 
    } 
} 

ich telefonieren bin es aus JUnit4 Test genannt.

public class UserManagerTest { 
    @Test 
    public void testGetBattleById() throws Exception { 
     User user = UserManager.getUserById(1); 
    } 
} 

Meine User Klasse ist in Ordnung und Gson ist in der Lage JSON in sie serialisiert werden. Die URL ist auch in Ordnung. Das Problem ist weder onResponse, noch wird onFailure tatsächlich aufgerufen. isCalled[0] bleibt false, user[0] bleibt null und ich sehe nichts in stderr.

Ich habe dieses Problem gegoogelt und einige sehr ähnliche Fälle gefunden, aber leider keine echte Lösung. Was mache ich falsch?

+2

[Es genannt wird] (http://ideone.com/PPHi95), nicht aber dann, wenn Sie denken ... – Selvin

+0

Sie den Benutzer nicht direkt nach dem Aufruf zurückkehren kann ... Es wird immer Null zurückgeben, da die Anfrage asynchron ist und Sie es auf eine synchrone Art und Weise verwenden. – Jaythaking

Antwort

2

Sie können das Ergebnis in dem Verfahren so nicht zurück. Sie müssen auf einen der beiden Callbacks warten und dann über eine Schnittstelle verfügen, um das Ergebnis an die Calling-Methode zu senden. Das liegt daran, dass die Methode enqueue asynchron ist. Sie können das Ergebnis nicht direkt nach dem Aufruf der Methode abrufen.

So etwas wie

public interface OnGetUserCallback { 
    void onGetUser(User user); 

    void onError(Throwable t); 
} 

public static void getUserById(Integer userId, OnGetUserCallback onGetUserCallback) { 
    Call<User> call = service.getUser(userId); 

    call.enqueue(new Callback<User>() { 
     @Override 
     public void onResponse(Call<User> call, Response<User> response) { 
      onGetUserCallback.onGetUser(response.body()); 
     } 

     @Override 
     public void onFailure(Call<User> call, Throwable t) { 
      onGetUserCallback.onError(t); 
     } 
    }); 
} 
+0

Brauchen Sie wirklich Ihre eigenen Schnittstelle? Verwenden Sie den mitgelieferten 'Callback ' als Parameter –

+0

Sie müssen nicht, aber ich werde das Callback-Objekt selbst nicht so übergeben. Schnittstellen machen die Dinge klarer, wenn Sie solche Rückrufe haben. Da wir die gesamte Implementierung des UserManagers unterdrücken können, ist die Schnittstelle identisch. Zum Beispiel das Ändern der Netzwerkbibliothek für die Instanz oder das Ändern der Datenquelle selbst. –

2

Sie verwenden enqueue in einem Synchron Weise, die Sie immer null zurückkehren wird, weil die Anfrage noch nicht abgeschlossen, wenn Sie zurückkommen ... auf diese Weise versuchen, um zu sehen, ob das ist funktioniert:

User user = call.execute(); 

Wenn Sie die Async Funktion verwenden möchten, müssen Sie irgendeine Art von BroadcastReceiver müssen abzufeuern, wenn die Anforderung die Ansicht zu informieren getan wird, dass die Daten nun abgerufen werden. Sie können auch einen EventBus für diesen Zweck verwenden.

Auch versuchen Sie, ein User zu einem Battle Objekt zuweisen ...

+4

Das würde offensichtlich NetworkOnMainThreadException verursachen ... ** edit: ** Es ist nicht nötig BroadcastReceiver oder EventBus zu benutzen ... was reicht: ist es, den Code in onResponse zu bewegen ... – Selvin

+1

Danke für die Antwort. Die Zuordnung 'User' zu' Battle' ist nur ein Tippfehler. Ich habe versucht, synchrone Anrufe zu verwenden, aber ich habe diese Ausnahme und entschied mich zu versuchen, "Retrofit" in asynchron zu verwenden, weil ich nicht mit Threads belästigen möchte. – Qumeric

+0

Es ist eigentlich weniger kompliziert im Zusammenhang mit Thread, wenn Sie synchrone Anrufe verwenden ... Können Sie hier die Ausnahme posten, die Sie bekommen haben? – Jaythaking