Android EventBus粘性事件实现机制探究

Android EventBus粘性事件实现机制探究

什么是EventBus粘性事件?

在使用EventBus时,除了普通的事件外,还有一种特殊的事件——粘性事件。

所谓的粘性事件,是指在post一个事件时,如果当前并没有订阅者,那么这个事件会被保存下来(以队列的形式),等这个订阅者再次订阅时,这个事件会再次被发送出去,这就是EventBus的粘性事件实现机制。

EventBus粘性事件的实现

在EventBus的源码中,其实现机制就是优先调用订阅者的onEvent方法来订阅事件,如果没有找到该方法,则再判断是否有onEventSticky方法,如果有则将事件添加到一个类似于队列的结构中,并在后续的订阅操作中将该事件发送出去。

/**
 * Posts the given event to the event bus.
 */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

同时,在移除粘性事件队列中的内容时,也需要对事件进行特判处理,以保证EventBus的粘性事件实现机制能够顺利执行。

/**
 * Dispatches the given {@code event} to the currently registered subscribers according to its
 * type. This method may be called from any thread, but the event will be dispatched on the main
 * thread using the {@link Handler} specified during construction.
 */
protected void dispatch(Object event, Handler handler) throws EventBusException {
    if (event == null) {
        throw new EventBusException("Event to dispatch must not be null.");
    }

    try {
        Class<?> eventType = event.getClass();
        if (eventInheritance) {
            List<Class<?>> eventTypes = lookupAllEventTypes(eventType);
            boolean dispatched = false;
            for (int i = 0, count = eventTypes.size(); i < count; i++) {
                Class<?> clazz = eventTypes.get(i);
                CopyOnWriteArrayList<Subscription> subscriptions;
                synchronized (this) {
                    subscriptions = subscriptionsByEventType.get(clazz);
                }
                if (subscriptions != null && !subscriptions.isEmpty()) {
                    for (Subscription subscription : subscriptions) {
                        enqueueEvent(event, subscription);
                    }
                    dispatched = true;
                }
            }
            if (!dispatched) {
                Log.d(TAG, "No subscribers registered for event " + eventType);
                if (eventClassBySubscriber.containsKey(eventType)) {
                    // For efficiency, we will avoid generating a stack trace since we should't
                    // have an error, this is just a warning!
                    Log.d(TAG, "(OK, don't worry, you are not using ProGuard.)");
                } else {
                    Log.d(TAG, "The eventClass " + eventType + " is not a subclass of any registered events");
                }
            }
        } else {
            CopyOnWriteArrayList<Subscription> subscriptions;
            synchronized (this) {
                subscriptions = subscriptionsByEventType.get(eventType);
            }
            if (subscriptions != null && !subscriptions.isEmpty()) {
                for (Subscription subscription : subscriptions) {
                    enqueueEvent(event, subscription);
                }
            } else {
                Log.d(TAG, "No subscribers registered for event " + eventType);
                if (eventClassBySubscriber.containsKey(eventType)) {
                    // For efficiency, we will avoid generating a stack trace since we should't
                    // have an error, this is just a warning!
                    Log.d(TAG, "(OK, don't worry, you are not using ProGuard.)");
                } else {
                    Log.d(TAG, "The eventClass " + eventType + " is not registered");
                }
            }
        }
    } finally {
        eventQueue.clear(); // 清空粘性事件队列
    }
}

EventBus粘性事件的使用

使用EventBus的粘性事件也非常简单,在EventBus.post()方法中,直接传入粘性事件即可。

EventBus.getDefault().postSticky(new TestEvent("sticky event"));

同时,在订阅的时候,和普通事件的订阅方式一致。

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(TestEvent event) {
    // do something
}

需要注意的是,在使用粘性事件的时候,需要使用sticky属性将事件标记为粘性事件,同时订阅者的onEvent方法也需要用sticky属性进行标记,以确保能够正确地接收到粘性事件。

示例

示例一

在第一个示例中,我们可以定义一个事件类TestEvent,以及在MainActivity中订阅这个事件,并且在Fragment中进行post,代码如下:

public class TestEvent {
    public String message;

    public TestEvent(String message) {
        this.message = message;
    }
}

public class MainActivity extends AppCompatActivity {

    private MyFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mFragment = new MyFragment();
        getSupportFragmentManager().beginTransaction().add(R.id.container, mFragment).commit();
        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(TestEvent event) {
        Toast.makeText(this, "sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
    }
}

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_my, container, false);
        rootView.findViewById(R.id.btn_send_sticky).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                EventBus.getDefault().postSticky(new TestEvent("sticky event"));
            }
        });
        return rootView;
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        EventBus.getDefault().removeAllStickyEvents();
    }
}

在MainActivity中的onStickyEvent()方法中订阅了TestEvent这个事件,并且在MyFragment的点击事件中post了这个事件,这时候事件并没有被订阅者接收到,但是当我们返回MainActivity时,这个事件会被后续的订阅者接收到。

示例二

在第二个示例中,我们可以定义一个粘性事件,然后在两个不同的Activity中订阅和post这个事件,来证明EventBus粘性事件的机制是全局的。

代码如下:

public class StickyEvent {
    public String message;

