热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

ProAndroid学习笔记(一零一):BroadcastReceiver(5):长时间处理通知小例子(上)

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处以及作者恺风Wei。IntentService的问题IntentService确实

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件。转载须注明出处以及作者@恺风Wei。

IntentService的问题

IntentService确实很不错,因为是service,可以确保进程运行,同时,不在主线程中执行,避免ANR消息。但是IntentService在广播接收器中使用会有一些问题。当一个广播接收器被触发时,特别是通过告警管理器进行触发,如果设备休眠,alarm管理器会通过一个wake lock(唤醒锁)通知电源管理器开启设备(只需能够运行代码,不需要UI呈现)。wake lock会在broadcast receiver返回时释放。然而IntentService的触发没有wake lock,设备可能在代码运行前就sleep了。

因此,我们需要在在线程开启前获取wake lock,并在处理结束时释放。为了在主线程和在worker线程中分别对同一wake lock的获取和释放,采用static的方式。在具体开始小例子之前,我们先看看wake lock。

长时间处理通知的小例子

利用IntentService实现长时间的通知处理,要求从收到通知开始,到通过IntentService,在线程完成通知的处理的整个过程中要持有wake lock,确保有资源执行任务,并在通知处理完后释放资源。小例子将提供一个通用的解决方法,较之以往的小例子,本例稍微复杂一点,但是了解小例子的设计,小例子不过就是个小例子。

对于一个通用的小例子,在context中如何持有和释放wake lock,需要考虑的是在上一次通知没有处理完,又收到新的通知。context中可能有多个接收器,触发不同的IntentService。这些情况都需要得到很好的处理。下图是整个小例子的框架流程。

在小例子中,开灯表示持有wake lock,关灯表示释放wake lock。房间表示线程处理,可能是在工作线程A,也可能在工作线程B。有个计数器,用于记录在线程中处理或者等待处理的的通知的总数。通知在onHandleIntent()中进行处理,处理完时,要离开房间,这时要检查一下是否所有的通知已全部处理(计数器为零),如果没有通知就要关灯。

此外通过stopService()可以强制终止service,强制清空了某个工作线程所要处理的全部通知,又或者由于onHandleIntent()的处理过程中出现异常退出,没能运行leaveRoom()。这种情况,计数器无法正确反映正在处理或等待处理的通知数。因此我们通过在onCreate()的registerClient()和在onDestroy()的unregisterClient(),记录当前运行的IntentService数目(房间数),如果发现房间数为零,也说明已经没有通知要处理。(要注意,stopService()会调用onDestroy(),当并不能终止后台运行的线程,后台线程将会继续执行,代码中可以通过设置状态检查等方式来终止后台线程)

wake lock的代码实现

使用wakelock需要WAKE_LOCK的权限,我们将上面的过程形象地模拟为:开启一个IntentService,如同客人注册,开一间房,当IntentService结束时,客人注销,退房。收到一个通知,表示一个人进入某一间房,处理完一个通知,表示某个人离开某间房。我们将统计在房间中的总人数,如果发现没有人,将关灯。由于某种情况,人离开房间可能没有被观察到,所以仅通过总人数判断不够。当发现所有的房间都退掉时,表示房间都没有人,要光灯,并复位房间的总人数为零。

下面通过LightedGreenRoom这个类来管理client人数、房间计数器、开灯和光灯的操作,相关处理代码如下:

public class LightedGreenRoom {
    private static String tag="LightedGreenRoom";     
    private int count;  //记录当前有多少通知在工作线程中处理或等待处理     
    private Context ctx = null;     
    PowerManager.WakeLock wl = null;     
    private int clientCount = 0;  //记录工作线程(客人)数目
    
    public LightedGreenRoom(Context inCtx){
        ctx = inCtx;
        wl = this.createWakeLock(inCtx); //基于context创建wake lock
    } 
    
