必发365bifa0000Android组件体系—-布罗兹castReceiver广播接收器

by admin on 2019年4月10日

必发365bifa0000,Nginx限制速度模块分为哪三种?按请求速率限制速度的burst和nodelay参数是怎么样看头?漏桶算法和令牌桶算法毕竟有哪些分裂?本文将带您1探毕竟。大家会透过某些简单的言传身教体现Nginx限制速度模块是何许做事的,然后结合代码讲解其幕后的算法和规律。

【声明】 

核心算法

在研究Nginx限速模块以前,我们先来看看网络传输中常用两个的流量控制算法:漏桶算法令牌桶算法。那五只“桶”到底有怎么样异同呢?

欢迎转发,但请保留文章原来出处→_→ 

漏桶算法(leaky bucket)

漏桶算法(leaky
bucket)
算法思想如图所示:

必发365bifa0000 1

贰个印象的解释是:

  • 水(请求)从上面倒入水桶,从水桶下方流出(被处理);
  • 比不上流出的水存在水桶中(缓冲),以固定速率流出;
  • 水桶满后水溢出(甩掉)。

那个算法的骨干是:缓存请求、匀速处理、多余的伸手直接丢掉。

生命1号:http://www.cnblogs.com/smyhvae/ 

令牌桶算法(token bucket)

令牌桶(token
bucket)
算法思想如图所示:

必发365bifa0000 2

算法思想是:

  • 令牌以固定速率产生,并缓存到令牌桶中;
  • 令牌桶放满时,多余的令牌被丢掉;
  • 请求要成本等比例的令牌才能被处理;
  • 令牌不够时,请求被缓存。

比较漏桶算法,令牌桶算法差别之处在于它不仅有七只“桶”,还有个体系,那些桶是用来存放令牌的,队列才是用来存放在请求的。

从功用上的话,漏桶和令牌桶算法最强烈的不同便是是不是同意从天而降流量(burst)的处理,漏桶算法能够强行限制数量的实时传输(处理)速率,对突发流量不做额外处理;而令牌桶算法能够在限定数量的平均传输速率的同时允许某种程度的突发传输

Nginx按请求速率限制速度模块使用的是漏桶算法,即能够强行保障请求的实时处理速度不会超过设置的阈值。

小说来源:http://www.cnblogs.com/smyhvae/p/3960623.html

Nginx限制速度模块

Nginx首要有二种限制速度措施:按连接数限制速度(ngx_http_limit_conn_module)、按请求速率限制速度(ngx_http_limit_req_module)。大家重点讲解按请求速率限制速度。

 

按连接数限制速度

按连接数限制速度是指限制单个IP(或许其余的key)同时提倡的连接数,超出那个范围后,Nginx将平素拒绝更多的连日。那几个模块的配置相比好精通,详见ngx_http_limit_conn_module官方文书档案

【正文】

按请求速率限速

按请求速率限制速度是指限制单个IP(恐怕其余的key)发送请求的速率,超出钦命速率后,Nginx将直接拒绝越来越多的请求。选拔leaky
bucket
算法完成。为深刻通晓那么些模块,我们先从试验现象谈起。起先在此之前大家先简单介绍一下该模块的布局方式,以上边包车型的士布置为例:

http {
    limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
    ...
    server {
        ...
        location /search/ {
            limit_req zone=mylimit burst=4 nodelay;
        }

使用limit_req_zone器重字,我们定义了多少个名称叫mylimit大小为十MB的共享内部存储器区域(zone),用来存放在限制速度相关的总计消息,限制速度的key值为2进制的IP地址($binary_remote_addr),限制速度上限(rate)为二r/s;接着我们采取limit_req驷马难追字将上述规则效率到/search/上。burstnodelay的功用稍后解释。

使用上述规则,对于/search/目录的造访,单个IP的访问速度被界定在了二请求/秒,超过那么些限制的走访将一直被Nginx拒绝。

一、广播的意义和特点

试行一——阿秒级总计

大家有如下配置:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit;
    }
}
...

上述规则限制了各类IP访问的进程为2r/s,并将该规则效用于跟目录。假设单个IP在相当的短的年华内并发发送几个请求,结果会怎么呢?

# 单个IP 10ms内并发发送6个请求
send 6 requests in parallel, time cost: 2 ms
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 200 OK
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 503 Service Temporarily Unavailable
end, total time cost: 461 ms

我们使用单个IP在拾ms内发并发送了多少个请求,只有1个成功,剩下的两个都被拒绝。大家设置的速度是2r/s,为啥唯有三个成功吗,是否Nginx限制错了?当然不是,是因为Nginx的限流总括是依据阿秒的,大家设置的快慢是2r/s,转换一下就是500ms内单个IP只同意通过1个请求,从501ms起首才允许通过第二个请求。

