我有一个像这样的改造api界面的方法。
@FormUrlEncoded
@POST("/token")
void generateToken( @Field("username") String user, @Field("password") String password,
@Field("grant_type") String grantType, Callback<JsonObject> callback);
我在api.generateToken(userName, userPassword, GRANT_TYPE, getLoginCallback(button));
方法getLoginCallback()
返回并回调。检查以下内容:
private Callback<JsonObject> getLoginCallback(final Button button){
return new Callback<JsonObject>() {
@Override
public void success(JsonObject jsonObject, Response response) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
populateToken(jsonObject.toString());
startActivity(intent);
finish();
}
@Override
public void failure(RetrofitError error) {
String toastErrorMessage = "Erro de login";
toastMessage(toastErrorMessage, Toast.LENGTH_SHORT);
dismissDialog();
button.setEnabled(true);
}
};
}
当我得到一个好结果时,一切都按预期工作。
但是当它出错时,不是执行我的回调的failure
方法,而是在非UI线程中抛出跟随错误。
E/AndroidRuntime: FATAL EXCEPTION: Retrofit-Idle
Process: neocom.dealerbook, PID: 5782
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Throwable.getMessage()' on a null object reference
at retrofit.CallbackRunnable.run(CallbackRunnable.java:50)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at retrofit.Platform$Android$2$1.run(Platform.java:142)
at java.lang.Thread.run(Thread.java:818)
我尝试过旧版改装(1.8.0),同样的事情。 我已经调试了改装代码,发现它在CllbackRunnable中调用了一个返回null的errorHandler.handleError。
try {
final ResponseWrapper wrapper = obtainResponse();
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.success((T) wrapper.responseBody, wrapper.response);
}
});
} catch (RetrofitError e) {
Throwable cause = errorHandler.handleError(e);
final RetrofitError handled = cause == e ? e : unexpectedError(e.getUrl(), cause);
callbackExecutor.execute(new Runnable() {
@Override public void run() {
callback.failure(handled);
}
});
}
}
调用unexpectedError(e.getUrl(), cause);
时会抛出错误
此时e
的值为retrofit.RetrofitError: 400 Bad Request
以下是unexpectedError
方法
public class RetrofitError extends RuntimeException {
// ...
public static RetrofitError unexpectedError(String url, Throwable exception) {
return new RetrofitError(exception.getMessage(), url, null, null, null, Kind.UNEXPECTED,exception);
}
// ...
}
到目前为止,我所做的不是调用api.generateToken(userName, userPassword, GRANT_TYPE, getLoginCallback(button));
,而是在Retrofit的API界面中创建了一个同步方法。
所以我自己做了所有线程控制。这样我就可以控制出了什么问题。
所以现在我在Api中使用这样的方法:
@FormUrlEncoded
@POST("/token")
JsonObject generateToken( @Field("username") String user, @Field("password") String password,
@Field("grant_type") String grantType);
然后在我的 Activity 中创建了两个方法,一个尝试通过新线程中的Api.generateToken方法获取令牌,另一个方法是在捕获异常时调用。以下是:
private void tryLogin(final Button button, final String userName, final String userPassword) {
new Thread(new Runnable() {
@Override
public void run() {
try {
API api = ApiFactory.getApi();
JsonObject response = api.generateToken(userName, userPassword, GRANT_TYPE);
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
populateToken(response.toString());
startActivity(intent);
finish();
} catch (RetrofitError |IllegalStateException e){
loginFail(button);
}
}
}).start();
}
private void loginFail(final Button button) {
button.post(new Runnable() {
public void run() {
String toastErrorMessage = "Erro de login";
toastMessage(toastErrorMessage, Toast.LENGTH_SHORT);
dismissDialog();
button.setEnabled(true);
}
});
}
因此,当用户触摸“登录”按钮时,我正在调用tryLogin(button, userName, userPassword);
而不是api.generateToken(userName, userPassword, GRANT_TYPE, getLoginCallback(button));
我仍然想知道一个更好的解决方案,或者如果我做错了导致Retrofit崩溃的话。但是现在我解决了我的问题。