转载请标明出处: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 包,代码更优雅,彻底解耦。

github地址:https://github.com/greenrobot/EventBus

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.POSTINGThreadMode.MAINThreadMode.BACKGROUNDThreadMode.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 使用总结的更多相关文章

  1. Android EventBus 3.0 实例使用详解

    EventBus的使用和原理在网上有很多的博客了,其中泓洋大哥和启舰写的非常非常棒,我也是跟着他们的博客学会的EventBus,因为是第一次接触并使用EventBus,所以我写的更多是如何使用,源码解 ...

  2. Unity3D 解决用Unity导出的Android工程在6.0及以上设备会弹出一串权限对话框的问题

    解决用Unity导出的Android工程在6.0及以上设备会弹出一串权限对话框的问题 <meta-data android:name="unityplayer.SkipPermissi ...

  3. android异常: java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused

    android手机做下载文件时,报了如下异常: java.net.ConnectException: localhost/127.0.0.1:8080 - Connection refused 模拟器 ...

  4. [转] 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 ...

  5. [原]如何在Android用FFmpeg+SDL2.0解码图像线程

    关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,关于如何在Android使用FFmpeg+SDL2.0解码声 ...

  6. [原]如何在Android用FFmpeg+SDL2.0解码声音

    关于如何在Android上用FFmpeg+SDL2.0解码显示图像参考[原]如何在Android用FFmpeg+SDL2.0解码显示图像 ,本文是基于上述文章和[原]零基础学习视频解码之解码声音 来移 ...

  7. [原]如何在Android用FFmpeg+SDL2.0解码显示图像

    如何在Android上使用FFmpeg解码图像参考文章[原]如何在Android用FFmpeg解码图像 ,如何在Android上使用SDL2.0来显示图像参考[原]零基础学习SDL开发之在Androi ...

  8. eclipse一直卡住,出现 “android sdk content loader 0%” 卡住的错误分析及解决方法

    分析:这种问题之前没有遇到过,也不知道什么原因,直接去网上查询,打开www.stackoverflow.com,输入要查询问题的关键词,我们输入 “android sdk content loader ...

  9. [转]android访问网络:java.net.ConnectException: localhost/127.0.0.1:8888 - Connection refused

    这对刚学会向tomcat模拟的本地服务器发送请求的同学非常重要! 转自:http://wing123.iteye.com/blog/1873763 描述:在做注册功能的时候,向本地服务器:127.0. ...

随机推荐

  1. js月份,日期加一天

    js没有直接可以用的函数,所以只能自己写,其中需要涉及到每个月天数的判断,如果是2月份的话,还要涉及到闰年的判断 var addDate = { //日期,在原有日期基础上,增加days天数,默认增加 ...

  2. hasOwnProperty()、propertyIsEnumerable()和isPrototypeOf()的用法

    javascript中有原型这么一个概念,任何一个构造函数都有它对应的原型(prototype),我们可以给这个原型赋予一些我们想要的属性,像下面这样: function Gadget(name, c ...

  3. .NET Core dotnet 命令大全

    dotnet 命令大全,让你理解dotnet 命令. 本文将以一个实例串起 dotnet 所有命令,让你玩转dotnet 命令. 本篇文章编写环境为windows 10 ,dotnet 命令同样适用于 ...

  4. [httpserver]如何解析HTTP请求报文

    这个http server的实现源代码我放在了我的github上,有兴趣的话可以点击查看哦. 在上一篇文章中,讲述了如何编写一个最简单的server,但该程序只是接受到请求之后马上返回响应,实在不能更 ...

  5. C++的性能C#的产能?! - .Net Native 系列五:.Net Native与反射

    此系列系小九的学堂原创翻译,翻译自微软官方开发向导,一共分为六个主题.本文是第五个主题:.Net Native与反射. 向导文链接:<C++的性能C#的产能?! - .Net Native 系列 ...

  6. 反向代理与 Real-IP 和 X-Forwarded-For

    开篇语:开涛新作<亿级流量网站架构核心技术>出版计划公布以来,博文视点遭受到一波又一波读者询问面世时间的DDos攻击.面对亿级流量的热情,感激之余,我们也很庆幸——这部作品质量的确过硬,不 ...

  7. 使用 Arrays 类操作 Java 中的数组

    Arrays 类是 Java 中提供的一个工具类,在 java.util 包中.该类中包含了一些方法用来直接操作数组,比如可直接实现数组的排序.搜索等(关于类和方法的相关内容在后面的章节中会详细讲解滴 ...

  8. HTML5_05之SVG扩展、地理定位、拖放

    1.SVG绘图总结: ①方法一:已有svg文件,<img src="x.svg">  方法二:<body><svg></svg>&l ...

  9. Asp.net中存储过程拖拽至dbml文件中,提示无法获得返回值

    Asp.net中存储过程拖拽至dbml文件中,提示无法获得返回值,去属性表中设置这时候会提示你去属性表中更改返回类型. 其实存储过程返回的也是一张表,只不过有时候存储过程有点复杂或者写法不规范的话不能 ...

  10. 初识 Sql Server存储过程

    开篇语 之前的公司并未使用存储过程来做项目,所以小生对存储过程的调用.使用也是一知半解,刚好这家公司就大量用到了存储过程 这次做的功能,为了保持风格一致,也是需要使用存储过程来实现动态sql和数据分页 ...