在发送信息时应用PendingIntent.FLAG_UPDATE_CURRENT

时间:2023-07-13 16:49:26

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.