必发365bifa0000 3

  • 广播的生命周期相当的短,经过调用对象–>完结onReceive–>停止,整个经过就得了了。从贯彻的复杂度和代码量来看,广播无疑是最Mini的Android
    组件,达成多次只需几行代码。广播对象被组织出来后经常只举行BroadcastReceiver.onReceive方法,便截至了其生命周期。所以有的时候大家得以把它看成函数看也不至于不可。
  • 和拥有组件1样,广播对象也是在动用进度的主线程中被组织,所以广播对象的举办必须是要共同且快速的。也不引入在中间开子线程,因为反复线程还未竣事,广播对象就早已履行实现被系统销毁。假使急需落成1项比较耗费时间的工作
    , 应该通过发送 Intent 给 Service, 由 Service 来形成。
  • 历次广播到来时 , 会重新创制 布罗兹castReceiver 对象 , 并且调用
    onReceive() 方法 , 执行完今后 , 该对象即被灭绝 . 当 onReceive()
    方法在 十 秒内尚未履行达成, Android 会认为该程序无响应。

实验二——burst允许缓存处理突发请求

尝试1我们看看,大家长期内发送了大批量请求,Nginx依据皮秒级精度总计,超出限制的呼吁直接拒绝。那在实际情况中未免过于苛刻,真实网络环境中呼吁到来不是匀速的,很大概有请求“突发”的情况,也正是“壹股子1股子”的。Nginx思虑到了那种场合,能够透过burst重大字开启对出人意料请求的缓存处理,而不是直接拒绝。

来看大家的计划:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4;
    }
}
...

小编们投入了burst=4,意思是每一个key(此处是每一种IP)最多允许陆个突发请求的来到。如若单个IP在十ms内发送四个请求,结果会如何呢?

# 单个IP 10ms内发送6个请求,设置burst
send 6 requests in parallel, time cost: 2 ms
HTTP/1.1 200 OK
HTTP/1.1 503 Service Temporarily Unavailable
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
HTTP/1.1 200 OK
end, total time cost: 2437 ms

相比实验壹成功数扩大了6个,这些大家设置的burst数目是同样的。具体处理流程是:二个请求被立刻处理,陆个请求被停放burst队列里,此外3个呼吁被拒绝。通过burst参数,大家使得Nginx限流具备了缓存处理突发流量的力量

不过请小心:burst的效应是让多余的央浼能够先放到行列里,稳步处理。假如不加nodelay参数,队列里的伸手不会立马处理,而是依照rate设置的速度,以微秒级精确的快慢日益处理。

 

实验三——nodelay下落排队时间

尝试第22中学大家看到,通过设置burst参数,大家能够允许Nginx缓存处理肯定水准的突发,多余的请求可以先放到行列里,逐步处理,那起到了平滑流量的法力。不过如若队列设置的可比大,请求排队的年华就会相比较长,用户角度看来正是猎豹CS陆T变长了,那对用户很不友好。有怎么样化解办法呢?nodelay参数允许请求在排队的时候就随即被拍卖,约等于说只要请求能够进入burst队列,就会登时被后台worker处理,请小心,那意味着burst设置了nodelay时,系统弹指间的QPS恐怕会超过rate设置的阈值。nodelay参数要跟burst一块使用才有效果。

延续实验贰的布署,我们参加nodelay选项:

...
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4 nodelay;
    }
}
...

单个IP 十ms内并发发送5个请求,结果如下:

# 单个IP 10ms内发送6个请求
   实验3, 设置burst和nodelay       |  实验2, 只设置burst
send 6 requests, time cost: 4 ms |  time cost: 2 ms
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 503 ...
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
HTTP/1.1 503 ...                 |  HTTP/1.1 200 OK
HTTP/1.1 200 OK                  |  HTTP/1.1 200 OK
total time cost: 465 ms          |  total time cost: 2437 ms

跟实验2比较,请求成功率没变化,可是完整耗费时间变短了。那怎么解释啊?实验第22中学,有四个请求被停放burst队列个中,工作历程每隔500ms(rate=二r/s)取一个伸手进行拍卖,最终一个伸手要排队2s才会被处理;实验三中,请求放入队列跟实验二是如出一辙的,但分化的是,队列中的请求同时拥有了被拍卖的资格,所以实验三中的四个请求可以说是同时启幕被处理的,花费时间自然变短了。

不过请留心,即使设置burst和nodelay能够降低突发请求的处理时间,可是长期来看并不会拉长吞吐量的上限,长时间吞吐量的上限是由rate决定的,因为nodelay只可以保险burst的请求被当即处理,但Nginx会限制队列成分释放的快慢,就像限制了令牌桶中令牌发生的进度。

