Android EventBus 3.0.0 使用总结
转载请标明出处:http://www.cnblogs.com/zhaoyanjun/p/6039221.html
本文出自【赵彦军的博客】
前言
- EventBus框架
EventBus是一个通用的叫法,例如Google出品的Guava,Guava是一个庞大的库,EventBus只是它附带的一个小功能,因此实际项目中使用并不多。用的最多的是greenrobot/EventBus,这个库的优点是接口简洁,集成方便,但是限定了方法名,不支持注解。另一个库square/otto修改自 Guava ,用的人也不少。所以今天我们研究的目标是greenrobot的EventBus.
- EventBus 简介
1、EventBus3.0.0 是最新的版本。
2、EventBus 是Android 发布/订阅事件总线,可简化 Activities, Fragments, Threads, Services 等组件间的消息传递。
3、可替代 Intent, Handler, BroadCast ,接口等传统方案,更快,代码更小,50K 左右的 jar 包,代码更优雅,彻底解耦。
EventBus原理图
如何添加依赖
- 在module的build.gredle 文件中的dependencies标签中添加
compile 'org.greenrobot:eventbus:3.0.0'
例如
 apply plugin: 'com.android.application'
android {
    compileSdkVersion 24
    buildToolsVersion "24.0.3"
    defaultConfig {
        applicationId "com.eventbus.app"
        minSdkVersion 14
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.1'
    compile 'org.greenrobot:eventbus:3.0.0'
}
如何使用
- 注册事件
EventBus.getDefault().register( this );
- 取消注册
EventBus.getDefault().unregister( this );
- 发送数据
EventBus.getDefault().post( "我发射了");
简单小例子:使用EventBus传递简单字符串
package com.eventbus.app;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册
        EventBus.getDefault().register( this );
        findViewById( R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().post( "我发射了");
            }
        });
    }
    /**
     *  自定义一个方法 hello() ,用来接收事件。
     *  方法名字可以随便写
     * @return
     */
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void hello ( String event){
        /* Do something */
        Toast.makeText( this , event , Toast.LENGTH_SHORT).show();
    };
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消注册 , 防止Activity内存泄漏
        EventBus.getDefault().unregister( this );
    }
}
效果图
线程模型
在接收事件消息的方法中,可以通过注解的方式设置线程模型,EventBus内置了4中线程模型,分别是ThreadMode.POSTING 、ThreadMode.MAIN、ThreadMode.BACKGROUND、ThreadMode.ASYNC
比如:
 @Subscribe(threadMode = ThreadMode.POSTING)
    public void onMessageEventPostThread(String event) {
        Log.e( "event PostThread", "消息: " + event + "  thread: " + Thread.currentThread().getName()  );
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEventMainThread(String event) {
        Log.e( "event MainThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onMessageEventBackgroundThread(String event) {
        Log.e( "event BackgroundThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onMessageEventAsync(String event) {
        Log.e( "event Async",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
- PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在这个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
- MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
- BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
- Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新操作。
小例子1: 在子线程发送数据
package com.eventbus.app;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册
        EventBus.getDefault().register( this );
        findViewById( R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Log.d( "event 发射数据线程 : " , Thread.currentThread().getName() ) ;
                        EventBus.getDefault().post( "我发射了");
                    }
                }).start() ;
            }
        });
    }
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onMessageEventPostThread(String event) {
        Log.e( "event PostThread", "消息: " + event + "  thread: " + Thread.currentThread().getName()  );
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEventMainThread(String event) {
        Log.e( "event MainThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onMessageEventBackgroundThread(String event) {
        Log.e( "event BackgroundThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onMessageEventAsync(String event) {
        Log.e( "event Async",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消注册 , 防止Activity内存泄漏
        EventBus.getDefault().unregister( this );
    }
}
运行结果:
D/event 发射数据线程 :: Thread-109
E/event BackgroundThread: 消息: 我发射了 thread: Thread-109
E/event PostThread: 消息: 我发射了 thread: Thread-109
E/event Async: 消息: 我发射了 thread: pool-1-thread-2
E/event MainThread: 消息: 我发射了 thread: main
小例子2: 在主线程发送数据
package com.eventbus.app;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册
        EventBus.getDefault().register( this );
        findViewById( R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d( "event 发射数据线程 : " , Thread.currentThread().getName() ) ;
                EventBus.getDefault().post( "我发射了");
            }
        });
    }
    @Subscribe(threadMode = ThreadMode.POSTING)
    public void onMessageEventPostThread(String event) {
        Log.e( "event PostThread", "消息: " + event + "  thread: " + Thread.currentThread().getName()  );
    }
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEventMainThread(String event) {
        Log.e( "event MainThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void onMessageEventBackgroundThread(String event) {
        Log.e( "event BackgroundThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void onMessageEventAsync(String event) {
        Log.e( "event Async",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消注册 , 防止Activity内存泄漏
        EventBus.getDefault().unregister( this );
    }
}
运行结果:
D/event 发射数据线程 :: main
E/event MainThread: 消息: 我发射了 thread: main
E/event PostThread: 消息: 我发射了 thread: main
E/event Async: 消息: 我发射了 thread: pool-1-thread-3
E/event BackgroundThread: 消息: 我发射了 thread: pool-1-thread-4
黏性事件
除了上面讲的普通事件外,EventBus还支持发送黏性事件。何为黏性事件呢?简单讲,就是在发送事件之后再订阅该事件也能收到该事件,跟黏性广播类似。具体用法如下:
- 注册
EventBus.getDefault().register( this );
- 事件接收
@Subscribe(threadMode = ThreadMode.MAIN , sticky = true )
public void onMessageEventMainThread(String event) {
Log.e( "event MainThread", "消息: " + event + " thread: " + > Thread.currentThread().getName());
}
- 取消注册
EventBus.getDefault().unregister( this ) ;
- 发送事件
EventBus.getDefault().postSticky( "我发射了");
小例子:在MainActivity发送事件,在Activity2里注册并且接收事件
MainActivity源码
package com.eventbus.app;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import org.greenrobot.eventbus.EventBus;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById( R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d( "event 发射数据线程 : " , Thread.currentThread().getName() ) ;
                EventBus.getDefault().postSticky( "我发射了");
                startActivity( new Intent( MainActivity.this , Activity2.class ));
            }
        });
    }
}
Activity2源码
package com.eventbus.app;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
public class Activity2 extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_2);
        //注册
        EventBus.getDefault().register( this );
    }
    @Subscribe(threadMode = ThreadMode.MAIN  , sticky =  true )
    public void onMessageEventMainThread(String event) {
        Log.e( "event MainThread",  "消息: " + event + "  thread: " + Thread.currentThread().getName());
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        //取消注册 , 防止Activity内存泄漏
        EventBus.getDefault().unregister( this ) ;
    }
}
这就是粘性事件,能够收到订阅之前发送的消息。但是它只能收到最新的一次消息,比如说在未订阅之前已经发送了多条黏性消息了,然后再订阅只能收到最近的一条消息。
EventBus源码分析
- Subscribe 接口源码
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    ThreadMode threadMode() default ThreadMode.POSTING;
    /**
     * If true, delivers the most recent sticky event (posted with
     * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
     */
    boolean sticky() default false;
    /** Subscriber priority to influence the order of event delivery.
     * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
     * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
     * delivery among subscribers with different {@link ThreadMode}s! */
    int priority() default 0;
}
可以看出默认的线程模型是ThreadMode.POSTING ;默认黏性事件为false,也就是默认不开启黏性事件;默认的优选级为0 。
- EventBus 类部分源码
  static volatile EventBus defaultInstance;
   /** Convenience singleton for apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
getDefault() 是一个单例模式 , 只有一个实例对象。
- ThreadMode 类源码
public enum ThreadMode {
    /**
     * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,
    /**
     * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is
     * the main thread, event handler methods will be called directly. Event handlers using this mode must return
     * quickly to avoid blocking the main thread.
     */
    MAIN,
    /**
     * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to
     * return quickly to avoid blocking the background thread.
     */
    BACKGROUND,
    /**
     * Event handler methods are called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
     */
    ASYNC
}
这个类是枚举类,定义了线程模型中的几种类型。
参考资料
【EventBus 3.0的用法详解】
【EventBus使用详解】
【腾讯Bugly干货】老司机教你“飙”EventBus3】
Android EventBus 3.0.0 使用总结的更多相关文章
- Android EventBus 3.0 实例使用详解
		EventBus的使用和原理在网上有很多的博客了,其中泓洋大哥和启舰写的非常非常棒,我也是跟着他们的博客学会的EventBus,因为是第一次接触并使用EventBus,所以我写的更多是如何使用,源码解 ... 
