在QuickBlox API中调用QBChatService.login()方法时出现`CalledFromWrongThreadException`

时间:2015-10-24 03:03:16

标签: android quickblox ui-thread

我正在使用Quickblox API开发IM应用,而我目前正在开发注册和登录功能。好吧,我的问题是,每当我尝试通过调用QBChatService登录QBChatService.login()时,我都会从Log Cat中收到此错误:

E/Event: Could not dispatch event: class regmoraes.jusstalk.session.SessionEvents to subscribing class class regmoraes.jusstalk.session.LoginPresenter
E/Event: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

我使用MVP模式和EventBus将模型(我称之为Managers)的事件发送给演示者。

以下是我的课程(最后他们之间的互动顺序):

LoginActivity:

public class LoginActivity extends Activity implements LoginView, View.OnClickListener{

private AutoCompleteTextView mUserField;
private EditText mPasswordField;
private TextView mSignUpTextView;
private Button mLoginButton;
private ProgressBar mProgressBar;
private LoginUIPresenter loginPresenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_login);

    mProgressBar = (ProgressBar) findViewById(R.id.login_progress);

    mUserField = (AutoCompleteTextView) findViewById(R.id.email);
    mPasswordField = (EditText) findViewById(R.id.password);

    mLoginButton = (Button) findViewById(R.id.button_sign_in);
    mLoginButton.setOnClickListener(this);

    mSignUpTextView = (TextView) findViewById(R.id.textView_sign_up);
    mSignUpTextView.setOnClickListener(this);

    this.loginPresenter = new LoginPresenter(this);
}

@Override
public void showMessageDialog(List errors) {

    AlertDialog.Builder dialog = new AlertDialog.Builder(this);
    dialog.setMessage("chat login errors: " + errors).create().show();
}

@Override
public void startNewActivity(Class activity) {

    Intent mIntent = new Intent(this, activity);
    startActivity(mIntent);
    finish();
}

@Override
public void showProgress(boolean show) {

    if(show){

        mProgressBar.setVisibility(View.VISIBLE);

        mUserField.setVisibility(View.INVISIBLE);
        mPasswordField.setVisibility(View.INVISIBLE);
        mLoginButton.setVisibility(View.INVISIBLE);

    }else{

        mProgressBar.setVisibility(View.GONE);

        mUserField.setVisibility(View.VISIBLE);
        mPasswordField.setVisibility(View.VISIBLE);
    }
}

@Override
public void onClick(View v) {

    switch(v.getId()){

        case R.id.button_sign_in:

            loginPresenter.login(mUserField.getText().toString(),
                    mPasswordField.getText().toString());
            break;

        case R.id.textView_sign_up:
            startNewActivity(SignUpActivity.class);
    }
}

@Override
public void showToast(String message, int length) {

    Toast.makeText(this, message,length).show();
}
}

LoginPresenter:

public class LoginPresenter implements LoginUIPresenter{

LoginView loginView;
SessionManager sessionManager;

public LoginPresenter(LoginView loginView) {

    EventBus.getDefault().register(this);

    /*...*/
}

@Override
public void login(String username, String password) {

    loginView.showProgress(true);
    sessionManager.login(username,password);
}

public void onEvent(SessionEvents sessionEvents){

    switch (sessionEvents.getEvent()){

        case SessionEvents.LOGIN_SUCCESSFULL:

            sessionManager.loginToChatService();
            break;

        case SessionEvents.LOGIN_FAILED:

            loginView.showProgress(false);
            loginView.showToast("Problem when connecting", Toast.LENGTH_SHORT);
            break;

        case SessionEvents.CHAT_SERVICE_CONNECTED:

            loginView.startNewActivity(MainActivity.class);
            break;

        default:break;
    }
}
}

SessionManager:

public class SessionManagement implements SessionManager,ConnectionListener {

private String TAG = SessionManagement.class.getName();

private SharedPreferences mSharedPreferences;
private Context mContext;
private SessionEvents sessionEvents;
private QBUser currentUser;
public QBChatService qbChatService;

public SessionManagement(Context context) {

    this.mContext = context;
    this.mSharedPreferences = (mContext)
            .getSharedPreferences("regmoraes.testapp", Context.MODE_PRIVATE);

    initChatServiceIfNeeded();

    this.sessionEvents = new SessionEvents();
    this.qbChatService = QBChatService.getInstance();
}

