我正在使用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);
}
});
}
}
这是我的类在用户想要登录时的交互方式:
LoginActivity
LoginActivity
来电LoginPresenter.signIn()
LoginPresenter
来电SessionManager.login()
SessionManager
将事件 LOGIN_SUCESSFULL 发送至LoginPresenter
LoginPresenter
来电SessionManager.loginToChatService()
我知道错误是由于后台线程调用UI线程方法,但login
方法效果很好,只有引发此错误的loginToChat
方法。
我该如何解决这个问题?
由于
答案 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的一个实例(可能是这个,取决于你的代码所在的位置)。