- Unity3D 解决用Unity导出的Android工程在6.0及以上设备会弹出一串权限对话框的问题
		解决用Unity导出的Android工程在6.0及以上设备会弹出一串权限对话框的问题 <meta-data android:name="unityplayer.SkipPermissi ... 
- android异常:  java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused
		android手机做下载文件时,报了如下异常: java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused 模拟器 ... 
- [转] This Android SDK requires Android Developer Toolkit version 23.0.0 or above
		问题描述: This Android SDK requires Android Developer Toolkit version 23.0.0 or above. Current version ... 
- [原]如何在Android用FFmpeg+SDL2.0解码图像线程
		关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,关于如何在Android使用FFmpeg+SDL2.0解码声 ... 
- [原]如何在Android用FFmpeg+SDL2.0解码声音
		关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,本文是基于上述文章和[原]零基础学习视频解码之解码声音 来移 ... 
- [原]如何在Android用FFmpeg+SDL2.0解码显示图像
		如何在Android上使用FFmpeg解码图像参考文章[原]如何在Android用FFmpeg解码图像 ,如何在Android上使用SDL2.0来显示图像参考[原]零基础学习SDL开发之在Androi ... 
- eclipse一直卡住,出现 “android sdk content loader 0%” 卡住的错误分析及解决方法
		分析:这种问题之前没有遇到过,也不知道什么原因,直接去网上查询,打开www.stackoverflow.com,输入要查询问题的关键词,我们输入 “android sdk content loader ... 
