android进阶4step3:Android常用框架——EventBus框架

Android Event Bus

学习步骤

  • EventBus简介
  • EventBus方法介绍
  • EventBus的实际应用
  • 总结

EventBus简介


以下来自:EventBus主页

开源项目地址:
https://github.com/greenrobot/EventBus

EventBus主页:

http://greenrobot.org/eventbus/

简介:

Introduction

EventBus is an open-source library for Android usingthe publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.

EventBus是一个AndroidJava的开源库,使用发布者/订阅者模式进行松散耦合。EventBus只需几行代码即可实现与分离类的集中通信 - 简化代码,消除依赖关系,并加速应用程序开发。

综合版:

EventBus是一个Android端优化的publish/subscribe消息总线,简化了应用程序内各组件间组件与后台线程间的通信。比如请求网络,等网络返回时通过HandlerBroadcast通知UI,两个Fragment之间需要通过Listener通信,这些需求都可以通过EventBus实现。

使用EventBus的好处:它......


  • 简化了组件之间的通信
  • 将事件发送者和接收者分离
  • 在UI工件(例如,活动,片段)和后台线程中表现良好
  • 避免复杂且容易出错的依赖关系和生命周期问题
  • 很快; 专门针对高性能进行了优化
  • 很小(<50k jar
  • 在实践中被证明通过应用与100,000,000+安装
  • 具有交付线程,用户优先级等高级功能。

进一步的EventBus功能


  • 方便的基于注释的API:基于便捷注释的API:只需将@Subscribe注释放入订阅者方法即可。由于注释的构建时间索引,EventBus不需要在应用程序的运行时进行注释反射。

  • Android主线程交付:当与UI交互时,无论事件如何发布,EventBus都可以在主线程中传递事件。
  • 后台线程传递:如果您的订阅者执行长时间运行的任务EventBus也可以使用后台线程来避免UI阻塞。
  • 事件和订阅者继承:EventBus中,面向对象的范例适用于事件和订阅者类。假设事件类A是B的超类。类型B的已发布事件也将发布给对A感兴趣的订阅者。同样考虑订阅者类的继承。
  • 快速入门您可以立即开始使用 - 无需配置任何内容 - 使用代码中任何位置提供的默认EventBus实例。
  • 可配置:  要根据您的要求调整EventBus,您可以使用构建器模式调整其行为。

开始使用EventBus


有关EventBus的第一步,请查看文档/教程,尤其是入门指南

 EventBus架构


作为一个消息总线,有三个主要的元素:

Event:事件
Subscriber:事件订阅者,接收特定的事件
Publisher:事件发布者,用于通知Subscriber有事件发生

Event

Event 可以是任意对象,用来描述传递的数据和事件类型。

 Publisher


可以在任意线程任意位置发送事件,直接调用EventBus的post(Object)方法 ,可以自己实例化EventBus对象,但一般使用默认的单例就好了: EventBus.getDefault(),根据post函数参数的类型,会自动调用订阅相应类型事件的函数。

Subscriber

EventBus中,使用约定来制定事件订阅者以简化使用。

3.0之 Subscriber函数的名字只能是以下4个

  1. onEvent
  2.  onEventMainThread
  3.  onEventBackgroundThread
  4. onEventSync

这四个,这个和ThreadMode有关。

ThreadMode

ThreadMode制定了会调用的函数

3.0之 前有以下四个ThreadMode:

@Subscribe(threadMode = ThreadMode.ASYNC)
  1. PostThread(发送和接都在同一个线程内)
  2. MainThread(发送和接都是在主线程)
  3. BackgroundThread  (同步的,接收是在单独的线程内,如果同时执行多个 依次执行)
  4. Async (异步的处理耗时操作,接收是异步的,独占一个线程池的线程来处理)

3.0之后加上@Subscriber后,函数的名字不固定。但需要指明ThreadMode

有以下四个ThreadMode:

  • POSTING(默认的)
  • MAIN
  • BACKGROUND
  • ASYNC

一、ThreadMode-PostThread

  • - 事件的处理在和事件的发送在相同的进程,所以事件的处理时间不应太长, 不然影响事件的发送线程,而这个线程可能是UI线程
  • - 对应的函数名是onEvent

简单案例:两个Activity间传递数据

布局文件省略

添加依赖

implementation 'org.greenrobot:eventbus:3.1.1'

第一个Activity代码:

public class MainActivity extends AppCompatActivity {

    private Button mButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = findViewById(R.id.id_btn_main);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //跳转到第二个Activity中
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
        //注册 必须有@Subscriber 订阅者
        EventBus.getDefault().register(this);
    }

    @Subscribe
    public void onEvent(MyEvent event) {
        Toast.makeText(this, "我是第一个Activity,收到Event:"+event.msg, Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注销
        EventBus.getDefault().unregister(this);

    }
}

第二个Activity代码:


public class SecondActivity extends AppCompatActivity {
    private Button mButton;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mButton = findViewById(R.id.id_btn_second);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyEvent event = new MyEvent();
                event.msg = " hello !";
                //直接post不需要注册
                EventBus.getDefault().post(event);
            }
        });
    }
    
}

