【问题标题】:Retrofit 2 callback issues with UI thread使用 UI 线程改造 2 回调问题
【发布时间】:2016-06-16 10:29:13
【问题描述】:

我正在制作一个简单的材料设计登录屏幕,它在改造获取数据时显示一个进度对话框。

我最近升级到改造 2,所以我对此很陌生。

我的 LoginActivity 代码:

private void login(){
        Log.d(TAG, "Attempting login");

        if(!validate()){
            onLoginFailure();
            return;
        }

        login_button.setEnabled(false);

        progressDialog = new ProgressDialog(LoginActivity.this, R.style.AppTheme_Dark_Dialog);
        progressDialog.setIndeterminate(true);
        progressDialog.setMessage("Authenticating...");
        progressDialog.show();

        String username = _usernameText.getText().toString().toLowerCase(); //all usernames are lowercase only
        String password = _passwordText.getText().toString();

        //here we handle the NWL section.

        startTime = System.currentTimeMillis();
        NWL.login(username, password, this); //This runs async to UI anyway.
    }

及LoginActivity中的其他功能:

@BindView(R.id.login_button)
Button login_button;

 public void loginOK() {
        progressDialog.dismiss();
        login_button.setEnabled(true);
        toastCreator.showToastLong("Login OK");
        Log.d(TAG, "Total time: " + (System.currentTimeMillis()-startTime));
 }

我的 NetworkLogic (NWL) 类中的代码:

public void login(String username, String password, final LoginActivity loginActivity){
    LoginUser loginUser = new LoginUser(username, password);

    Call<Username> call = apiService.login(loginUser);
    Log.d(TAG, call.toString());
    call.enqueue(new Callback<Username>() {
       @Override
       public void onResponse(Call<Username> call, Response<Username> response) {
           Log.d(TAG, "Responsecode : " + response.code());
           try {
               Thread.sleep(2000); //To test if the progressdialog actually shows up
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           if(response.code()==200){
               loginActivity.loginOK();
           } else {
               loginActivity.loginFail();
           }
       }

       @Override
       public void onFailure(Call<Username> call, Throwable t) {
           Log.d(TAG, "Failed: " + t.getMessage());
           loginActivity.loginFail();
       }
   });
}

现在我不断收到错误消息,例如: - android.util.AndroidRuntimeException:动画只能在 Looper 线程上运行(关于登录按钮) - java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare() (关于 toastCreator)

我了解导致问题的原因(改造 2 中的异步线程正在执行调用),它应该将调用传递给 ui 线程。 我的问题是,处理这个问题的最佳解决方案是什么?当然,我可以使用 CountdownLatch 破解一些代码并将整个异步调用作为强制同步运行,但这并不是我真正想要的。 我应该使用处理程序吗?如果是这样,实现这一点的最佳方法是什么?

所有代码都可用:https://github.com/mathieudevos/pinkiponki-app

期待答案!

【问题讨论】:

    标签: android retrofit2 android-handler ui-thread


    【解决方案1】:

    好的,既然没有人正确回答这个问题,那就去吧!

    使用处理程序并向其发布消息似乎可以解决问题。

    在 onCreate 方法中:

    handler = new Handler(Looper.getMainLooper()) {
            @Override
            public void handleMessage(Message msg){
                switch (msg.what){
                    case 200:
                        //Got the good response
                        loginOK();
                        break;
                    case 401:
                        loginFail();
                        break;
                    default:
                        loginFail();
                }
            }
        };
    

    在我的网络逻辑类中:

    Call<Username> call = apiService.login(userObject);
        call.enqueue(new Callback<Username>() {
           @Override
           public void onResponse(Call<Username> call, Response<Username> response) {
               Log.d(TAG, "Responsecode: " + response.code());
               Message msg = handler.obtainMessage(response.code());
               msg.sendToTarget();
    
           }
    
           @Override
           public void onFailure(Call<Username> call, Throwable t) {
               Log.d(TAG, "Failed: " + t.getMessage());
               Message msg = handler.obtainMessage(0); //0 for errors
               msg.sendToTarget();
           }
    }
    

    因为这是在 mainLooper 上运行的,所以我可以在这些函数中使用我的 UI。 这应该是正确的答案。

    【讨论】:

      【解决方案2】:

      Retrofit onResponse 适用于 UI 线程,因此您的 thread.sleep() 将为您制造问题。

       Thread.sleep(2000); //This line must not be on UI thread.
      

      如果您需要一些延迟,您可以轻松使用处理程序。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-06
      • 2016-10-20
      • 2015-03-06
      • 2018-04-13
      • 2019-03-21
      • 1970-01-01
      • 1970-01-01
      • 2019-07-22
      相关资源
      最近更新 更多