- [转]android访问网络:java.net.ConnectException: localhost/127.0.0.1:8888 - Connection refused
		这对刚学会向tomcat模拟的本地服务器发送请求的同学非常重要! 转自:http://wing123.iteye.com/blog/1873763 描述:在做注册功能的时候,向本地服务器:127.0. ... 
随机推荐
- .NET开发邮件发送功能的全面教程(含邮件组件源码)
			今天,给大家分享的是如何在.NET平台中开发“邮件发送”功能.在网上搜的到的各种资料一般都介绍的比较简单,那今天我想比较细的整理介绍下: 1) 邮件基础理论知识 2) ... 
- ABP(现代ASP.NET样板开发框架)系列之21、ABP展现层——Javascript函数库
			点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之21.ABP展现层——Javascript函数库 ABP是“ASP.NET Boilerplate Project ... 
- The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files.
			参照 http://stackoverflow.com/questions/24301986/the-type-java-lang-charsequence-cannot-be-resolved-in ... 
- Material Design Reveal effect(揭示效果) 你可能见过但是叫不出名字的小效果
			Material Design Reveal effect(揭示效果) 你可能见过但是叫不出名字的小效果 前言: 每次写之前都会来一段(废)话.{心塞...} Google Play首页两个tab背景 ... 
- Winform下CefSharp的引用、配置、实例与报错排除(源码)
			Winform下CefSharp的引用.配置.实例与报错排除 本文详细介绍了CefSharp在vs2013..net4.0环境下,创建Winfrom项目.引用CefSharp的方法,演示了winfro ... 
- Atitit rss没落以及替代品在线阅读器
			Atitit rss没落以及替代品在线阅读器 1.1. 对RSS的疯狂追逐,在2005年达到了一个高峰.1 1.2. Rss的问题,支持支rss,不支持url1 1.3. ,博客受到社交网络的冲击.s ... 
- Bootstrap WPF Style,Bootstrap风格的WPF样式
			简介 GitHub地址:https://github.com/ptddqr/bootstrap-wpf-style 此样式基于bootstrap-3.3.0,样式文件里的源码行数都是指的这个版本.CS ... 
- 《ES6基础教程》之 map、forEach、filter indexOf 用法
			1,map,对数组的每个元素进行一定操作,返回一个新的数组. var oldArr = [{first_name:"Colin",last_name:"Toh" ... 
- [.NET] 开头不讲"Hello Word",读尽诗书也枉然 : Word 操作组件介绍 - Spire.Doc
			开头不讲"Hello Word",读尽诗书也枉然 : Word 操作组件介绍 - Spire.Doc [博主]反骨仔 [原文地址]http://www.cnblogs.com/li ... 
- 剖析并利用Visual Studio Code在Mac上编译、调试c#程序
			0x00 前言 一周多以前的微软的Build大会上,微软发布了一个让很多人眼前一亮的工具,也是本文的主角——Visual Studio Code.很多使用Windows的朋友都很高兴,认为又多了一个很 ... 
