Android高級EventBus使用詳細

28265 ワード

書きたいですが、ネットで見たら、このような感じではないです.http://www.jianshu.com/p/da9e193e8b03
前書き:EventBusが出てきてもうしばらくが経ちました.ギthubの上にも多くのオープンソースプロジェクトの中でEventBusが使われています.暇を見つけて勉強したついでに整理してきました.現在のEventBusの最新バージョンは3.0ですので、本稿はEventBus 3.0に基づいています.
関連記事はEventBusを使用して、EventBusのソースコードを詳しく解析します.
概要
EventBusはAndroidに対するリリース/購読イベントバスです.これは、Androidの様々なコンポーネント間のメッセージの伝達を容易に実現することができ、コードの読み取り可能性がより良く、結合度がより低い.
どう使いますか
(1)まず、任意のベースクラスを引き継がなくても良いし、インターフェースを実装する必要もないメッセージクラスを定義する必要がある.例えば:
public class MessageEvent {
    ......
}
(2)イベントを購読する場所にイベントを登録する
EventBus.getDefault().register(this);
(3)イベントが発生する、すなわちメッセージを送信する
EventBus.getDefault().post(messageEvent);
(4)メッセージの処理
@Subscribe(threadMode = ThreadMode.PostThread)
public void XXX(MessageEvent messageEvent) {
    ...
}
3.0の前にEventBusはまだ注釈方式を使っていません.メッセージ処理の方法はオンEvent、オンEvent MainThread、オンEvent Background Thread、オンEventAyncに限定され、それぞれ四つのスレッドモデルを表します.3.0の後、メッセージ処理の方法は任意に名前を付けることができますが、コメント@Subscribeを追加し、スレッドモデル(デフォルトはPostThread)を指定します.注意してください.イベントハンドラのアクセス権限はpublicでなければ異常となります.
(5)メッセージの購読をキャンセルする
EventBus.getDefault().unregister(this);
何か長所がありますか
メッセージの発行/購読を採用する大きな利点は、コードの簡潔性であり、メッセージ発信者と購読者との結合度を効果的に低減することができることである.例えば、2つのインターフェースがあります.ActivityAとActivityBは、ActivityAインターフェースからActivityBインターフェースに遷移した後、ActivityBはActivityAにメッセージを送信します.ActivityAはメッセージを受信して画面に表示します.最初に考えた方法は、放送を使用して、この必要なコードを実現することです.まず、ActivityAでブロードキャスト受信機を定義する必要があります.
public class MessageBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        mMessageView.setText("Message from SecondActivity:" + intent.getStringExtra("message"));
    }
}
オンクリアー()方法でブロードキャスト受信機を登録する必要があります.
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //    
    EventBus.getDefault().register(this);
    //    
    IntentFilter intentFilter = new IntentFilter("message_broadcast");
    mBroadcastReceiver = new MessageBroadcastReceiver();
    registerReceiver(mBroadcastReceiver, intentFilter);
    ......
}
その後、オンデマンド()方法でブロードキャスト受信機の登録をキャンセルする.
@Override
protected void onDestroy() {
    super.onDestroy();
    ......
    //      
    unregisterReceiver(mBroadcastReceiver);
}
最後にActivityBインターフェースでブロードキャストメッセージを送る必要があります.
findViewById(R.id.send_broadcast).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String message = mMessageET.getText().toString();
        if(TextUtils.isEmpty(message)) {
            message = "defaule message";
        }
        Intent intent = new Intent();
        intent.setAction("message_broadcast");
        intent.putExtra("message", message);
        sendBroadcast(intent);
    }
});
上の実現コードを見ていて、感じも悪くないです.いいです.以下の比較からEventBusを使ってどうやって実現しますか?記事の一番前に述べたEventBusの使用手順によれば、まずメッセージ・イベントクラスを定義する必要があります.
public class MessageEvent {

    private String message;

