致命异常:在@FormUrlEncoded api方法

时间:2016-05-05 15:09:05

标签: android retrofit

我有一个像这样的改造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崩溃的话。但是现在我解决了我的问题。

0 个答案:

没有答案