1. 连续发送两条信息时,出现bug。以下是bug现象描述。

发送第一条信息,sentReceiver弹出toast告知发送成功,同时在listview中的发送状态立即同步更新为发送成功。

继续发送第二条信息,sentReceiver也弹出toast告知发送成功,但是在listView中发送状态仍然是正在发送中。

在QQ通讯录中查看第二条信息,发现它的发送状态也是正在发送中,QQ通讯录已经将状态改为发送失败了。

再次试验,连续发送两条信息之后,第二条发送成功之后,它的发送状态没有改变为发送成功,而是仍然保留正在发送中。

2. 确定产生bug的代码。

private void createAndRegisterSentReceiver() {
    sentReceiver = new BroadcastReceiver(){
        @Override
        public void onReceive(Context context, Intent intent) {
            Uri uri = intent.getParcelableExtra(TxrjConstant.EXTRA_SENT_URI);
            int resultCode = getResultCode();
            if(resultCode == RESULT_OK) {
                Toast.makeText(context, "send message success.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_SENT); // 难道是这条语句没有执行
            } else if(resultCode == SmsManager.RESULT_ERROR_GENERIC_FAILURE) {
                Toast.makeText(context, "Generic failure.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            } else if(resultCode == SmsManager.RESULT_ERROR_NO_SERVICE) {
                Toast.makeText(context, "service is currently unavailable.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            } else if(resultCode == SmsManager.RESULT_ERROR_NULL_PDU) {
                Toast.makeText(context, "no pdu provided.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            } else if(resultCode == SmsManager.RESULT_ERROR_RADIO_OFF) {
                Toast.makeText(context, "radio was explicitly turned off.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            }
        }
    };
    IntentFilter filter = new IntentFilter(TxrjConstant.ACTION_SEND_SMS);
    mContext.registerReceiver(sentReceiver, filter);
}

private void updateMsgType(Uri uri, int type) {
    ContentValues values = new ContentValues();
    values.put(Sms.TYPE, type);
    getContentResolver().update(uri, values, null, null);
}

private PendingIntent getSentIntent(final Uri uri) {
    if(sentReceiver == null) {
        createAndRegisterSentReceiver();
    }
    Intent sentIntent = new Intent(TxrjConstant.ACTION_SEND_SMS);
    sentIntent.putExtra(TxrjConstant.EXTRA_SENT_URI, uri);
    return PendingIntent.getBroadcast(mContext, 0, sentIntent, 0);
}

3. 跟踪看看每次Uri是不是相同。

先后发出两条信息的URI在在performSendMessage方法中表现为不同,但在onReceive方法中表现为相同。

07-16 12:34:09.879: I/txrjsms(3333): performSendMessage. Uri:content://sms/4003
07-16 12:34:12.421: I/txrjsms(3333): sentReceiver. Uri:content://sms/4003
07-16 12:34:27.646: I/txrjsms(3333): performSendMessage. Uri:content://sms/4005
07-16 12:34:30.229: I/txrjsms(3333): sentReceiver. Uri:content://sms/4003

4. 将getSentIntent(final Uri uri)中的final修饰符去掉,继续测验。

07-16 12:35:58.885: I/txrjsms(3592): performSendMessage. Uri:content://sms/4007
07-16 12:36:01.278: I/txrjsms(3592): sentReceiver. Uri:content://sms/4007
07-16 12:36:21.347: I/txrjsms(3592): performSendMessage. Uri:content://sms/4009
07-16 12:36:24.090: I/txrjsms(3592): sentReceiver. Uri:content://sms/4007

5. 在中间过程多加几个LOG。

07-16 12:39:36.648: I/txrjsms(3886): performSendMessage. Uri:content://sms/4011
07-16 12:39:36.658: I/txrjsms(3886): sendMessage. Uri:content://sms/4011
07-16 12:39:36.888: I/txrjsms(3886): getSentIntent. Uri:content://sms/4011
07-16 12:39:39.290: I/txrjsms(3886): sentReceiver. Uri:content://sms/4011
07-16 12:39:55.766: I/txrjsms(3886): performSendMessage. Uri:content://sms/4013
07-16 12:39:55.776: I/txrjsms(3886): sendMessage. Uri:content://sms/4013
07-16 12:39:55.786: I/txrjsms(3886): getSentIntent. Uri:content://sms/4013
07-16 12:39:58.199: I/txrjsms(3886): sentReceiver. Uri:content://sms/4011

由此可以初步判断出BUG的原因是以下语句在传递Uri数据时出现了问题。

    Intent sentIntent = new Intent(TxrjConstant.ACTION_SEND_SMS);
    sentIntent.putExtra(TxrjConstant.EXTRA_SENT_URI, uri);
    return PendingIntent.getBroadcast(mContext, 0, sentIntent, 0);

6. 改成如下代码也无法解决问题。

private void createSentReceiver() {
    sentReceiver = new BroadcastReceiver(){
        @Override
        public void onReceive(Context context, Intent intent) {
            Uri uri = intent.getParcelableExtra(TxrjConstant.EXTRA_SENT_URI);
            Log.i("txrjsms", "sentReceiver. Uri:"+uri);
            int resultCode = getResultCode();
            if(resultCode == RESULT_OK) {
                Toast.makeText(context, "send message success.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_SENT);
            } else if(resultCode == SmsManager.RESULT_ERROR_GENERIC_FAILURE) {
                Toast.makeText(context, "Generic failure.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            } else if(resultCode == SmsManager.RESULT_ERROR_NO_SERVICE) {
                Toast.makeText(context, "service is currently unavailable.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            } else if(resultCode == SmsManager.RESULT_ERROR_NULL_PDU) {
                Toast.makeText(context, "no pdu provided.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            } else if(resultCode == SmsManager.RESULT_ERROR_RADIO_OFF) {
                Toast.makeText(context, "radio was explicitly turned off.", Toast.LENGTH_SHORT).show();
                updateMsgType(uri, Sms.MESSAGE_TYPE_FAILED);
            }
            mContext.unregisterReceiver(sentReceiver);
            sentReceiver = null;
        }
    };
}

private void updateMsgType(Uri uri, int type) {
    ContentValues values = new ContentValues();
    values.put(Sms.TYPE, type);
    getContentResolver().update(uri, values, null, null);
}

private PendingIntent getSentIntent(Uri uri) {
    Log.i("txrjsms", "getSentIntent. Uri:"+uri);
   createSentReceiver();
    IntentFilter filter = new IntentFilter(TxrjConstant.ACTION_SEND_SMS);
    mContext.registerReceiver(sentReceiver, filter);
    Intent sentIntent = new Intent(TxrjConstant.ACTION_SEND_SMS);
    sentIntent.putExtra(TxrjConstant.EXTRA_SENT_URI, uri);
    return PendingIntent.getBroadcast(mContext, 0, sentIntent, 0);
}

7. 在getBroadcast方法中设置第4个参数Flag为PendingIntent.FLAG_UPDATE_CURRENT.

private PendingIntent getSentIntent(Uri uri) {
    Log.i("txrjsms", "getSentIntent. Uri:"+uri);    
    if(sentReceiver == null) {
        createAndRegisterSentReceiver();
    }
    Intent sentIntent = new Intent(TxrjConstant.ACTION_SEND_SMS);
    sentIntent.putExtra(TxrjConstant.EXTRA_SENT_URI, uri);
    return PendingIntent.getBroadcast(mContext, 0, sentIntent, PendingIntent.FLAG_UPDATE_CURRENT);
}

再查看日志。从日志可以反映出此时extra中的数据已经更新过。

07-16 14:15:53.096: I/txrjsms(7675): performSendMessage. Uri:content://sms/4019
07-16 14:15:53.106: I/txrjsms(7675): sendMessage. Uri:content://sms/4019
07-16 14:15:53.247: I/txrjsms(7675): getSentIntent. Uri:content://sms/4019
07-16 14:15:56.390: I/txrjsms(7675): sentReceiver. Uri:content://sms/4019
07-16 14:16:27.250: I/txrjsms(7675): performSendMessage. Uri:content://sms/4021
07-16 14:16:27.260: I/txrjsms(7675): sendMessage. Uri:content://sms/4021
07-16 14:16:27.270: I/txrjsms(7675): getSentIntent. Uri:content://sms/4021
07-16 14:16:29.612: I/txrjsms(7675): sentReceiver. Uri:content://sms/4021

8. PendingIntent中定义了几个FLAG。

(1) android.app.PendingIntent.FLAG_UPDATE_CURRENT

如果PendingIntent已经存在,保留它并且只替换它的extra数据。

int android.app.PendingIntent.FLAG_UPDATE_CURRENT = 134217728 [0x8000000]
Flag for use with getActivity, getBroadcast, and getService: if the described PendingIntent already exists, then keep it but its replace its extra data with what is in this new Intent. This can be used if you are creating intents where only the extras change, and don't care that any entities that received your previous PendingIntent will be able to launch it with your new extras even if they are not explicitly given to it.

(2) android.app.PendingIntent.FLAG_CANCEL_CURRENT

如果PendingIntent已经存在,那么当前的PendingIntent会取消掉,然后产生一个新的PendingIntent。

int android.app.PendingIntent.FLAG_CANCEL_CURRENT = 268435456 [0x10000000]
Flag for use with getActivity, getBroadcast, and getService: if the described PendingIntent already exists, the current one is canceled before generating a new one. You can use this to retrieve a new PendingIntent when you are only changing the extra data in the Intent; by canceling the previous pending intent, this ensures that only entities given the new data will be able to launch it. If this assurance is not an issue, consider FLAG_UPDATE_CURRENT.

(3) android.app.PendingIntent.FLAG_ONE_SHOT

PendingIntent只能使用一次。调用了实例方法send()之后,它会被自动cancel掉,再次调用send()方法将失败。

int android.app.PendingIntent.FLAG_ONE_SHOT = 1073741824 [0x40000000]
Flag for use with getActivity, getBroadcast, and getService: this PendingIntent can only be used once. If set, after send() is called on it, it will be automatically canceled for you and any future attempt to send through it will fail.

(4) android.app.PendingIntent.FLAG_NO_CREATE

如果PendingIntent不存在,简单了当返回null。

int android.app.PendingIntent.FLAG_NO_CREATE = 536870912 [0x20000000]
Flag for use with getActivity, getBroadcast, and getService: if the described PendingIntent does not already exist, then simply return null instead of creating it.

9. android.app.PendingIntent.getBroadcast

取得一个PendingIntent,它会执行一个广播。效果就像Context.sendBroadcast()那样。

PendingIntent android.app.PendingIntent.getBroadcast(Context context, int requestCode, Intent intent, int flags)
Retrieve a PendingIntent that will perform a broadcast, like calling Context.sendBroadcast().

Parameters:
context The Context in which this PendingIntent should perform the broadcast.
requestCode Private request code for the sender (currently not used).
intent The Intent to be broadcast.
flags May be FLAG_ONE_SHOT, FLAG_NO_CREATE, FLAG_CANCEL_CURRENT, FLAG_UPDATE_CURRENT, or any of the flags as supported by Intent.fillIn() to control which unspecified parts of the intent that can be supplied when the actual send happens.
Returns:
Returns an existing or new PendingIntent matching the given parameters. May return null only if FLAG_NO_CREATE has been supplied.

在发送信息时应用PendingIntent.FLAG_UPDATE_CURRENT的更多相关文章

  1. java在线聊天项目0.5版 解决客户端向服务器端发送信息时只能发送一次问题 OutputStreamWriter DataOutputStream socket.getOutputStream()

    没有解决问题之前客户端代码: package com.swift; import java.awt.BorderLayout; import java.awt.Color; import java.a ...

  2. linux上给其他在线用户发送信息(wall, write, talk, mesg)

        linux上给其他在线用户发送信息(wall, write, talk, mesg)   2018-01-05 lonskyMR 转自 恶之一眉 修改 微信分享: 设置登录提示     /et ...

  3. 利用MediaSession发送信息到蓝牙音箱

    1.利用MediaSession发送信息到蓝牙音箱,如:播放音乐时接收的歌曲信息,但是每一首歌连续播放时,再次发送的重复信息会被丢弃.则利用MediaSession发现信息时,要保证信息的不重复性. ...

  4. HTTP 请求方式: GET和POST的比较当发送数据时,GET 方法向 URL 添加数据;URL 的长度是受限制的(URL 的最大长度是 2048 个字符)。

    什么是HTTP? 超文本传输协议(HyperText Transfer Protocol -- HTTP)是一个设计来使客户端和服务器顺利进行通讯的协议. HTTP在客户端和服务器之间以request ...

  5. Linux常用命令 查看进程信息时 copy的-----温故而知新

    1.查进程    ps命令查找与进程相关的PID号:    ps a 显示现行终端机下的所有程序,包括其他用户的程序.    ps -A 显示所有程序.    ps c 列出程序时,显示每个程序真正的 ...

  6. [android]-如何在向服务器发送request时附加已保存的cookie数据

    [android]-如何在向服务器发送request时附加已保存的cookie数据 应用场景:在开发android基于手机端+服务器端的应用时,登陆->获取用户信息->获取授权用户相关业务 ...

  7. 基于nodejs+webSocket的聊天室(实现:加入聊天室、退出聊天室、在线人数、在线列表、发送信息、接收信息)

    1  安装 socket.io模块 npm install "socket.io": "latest" 2 app.js相关 ws = require('soc ...

  8. Microsoft Word 2007 向程序发送命令时出现问题解决方法

    最近在打开Word文档时总是出现“向程序发送命令时出现问题”对话框,而且不确定性,关闭重新打开有时没事了有时还不行, 很让人头疼,经过尝试,把问题解决了 1.问题截图如下: 2.解决方法 1)方法一: ...

  9. day29 python 套接字socket TCP udp 形式发送信息的区别

    我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信. socket起源于UNIX,在 ...

随机推荐

  1. iOS开发-自定义UIAlterView(iOS 7)

    App中不可能少了弹框,弹框是交互的必要形式,使用起来也非常简单,不过最近需要自定义一个弹框,虽然iOS本身的弹框已经能满足大部分的需求,但是不可避免还是需要做一些自定义的工作.iOS7之前是可以自定 ...

  2. WebView JS交互 JSBridge 案例 原理 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  3. 使用Java语言开发微信公众平台(八)——自定义菜单功能

    随着上一篇文章的结束,我们已经实现了所有消息的类型的回复功能.今天,我们来学习更加高大上,也更加重要的自定义菜单功能. 一.了解自定义菜单 自定义菜单是微信公众平台最常用也是最重要的功能之一.根据微信 ...

  4. mahout安装

    mahout是hadoop的一种高级应用.运行mahout需要提前安装好hadoop.hadoop的安装网上很多.而且也不复杂,这里不再讲述.这里默认hadoop已经安装完成. 1:下载二进制解压安装 ...

  5. Redis:解决分布式高并发修改同一个Key的问题

    本篇文章是通过watch(监控)+mutil(事务)实现应用于在分布式高并发处理等相关场景.下边先通过redis-cli.exe来测试多个线程修改时,遇到问题及解决问题. 高并发下修改同一个key遇到 ...

  6. Kafka:ZK+Kafka+Spark Streaming集群环境搭建(十九)ES6.2.2 安装Ik中文分词器

    注: elasticsearch 版本6.2.2 1)集群模式,则每个节点都需要安装ik分词,安装插件完毕后需要重启服务,创建mapping前如果有机器未安装分词,则可能该索引可能为RED,需要删除后 ...

  7. GIT 如何在不提交Commit的情况下切换分支

    最近遇到一个问题,事情是这样子的,刚刚接到客户说他的项目有问题,于是就打开本地的源码查看经过排查确定了问题,于是就开始进行修正工作 将问题修复好准备提交到git的时候发现当前的分支是不对的,但问题是我 ...

  8. matlab之simulink仿真入门

    Matlab Simulink仿真工具的应用 ****Simulink是一个用来对动态系统进行建模.仿真和分析的软件包.使用Simulink来建模.分析和仿真各种动态系统(包含连续系统.离散系统和混合 ...

  9. springboot项目中报错:listener does not currently know of SID given in connect descriptor

    springboot项目中报错:listener does not currently know of SID given in connect descriptor 出现这个问题的原因是SID写错了 ...

  10. android中NavigationView(Design Support)的使用

    NavigationView可以实现美观的菜单功能展示,下面看一下怎么使用NavigationView 先是主Activity activity_main.xml: <?xml version= ...