    /* .... */
private void initChatServiceIfNeeded() {

    if (!QBChatService.isInitialized()) {
        QBChatService.setDebugEnabled(true);
        QBChatService.init(mContext);
        QBChatService.getInstance().addConnectionListener(this);
    }
}

@Override
public void login(final String username, final String password) {

    final QBUser qbUser = new QBUser(username,password);

    QBAuth.createSession(qbUser, new QBEntityCallbackImpl<QBSession>() {

        @Override
        public void onSuccess(QBSession qbSession, Bundle params) {

            currentUser = qbUser;
            currentUser.setId(qbSession.getId());

            saveCredentials(currentUser.getLogin(), currentUser.getPassword());

            sessionEvents.setEvent(SessionEvents.LOGIN_SUCCESSFULL);
            EventBus.getDefault().post(sessionEvents);
        }

        @Override
        public void onError(List<String> errors) {

            sessionEvents.setEvent(SessionEvents.LOGIN_FAILED);
            EventBus.getDefault().post(sessionEvents);
        }
    });
}

@Override
public void loginToChatService(){

    qbChatService.login(currentUser, new QBEntityCallbackImpl() {

        @Override
        public void onSuccess() {

            try {

                qbChatService.startAutoSendPresence(30);

                sessionEvents.setEvent(SessionEvents.CHAT_SERVICE_CONNECTED);
                EventBus.getDefault().post(sessionEvents);

            } catch (SmackException.NotLoggedInException e) {

                e.printStackTrace();
            }
        }

        @Override
        public void onError(List errors) {

            sessionEvents.setEvent(SessionEvents.LOGIN_FAILED);
            EventBus.getDefault().post(sessionEvents);
        }
    });
}
}

这是我的类在用户想要登录时的交互方式:

  1. 用户点击LoginActivity
  2. 中的登录按钮
  3. LoginActivity来电LoginPresenter.signIn()
  4. LoginPresenter来电SessionManager.login()
  5. SessionManager将事件 LOGIN_SUCESSFULL 发送至LoginPresenter
  6. LoginPresenter来电SessionManager.loginToChatService()
  7. 错误
  8. 我知道错误是由于后台线程调用UI线程方法,但login方法效果很好,只有引发此错误的loginToChat方法。

    我该如何解决这个问题?

    由于

2 个答案:

答案 0 :(得分:1)

正如@logcat所说:

  

看起来onEvent方法是由后台线程触发的,不像Android UI事件已经在UI线程上为你调用。

他是对的,onEvent方法是由SessionManager.loginToChat()方法触发的,所以为了解决这个问题,我不得不在UI线程上触发onEvent。

搜索EvenBus Doc后,我在Delivery Threads and Threadmodes部分看到了这一点:

  

EventBus可以为您处理线程:事件可以发布在与发布线程不同的线程中。 (...)

     

在EventBus中,您可以使用ThreadMode定义将调用事件处理方法onEvent的线程(...)

     

MainThread:将在Android的主线程(有时称为UI线程)中调用订阅者。如果发布线程是主线程,则将直接调用事件处理程序方法。使用此模式的事件处理程序必须快速返回以避免阻塞主线程。实施例

// Called in Android UI's main thread
public void onEventMainThread(MessageEvent event) {

   textField.setText(event.message);
}

所以,我必须做的是将onEvent LoginPresenter方法更改为onEventMainThread!这样,LoginPresenter就可以在UI线程上处理收到的事件。

答案 1 :(得分:0)

在loginToChatService()方法中,尝试将登录调用的代码放在runOnUiThread调用中,如下所示:

activity.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        qbChatService.login(currentUser, new QBEntityCallbackImpl() {
        ...
    }
});

活动应该是Activity的一个实例(可能是这个,取决于你的代码所在的位置)。