EventBus 3.0接入問題

RomRSDY 8年前發布 | 9K 次閱讀 EventBus Android開發 移動開發

在開發過程中,為了避免頁面之間的數據層層傳遞,很容易就想到使用訂閱/發布的形式來傳遞數據,有一個出名開源庫就是做這類事情的 

那么如何在自己的項目中集成這個庫呢?在上面給出的鏈接中,官方給出了集成的步驟,為了方便說明,直接貼過來:

EventBus in 3 steps

  • Define events:
    public static class MessageEvent { /* Additional fields if needed */ }
  • Prepare subscribers: Declare and annotate your subscribing method, optionally specify a thread mode :
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};
  • Register and unregister your subscriber. For example on Android, activities and fragments should usually register according to their life cycle:
    @Overridepublic
    void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }
    @Overridepublic
    void onStop() { 
         super.onStop();
         EventBus.getDefault().unregister(this);
    }

Post events:

EventBus.getDefault().post(new MessageEvent());

看完這個步驟,那么開始著手寫一個樣例,樣例是這樣的:

有三個頁面,第一個頁面點擊一個按鈕進入到第二個頁面,第二個頁面點擊一個按鈕進入到第三個頁面,第三個頁面點擊一個按鈕發出消息,第一個頁面和第二個頁面接收消息并展示出來,如下圖

下面開始編寫代碼,也很簡單:

FirstActivity.java

package com.vip.veblonwang.eventbus;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class FirstActivity extends Activity {

    private TextView tvMsg;
    private Button btnGoNext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        tvMsg = (TextView) findViewById(R.id.tvMsg);
        btnGoNext = (Button) findViewById(R.id.btnGoNext);

        btnGoNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
                startActivity(intent);
            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(SendMsgEvent event) {
        final String msg = event.getMsg();
        tvMsg.setText(msg);
    }
}

activity_first.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="頁面一" />

    <Button
        android:id="@+id/btnGoNext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="下一個頁面"/>

    <TextView
        android:id="@+id/tvMsg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</LinearLayout>

SecondActivity.java

package com.vip.veblonwang.eventbus;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

public class SecondActivity extends Activity {

    private TextView tvMsg;
    private Button btnGoNext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        tvMsg = (TextView) findViewById(R.id.tvMsg);
        btnGoNext = (Button) findViewById(R.id.btnGoNext);

        btnGoNext.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this, ThirdActivity.class);
                startActivity(intent);
            }
        });

    }

    @Override
    protected void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(SendMsgEvent event) {
        String msg = event.getMsg();
        tvMsg.setText(msg);
    }

}

activity_second.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="頁面二" />

    <Button
        android:id="@+id/btnGoNext"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="下一個頁面"/>

    <TextView
        android:id="@+id/tvMsg"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />
</LinearLayout>

ThirdActivity.java

package com.vip.veblonwang.eventbus;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import org.greenrobot.eventbus.EventBus;

public class ThirdActivity extends Activity {

    private Button btnSendMsg;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_third);

        btnSendMsg = (Button) findViewById(R.id.btnSendMsg);
        btnSendMsg.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post(new SendMsgEvent("三千弱水,不飲一瓢!"));
            }
        });
    }
}

activity_third.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="第三個頁面" />

    <Button
        android:id="@+id/btnSendMsg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="發消息"/>
</LinearLayout>

到目前為止,三個頁面都寫完了,可以發現,官方定義的步驟在上面的代碼中都已經體現出來了,除了第一步,自己定義一個Event類(Define events)

SendMsgEvent.java

package com.vip.veblonwang.eventbus;

public class SendMsgEvent {

    private String msg;

    public SendMsgEvent(String msg) {
        this.msg = msg;
    }

    public String getMsg() {
        return msg;
    }
}

按照要求都做完了,接下來可以滿心歡喜地運行demo來看結果 ~~

demo的主頁面是頁面一,點擊按鈕后進入到頁面二,在頁面二點擊按鈕進入到第三個頁面,在第三個頁面點擊發消息后,一切正常。

關鍵的時刻到了,如果點擊返回鍵,會回到頁面二,頁面一,按照預計,頁面二和頁面一會展示出頁面三發出的消息。

可是,當你點擊返回的時候,看到的并非這樣,消息發出去了,但頁面一和頁面二并沒有接收到消息。

為什么會這樣呢?

仔細分析一下代碼,找到了問題所在,當頁面一進入到頁面二時,頁面一變不可見,其生命周期會調用onStop()方法,而在onStop()方法里,我們注銷了對消息事件的監聽(調用了 EventBus.getDefault().unregister(this); ),頁面二進入到頁面三時,也是一樣。所以在頁面三里發消息,他們是接收不到的。

如何解決呢?

我們期望在頁面變成不可見時,不要注銷掉eventbus,把注銷代碼寫在生命周期的onStop()事件中。在頁面一和頁面二里刪除onStart()方法,加上下面代碼。

@Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

修改完成后,再次運行demo,結果還是出人意料 ~

頁面三發完消息點擊返回時,程序無異常卻重啟了,查找日志卻只看見 I/Process: Sending signal. PID: 16048 SIG: 9

what the fuck !~什么情況?繼續排查問題出在什么地方 ~

在接收消息的方法里加入Toast提示:

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(SendMsgEvent event) {
        final String msg = event.getMsg();
        tvMsg.setText(msg);
        Toast.makeText(this, event.getMsg(), Toast.LENGTH_SHORT).show();
    }

再次運行demo,在頁面三點擊發消息時,頁面一和頁面二確實收到了消息(因為有Toast彈出),但是當從頁面三返回到頁面二時就重啟了應用。

好的,那么問題出在了頁面二由不可見到可見這一個過程當中。

這個過程中會執行onRestart()-->onStart()-->onResume(),而這三個方法中我們只重寫了一個onStart(),并且里面只有一句話,那就是注冊eventbus。難道說是 一個頁面不能在未注銷eventbus時再注冊一次eventbus ?

如果是這樣,那么eventbus的注冊事件就不能寫在onStart()里,而應該寫在onCreate()里。

為了驗證這個想法,我們把eventbus注冊代碼寫到onCreate()方法里,再次運行demo,一切順利~~~

寫了這么多,其實是想說,應該 把注冊寫在onCreate()里,注銷寫在onDestory()里 ,不要被官方文檔給騙了~

 

來自:http://www.jianshu.com/p/2d4e435a3413

 

 本文由用戶 RomRSDY 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!