见到此间您大概会问,参预了nodelay参数之后的限快速总结法,到底算是哪三个“桶”,是漏桶算法依然令牌桶算法?当然还算是漏桶算法。思索一种景况,令牌桶算法的token为耗尽时会如何是好吗?由于它有一个呼吁队列,所以会把接下去的乞请缓存下来,缓存多少受限于队列大小。但那时缓存这一个请求还有意思呢?假如server已经过载,缓存队列更加长,宝马X伍T更高,就算过了很久请求被处理了,对用户来说也没怎么价值了。所以当token不够用时,最明智的做法正是一向拒绝用户的伸手,那就成了漏桶算法,哈哈~

二、接收系统广播:

源码剖析

经过地点的以身作则,大家队请求限制速度模块有了迟早的认识,未来大家深深解析代码实现。按请求速率限流模块ngx_http_limit_req_module代码位于src/http/modules/ngx_http_limit_req_module.c,900多好代码可谓短小精悍。相关代码有多少个着力数据结构:

  1. 红黑树:通过红黑树记录每一种节点(遵照证明时钦点的key)的总括消息,方便寻找;
  2. LRU队列:将红黑树上的节点依据近期拜会时间排序,时间近的放在队列尾部,以便利用LRU队列淘汰旧的节点,幸免内部存款和储蓄器溢出。

那多少个重大指标存款和储蓄在ngx_http_limit_req_shctx_t中:

typedef struct {
    ngx_rbtree_t                  rbtree; /* red-black tree */
    ngx_rbtree_node_t             sentinel; /* the sentinel node of red-black tree */
    ngx_queue_t                   queue; /* used to expire info(LRU algorithm) */
} ngx_http_limit_req_shctx_t;

个中除了rbtree和queue之外,还有2个称作sentinel的变量,那么些变量用作红黑树的NIL节点。

该模块的基本逻辑在函数ngx_http_limit_req_lookup()中,那么些函数首要流程是怎么着呢?对于每多个呼吁:

  1. 从根节点开始查找红黑树,找到key对应的节点;
  2. 找到后修改该点在LRU队列中的地方,表示该点近年来被访问过;
  3. 举行漏桶算法;
  4. 没找到时依据LRU淘汰,腾出空间;
  5. 变化并插入新的红黑树节点;
  6. 实行下一条限流规则。

流程很清楚,可是代码中牵涉到红黑树、LRU队列等高档数据结构,是还是不是会写得很复杂?幸亏Nginx小编功力深厚,代码写得不难易懂,哈哈~

// 漏桶算法核心流程
ngx_http_limit_req_lookup(...){
  while (node != sentinel) {
    // search rbtree
    if (hash < node->key) { node = node->left; continue;} // 1. 从根节点开始查找红黑树
    if (hash > node->key) { node = node->right; continue;}
    rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
    if (rc == 0) {// found
      ngx_queue_remove(&lr->queue); // 2. 修改该点在LRU队列中的位置,表示该点最近被访问过
      ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2
      ms = (ngx_msec_int_t) (now - lr->last);
      excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法
      if (excess < 0) 
        excess = 0;
      if ((ngx_uint_t) excess > limit->burst)
        return NGX_BUSY; // 超过了突发门限,拒绝
      if (account) {// 是否是最后一条规则
        lr->excess = excess;    
        lr->last = now;    
        return NGX_OK; // 未超过限制,通过
      }
      ...
      return NGX_AGAIN; // 6. 执行下一条限流规则
    }
    node = (rc < 0) ? node->left : node->right; // 1
  } // while
  ...
  // not found
  ngx_http_limit_req_expire(ctx, 1); // 4. 根据LRU淘汰,腾出空间
  node = ngx_slab_alloc_locked(ctx->shpool, size); // 5. 生成新的红黑树节点
  ngx_rbtree_insert(&ctx->sh->rbtree, node);// 5. 插入该节点,重新平衡红黑树
  ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
  if (account) {    
    lr->last = now; 
    lr->count = 0;
    return NGX_OK;
  }
  ...
  return NGX_AGAIN; // 6. 执行下一条限流规则
}

代码有三种重临值,它们的意味是:

  • NGX_BUSY 超过了突发门限,拒绝
  • NGX_OK 未抢先限制,通过
  • NGX_AGAIN 未超越限定,然而还有规则未执行,需举行下一条限流规则

上述代码简单精晓,但大家还有多少个难题:

  1. LRU是何等贯彻的?
  2. 漏桶算法是什么促成的?
  3. 种种key相关的burst队列在哪儿?

播音接收器能够自由地对本人感兴趣的广播举行注册,那样当有对应的播音发出时,广播接收器就能接受该广播,并在中间处理相应的逻辑。注册广播的艺术有三种,在代码中登记和在清单文件中登记,前者称为动态注册,后者称为静态注册。

LRU是何等完结的

LRU算法的贯彻相当的粗略,只要一个节点被访问了,那么就把它移到行列的头顶,当空间欠缺必要淘汰节点时,就选出队列尾巴部分的节点淘汰掉,主要呈今后如下代码中:

ngx_queue_remove(&lr->queue); // 2. 修改该点在LRU队列中的位置,表示该点最近被访问过
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);// 2
...
ngx_http_limit_req_expire(ctx, 1); // 4. 根据LRU淘汰,腾出空间

壹、动态注册监听互联网转移:

漏桶算法是什么样促成的

漏桶算法的兑现也比大家想像的大致,其主题是那1行公式excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000,那样代码的意趣是:excess表示近日key上遗留的乞请数,本次遗留的请求数
= 上次遗留的央浼数 – 预设速率 X 过去的年华 +
。这些一意味着方今这些请求,由于Nginx内部表示将单位压缩了一千倍,所以三个请求要转换到一千。

excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; // 3. 执行漏桶算法
if (excess < 0) 
    excess = 0;
if ((ngx_uint_t) excess > limit->burst)
    return NGX_BUSY; // 超过了突发门限,拒绝
if (account) { // 是否是最后一条规则
    lr->excess = excess;    
    lr->last = now;    
    return NGX_OK; // 未超过限制,通过
}
...
return NGX_AGAIN; // 6. 执行下一条限流规则

上述代码受限算出当前key上遗留的央求数,假设超越了burst,就径直拒绝;由于Nginx允许多条限制速度规则同时起功用,倘诺已是最终一条规则,则允许通过,不然执行下一条规则。

新建筑工程程文件,首先在MainActivity中定义二个里面类netWorkChangeReceiver,一碗水端平写父类的onReceive()方法,这样每当网络状态发生变化时,onReceive()方法就会赢得推行,那里运用Toast提示一段文本音信,代码如下:

单个key相关的burst队列在何地

未曾单个key相关的burst队列。下边代码中大家来看当到达最终一条规则时,只要excess<limit->burst限制速度模块就会回到NGX_OK,并不曾把剩余请求放入队列的操作,那是因为Nginx是依据timer来保管请求的,当限制速度模块再次回到NGX_OK时,调度函数会总计贰个延迟处理的光阴,同时把这几个请求放入到共享的timer队列中(一棵按等待时间从小到大排序的红黑树)。

ngx_http_limit_req_handler(ngx_http_request_t *r)
{
    ...
    for (n = 0; n < lrcf->limits.nelts; n++) {
        ...
        ngx_shmtx_lock(&ctx->shpool->mutex);// 获取锁
        rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess, // 执行漏桶算法
                                       (n == lrcf->limits.nelts - 1));
        ngx_shmtx_unlock(&ctx->shpool->mutex);// 释放锁
        ...
        if (rc != NGX_AGAIN)
            break;
    }
    ...
    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);// 计算当前请求需要的延迟时间
    if (!delay) {
        return NGX_DECLINED;// 不需要延迟,交给后续的handler进行处理
    }
    ...
    ngx_add_timer(r->connection->write, delay);// 否则将请求放到定时器队列里
    return NGX_AGAIN; // the request has been successfully processed, the request must be suspended until some event. http://www.nginxguts.com/2011/01/phases/
}

大家见到ngx_http_limit_req_handler()调用了函数ngx_http_limit_req_lookup(),并基于其重临值决定哪些操作:或是拒绝,或是交给下二个handler处理,或是将呼吁放入定期器队列。当限制速度规则都经过后,该hanlder通过调用函数ngx_http_limit_req_account()搜查缉获当前恳请须要的延迟时间,即使不要求延期,就将请求提交后续的handler进行拍卖,不然将呼吁放到定时器队列里。注意那些定时器队列是共享的,并不曾为独立的key(比如,每一个IP地址)设置队列。关于handler模块背景知识的介绍,可参看Tengine团队编写的Nginx开发从入门到驾驭

至于按请求速率限制速度的规律教学,可参看Rate Limiting with NGINX and NGINX
Plus
,关于源码更详实的剖析可参看ngx_http_limit_req_module
源码分析
以及y123456yz的Nginx源码分析的git项目

class netWorkChangeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show();
        }        
    }

结尾

正文重要教学了Nginx按请求速率限制速度模块的用法和规律,个中burst和nodelay参数是便于招惹误解的,尽管可透过burst允许缓存处理突发请求,结合nodelay可以降低突发请求的处理时间,不过长时间来看她们并不会拉长吞吐量的上限,长时间吞吐量的上限是由rate决定的。必要尤其注意的是,burst设置了nodelay时,系统须臾间的QPS或许会超过rate设置的阈值。

正文只是对Nginx窥豹一斑,更多关于Nginx介绍的作品,可参看Tengine团队编写的Nginx开发从入门到驾驭