    public MessageEvent(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
ActivityAインターフェイスでは、まずイベントを登録します.
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //    
    EventBus.getDefault().register(this);
    ......
}
その後、オンデマンド()の方法で購読をキャンセルします.
@Override
protected void onDestroy() {
    super.onDestroy();
    //      
    EventBus.getDefault().unregister(this);
}
もちろん、メッセージ処理の方法も定義される.
@Subscribe(threadMode = ThreadMode.MainThread)
public void onShowMessageEvent(MessageEvent messageEvent) {
    mMessageView.setText("Message from SecondActivity:" + messageEvent.getMessage());
}
これで、情報購読者はもう定義しました.またActivityBでメッセージを発表します.
findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        String message = mMessageET.getText().toString();
        if(TextUtils.isEmpty(message)) {
            message = "defaule message";
        }
        EventBus.getDefault().post(new MessageEvent(message));
    }
});
コードを比べてみたら、誰かが言ってくれます.このニマにはどんな違いがありますか?話が簡潔ですか?みなさん、焦らないでください.ここでは簡単な例を挙げましたが、この例から見ると、EventBusのメリットは現れていません.今は需要を少し変えて、ActivityAがメッセージを受け取ったら、ネットワークサーバからデータを取得して、データを展示する必要があります.ブロードキャストを使用するなら、ActivityAのブロードキャスト受信機コードはこう書くべきである.
public class MessageBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //         
                ......
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        //            
                        ......
                    }
                });
            }
        }).start();
    }
}
このコードを見たら、感想が分かりません.とにかく見ていて気持ちが悪いです.入れ子のレベルが多すぎて、Cleean Codeの原則に違反しました.EventBusを使って実現するのはどうですか?ちょっと見てみます
@Subscribe(threadMode = ThreadMode.BackgroundThread)
public void onGetDataEvent(MessageEvent messageEvent) {
    //         
    ......
    EventBus.getDefault().post(new ShowMessageEvent());
}

