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开发-简单抽奖

    路过商场,看过抽奖感觉挺有意思的,商场进行抽奖活动,三个奖项,一等奖的概率1/10,二等奖的概率的3/10,三等奖的概率是6/10,具体奖品我没仔细看,回来随便练手了一下,思考了一下,奖品分为10份, ...

  2. 【转载】神奇的css属性pointer-events

    绝对定位元素盖住链接或添加某事件handle的元素后,那么该链接的默认行为(页面跳转)或元素事件将不会被触发.现在Firefox3.6+/Safari4+/Chrome支持一个称为pointer-ev ...

  3. 25个可遇不可求的jQuery插件

    随着jQuery插件在网站建设过程中的使用率不断的增加,所以有必要跟进时代步伐开发出一些新的插件/代码片段,以此来巩固并提高前端用户体验,将用户体验提升到一个新的高度. 接下来所推荐的这些插件中有滑块 ...

  4. variable_scope和name_scope差别

    先看代码:   #命名空间函数tf.variable_scope()和tf.name_scope()函数区别于使用       import tensorflow as tf       with t ...

  5. php5.6 的interactive模式

    1. 发现运行php 的interactive shell 的时候,不能输入一行执行一行,而要 输入完一整段内容,再按  ctrl + d才能执行这段内容. 原因是,没安装 readline这个模块, ...

  6. ASP入门(十八)-访问Access中的数据库

    Northwind 数据库 这是一个微软为软件的使用者提供的一个练习用的数据库,它模拟了一个商贸公司日常业务流程所需要的一些数据. 并且网络上大部分的 SQL 语句的讲解都是以这个数据库为例子讲解的. ...

  7. C#.NET常见问题(FAQ)-如何让文本框textbox内容限制为数字

    //限制文本框的输入 private void txtQuestionScore_KeyPress(object sender, KeyPressEventArgs e) { if (e.KeyCha ...

  8. 【转】Ant之build.xml详解

    关键字: ant build.xml Ant的概念 可能有些读者并不连接什么是Ant以及入可使用它,但只要使用通过Linux系统得读者,应该知道make这个命令.当编译Linux内核及一些软件的源程序 ...

  9. C#学习记录二:高级数据存储方式

    1,结构 结构和类的区别主要在于存储和访问方式,结构是一种值 数据类型,而类是一一种引用数据类型.所谓值数据类型存储个人理解是存在堆里面的,所谓引用数据类型存储在了栈里面,相当于存储了一个指针. 结构 ...

  10. 微信小程序 - loading(组件)

    更新日期: 2019/3/8:首次发布 2019/3/12:增加loadOpacity透明度控制,默认0.5. 以及修改居中方式 Loading 参数: 1. type:loading(必需参数) 2 ...