Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/197.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 多个通知如何记住onClick所需的数据而不相互混淆?_Java_Android_Android Notifications_Android Pendingintent - Fatal编程技术网

Java 多个通知如何记住onClick所需的数据而不相互混淆?

Java 多个通知如何记住onClick所需的数据而不相互混淆?,java,android,android-notifications,android-pendingintent,Java,Android,Android Notifications,Android Pendingintent,我有一个SMS服务,我的应用程序在其中响应特定的SMS消息。 在短信息查询托管应用程序的电话号码之前,应用程序会尝试验证用户。 该过程应通过在第一次发送SMS时显示通知提示操作来完成 从一个新号码接收。通知提供两个选项,批准或拒绝。 在默认共享首选项中,批准或拒绝另存为布尔值 将发件人的电话号码作为密钥 至少这是它应该做的 当我用这三个类来实现上面的交互时,我被一些古怪的行为所困扰。 它们是SMSReceiver、NotificationUtils和SMSAuthReceiver SMSRece

我有一个SMS服务,我的应用程序在其中响应特定的SMS消息。 在短信息查询托管应用程序的电话号码之前,应用程序会尝试验证用户。 该过程应通过在第一次发送SMS时显示通知提示操作来完成 从一个新号码接收。通知提供两个选项,批准或拒绝。 在默认共享首选项中,批准或拒绝另存为布尔值 将发件人的电话号码作为密钥

至少这是它应该做的

当我用这三个类来实现上面的交互时,我被一些古怪的行为所困扰。 它们是SMSReceiver、NotificationUtils和SMSAuthReceiver

SMSReceiver解析并响应传入的SMS消息。如果检测到来自新用户的授权请求, 它创建NotificationUtils的实例,并使用showNotification方法显示通知。 showNotification使用上下文对象和名为sender的字符串来保存传入请求的电话号码。 通知提供拒绝意图和批准意图,由SMSAuthReceiver处理。 无论请求被批准还是被拒绝,共享首选项都将相应更新,请参见下面的代码

问题行为如下所示: 安装应用程序后,新用户第一次通过短信联系时,身份验证过程将顺利运行。 但是,所有后续的身份验证请求都会在SMSAuthReceiver阶段失败。它总是依赖于中包含的数据 安装应用程序时引发的第一个通知意图

我试着将频道ID和通知ID随机化,希望它们能被分开处理,但很明显,我在某些方面错过了机会

如何在对下面的代码进行最小更改的情况下实现所需的行为

来自SMSReceiver.java的相关行:

if (
    (StringUtils.equalsIgnoreCase(message,"myapp sign up")) ||
    (StringUtils.equalsIgnoreCase(message,"myapp signup"))  ||
    (StringUtils.equalsIgnoreCase(message,"myapp start"))
){
    NotificationUtils notificationUtils = new NotificationUtils();
    notificationUtils.showNotification(context,sender); //Problems start here...
    SendSMS(context.getString(R.string.sms_auth_pending));
}
NotificationUtils.java:

package com.myapp.name;

import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.util.Log;

import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;

import org.apache.commons.lang3.RandomStringUtils;

public class NotificationUtils {
    private static final String TAG = NotificationUtils.class.getSimpleName();

    private int notificationID;
    private String channelID;

    public void hideNotification(Context context, int notificationId){
        try {
            NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
            if (notificationId == 0) {
                notificationManager.cancelAll();
            } else {
                notificationManager.cancel(notificationId);
            }
        }catch (Exception ignore){}
    }

    public void showNotification(Context context,String sender){
        createNotificationChannel(context);
        notificationID = getRandomID();
        channelID = String.valueOf(getRandomID());
        Log.d(TAG, "showNotification: Notification ID: "+notificationID);
        Log.d(TAG, "showNotification:      Channel ID: "+channelID);
        Log.d(TAG, "showNotification:          Sender: "+sender);

        Intent approveAuth = new Intent(context, SMSAuthReceiver.class);
        approveAuth.setAction("org.myapp.name.APPROVE_AUTH");
        approveAuth.putExtra("sender",sender);
        approveAuth.putExtra("notification_id",notificationID);
        PendingIntent approveAuthP =
                PendingIntent.getBroadcast(context, 0, approveAuth, 0);

        Intent denyAuth = new Intent(context, SMSAuthReceiver.class);
        denyAuth.setAction("org.myapp.name.DENY_AUTH");
        denyAuth.putExtra("sender",sender);
        denyAuth.putExtra("notification_id",notificationID);
        PendingIntent denyAuthP =
                PendingIntent.getBroadcast(context, 0, denyAuth, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channelID)
                .setSmallIcon(R.drawable.ic_lock_open)
                .setContentTitle(context.getResources().getString(R.string.app_name))
                .setContentText(sender+" "+context.getString(R.string.sms_noti_request))
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(approveAuthP)
                .addAction(R.drawable.ic_lock_open, context.getString(R.string.approve), approveAuthP)
                .addAction(R.drawable.ic_lock_close, context.getString(R.string.deny), denyAuthP);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify(notificationID, builder.build());
    }

    private int getRandomID(){
        return Integer.parseInt(
                RandomStringUtils.random(
                        8,
                        '1', '2', '3', '4', '5', '6', '7', '8', '9')
        );
    }

    private void createNotificationChannel(Context context) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "myapp Authorization Channel";
            String description = "myapp SMS Service Authorizations";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel channel = new NotificationChannel(channelID, name, importance);
            channel.setDescription(description);
            NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
            try {
                assert notificationManager != null;
                notificationManager.createNotificationChannel(channel);
            } catch (NullPointerException ex) {
                Log.e(TAG,ex.getMessage(),ex);
            }

        }

    }
}
SMSAuthReceiver.java

package com.myapp.name;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.telephony.SmsManager;
import android.util.Log;
import android.widget.Toast;

import androidx.preference.PreferenceManager;

import java.util.Objects;

public class SMSAuthReceiver extends BroadcastReceiver {
    private static final String TAG = SMSAuthReceiver.class.getSimpleName();

    @Override
    public void onReceive(Context context, Intent intent) {

        try {
            String sender = intent.getStringExtra("sender");
            int id = intent.getIntExtra("notification_id",0);

            /*Todo: bug! for some reason, data is always read from first intent, even if if
            * more request come in. this causes the approval feature to add the same guy a bunch
            * of times, and to mishandle dismissing the notification. (the purpose of this question...)*/

            Log.d(TAG, "onReceive: Sender: "+sender);

            NotificationUtils notificationUtils = new NotificationUtils();
            notificationUtils.hideNotification(context,id);

            SharedPreferences sharedPrefs = PreferenceManager.getDefaultSharedPreferences(context);
            switch (Objects.requireNonNull(intent.getAction())) {
                case "org.myapp.name.APPROVE_AUTH":
                    sharedPrefs.edit().putBoolean(sender,true).apply();
                    Toast.makeText(context, sender+" Approved!", Toast.LENGTH_SHORT).show();
                    SendSMS(context.getString(R.string.sms_invitation),sender);
                    break;
                case "org.myapp.name.DENY_AUTH":
                    sharedPrefs.edit().putBoolean(sender,false).apply();
                    Toast.makeText(context, sender+" Denied!", Toast.LENGTH_SHORT).show();
                    SendSMS(context.getString(R.string.denied),sender);
                    break;
            }
        }catch (Exception e){
            Log.e("SMSAuthReceiver", "onReceive: Error committing sender to preferences! ", e);
        }
    }

    void SendSMS(String smsBody, String phone_number){
        SmsManager manager = SmsManager.getDefault();
        manager.sendTextMessage(phone_number,null,smsBody,null,null);
    }

}
NotificationUtils.java生成的日志始终输出发件人的当前电话号码,而SMSAuthReceiver.java生成的日志始终反映应用程序测试的第一部手机。
为什么…?

多亏了@MikeM。是谁把我卷进来的

这里的问题是用于向通知传递操作的一对PendingEvent对象。其构造函数的第二个参数接受一个唯一的ID,该ID可用于标识PendingEvent的特定实例。在我的例子中,ID始终为0,从而导致在每个通知中重用相同的实例

我使用的解决方案是将为通知ID生成的随机数作为PendingEvent的第二个参数,如下所示:

PendingIntent approveAuthP =
                PendingIntent.getBroadcast(context, notificationID, approveAuth, 0);
而不是我使用的:

PendingIntent approveAuthP =
                PendingIntent.getBroadcast(context, 0, approveAuth, 0);
我希望这能帮助那些在Pending帐篷方面遇到类似问题的人


我确实有一个非常小的机会为两个不同的实例生成相同的随机数,所以也许使用计数器之类的方法会是一个更好的解决方案

我不确定我是否正确地遵循了您的描述,但听起来您一直在取回相同的第一对未修改的PendingEvents,因为意图总是相同的–per–并且您没有通过任何标志来清除或更新以前的意图或其额外内容。我不知道您的设计目标的细节,但getBroadcast中的第二个参数是请求代码,您可以使用该代码获得单独的、唯一的PendingEvent实例……以实现其他相同的目的。另外,最后一个参数用于调整PendingEvent实例和额外行为;e、 例如,如果挂起内容已经存在,则标志“更新”CURRENT将更新额外内容。@MikeM。好的,在每个实例中,尝试在PendingEvent的第二个参数处使用唯一的int,效果非常好。非常感谢。请把它作为一个答案,所以我可以接受。