GVKun编程网logo

Android 中 EventBus 的使用之多线程事件处理(android多线程通信)

1

在本文中,我们将给您介绍关于Android中EventBus的使用之多线程事件处理的详细内容,并且为您解答android多线程通信的相关问题,此外,我们还将为您提供关于AndroidEventBus-

在本文中,我们将给您介绍关于Android 中 EventBus 的使用之多线程事件处理的详细内容,并且为您解答android多线程通信的相关问题,此外,我们还将为您提供关于Android EventBus - hasSubscriberForEvent() 总是返回 true、Android EventBus - 接收消息的序列、android EventBus 3.0 混淆配置、Android EventBus 3.0.0 使用总结(必看篇)的知识。

本文目录一览:

Android 中 EventBus 的使用之多线程事件处理(android多线程通信)

Android 中 EventBus 的使用之多线程事件处理(android多线程通信)

在这一系列教程的最后一篇中,我想谈谈GR的EventBus,在处理多线程异步任务时是多么简单而有效。

AsyncTask,Loader和Executor…… 拜托!

Android中有很多种执行异步操作的方法(指平行于UI线程的)。AsyncTask对于用户来说是最简单的一种机制,并且只需要少量的设置代码即可。然而,它的使用是有局限的,正如Android官方文档中所描述的:

AsyncTask被设计成为一个工具类,在它内部包含了Thread和Handler,但它本身并不是通用线程框架的一部分。AsyncTask应该尽可能地被用在执行一些较短的操作中(最多几秒)。如果你需要在线程中执行较长时间的任务,那么建议你直接使用java.util.concurrent包中提供的各种API,如Executor、 ThreadPoolExecutor以及FutureTask。

不过即便是执行短时间的操作也会带来一些问题,特别是在与Activity/Fragment生命周期有关的地方。由于AsyncTask会持续地运行下去(即使启动它们的Activity/Fragment已经被销毁了)。这样,一旦你在onPostExecute方法中试图对UI进行更新,那么最终将导致抛出一个IllegalStateException异常。

Android 3.0中引入了Loader API用来解决Activity/Fragment生命周期的问题(它们的确很有效)。Loader API被设计成向Activity/Fragment中以异步方式加载数据。尽管加载数据是一种非常常见的异步操作,但并非唯一一种需要从UI线程中分开的操作。Loader还需要在Activity/Fragment中实现另外一个监听接口。尽管这么做没有错,但我个人并不喜欢这种模式(我的意思是最终你的代码中会包含许多的回调函数,导致代码的可读性变得很差)。最后,Activity和Fragment也并非唯一需要对异步操作分线程的地方。例如如果在Service里,你就不能访问LoaderManager,所以最终你还是得使用AsyncTask或者java.util.concurrent。

java.util.concurrent包很不错,我在Android和非Android项目中都可以使用。不过使用时需要对其进行多一点儿配置和管理,不象AsyncTask那么简单。你需要对ExecutorService进行初始化,管理和监视它的生命周期,并且可能需要跟一些Future对象打交道。

只要使用恰当,AsyncTask、 Loader和Executor都是非常有效的。但在复杂应用中,需要为每个任务选择合适的工具,最终你可能三种都会用到。这样你就得维护三种不同的处理并发的框架代码。

Green Robot来帮忙了!

GR的EventBus中内置了一个非常棒的并发处理机制。在监听类中,你可以实现4种不同类型的处理方法。当一个匹配事件被发送过来时,EventBus会根据不同的并发模型将事件发送到相应的处理方法中:

onEvent(T event):运行在和被发送事件相同的线程中。
onEventMainThread(T event):运行在主(UI)线程中,不管事件从哪个线程中被发送过来。
onEventAsync(T event):运行在单独的线程中,即非UI线程,也非发送事件的线程。
onEventBackgroundThread(T event):如果发送事件的线程不是UI线程,则运行在该线程中。如果发送事件的是UI线程,则它运行在由EventBus维护的一个单独的线程中。多个事件会同步地被这个单独的后台线程所处理。

这些方法功能强大而且使用简单。例如有一个比较耗时的操作(可能是网络调用,大量数据处理等),这一操作需要由UI上的行为来触发,并且当操作执行完毕后还需对UI进行更新。在这个例子中,UI行为即按钮点击,按钮在activity中,耗时操作在service中。我们可以按下面的方式来实现:

Java