继之在onCreate方法中展开动态注册,然后在onDestroy方法中进行撤消注册:

 1     private IntentFilter intentFilter;
 2     private netWorkChangeReceiver netWorkChangeReceiver;
 3     
 4     @Override
 5     protected void onCreate(Bundle savedInstanceState) {
 6         super.onCreate(savedInstanceState);
 7         setContentView(R.layout.activity_main);
 8         
 9         //动态注册:创建一个IntentFilter的实例,添加网络变化的广播(功能是对组件进行过滤,只获取需要的消息)
10         intentFilter = new IntentFilter();
11         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
12         //创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
13         netWorkChangeReceiver = new netWorkChangeReceiver();
14         registerReceiver(netWorkChangeReceiver, intentFilter);
15         
16     }
17 
18     //取消注册,一定要记得,不然系统会报错
19     @Override
20     protected void onDestroy() {
21         super.onDestroy();
22         unregisterReceiver(netWorkChangeReceiver);
23     }

上面代码解释如下:

11行:给意图过滤器intentFilter添加贰个值为android.net.conn.CONNECTIVITY_CHANGE的action。因为每当互联网状态发生变化时,系统就会发生一条值为android.net.conn.CONNECTIVITY_CHANG的广播。

注:最终要记得,动态注册的播放接收器一定要撤除注册才行。

运行程序,就足以了。

但是只是提示互连网产生变化还不够人性化,为了可以规范的报告用户近来是有网络可能未有互连网,大家还需求对上述代码进一步优化,修改netWorkChangeReceiver中的代码如下:

 1     class netWorkChangeReceiver extends BroadcastReceiver {
 2         
 3         @Override
 4         public void onReceive(Context context, Intent intent) {
 5             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
 6             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
 7             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
 8             if(networkInfo != null && networkInfo.isAvailable()){
 9                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
10             }else{
11                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
12             }
13                 
14         }
15     }

上面代码解释:

0陆行:在onReceive()方法中,首先通过通过getSystemService()方法赢得connectionManager那几个体系服务类,专门用于管理互联网连接。

0柒行:然后调用它的getActiveNetworkInfo()方法能够获取NetworkInfo的实例,接着调用NetworkInfo的isAvailable()方法,就能够判定当前是否有互连网了,最终经过Toast提示用户。

其它,查询系统的互连网状态是亟需说明权限的,打开清单文件,添加如下放权力限:

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

注:访问http://developer.android.com/reference/android/Manifest.permission.html能够查阅Android系统有着的可申明的权位。

前几日运作程序,就能够了。

上面程序完整版代码如下:

必发365bifa0000 4必发365bifa0000 5

 1 package com.example.m05_broadcastreceiver01;
 2 import android.app.Activity;
 3 import android.content.BroadcastReceiver;
 4 import android.content.Context;
 5 import android.content.Intent;
 6 import android.content.IntentFilter;
 7 import android.net.ConnectivityManager;
 8 import android.net.NetworkInfo;
 9 import android.os.Bundle;
10 import android.widget.Toast;
11 public class MainActivity extends Activity {
12     private IntentFilter intentFilter;
13     private netWorkChangeReceiver netWorkChangeReceiver;
14     @Override
15     protected void onCreate(Bundle savedInstanceState) {
16         super.onCreate(savedInstanceState);
17         setContentView(R.layout.activity_main);
18         // 动态注册:创建一个IntentFilter的实例,添加网络变化的广播
19         intentFilter = new IntentFilter();
20         intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");
21         // 创建NetWorkChangeReceiver的实例,并调用registerReceiver()方法进行注册
22         netWorkChangeReceiver = new netWorkChangeReceiver();
23         registerReceiver(netWorkChangeReceiver, intentFilter);
24     }
25     // 取消注册,一定要记得,不然系统会报错
26     @Override
27     protected void onDestroy() {
28         super.onDestroy();
29         unregisterReceiver(netWorkChangeReceiver);
30     }
31     class netWorkChangeReceiver extends BroadcastReceiver {
32         @Override
33         public void onReceive(Context context, Intent intent) {
34             //通过getSystemService()方法得到connectionManager这个系统服务类,专门用于管理网络连接
35             ConnectivityManager connectionManager = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
36             NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();
37             if(networkInfo != null && networkInfo.isAvailable()){
38                 Toast.makeText(context, "network is available",Toast.LENGTH_SHORT).show();
39             }else{
40                 Toast.makeText(context, "network is unavailable",Toast.LENGTH_SHORT).show();
41             }
42                 
43         }
44     }
45 }

View Code 

 

二、静态注册达成开机运营:

动态注册的艺术比较灵敏,但缺点是:必须在先后运维以往才能选择到广播,因为登记的逻辑是写在onCreate()方法中的。为了让程序在未运转的事态下就能接到到广播,这里就须求采取到静态注册。

此地大家准备让程序接收一条开机广播,当收到那条广播时,就能够在onReceive()方法中实践相应的逻辑,从而完成开机运维的功能。