二、ThreadMode-BackgroundThread


事件的处理会在一个后台线程中执行,对应的函数名是 onEventBackgroundThread,虽然名字是BackgroundThread,事件处理是在后台线程,但事件处理时间还是不应该太长,因为如果发送事件的线程是后台线程,会直接执行事件,如果当前线程是UI线程,事件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。

三、ThreadMode-Async

事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作 ,每个事件会开启一个线程(有线程池),但最好限制线程的数目。

 EventBus 简单案例 :

  • * 1. 添加BusEvent框架到自己的项目中:build.gradle 添加BusEvent的依赖
  • * 2.获取Bus实例,来给APP中所有的Activity或者Fragment提供
  • * 3. register,unregister 事件总线
  • * 4. 在要发送事件的地方调用bus.post(Event)
  • * 5. 接收的地方定义订阅的函数@Subscribe ...

在MainAcitivity中有两个fragment

一个HistoryFragment显示经纬度的listview
一个MapFragment显示通过百度API通过经纬度下载的静态图片的Image
当点击MOVELOCATION时发送一个随机经纬度

  • HistoryFragment 订阅者:接收到经纬度的数据,更新adapter
  • MapFragment 订阅者: 也接受到经纬度通过aysncTask在doInBackground中使用百度的API下载
  • onPostExcute 中 返回下载好的drawable图像 此时post给自己(本身的fragment)显示图片

注意添加网络权限

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

注意:android 9.0 使用这个百度API 会报错

解决方法: android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法

完整代码:

模块的build.gradle中添加otto框架的依赖

implementation 'org.greenrobot:eventbus:3.1.1'

布局文件:

activity_main.xml

这里的fragment是静态添加的  注意修改为自己的包名

        class="包名.LocationMapFragment"

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
 
        <Button
            android:id="@+id/bt_clear_location"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Clear Location" />
 
        <Button
            android:id="@+id/bt_move_location"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Move Location" />
    </LinearLayout>
 
    <!--静态加载fragment-->
    <fragment
        android:id="@+id/fragment_map"
        class="com.demo.ottosample.LocationMapFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
 
    <fragment
        android:id="@+id/fragment_history"
        class="com.demo.ottosample.LocationHistoryFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
 
 
</LinearLayout>

Fragment:

LocationHistroyFragment.java

显示历史经纬度的Fragment

import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
 
import com.squareup.otto.Subscribe;
 
import java.util.ArrayList;
import java.util.List;
 
/**
 * 显示历史坐标的fragment
 */
public class LocationHistoryFragment extends ListFragment {
    private final List<String> locationEvents = new ArrayList<>();
    private ArrayAdapter<String> adapter;
 
 
    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        adapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_1, locationEvents);
        setListAdapter(adapter);
    }
 
    @Override
    public void onResume() {
        super.onResume();
        //注册Bus
        EventBus.getDefault().register(this);
    }
 
    @Override
    public void onStop() {
        super.onStop();
        //注销Bus
        EventBus.getDefault().unregister(this);
    }
 
    /**
     * 订阅 坐标移动的事件 更新 listview
     *
     * @param event
     */
    @Subscribe
    public void onLocationMoveEvent(LocationMoveEvent event) {
        float lng = event.longitude;
        float lat = event.latitude;
        locationEvents.add(String.format("[%s, %s]", lng, lat));
        adapter.notifyDataSetChanged();
    }
 
    /**
     * 订阅 坐标 清除的事件 清除list数据
     *LocationClearEvent 暂时用不上 所以为空类
     * @param event
     */
    @Subscribe
    public void onLocationClearEvent(LocationClearEvent event) {
        locationEvents.clear();
        adapter.notifyDataSetChanged();
    }
}

 LocationMapFragment.java

显示地图的静态图片的Fragment

