关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android事件总线还能怎么玩?

    Android事件总线还能怎么玩?

    2015-04-28 00:00:00 出处:Mr.Simple的专栏
    分享

    顾名思义,AndroidEventBus ( github链接 : https://github.com/bboyfeiyu/AndroidEventBus )是一个Android平台的事件总线框架,它简化了Activity、Fragment、Service等组件之间的交互,很大程度上降低了它们之间的耦合,使我们的代码更加简洁,耦合性更低,提升了我们的代码质量。但它能做的却不仅限于这些。经过定制,它能完成很多有意思的功能,那么究竟该怎么做呢?就让我们一起往下看吧。

    不堪回首的痛

    首先,让我们先来看看这么一个场景:你是否在开发的过程中遇到过从Activity-A跳转到Activity-B,然后需要在Activity-B处理完某些工作之后回调Activity-A中的某个函数,但Activity又不能手动创建对象来设置一个Listener的情况?或者遇到在某个Service中更新Activity或Fragment中的界面等组件之间的交互问题……

    一经思考,你会发现Android中的Activity、Fragment、Service之间的交互是比较麻烦的,可能我们第一想到的是使用广播接收器来在它们之间进行交互。如上文所说,在Activity-B中发一个广播,在Activity-A中注册一个广播接收器来接收该广播。但使用广播接收器稍显麻烦,假如你要将一个实体类当作数据在组件之间传递,那么该实体类还得实现序列化接口,这个成本实在有点高!如代码1所示。

    class ActivityA extends Activity {  
            @Override
            protected void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);             
                  // ActivityA中注册广播接收器  
                registerReceiver(new BroadcastReceiver() {                  
                    @Override
                    public void onReceive(Context context, Intent intent) {  
                        User person = intent.getParcelableExtra("user") ;  
                    }
                }, new IntentFilter("my_action")) ;  
            }
            // ......   
        }
        // ActivityB中发布广播  
        class ActivityB extends Activity {  
            @Override
            protected void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);              
                // 发布广播  
                Intent intent  = new Intent("my_    action");  
                intent.putExtra("user", new User("mr.simple")) ;  
                sendBroadcast(intent);
            }
            // ......   
        }
        // 实体类需要实现序列化  
        class User implements Parcelable {  
            String name ;
            public User(String aName) {  
                name = aName ;
            }
           // 代码省略  
            @Override
            public void writeToParcel(Parcel dest, int flags) {  
                dest.writeString(name);
            }
        }

    代码1

    是不是有很麻烦的感觉?我们再来看一个示例,在开发过程中,我们经常要在子线程中做一些耗时操作,然后将结果更新到UI线程,除了AsyncTask之外,Thread加Handler是我们经常用的手段。如代码2所示。

    class MyActivity extends Activity {          
            Handler mHandler = new Handler () {  
                public void handleMessage(android.os.Message msg) {  
                    if ( msg.what == 1 ) {  
                        User user = (User)msg.obj ;
                        // do sth  
                    }
                };
            } ;
            @Override
            protected void onCreate(Bundle savedInstanceState) {  
                super.onCreate(savedInstanceState);  
                // code ......             
                new Thread(  
                    new Runnable() {  
                        public void run() {  
                            // do sth  
                            User newUser = new User("simple") ;  
                            Message msg = mHandler.obtainMessage() ;
                            msg.what = 1 ;
                            msg.obj = newUser ;
                            mHandler.sendMessage(msg) ;
                        }
                }).start();
            }
        }

    代码2

    是不是依然相当麻烦?当然你也可以使用AsyncTask来简化操作,但AsyncTask的几个泛型参数让你的代码看起来并不那么简洁,因此GitHub上出现了TinyTask、SimpleTask这些开源库来简化AsyncTask的使用。而这些,使用AndroidEventBus都可以很好地解决!

    下面就让我们来领悟一下AndroidEventBus的强大魅力吧。

    初见AndroidEventBus

    使用AndroidEventBus简单概括只有三个步骤:

    将对象注册到AndroidEventBus中; 使用@Subcriber标注订阅函数(只能有一个参数); 通过post函数发布事件。

    接下来就是注册订阅对象,如代码3所示。

    public class MainActivity extends Activity {  
        @Override
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.activity_main);
            // 将对象注册到事件总线中, ****** 注意要在onDestory中进行注销 ****  
            EventBus.getDefault().register(this);  
        }
        @Override
        protected void onDestroy() {  
            super.onDestroy();  
            // ****** 不要忘了进行注销 ****  
            EventBus.getDefault().unregister(this);  
        }
        // 代码省略  
    }

    代码3

    在onCreate中注册之后,MainActivity就可以添加订阅函数来接收消息了。需要注意的是在onDestory中需要将MainActivity从事件总线中注销。通过AndroidEventBus你可以去除Activity、Fragment、Service等组件的回调,减少了耦合,简化了代码。

    事件订阅函数

    事件订阅需要使用@Subscriber注解进行标识,且订阅函数的参数必须为一个。事件总线凭借参数类型和@Subscriber注解的tag值来标识订阅函数的唯一性。当用户发布事件时,总线库会根据事件类型和tag来查找符合要求的订阅函数,并且将这些订阅函数执行在对应的线程中。我们先来看看代码4的订阅函数示例。

    public class MainActivity extends Activity {  
        // 代码省略   
        @Subcriber(tag = "csuicide")  
        private void csuicideMyself(String msg) {  
            // do sth  
            finish();
        }
        @Subcriber(mode = ThreadMode.MAIN)
        private void toastMsgFromEvent(String msg) {  
            // do sth  
        }
        @Subcriber(tag = "async", mode = ThreadMode.ASYNC)  
        private void executeAsync(final String msg) {  
           // do sth  
        }
        // 代码省略  
    }

    代码4

    在代码4中,我们为MainActivity添加了以下三个订阅函数:

    csuicideMyself:该订阅函数执行在主线程,接收事件的类型为String,tag为csuicide。当用户发布一个事件类型为String,且tag为csuicide的事件时将会触发该方法。 toastMsgFromEvent:该订阅函数也是执行在主线程,事件类型为String,且tag为默认。当用户发布一个事件类型为String,且tag为默认的事件时将会触发该方法。 executeAsync:该订阅函数也是执行在一个异步线程,事件类型为String,且tag为async。当用户发布一个事件类型为String,且tag为async的事件时将会触发该方法。

    从上述的描述中我们可以知道,事件接收函数主要有两个约束:事件类型和tag(类似于Intent中的Action)。添加tag是因为在事件类型一样时,假如投递一个消息,那么单纯以事件类型(例如String)作为投递依据,那么多个参数为String的订阅函数将会被触发,这极大地降低了灵活性。

    发布事件

    参数1为事件类型,无tag:EventBus.getDefault().post(这是一个执行在异步线程的事件);参数2为tag,tag的类型为String,类似Intent的Action:EventBus.getDefault().post(这是一个执行在异步线程的事件:“async”)。

    发布事件时可以构造任意类型的事件,假如没有tag则该参数可以省略。发布事件后,AndroidEventBus会根据事件类型和tag到已注册的订阅对象中查找符合要求的订阅函数,例如投递的第二个事件类型为String、tag为async,那么在MainActivity中符合要求的订阅函数就是:

    @Subcriber(tag = "async", mode = ThreadMode.ASYNC)
     private void executeAsync(final String msg) {
       // do sth
     }

    AndroidEventBus的ThreadMode

    在上述代码中有一段代码是这样的:

    @Subcriber(mode = ThreadMode.MAIN)  
     private void toastMsgFromEvent(String msg) {        
     }

    这个mode可是大有来头,它指定这个事件接收函数执行在哪个线程中。具体有如下三个选项:

    ThreadMode.MAIN,事件接收函数执行在UI线程; ThreadMode.POST,事件在哪个线程发布,接收函数就执行在哪个线程; ThreadMode.ASYNC,事件执行在一个独立的异步线程中。

    图1中,事件接收函数就执行在异步线程。通过这几个线程模型,我们就可以定制接收函数的执行线程。这样我们就可以使用AndroidEventBus做很多事了。比如发布一个事件,在这个事件接收函数中进行耗时操作;或下载图片、进行HTTP请求、I/O操作等,以及替换Thread、AsyncTask等组件。不过,AndroidEventBus的功能远不止于此,下面我们就看看如何进行更高端的操作。

    图1 接收函数执行在异步线程中

    还可以怎么玩?

    退出应用的另类实现

    在Android应用开发中,有些情况下我们需要可以直接退出程序。但问题是,回退栈中含有其他的Activity存在,直接使用返回键并不能退出应用。此时我们常见的做法是再自定义一个Application子类,在子类中维护一个Activity的列表,然后在进入Activity时,将Activity添加到列表中,在Activity销毁之前将自己从Application子类的列表中移除。在需要退出应用时遍历Application子类的Activity列表,然后调用每个Activity的finish函数。那我们看看AndroidEventBus怎么实现这个功能。如代码5所示。

    public class CsuicideActivity extends Activity {  
            @Override
        protected void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            // 将对象注册到事件总线中, ****** 注意要在onDestory中进行注销 ****  
            EventBus.getDefault().register(this);  
        }
        @Override
        protected void onDestroy() {  
            super.onDestroy();       
            // ****** 不要忘了进行注销 ****  
            EventBus.getDefault().unregister(this);  
        }
        @Subcriber(tag = "csuicide")  
        private void csuicideMyself(String msg) {  
            finish();
        }
    }

    代码5

    代码5中,我们定义一个CsuicideActivity在onCreate中注册该Activity对象,在onDestroy中注销,还添加了一个csuicideMyself的订阅函数。所有的Activity类可以继承自CsuicideActivity。当需要退出应用时,直接发布一个类型为String、tag为csuicide的事件即可。这样所有的Activity就会触发csuicideMyself,而该函数中又调用了finish方法,因此所有的Activity都将退出,通过这种方式就完成了应用退出。

    自定义事件处理器 ( EventHandler )

    AndroidEventBus在设计之初就考虑到了可扩展性,主要可扩展的地方就是订阅函数的搜索策略,具体可以调用EventBus.getDefualt().setMatchPolicy(MatchPolicy policy)来实现策略替换。另一个比较重要的扩展就是事件处理器EventHandler,用户可以通过setter函数来设置三个事件处理器。如代码6所示。

     /**
       * 设置执行在UI线程的事件处理器
       * @param handler   UI线程事件处理器
       */  
      public void setUIThreadEventHandler(EventHandler handler) {  
          mDispatcher.mUIThreadEventHandler = handler;
      }
      /**
       * 设置执行在post线程的事件处理器
       * @param handler 事件在哪个线程投递,事件就执行在哪个线程的事件处理器
       */  
      public void setPostThreadHandler(EventHandler handler) {  
    mDispatcher.mPostThreadHandler = handler;
      }
      /**
       * 设置执行在异步线程的事件处理器
       * @param handler 异步线程事件处理器
       */  
      public void setAsyncEventHandler(EventHandler handler) {  
          mDispatcher.mAsyncEventHandler = handler;
      }

    代码6

    EventHandler的接口定义如代码7所示,只需实现handleEvent即可,然后将该实现注入到EventBus即可。

    /**
     * 事件处理接口,处理事件的抽象
     */  
    public interface EventHandler {  
        /**
         * 处理事件
         * @param subscription 订阅对象
         * @param event 待处理的事件
         */  
        void handleEvent(Subscription subscription, Object event);  
    }

    代码7

    默认有DefaultEventHandler、UIThreadEventHandler、AsyncEventHandler三个实现:

    DefaultEventHandler:事件在哪个线程发布,就将事件接收函数执行在哪个线程; UIThreadEventHandler:将事件接收函数执行在UI线程; AsyncEventHandler:将事件接收函数执行在异步线程。

    下面我们以自定义异步事件处理器,也就是AsyncEventHandler,通过实现EventHandler接口,将事件处理函数执行在一个线程池中,从而实现图片下载的功能。如代码8所示。

    public class ThreadPoolHandler implements EventHandler {  
        ExecutorService mExecutorService = Executors.newFixedThreadPool(3);
        EventHandler mHandler = new DefaultEventHandler();  
        @Override
        public void handleEvent(final Subscription subscription, final Object event) {  
            mExecutorService.submit(new Runnable() {  
                @Override
                public void run() {  
                    mHandler.handleEvent(subscription, event);
                }
            });
        }
    }

    代码8

    然后通过如下代码将ThreadPoolEventHandler注入到:

    图2 图片下载中页面

    图3 图片下载完成页面

    EventBus中:

    // 自定义的异步事件处理器,使用线程池  
     EventBus.getDefault().setAsyncEventHandler(new ThreadPoolHandler());

    再在订阅对象中添加代码9所示的订阅方法 :

    @Subcriber(tag = "download", mode = ThreadMode.ASYNC)  
    private void downloadImage(final String imageUrl) {  
        HttpURLConnection urlConnection = null;  
        try {  
            final URL url = new URL(imageUrl);  
            urlConnection = (HttpURLConnection) url.openConnection();
            final Bitmap bmp = BitmapFactory.decodeStream(urlConnection.getInputStream());           
            // 将Bitmap投递给ImageView之类的工作  
        } catch (IOException e) {  
        } finally {  
            if (urlConnection != null) {  
                urlConnection.disconnect();
            }
        }
    }

    代码9

    最后,当需要下载图片时,通过post发布一个参数为String类型、tag为download的事件即可执行downloadImage函数,这个函数将执行在线程池中,我们的简易ImageLoader就这么实现了。图2、图3分别为图片下载中和图片下载完成的页面。

    当然,由于AndroidEventBus的高度定制化,我们还可以通过AndroidEventBus来实现各种各样的功能,它到底还能怎么玩,我就不做过多的演示了,开发者可以充分发挥自己的聪明才智和想象力。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    帝国CMS7.5编辑器上传图片取消宽高的三种方法

    帝国cms如何自动生成缩略图的实现方法

    Windows 12即将到来,将彻底改变人机交互

    帝国CMS 7.5忘记登陆账号密码怎么办?可以phpmyadmin中重置管理员密码

    帝国CMS 7.5 后台编辑器换行,修改回车键br换行为p标签

    Windows 11 版本与 Windows 10比较,新功能一览

    Windows 11激活产品密钥收集及专业版激活方法

    IT头条

    无线路由大厂 TP-Link突然大裁员:补偿N+3

    02:39

    Meta 千万美金招募AI高级人才

    00:22

    更容易爆炸?罗马仕充电宝被北京多所高校禁用,公司紧急回应

    17:19

    天衍”量子计算云平台,“超算+量算” 告别“算力孤岛时代”

    18:18

    华为Pura80系列新机预热,余承东力赞其复杂光线下的视频拍摄实力

    01:28

    技术热点

    MySQL基本调度策略浅析

    MySQL使用INSERT插入多条记录

    SQL Server高可用的常见问题

    3D立体图片展示幻灯片JS特效

    windows 7上网看视频出现绿屏的原因及解决方法

    windows 7 64位系统的HOSTS文件在哪里?想用它加快域名解析

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。