    public StickyEvent(String message) {
        this.message = message;
    }
}

public class FirstActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_first);

        EventBus.getDefault().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(StickyEvent event) {
        Toast.makeText(this, "FirstActivity sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
    }

    public void startSecondActivity(View view) {
        startActivity(new Intent(this, SecondActivity.class));
    }
}

public class SecondActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        // post sticky event
        EventBus.getDefault().postSticky(new StickyEvent("sticky event"));
    }

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

    @Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
    public void onStickyEvent(StickyEvent event) {
        Toast.makeText(this, "SecondActivity sticky event msg : " + event.message, Toast.LENGTH_SHORT).show();
    }

    public void startFirstActivity(View view) {
        startActivity(new Intent(this, FirstActivity.class));
    }
}

在FirstActivity和SecondActivity中都订阅了StickyEvent这个粘性事件,并且在SecondActivity中post了这个事件,随后在FirstActivity中启动SecondActivity,返回FirstActivity后,我们可以看到之前post的事件被接收到了。

总结

EventBus的粘性事件机制,可以帮助我们在事件分发时,使得那些尚未订阅的订阅者能够在之后订阅时被及时地接收到,不错过任何一个事件。使用起来也十分简单,并且支持全局的粘性事件分发。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Android EventBus粘性事件实现机制探究 - Python技术站

(0)
上一篇 2023年6月13日
下一篇 2023年6月13日

相关文章

  • Python pickle类库介绍(对象序列化和反序列化)

    当我们需要在Python程序中,将一个Python对象直接持久化至磁盘中,或是从磁盘中加载一个Python对象时,我们可以使用pickle类库。其实,pickle类库实现的是Python对象的序列化和反序列化。 接下来,我们将会详细讲解pickle类库的一些相关概念、函数的基本使用方法以及示例。 1. 序列化和反序列化 所谓序列化,即是将一个Python对象…

    python 2023年6月2日
    00
  • Python实现扫码工具的示例代码

    下面我详细讲解一下 Python 实现扫码工具的示例代码的完整攻略。 1. 需要的 Python 库 在 Python 中实现扫码工具,我们需要使用一些第三方库。 opencv-python: 实现图像处理,可以用来打开摄像头和捕获图像。 pyzbar: 解码二维码,读取二维码的信息。 pillow: 显示图片或编码图片。 可以使用 pip 命令来安装以上三…

    python 2023年5月19日
    00
  • python 将数据保存为excel的xls格式(实例讲解)

    下面是“Python将数据保存为Excel的xls格式(实例讲解)”的完整实例教程。 什么是xls格式 xls格式是Microsoft Excel电子表格文件的标准文件格式。它是一种二进制文件格式,包含格式、布局和其他电子表格内容。在Python中,我们可以使用第三方库来创建和保存xls文件。 1. 安装依赖库 在Python中,我们可以使用Python的o…

    python 2023年5月13日
    00
  • 利用python修改json文件的value方法

    当我们需要修改一个JSON文件的数据时,可以使用Python提供的json模块来读取JSON文件到Python中,使用Python中的数据处理操作来修改需要修改的数据,最后再将修改后的数据写回到JSON文件中。 下面是修改JSON文件value的具体步骤: 导入json模块,使用open()函数读取JSON文件到Python中: “`python impo…

    python 2023年6月3日
    00
  • python 3x上的属性错误[关闭]

    【问题标题】:Attribute Error on python 3x [closed]python 3x上的属性错误[关闭] 【发布时间】:2023-04-03 20:25:01 【问题描述】: 我正在使用 tensorflow api 进行对象检测。我在 githup 上编写代码并尝试进行调试,然后我将面对这个错误。 File “<ipython-…

    Python开发 2023年4月8日
    00
  • Java Web开发过程中登陆模块的验证码的实现方式总结

    Java Web开发过程中登陆模块的验证码的实现方式总结 背景 在Java Web开发中,登陆模块的验证码是必不可少的一部分,主要为了防止机器或者恶意用户对系统进行暴力破解。本文将介绍Java Web开发中登陆模块的验证码实现方式,并给出两个示例。 实现方式 Java Web开发中登陆模块的验证码主要有以下三种实现方式: 使用第三方验证码服务,例如Googl…

    python 2023年6月3日
    00
  • Python项目打包成exe文件

    为将Python项目打包成exe文件,可以使用PyInstaller等第三方打包工具,下面将介绍使用PyInstaller打包的步骤。 环境准备 安装PyInstaller pip install pyinstaller 进入需要打包成exe文件的Python项目目录 cd path/to/your/project 打包 在项目目录下运行以下命令,将项目打包…

    python 2023年6月2日
    00
  • python反转(逆序)字符串的6种方法详细

    Python反转(逆序)字符串的6种方法详细 在Python中,有多种方法可以实现字符串的反转或逆序操作。下面介绍6种常用的方法。 1. 使用切片 通过切片的方式,可以快速地实现字符串的反转。具体实现方法是将步长设置为-1,即可将字符串反转。 str1 = "Hello World" reverse_str = str1[::-1] pr…

    python 2023年6月3日
    00
合作推广
合作推广
分享本页
返回顶部