尽管这个例子比较简单,但它却非常简明扼要地说明了问题。这里即不需要实现监听接口,也不会出现类似生命周期之类的问题(由于activity只能在它存在的时候才能接收到OperationCompleteEvent事件)。除此之外,如果发生了配置改变(旋转屏幕)或其他什么原因导致activity在两次事件发生之间被销毁并重建,最终仍可以接收到OperationCompleteEvent事件。

此外,我们也可以容易地想到一些其它用法。比如,如果需要将更新进度发出去,你只需另外实现一个封装了进度值的事件类,然后将其发送出去即可。或者,如果你想让其它一些事件(不管是相同还是不同类型)不被并行处理(同步执行),你可以选择使用onEventBackgroundThread。

依赖Bus

实例化EventBus最简单的方法就是通过EventBus.getDefault()。然而,在EventBusBuilder类(通过EventBus.builder()获得)中还包含了另外一些有用的配置方法。特别是在本文中提到过的使用你自己的ExecutorService。缺省情况下EventBus通过Executors.newCachedThreadPool()创建自己的ExecutorService,在大多数情况下都已满足你的需要。然而,有时你可能仍然想要显示地控制EventBus所使用的线程数量,这种情况下你就可以象下面这样初始化EventBus:

Java

EventBus.builder().executorService(Executors.newFixedTheadPool(NUM_THREADS)).installDefaultEventBus();

在EventBusBuilder中另外一些可供配置的是一些和异常处理的有关的控制,以及一个是否允许事件类被继承的控制开关。这些内容超出了本文所讨论的范围,但我还是建议你仔细去研究一番。GR可能并没有把这些内容都写在文档里,但如果你读一读EventBusBuilder和EventBus的源代码,相信你会很容易理解它们的。

Android EventBus - hasSubscriberForEvent() 总是返回 true

Android EventBus - hasSubscriberForEvent() 总是返回 true

如何解决Android EventBus - hasSubscriberForEvent() 总是返回 true

当没有事件订阅者时,EventBus 方法 hasSubscriberForEvent() 怎么可能返回 true? 在我的应用程序中,我使用 EventBus 在两个服务之间进行通信。我还使用 hasSubscriberForEvent() 来检查 Service with Subscriber 是否正常工作。它曾经工作得很好。现在不是,我不知道为什么。

我创建了新的虚拟事件,它没有用于测试目的的订阅者,这总是返回真:

L.d(TAG,"hasSubscriberForDummyEvent? " + eventBus.hasSubscriberForEvent(DummyEvent.class));

有没有人在 EventBus 上遇到过类似的问题?我使用的是 greenrobot 的 3.2.0 版本。

Android EventBus - 接收消息的序列

Android EventBus - 接收消息的序列

EventBus 确实有传递顺序,但默认情况下没有,在订阅 EventBus 时,您可以使用 priority 设置 @Subscribe(priority = 0),更高优先级的订阅者将首先收到消息它们在相同的线程策略上。但是订阅者是任何传输的终点,这意味着对一个订阅者中的数据所做的修改不会自动发送给另一个订阅者。 为了实现您的需求,您只能将主要订阅者注册为 Application,然后转换数据并使用 Fragment/Activity 将订阅的不同参数再次广播它。

class App: Application(){

    @Subscribe(threadMode = ThreadMode.MAIN)
    void onOriginalData(OriginalData data){
        //do transformations on data and send transformedData to Activity/Fragment
    }
}

class Activity: AppCompatActivity {
    
    @Subscribe(threadMode = ThreadMode.MAIN)
    void onTransformedData (TransformedData data){

    }
}

android EventBus 3.0 混淆配置

android EventBus 3.0 混淆配置

https://github.com/greenrobot/EventBus  

使用的这个库在github的官网README中没有写明相应混淆的配置.

经过对官网的查询,在一个小角落还是被我找到了。

-keepattributes *Annotation*
-keepclassmembers class ** {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(Java.lang.Throwable);
}

链接:http://greenrobot.org/eventbus/documentation/proguard/

Android EventBus 3.0.0 使用总结(必看篇)

Android EventBus 3.0.0 使用总结(必看篇)

前言

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.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());
  }


  @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",防止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
}

这个类是枚举类,定义了线程模型中的几种类型。

以上这篇Android EventBus 3.0.0 使用总结(必看篇)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持编程小技巧。

我们今天的关于Android 中 EventBus 的使用之多线程事件处理android多线程通信的分享就到这里,谢谢您的阅读,如果想了解更多关于Android EventBus - hasSubscriberForEvent() 总是返回 true、Android EventBus - 接收消息的序列、android EventBus 3.0 混淆配置、Android EventBus 3.0.0 使用总结(必看篇)的相关信息,可以在本站进行搜索。

本文标签: