EventBus 简介 EventBus是一种用于Android的事件发布-订阅总线,由GreenRobot开发,Gihub地址是:EventBus。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是碎片之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。
1.1 三个角色
- Event:事件,它可以是任意类型,EventBus会根据事件类型进行全局的通知。
- Subscriber:事件订阅者,在EventBus 3.0之前我们必须定义以onEvent开头的那几个方法,分别是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件处理的方法名可以随意取,不过需要加上注解@subscribe,并且指定线程模型,默认是POSTING。
- Publisher:事件的发布者,可以在任意线程里发布事件。一般情况下,使用EventBus.getDefault()就可以得到一个EventBus对象,然后再调用post(Object)方法即可。
1.2 四种线程模型
EventBus3.0有四种线程模型,分别是:
- POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
- MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
- BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
- ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
先看下以下的一种场景:
现在有在同一个进程的ActivityA和ActivityB,现在的简单需求是点击ActivityB中的按钮,修改ActivityA中TextView中的内容.
public class ActivityA extends Activity implements View.OnClickListener {
private TextView textView;
public static ActivityA
activityA;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_a);
textView = findViewById(R.id.
tv);
Button button = findViewById(R.id.
btnStartB);
button.setOnClickListener(this);
activityA = this;
}
public void setTextView(){
textView.setText("ActivityB set ActivityA");
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(this,ActivityB.class);
startActivity(intent);
}
public void onDestroy(){
super.onDestroy();
activityA = null;
//防止内存漏洞}
}
public class ActivityB extends Activity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_b);
Button button = findViewById(R.id.
btn);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ActivityA.
activityA.setTextView();
}
}
以上的代码是以一种,这种方式的问题是直接引用在ActivityB引用了ActivityA类,耦合度高。
现在做以下的优化:
新增ActivityGloble
public class ActivityGloble {
privateActivityA activityA;
public static ActivityGloble
activityGloble = new ActivityGloble();
public void setActivityA(ActivityA activityA){
this.activityA = activityA;
}
public void setTextView(){
activityA.setTextView();
}
}
public class ActivityA extends Activity implements View.OnClickListener {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_a);
textView = findViewById(R.id.
tv);
Button button = findViewById(R.id.
btnStartB);
button.setOnClickListener(this);
ActivityGloble.
activityGloble.setActivityA(this);
//activityA = this;
}
public void setTextView(){
textView.setText("ActivityB set ActivityA");
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(this,ActivityB.class);
startActivity(intent);
}
public void onDestroy(){
super.onDestroy();
ActivityGloble.
activityGloble.setActivityA(null);
//防止内存漏洞}
}
public class ActivityB extends Activity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_b);
Button button = findViewById(R.id.
btn);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ActivityGloble.
activityGloble.setTextView();
}
}
现在看起来已经优雅了很多了,但是还有以下一些问题要:
- 如果不是ActivityA变成了其他的ActivityC或者其他的类,要怎么处理?
- 现在是只有一个方法,如果方法多了,ActivityGloble是要手动增加方法的,如何才能做到自动获取方法?
针对以上两点,要做如下的改动:将要引用的类改成Object类型,要被执行的方法通过反射获取。
public class ActivityGloble {
privateObject subscriber;
//要订阅的类,public static ActivityGloble
activityGloble = new ActivityGloble();
private HashMap methodMap = new HashMap<>();
//需要被执行方法map/** @param subscriber//要订阅的类* @param postMethodNames,订阅类要执行的方法列表*/public void register(Object subscriber,String[] postMethodNames){
this.subscriber = subscriber;
if(subscriber == null){
return;
}
if(postMethodNames != null){
Class> subscriberClass = subscriber.getClass();
//获取类Method[] methods = subscriberClass.getDeclaredMethods();
//获取所有方法for(int i = 0;
i < methods.length;
i++){
for(int j = 0;
j < postMethodNames.length;
j++){
if(methods[i].getName().equals(postMethodNames[j])){
StringBuilder paramTypeBuilde = null;
//用于处理函数的重载Class>[] paramTypes = methods[i].getParameterTypes();
if(paramTypes != null && paramTypes.length > 0){
paramTypeBuilde = new StringBuilder();
for(int k = 0;
k < paramTypes.length;
k++){
paramTypeBuilde.append(paramTypes[k].getName());
//取出函数对应的参数}
}
if(paramTypeBuilde == null){
methodMap.put(postMethodNames[j],methods[i]);
//如果这个函数没有参数}else{
//如果有参数,参数也和方法一起,作为一个key//比如setTextView(String arg1,Integer arg3)会转化为 setTextViewjava.lang.Stringjava.lang.IntegermethodMap.put(postMethodNames[j]+paramTypeBuilde.toString(),methods[i]);
}
}
}
}
}
}
/*** @param methodName* @param args,要传入的参数*/public void post(String methodName, Object... args) {
if(args != null && args.length > 0){
//如果有参数,对方法进行拼接StringBuilder builder = new StringBuilder();
builder.append(methodName);
for(int i = 0;
i < args.length;
i++){
builder.append(args[i].getClass().getName());
}
methodName = builder.toString();
}
Method method = methodMap.get(methodName);
if(method != null){
try {
if(args == null || args.length == 0){
method.invoke(subscriber);
}else{
method.invoke(subscriber,args);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
}
对于post方法,这里要进行说明下:
因为是Object... args这样的方式传参,所以会导致int,long,float,double,boolean,byte转为Integer,Long,Float,Double,Boolean,Byte
所以为了处理这个问题,所以要声名的函数int,long,float,double,boolean,byte这些类型都要转为Integer,Long,Float,Double,Boolean,Byte。
具体的调用:
public class ActivityA extends Activity implements View.OnClickListener {
private TextView textView;
private String[] methods = {"setTextView","setTextView1"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_a);
textView = findViewById(R.id.
tv);
Button button = findViewById(R.id.
btnStartB);
button.setOnClickListener(this);
ActivityGloble.
activityGloble.register(this,methods);
//activityA = this;
}
public void setTextView(){
textView.setText("ActivityB set ActivityA");
}
public void setTextView1(String arg1,String arg2){
TextView textView = findViewById(R.id.
tv1);
textView.setText("ActivityB set ActivityA "+arg1+" "+arg2);
}
public void setTextView(String arg1,Integer arg3){
TextView textView = findViewById(R.id.
tv2);
textView.setText("ActivityB set ActivityA "+arg1+" "+" arg3 is "+3);
}
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setClass(this,ActivityB.class);
startActivity(intent);
}
public void onDestroy(){
super.onDestroy();
ActivityGloble.
activityGloble.register(null,null);
//防止内存漏洞}
}
public class ActivityB extends Activity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_b);
Button button = findViewById(R.id.
btn);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
ActivityGloble.
activityGloble.post("setTextView");
ActivityGloble.
activityGloble.post("setTextView1","qaa","bbb");
ActivityGloble.
activityGloble.post("setTextView","wwww",1);
}
}
这样看来解决之前提出的两个问题。
现在又有新的问题出现了:
1.是否可以注册多个类?
2.这里是手动把要注册的方法定好了,有没有更方式,让我们不需要提前写好方法,就能知道哪个方法要被执行?
3.执行的时候,可不可以只传个参数就能知道调用哪个方法?
分析问题1:
参照了EventBus里的源码,可以用缓存做处理,如下:
HashMap