@Subscribe(threadMode = ThreadMode.MainThread)
public void onShowDataEvent(ShowMessageEvent showMessageEvent) {
    //            
    ......
}
上記の二つのコードを比較すると、EventBusの優位性が明確に感じられます.コードが簡潔で、明確で、コードの可読性とメンテナンス性が大幅に向上しました.私はこれは簡単な需要を加えただけです.業務がますます複雑になるにつれて、EventBusを使う優勢がますます明らかになりました.
よく使うAPIの紹介
スレッドモデル
イベントハンドラのイベントハンドラ関数では、スレッドモデルを指定する必要があります.すなわち、イベントハンドラの実行先を指定するスレッドです.上記ではEventBusの四つのスレッドモデルに接触しました.彼らはどんな違いがありますか?EventBusでの観察者は通常、PostThread(デフォルト)、MainThread、BackgroundThread、Ayncの4つのスレッドモデルがあります.
  • PostThread:イベント処理関数を使用して、スレッドモデルがPostThreadであることを指定すると、イベントはどのスレッドで発行されたか、イベント処理関数はこのスレッドで実行され、つまりイベントの投稿と受信は同じスレッドで行われる.スレッドモデルがPostThreadであるイベントハンドリング関数では、イベントの転送をブロックしたり、ANRを引き起こす可能性があるので、時間のかかる動作はできるだけ避けるようにしています.
  • MainThread:イベント処理関数を使用してスレッドモデルがMainThreadであることを指定すると、イベントはどのスレッドで発表されたかに関わらず、イベント処理関数はUIスレッドで実行される.この方法はUIを更新するために使用されてもよいが、時間のかかる動作を処理することはできない.
  • Background Thread:イベントハンドラ関数を使用して、スレッドモデルがBackgroundThreadであることを指定した場合、イベントがUIスレッドで発表された場合、イベントハンドラ関数は新しいスレッドで実行され、イベントが元々サブスレッドで発生した場合、イベントハンドラ関数はそのままイベントのスレッドで実行される.このイベント処理関数では、UI更新操作は禁止されています.
  • Async:イベント処理関数を使用してスレッドモデルがAsyncであることを指定すると、イベントはどのスレッドで発行されても、このイベント処理関数は新しいサブスレッドで実行されます.同様に、このイベント処理関数ではUI更新操作が禁止されています.
  • 以上の四つの方法を検証するために、小さな例を書きました.
    @Subscribe(threadMode = ThreadMode.PostThread) public void onMessageEventPostThread(MessageEvent messageEvent) {
        Log.e("PostThread", Thread.currentThread().getName());
    }
    
    @Subscribe(threadMode = ThreadMode.MainThread) public void onMessageEventMainThread(MessageEvent messageEvent) {
        Log.e("MainThread", Thread.currentThread().getName());
    }
    
    @Subscribe(threadMode = ThreadMode.BackgroundThread) public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
        Log.e("BackgroundThread", Thread.currentThread().getName());
    }
    
    @Subscribe(threadMode = ThreadMode.Async) public void onMessageEventAsync(MessageEvent messageEvent) {
        Log.e("Async", Thread.currentThread().getName());
    }
    上記の4つの方法を使って、同じイベントを購読し、彼らが実行しているスレッドを印刷します.まず、UIスレッドでMessage Eventのメッセージを発表します.ログの印刷結果は何ですか?
    findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e("postEvent", Thread.currentThread().getName());
                EventBus.getDefault().post(new MessageEvent());
            }
        });
    印刷結果は以下の通りです.
    2689-2689/com.lling.eventbusdemo E/postEvent﹕ main
    2689-2689/com.lling.eventbusdemo E/PostThread﹕ main
    2689-3064/com.lling.eventbusdemo E/Async﹕ pool-1-thread-1
    2689-2689/com.lling.eventbusdemo E/MainThread﹕ main
    2689-3065/com.lling.eventbusdemo E/BackgroundThread﹕ pool-1-thread-2
    ログ印刷の結果から、UIスレッドにイベントが投稿されると、スレッドモデルはPostThreadのイベント処理関数として、UIスレッドでも実行され、イベントのスレッドと一致することが分かります.スレッドモデルはAsyncのイベントハンドラ関数として、pool-1-thread-1という名前の新しいスレッドに実行されます.MainThreadのイベントハンドリング関数は、UIスレッドで実行され、BackgroundThreadのタイムハンドリング関数は、pool-1-thread-2という名前の新しいスレッドに実行されます.
    サブスレッドでMessage Eventのメッセージを発表すると、どのような結果が出るか見てみましょう.
    findViewById(R.id.send).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("postEvent", Thread.currentThread().getName());
                        EventBus.getDefault().post(new MessageEvent());
                    }
                }).start();
            }
        });
    印刷結果は以下の通りです.
    3468-3945/com.lling.eventbusdemo E/postEvent﹕ Thread-125
    3468-3945/com.lling.eventbusdemo E/PostThread﹕ Thread-125
    3468-3945/com.lling.eventbusdemo E/BackgroundThread﹕ Thread-125
    3468-3946/com.lling.eventbusdemo E/Async﹕ pool-1-thread-1
    3468-3468/com.lling.eventbusdemo E/MainThread﹕ main
    ログ印刷の結果から、スレッドモデルがPostThreadのイベント処理関数であることが分かります.スレッドモデルは、スレッドと一致します.Background Threadイベントモデルもリリースイベントと同じスレッドで実行されます.Asyncはpool-1-thread-1という新しいスレッドで実行します.MainThreadはまだUIスレッドで実行されます.
    上記の例では、異なるスレッドモデルを指定するイベント処理方法の実行スレッドを十分に検証した.
    粘着性事件
    上記のような一般的なイベント以外にも、イベントBussは粘っこいイベントの送信をサポートします.何が粘着性事件ですか?簡単に言えば、イベントを送信してから購読しても、このイベントを受け取ることができます.粘性放送と似ています.具体的な使い方は以下の通りです.
    粘着性イベントを購読:
    EventBus.getDefault().register(StickyModeActivity.this);
    粘性事象処理関数:
    @Subscribe(sticky = true)
    public void XXX(MessageEvent messageEvent) {
        ......
    }
    粘着性イベントを送信:
    EventBus.getDefault().postSticky(new MessageEvent("test"));
    メッセージイベントの処理およびキャンセルは上記と同じです.
    簡単な粘着性事件の例を見て、簡単のためにここでActivityで実証しました.
    Activityコード:
    public class StickyModeActivity extends AppCompatActivity {
    
        int index = 0;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sticky_mode);
            findViewById(R.id.post).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    EventBus.getDefault().postSticky(new MessageEvent("test" + index++));
                }
            });
            findViewById(R.id.regist).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    EventBus.getDefault().registerSticky(StickyModeActivity.this);
                }
            });
    
            findViewById(R.id.unregist).setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    EventBus.getDefault().unregister(StickyModeActivity.this);
                }
            });
    
        }
    
        @Subscribe(threadMode = ThreadMode.PostThread, sticky = true)
        public void onMessageEventPostThread(MessageEvent messageEvent) {
            Log.e("PostThread", messageEvent.getMessage());
        }
    
        @Subscribe(threadMode = ThreadMode.MainThread, sticky = true)
        public void onMessageEventMainThread(MessageEvent messageEvent) {
            Log.e("MainThread", messageEvent.getMessage());
        }
    
        @Subscribe(threadMode = ThreadMode.BackgroundThread, sticky = true)
        public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
            Log.e("BackgroundThread", messageEvent.getMessage());
        }
    
        @Subscribe(threadMode = ThreadMode.Async, sticky = true)
        public void onMessageEventAsync(MessageEvent messageEvent) {
            Log.e("Async", messageEvent.getMessage());
        }
    
    }
    レイアウトコードactivity_stickymode.xml:
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:paddingBottom="@dimen/activity_vertical_margin" android:orientation="vertical" tools:context="com.lling.eventbusdemo.StickyModeActivity">
    
        <Button android:id="@+id/post" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Post"/>
    
        <Button android:id="@+id/regist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Regist"/>
    
        <Button android:id="@+id/unregist" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="UnRegist"/>
    
    </LinearLayout>
    コードはとても簡単で、インタフェースの上で3つのボタン、ひとつは粘っこい性のイベントを発送して、一つはイベントを購読することに用いて、もう一つは購読をキャンセルすることに用います.まず、未購読の場合は送信ボタンをクリックして、粘着性イベントを送信し、購読をクリックします.ログの印刷結果は以下の通りです.
    15246-15246/com.lling.eventbusdemo E/PostThread﹕ test0
    15246-15391/com.lling.eventbusdemo E/Async﹕ test0
    15246-15246/com.lling.eventbusdemo E/MainThread﹕ test0
    15246-15393/com.lling.eventbusdemo E/BackgroundThread﹕ test0
    これはスティッキーイベントで、購読前に送信されたメッセージを受信することができます.しかし、これは最新のメッセージしか受信できません.例えば、未購読の前にもう複数の粘性メッセージを送りました.そして購読は一番近いニュースしか受信できません.これは確認できます.私たちは5回連続してPOSTボタンを押して5つの粘着性イベントを送ります.そしてREGISTボタンをクリックして購読します.印刷結果は以下の通りです.
    6980-6980/com.lling.eventbusdemo E/PostThread﹕ test4
    6980-6980/com.lling.eventbusdemo E/MainThread﹕ test4
    6980-7049/com.lling.eventbusdemo E/Async﹕ test4
    6980-7048/com.lling.eventbusdemo E/BackgroundThread﹕ test4
    印刷の結果から見れば、確かに一番近い粘着性のイベントしか受信していません.
    はい、EventBusの使用はここまでしばらく分析して、サンプルコードはここから取得します.次はEventBusのソースコードを説明します.
    最初の記事:http://liuling123.com/2016/01/EventBus-explain.html
    文/Lauren_Liuling(簡書作者)
    原文のリンク:http://www.jianshu.com/p/da9e193e8b03
    著作権は著者の所有になります.転載は著者に連絡して授権し、「略書の作者」と表示してください.