新建三个类:BootCompleteReceiver,让他继续布RhodescastReceiver,在onReceive()方法中总结地Toast一下,代码如下:

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_SHORT).show();
    }
}

能够看出,那里不再利用个中类的不二等秘书籍来定义广播接收器,因为稍后大家须要在清单文件AndroidManifest.xml中校以此广播接收器的类名注册进去。

下一场修改清单文件AndroidManifest.xml,代码如下:

 1 <uses-sdk
 2         android:minSdkVersion="8"
 3         android:targetSdkVersion="16" />
 4     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 5     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
 6 
 7     <application
 8         android:allowBackup="true"
 9         android:icon="@drawable/ic_launcher"
10         android:label="@string/app_name"
11         android:theme="@style/AppTheme" >
12         <activity
13             android:name="com.example.m05_broadcastreceiver01.MainActivity"
14             android:label="@string/app_name" >
15             <intent-filter>
16                 <action android:name="android.intent.action.MAIN" />
17 
18                 <category android:name="android.intent.category.LAUNCHER" />
19             </intent-filter>
20         </activity>
21         
22         <receiver android:name=".BootCompleteReceiver">
23             <intent-filter >
24                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
25             </intent-filter>
26         </receiver>
27     </application>

代码解释如下:

归根结蒂,<application>标签内多了个子标签<receiver>,全体的静态注册的播音接收器都以在此间展开挂号的。

2贰行:name中为广播接收器的名字

2四行:想要接收的播音。Android系统运转完结后,会产生那条名字为android.intent.action.BOOT_COMPLETED的广播。

0五行:监听系统开机广播需求证明权限。

运维程序后,将手提式有线电话机关机重启,就能接受这条广播了。

 

叁、发送自定义广播

一、发送正式广播  

新建筑工程程文件。在发广播在此之前,大家先定义二个广播接收器来收取此广播才行。由此,新建3个类:My布罗兹castReceiver,让她继承布罗兹castReceiver,代码如下:

public class MyBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
    }
}

那边,当My布罗兹castReceiver
收到自定义的播放时,就会实施onReceive()方法中的逻辑,弹出二个Toast。

随即,要在清单文件AndroidManifest.xml中对那些广播接收器举行挂号:

 1     <application
 2         android:allowBackup="true"
 3         android:icon="@drawable/ic_launcher"
 4         android:label="@string/app_name"
 5         android:theme="@style/AppTheme" >
 6         <activity
 7             android:name="com.example.m05_broadcastreceiver02.MainActivity"
 8             android:label="@string/app_name" >
 9             <intent-filter>
10                 <action android:name="android.intent.action.MAIN" />
11 
12                 <category android:name="android.intent.category.LAUNCHER" />
13             </intent-filter>
14         </activity>
15         
16         <receiver android:name=".MyBroadcastReceiver">
17             <intent-filter >
18                 <action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/>
19             </intent-filter>
20         </receiver>
21     </application>

代码解释:

18行:让My布罗兹castReceiver接收一条值为om.example.m0伍_broadcastreceiver02.MY_BROADCAST的播放,由此待会儿在发送广播的时候,大家就须求发出那样的一条广播。

接着,修改activity.xml中的代码,添加三个按钮Button。

接下来,修改MainActivity.java中的代码,添加Button的监听事件:点击按钮时,发送广播

 

        Button button1=(Button)findViewById(R.id.button1);
        button1.setOnClickListener(new OnClickListener() {            
            @Override
            public void onClick(View v) {
                Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");
                sendBroadcast(intent);
            }
        });

总括:能够看到,点击按钮时,发送com.example.m0伍_broadcastreceiver02.MY_BROADCAST那条广播,那样,全部能够监听com.example.m0伍_broadcastreceiver02.MY_BROADCAST那条广播的播报接收器就都会同时收纳音信,此时发出去的正是一条标准广播,即严节广播。所以接下去就供给讲到有序广播。

 

2、发送有序广播:

广播是1种能够跨进程的通讯形式,别的应用程序是还不错的。今后大家来发一条有序广播。

稳步广播不仅有先后顺序,而且前边的播放仍是可以够将前边的播报截断。

在三.一的代码基础之上,将按钮的监听事件修改如下:

1         Button button1=(Button)findViewById(R.id.button1);
2         button1.setOnClickListener(new OnClickListener() {            
3             @Override
4             public void onClick(View v) {
5                 Intent intent =new Intent("com.example.m05_broadcastreceiver02.MY_BROADCAST");
6                 sendOrderedBroadcast(intent, null);
7             }
8         });

就要06行代码修改一下,将send布罗兹cast()方法改为sendOrdered布罗兹cast()方法,sendOrdered布罗兹cast()方法接收七个参数,第三个参数是一个与权力相关的字符串,那里传出null即可。

继而,修改清单文件AndroidManifest.xml中对播音接收器的挂号,设置优先级:

1         <receiver android:name=".MyBroadcastReceiver">
2             <intent-filter android:priority="100">
3                 <action android:name="com.example.m05_broadcastreceiver02.MY_BROADCAST"/>
4             </intent-filter>
5         </receiver>

即添加第0二行代码。能够看出,通过android:priority属性给广播接收器设置了优先级。那脾个性的限量在-一千到1000,数值越大,优先级越高。

接下去,若是想要拦截这些广播,防止让后边的播音接收器也吸收到了这几个广播。能够修改My布罗兹castReceiver中的代码:

1 public class MyBroadcastReceiver extends BroadcastReceiver {
2 
3     @Override
4     public void onReceive(Context context, Intent intent) {
5         Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show();
6         abortBroadcast();//拦截广播,防止后面的接收到
7     }
8 }

即添加第0陆行代码。如若在onReceive()方法中调用了abort布罗兹cast()方法,就意味着是将那条广播拦截,前面包车型的士广播接收器将无法再接收到。

专程关切:

  • 广播接收器的生命周期:关键在于布罗兹castReceiver中的onReceive()方法,从onReceive()里的率先行代码发轫,onReceive()里的末尾一行代码截至。
  • 两个播放到来的时候,用什么办法提示用户是最要好的吧?第叁种艺术是吐司,第二种格局是打招呼。注:不要选拔对话框,防止中断了用户正在进展的操作。

 

四、使用本地广播:

事先我们发送和收取的播音全体都以属于全局广播,即发出去的播放可以被别的任何应用程序接收到,并且大家也可以吸收接纳来自于其它任何应用程序的广播。那样壹来,必然会招致安全难点。于是便有了地点广播:即只万幸本应用程序中发送和吸收播放。那就要选取到了Local布罗兹castManager那个类来对广播实行保管。

我们修改贰.第11中学动态注册广播接收器的代码,即修改MainActivity.java中的代码如下:

package com.example.broadcasttest;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class MainActivity extends Activity {
    private IntentFilter intentFilter;

    private LocalReceiver localReceiver;

    private LocalBroadcastManager localBroadcastManager;

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

        //通过LocalBroadcastManager的getInstance()方法得到它的一个实例
        localBroadcastManager = LocalBroadcastManager.getInstance(this);
        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(
                        "com.example.broadcasttest.LOCAL_BROADCAST");
                localBroadcastManager.sendBroadcast(intent);//调用sendBroadcast()方法发送广播
            }
        });
        //动态注册本地的广播接收器
        intentFilter = new IntentFilter();
        intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST");
        localReceiver = new LocalReceiver();
        localBroadcastManager.registerReceiver(localReceiver, intentFilter);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        localBroadcastManager.unregisterReceiver(localReceiver);
    }

    class LocalReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "received local broadcast",
                    Toast.LENGTH_SHORT).show();
        }
    }
}

注:本地广播是无能为力透过静态注册的章程来收纳的。其实也完全可以清楚,因为静态注册重要正是为了让程序在未运转的状态下也能接过广播。而发送本地广播时,大家的主次一定是已经起步了,未有须求运用到静态注册的功能。

 

五、种种种种的广播: 