   //有一个通知要处理:模拟为进入房间,房间内人数++ 
    public synchronized int enter(){
        count ++;
        Log.d(tag,"A new visitor , count = " + count);
        return count;
    }
    //有一个通知处理结束:模拟为离开房间,房间内人数--
    public synchronized int leave(){
        if(count == 0){
            Log.d(tag,"No one in room.");
            return count;
        }
       
        count --;
        Log.d(tag,"one leave, count = " + count);
        if(count == 0){
            turnOffLights();
        }
        return count;       
    }
   // 获取当前要处理的通知数:模拟为有多少人在房间? 
    synchronized public int getCount(){
        return count;
    }
    //开灯
    private void turnOnLights(){
        Log.d(tag,"Turning on lights. Count = " + count);
        this.wl.acquire();//wake lock操作(3):请求持有wake lock
    }
    //关灯
    private void turnOffLights(){
        if(this.wl.isHeld()){
            Log.d(tag,"Turning off ligths. Count = " + count);
            this.wl.release(); //wake lock操作(4):释放wake lock
        }
    }
    //对于wake lock的操作:(1)获得电源管理器参考;(2)获得wake lock对象,PARTIAL_WAKE_LOCK表示无需屏幕亮,维持最小的运行需求
    private PowerManager.WakeLock createWakeLock(Context inCtx){
        PowerManager pm = (PowerManager) inCtx.getSystemService(Context.POWER_SERVICE);
        PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, tag);

        return wl;
    }
    //IntentService的开启:模拟为客人注册,开间房 
    private int registerClient(){
        Utils.logThreadSignature(tag);
        this.clientCount ++;
        Log.d(tag,"register a new Client ,client count = " + clientCount);
        return clientCount ;
    }
    //IntentService的结束:模拟为客人注销,房间退 
    private int unregisterClient(){
        Utils.logThreadSignature(tag);
        if(clientCount == 0){
            Log.d(tag,"no client registed!");
            return 0;
        }
        clientCount --;
        Log.d(tag,"unregister a client , client count = " + clientCount);
        if(clientCount == 0){
            emptyTheRoom();
        }
        return clientCount;
    }
    //清空房间
    synchronized public void emptyTheRoom(){
        Log.d(tag,"Empty the Room ");
        count = 0;
        this.turnOffLights();
    }
   
}

在IntentService中的应用

从流程框架图中,我们观察到LightedGreenRoom需要在接收器、IntentService的的主线程已经工作线程中调用。接收器开启IntentService,这是通过intent进行数据传递,是没有办法传递对象的参考。因此,我们需要一个全部的LightedGreenRoom对象,要么采用static的方式,要么作为一个application的变量。作为通用范例,能更好地在不同app中使用,我们采用了static方式。为此,对LightedGreenRoom进行了补充:

public class LightedGreenRoom {
    ... ...
    private static LightedGreenRoom s_self = null;  //维持一个全局的LightedGreenRoom对象
    //第一次收到通知,生成全局对象,并开灯(在具体处理的前期准备也要开灯,只要在处理,都要开灯)
    public static void setup(Context inCtx){
        if(s_self == null){
            Log.d(tag,"Create Lighted Green Room.");
            s_self = new LightedGreenRoom(inCtx);
            s_self.turnOnLights();
        }
    }
   
    public static boolean isSetUp(){
        return (s_self == null ? false: true);
    }
    //收到通知,模拟有人进入房间
    public static int s_enter(){
        assertSetup();
        return s_self.enter();
    }
    //通知处理完:模拟有人离开房间
    public static int s_leave(){
        assertSetup();
        return s_self.leave();
    }
    //清空房间
    public static void ds_emptyTheRoom(){
        assertSetup();
        s_self.emptyTheRoom();
    }
    //开启IntentService,模拟开房
    public static void s_registerClient(){
        assertSetup();
        s_self.registerClient();
    }
    //结束IntentService,模拟退房
    public static void s_unregisterClient(){
        assertSetup();
        s_self.unregisterClient();
    }
    //检查是否已经正确初始化
    private static void assertSetup(){
        if(!LightedGreenRoom.isSetUp()){
            Log.w(LightedGreenRoom.tag,"You need to call setup first.");
            throw new RuntimeException("You need to setup GreenRoom first");
        }
    }
   
}

 

小例子代码在:Pro Android学习:Broadcast小例子

相关链接:我的Android开发相关文章


推荐阅读
  • Android系统源码分析Zygote和SystemServer启动过程详解
    本文详细解析了Android系统源码中Zygote和SystemServer的启动过程。首先介绍了系统framework层启动的内容,帮助理解四大组件的启动和管理过程。接着介绍了AMS、PMS等系统服务的作用和调用方式。然后详细分析了Zygote的启动过程,解释了Zygote在Android启动过程中的决定作用。最后通过时序图展示了整个过程。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
author-avatar
mobiledu2502891487
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有