public class LocationMapFragment extends Fragment {
    private ImageView mImageView;
    private final String URL = "http://api.map.baidu.com/staticimage?width=1000&height=1000&center=%s,%s&zoom=15";
    private DownloadTask mTask;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

    }

    @Override
    public void onResume() {
        super.onResume();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    //订阅  异步下载图片
    @Subscribe(threadMode =ThreadMode.ASYNC)
    public void onEvent(LocationEvent event) {
        float longitude = event.longitude;
        float latitude = event.latitude;
        String url = String.format(URL, longitude, latitude);
        mTask = new DownloadTask();
        mTask.execute(url);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        mImageView = new ImageView(getActivity());

        return mImageView;
    }


    private class DownloadTask extends AsyncTask<String, Void, Drawable> {
        @Override
        protected Drawable doInBackground(String... params) {
            String downloadUrl = params[0];
            try {
                return BitmapDrawable.createFromStream(new URL(downloadUrl).openStream(), "bitmap.jpg");
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        protected void onPostExecute(Drawable drawable) {
            super.onPostExecute(drawable);
           mImageView.setImageDrawable(drawable);
        }
    }
}

MainAcitivity.java  展示两个fragment  清除list和随机产生一个经纬度的post

public class MainActivity extends AppCompatActivity {

    private Button mClearButton;
    private Button mMoveButton;

    private float DEFAULT_LONGITUDE = 116.413554F;
    private float DEFAULT_LATITUDE = 39.911013f;

    private float longitude;
    private float latitude;

    private float OFFSET = 0.1f;
    private Random random = new Random();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mClearButton = (Button)findViewById(R.id.bt_clear_location);
        mMoveButton = (Button)findViewById(R.id.bt_move_location);

        mClearButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                EventBus.getDefault().post(new ClearLocationEvent());
            }
        });

        mMoveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                longitude = DEFAULT_LONGITUDE + OFFSET * random.nextFloat();
                latitude = DEFAULT_LATITUDE + OFFSET * random.nextFloat();
                EventBus.getDefault().post(new LocationEvent(longitude, latitude));
            }
        });

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
    }

}

事件:

移动坐标的事件(封装的实体类,用来传递数据的)

LocationMoveEvent.java

public class LocationMoveEvent {
    public float longitude;
    public float latitude;
 
    public LocationMoveEvent(float lng, float lat) {
        this.longitude = lng;
        this.latitude = lat;
    }
}

LocationClearEvent.java 清除list的数据 这里比较简单,可以定义为空 

 
/**
 * 清除坐标的事件
 */
public class LocationClearEvent {
}

完成 

  • 与OTTO事件总线的对比(涉及复杂的推荐EventBus,简单的UI组件推荐Otto)

  • 转:浅析Otto框架,并与EventBus对比
  • 从事件订阅的处理差别来看:
  • EventBus是采用反射的方式对整个注册的类的所有方法进行扫描来完成注册;
  • Otto采用了注解的方式完成注册;
  • 共同的地方缓存所有注册并有可用性的检测。同时可以移除注册;
  • 注册的共同点都是采用method方法进行一个集成。
  • EventBus. 事件响应有更多的线程选择

  • EventBus. 支持Sticky(黏贴型) Event 和 处理事件的优先级

  • Otto更多使用场景应该就是在主线程中,因为它内部没有异步线程的场景。(也许是它自身的定位不一样,它就是为了解决UI的通信机制。所以出发点就是轻量级)在代码中主要体现这一特色的地方就是在接口ThreadEnforcer以及内部的实现域ANY和MAIN。在MAIN内部有一个是否是主线程的检查,而ANY不做任何检查的事情。
  • EventBus3.0以前,还需要根据四种线程模式分别对应固定接收方法,而OTTO则可以通过注解的方法自定义方法,比较方便,但是EventBus3.0也实现了通过注解自定义方法了。而Otto介绍上不管是订阅者还是发送者都需要注册事件,但是我发现现在发送者不用注册也可以发送了。
  • 每个框架都有自己的特点,我们开发者必须明白每个框架的出发点才能更好的使用,没有哪个框架好不好的问题,只要开发者自己使用哪个舒服,哪个就是最好的。适合自己的才是最好的。
  • 最后我想说,可能EventBusOtto很早以前就有了,现在RxJava就能实现这样的功能,但是对于不了解Rx技术的人来说,这些还是非常有用的,Rx技术虽好,虽然很新,如果没有搞懂的情况下,贸然使用估计会给你带来很大的困难。最好在有一个比较懂Rx技术的人的前提下,开始使用,提高自己。

总结:

简单的使用

基本的使用步骤就是如下4步:

在模块build.gradle中添加EventBus框架的依赖:

implementation 'org.greenrobot:eventbus:3.1.1'
  • 1. 定义事件类型:
public class MyEvent {} 
  • 2. 定义事件处理方法:
public void onEventMainThread
  • 3. 注册订阅者: 注册之后该类必须要有@Subscriber 否则报错
EventBus.getDefault().register(this)
  • 4. 发送事件:  如果只需要发送数据,直接post 不需要注册
EventBus.getDefault().post(new MyEvent())

猜你喜欢

转载自blog.csdn.net/qq_17846019/article/details/85101201