在android中有许多系统自带的intent.action,通过监听那个事件大家落实很多效应。

  1. 开机:
    String BOOT_COMPLETED_ACTION
    广播:在系统运营后。那个动作被广播二次(唯有一次)。监听:
    “android.intent.action.BOOT_COMPLETED”
  2. 对讲机拨入:
    String ANSWER_ACTION 动作:处理拨入的对讲机。监听:
    “android.intent.action.ANSWE奥迪Q三”
  3. 电量变化:
    String BATTERY_CHANGED_ACTION
    广播:充电状态,或许电池的电量发生变化。监听:
    “android.intent.action.BATTE凯雷德Y_CHANGED”
  4. 日期变更:
    String DATE_CHANGED_ACTION 广播:日期被更改。
    监听:“android.intent.action.DATE_CHANGED”
  5. 注销更新下载:
    String FOTA_CANCEL_ACTION 广播:撤消全部被挂起的 (pending)
    更新下载。 监听:“android.server.checkin.FOTA_CANCEL”
  6. 创新开头设置:
    String FOTA_READY_ACTION 广播:更新已经被下载 能够起来设置。监听
    “android.server.checkin.FOTA_READY”
  7. 主屏幕:
    String HOME_CATEGOWranglerY 体系:主荧屏(activity)。设备运营后出示的首先个 activity。
    监听:”android.intent.category.HOME”
  8. 新应用:
    String PACKAGE_ADDED_ACTION
    广播:设备上新装置了四个行使程序包。监听:
    “android.intent.action.PACKAGE_ADDED”
  9. 删去应用:
    String PACKAGE_REMOVED_ACTION
    广播:设备上删除了1个利用程序包。监听:
    “android.intent.action.PACKAGE_REMOVED”
  10. 显示器关闭:
    String SCREEN_OFF_ACTION 广播:荧屏被关门。监听:
    “android.intent.action.SCREEN_OFF”
  11. 显示屏开启:
    String SCREEN_ON_ACTION 广播:显示器已经被打开。
    监听:“android.intent.action.SCREEN_ON”
  12. 时区改变:
    String TIMEZONE_CHANGED_ACTION 广播:时区已经济体改成。监听:
    “android.intent.action.TIMEZONE_CHANGED”
  13. 岁月转移:
    String TIME_CHANGED_ACTION 广播:时间已经改变(重新安装)。
    “android.intent.action.TIME_SET”
  14. 时光流逝:
    String TIME_TICK_ACTION 广播:当前时刻已经变更(不荒谬的大运流逝)。
    “android.intent.action.TIME_TICK”
  15. 进去大容积存款和储蓄情势:
    String UMS_CONNECTED_ACTION 广播:设备进入 USB 大体量存储方式。
    “android.intent.action.UMS_CONNECTED”
  16. 脱离大体积存款和储蓄格局:
    String UMS_DISCONNECTED_ACTION 广播:设备从 USB
    大体积存款和储蓄格局剥离。 “android.intent.action.UMS_DISCONNECTED”
  17. 壁纸改变:
    String WALLPAPER_CHANGED_ACTION 广播:系统的墙纸已经济体改变。
    “android.intent.action.WALLPAPE大切诺基_CHANGED”
  18. web搜索:
    String WEB_SEARCH_ACTION 动作:执行 web 搜索。
    “android.intent.action.WEB_SEARCH”
  19. 网络转移:
    String CONNECTIVITY_CHANGE_ACTION
    动作:互联网转移。“android.intent.action.CONNECTIVITY_CHANGE_ACTION”

 

六、实例:动用动态注册,监听手提式有线电话机的电量变化。

1体化版代码如下:

(1)activity_main.xml代码如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:paddingBottom="@dimen/activity_vertical_margin"
 6     android:paddingLeft="@dimen/activity_horizontal_margin"
 7     android:paddingRight="@dimen/activity_horizontal_margin"
 8     android:paddingTop="@dimen/activity_vertical_margin"
 9     tools:context=".MainActivity" >
10 
11     <TextView
12         android:id="@+id/textView1"
13         android:layout_width="match_parent"
14         android:layout_height="wrap_content"
15         android:textSize="30dp"
16         android:gravity="center"/>
17 
18 </LinearLayout> 

(二)MainActivity.java的代码如下:

 1 package com.example.m05_broadcastreceiver02;
 2 
 3 import android.app.Activity;
 4 import android.content.BroadcastReceiver;
 5 import android.content.Context;
 6 import android.content.Intent;
 7 import android.content.IntentFilter;
 8 import android.os.Bundle;
 9 import android.widget.TextView;
10 
11 public class MainActivity extends Activity {
12 
13     
14     private BatteryBroadcastReceiver batteryBroadcastReceiver;
15     private TextView textView;
16     @Override
17     protected void onCreate(Bundle savedInstanceState) {
18         super.onCreate(savedInstanceState);
19         setContentView(R.layout.activity_main);
20         textView=(TextView)findViewById(R.id.textView1);
21         
22         //动态注册监听电量的广播接收器
23         IntentFilter intentFilter = new IntentFilter();
24         intentFilter.addAction("android.intent.action.BATTERY_CHANGED");
25         batteryBroadcastReceiver = new BatteryBroadcastReceiver();
26         registerReceiver(batteryBroadcastReceiver, intentFilter);       
27     }
28     
29     //取消注册监听电量的广播接收器
30     @Override
31     protected void onDestroy() {
32         super.onDestroy();
33         unregisterReceiver(batteryBroadcastReceiver);
34     }
35 
36     //新建一个广播接收器,监听电量的变化
37     public class BatteryBroadcastReceiver extends BroadcastReceiver {
38         @Override
39         public void onReceive(Context context, Intent intent) {
40             if(intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
41                 //获取当前电量
42                 int level = intent.getIntExtra("level", 0);
43                 //电量的总刻度
44                 int scale = intent.getIntExtra("scale", 100);
45                 textView.setText("电池电量为"+((level*100) / scale)+"%");
46                 
47                 //当电量低时,可以进行一些操作,例如弹出通知等
48 /*                if(level<15){
49                     do something
50                 }*/
51             }
52         }
53         
54     }
55     
56 }

继之,在清单文件中开始展览权力注解:

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

MainActivity.java的代码解释如下:

40至四五行:固定代码,用于获取当前电量

4八至50行:当电量低时,能够进行局地操作,例如弹出通告等

运营后,界面如下:

必发365bifa0000 6

 

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图