Android: 警报未通过 AlarmManager 触发

Android: Alarm not being triggered through AlarmManager

我正在编写一个简单的 Android 程序,通过 AlarmManager 在应用程序初始化(播放默认铃声并推送通知)后 15 秒触发警报。下面是我的代码:

MainActivity.java:

package com.example.basicalarmsetter;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;

public class MainActivity extends AppCompatActivity {
    private int uniqueId = 0;

    // Schedules a notification in the future given the delay
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void scheduleNotification(int matchId, long delay) {
        // Construct the PendingIntent which will trigger our alarm to go off
        Intent notificationIntent = new Intent();
        notificationIntent.setAction("com.example.basicalarmsetter.MatchNotification");

        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), matchId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT) ;
        long futureInMillis = SystemClock.elapsedRealtime() + delay;

        // Set off our PendingIntent
        AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, futureInMillis, pendingIntent);
        assert alarmManager != null;
        alarmManager.setExact(AlarmManager. ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
    }

    // Sets an Alarm at a future specified date
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void setAlarm(long notificationDelay) {
        try {
            System.out.println("Setting alarm at " + notificationDelay + " seconds");

            // Sets off a notification after 5 seconds
            scheduleNotification(uniqueId, notificationDelay);

            uniqueId++;

        } catch (Exception ex) {
            System.out.println("Cannot print alarm!");
            System.out.println("Exception: " + ex.toString());
        }
    }

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

        setAlarm(15000);
    }
}

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.basicalarmsetter">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name="com.example.basicalarmsetter.MatchNotification"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                ...
                <action android:name="com.example.notificationtest.MatchNotification" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

MatchNotification.kt:

package com.example.basicalarmsetter

import android.app.Notification
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.media.MediaPlayer
import android.media.RingtoneManager
import android.net.Uri
import androidx.core.app.NotificationCompat

class MatchNotification : BroadcastReceiver() {
    var NOTIFICATION_ID = "notification-id"
    var NOTIFICATION_CHANNEL_ID = "10001";

    private lateinit var player: MediaPlayer;
    private lateinit var context: Context;

    // Construct the notification to push to the user given the teams in the match
    private fun getNotification(
        content: String
    ): Notification? {
        val builder = NotificationCompat.Builder(
            context,
            "default"
        )

        builder.setContentTitle("NBA Alarm")
        builder.setStyle(NotificationCompat.BigTextStyle().bigText(content))
        builder.setContentText(content)
        builder.setSmallIcon(R.drawable.ic_launcher_foreground)
        builder.setAutoCancel(true)
        builder.setChannelId(NOTIFICATION_CHANNEL_ID)

        return builder.build()
    }

    override fun onReceive(context: Context, intent: Intent) {
        System.out.println("Match Notification Activated.");

        this.context = context

        val notificationManager =
            context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        val id = intent.getIntExtra(NOTIFICATION_ID, 0)
        notificationManager.notify(id, getNotification("Trigger Notification!"))

        // Retrieve the URI of the alarm the user has set
        var ringtoneUri:Uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)
        player = MediaPlayer.create(context, ringtoneUri)

        player.start()
    }

}

这看起来很奇怪,考虑到我在 AndroidManifest.xml 文件中将 MatchNotification class 指定为 receiver

测试设备

  1. 摩托罗拉 Moto E6 (Android 9)
  2. Pixel 2 模拟器(API 26)

注意:方案中MainActivity代码要在Java

您好,您似乎还没有检查过是否在后台发出警报 运行ning 并且一定有一些旧代码

试试下面的代码,只需粘贴它,无需对 AndroidManifest 文件和 运行.

做任何额外的事情
class MyActivity : AppCompatActivity() {
private var alarmManager: AlarmManager? = null
private var broadcastReceiver: BroadcastReceiver? = null
private var pendingIntent: PendingIntent? = null
private val REQUEST_CODE = 45645
private var id: String? = "myname"
private var timeInMilliSeconds: Long = 15000

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
    initAlarm()
}

private fun initAlarm() {

    //creating intent
    val intent = Intent(id)
    val alarmRunning = PendingIntent.getBroadcast(
        this,
        REQUEST_CODE,
        intent,
        PendingIntent.FLAG_NO_CREATE
    ) != null

    //setting broadcast
    broadcastReceiver = getBroadcastReceiver()
    registerReceiver(
        broadcastReceiver,
        IntentFilter(id)
    )

    //setting alarm
    val ensurePositiveTime = Math.max(timeInMilliSeconds, 0L)
    pendingIntent = PendingIntent.getBroadcast(
        this,
        REQUEST_CODE,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )

    //Check if alarm is already running
    if (!alarmRunning) {
        alarmManager?.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + ensurePositiveTime, pendingIntent)

    } else {
        updateAlarm()
        Log.e("Alarm", "Alarm already running.!")
    }
}

private fun updateAlarm() {

    //calculating alarm time and creating pending intent
    val intent = Intent(id)
    val ensurePositiveTime = Math.max(timeInMilliSeconds, 0L)
    pendingIntent = PendingIntent.getBroadcast(
        this,
        REQUEST_CODE,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT
    )

    //removing previously running alarm
    alarmManager?.cancel(pendingIntent)
    unregisterReceiver(broadcastReceiver)

    //setting broadcast
    broadcastReceiver = getBroadcastReceiver()
    registerReceiver(
        broadcastReceiver,
        IntentFilter(id)
    )

    //Check if alarm is already running
    alarmManager?.set(
        AlarmManager.RTC_WAKEUP,
        System.currentTimeMillis() + ensurePositiveTime,
        pendingIntent
    )

    Log.e("Alarm", "Alarm updated..!")

}

/**
 * This will receive broadcast after completed seconds
 */
private fun getBroadcastReceiver(): BroadcastReceiver {
    return object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            createNotification()
        }
    }
}

/***
 * It creates notification
 */
private fun createNotification() {
    val channelId = "fcm_default_channel"
    val channelName = "notification"

    val defaultSoundUri =
        RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
    val notificationBuilder = NotificationCompat.Builder(this@MyActivity, channelId)
        .setSmallIcon(R.mipmap.ic_launcher)
        .setContentText("I am alarm from my activity")
        .setContentTitle(getString(R.string.app_name))
        .setAutoCancel(true)
        .setSound(defaultSoundUri)
        .setDefaults(NotificationCompat.DEFAULT_ALL)
        .setPriority(NotificationCompat.PRIORITY_HIGH)

    val mNotificationManager =
        getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel =
            NotificationChannel(
                channelId,
                channelName,
                NotificationManager.IMPORTANCE_HIGH
            )
        notificationBuilder.setChannelId(channelId)
        mNotificationManager.createNotificationChannel(channel)
    }

    val notification = notificationBuilder.build()
    mNotificationManager.notify(0, notification)
}

/**
 * Use this to cancel alarm
 */
private fun cancelAlarm() {


    if (pendingIntent != null) {
        alarmManager?.cancel(pendingIntent)
    }
    if (broadcastReceiver != null) {
        unregisterReceiver(broadcastReceiver)
        broadcastReceiver = null
    }

    Log.e("Alarm", "Alarm has been canceled..!")
}

}

下面是 java 代码

public class AlarmActivity extends AppCompatActivity {
private AlarmManager alarmManager;
private PendingIntent pendingIntent;
private int REQUEST_CODE = 45645;
private String id = "myname";
private long timeInMilliSeconds = 5000;
static String APP_TAG = "classname";
private BroadcastReceiver broadcastReceiver;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    initAlarm();
}

private void initAlarm() {

    //creating intent
    Intent intent = new Intent(id);
    boolean alarmRunning = PendingIntent.getBroadcast(
            this,
            REQUEST_CODE,
            intent,
            PendingIntent.FLAG_NO_CREATE
    ) != null;

    //setting broadcast
    broadcastReceiver = new MyReceiver();
    registerReceiver(
            broadcastReceiver,
            new IntentFilter(id)
    );

    //setting alarm
    long ensurePositiveTime = Math.max(timeInMilliSeconds, 0L);
    pendingIntent = PendingIntent.getBroadcast(
            this,
            REQUEST_CODE,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT
    );

    //Check if alarm is already running
    if (!alarmRunning) {
        alarmManager.set(
                AlarmManager.RTC_WAKEUP,
                System.currentTimeMillis() + ensurePositiveTime,
                pendingIntent
        );

    } else {
        updateAlarm();
        Log.e("Alarm", "Alarm already running.!");
    }
}

private void updateAlarm() {

    //calculating alarm time and creating pending intent
    Intent intent = new Intent(id);
    long ensurePositiveTime = Math.max(timeInMilliSeconds, 0L);
    pendingIntent = PendingIntent.getBroadcast(
            this,
            REQUEST_CODE,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT
    );

    //removing previously running alarm
    alarmManager.cancel(pendingIntent);
    unregisterReceiver(broadcastReceiver);

    //setting broadcast
    broadcastReceiver = new MyReceiver();
    registerReceiver(
            broadcastReceiver,
            new IntentFilter(id)
    );

    //Check if alarm is already running
    alarmManager.set(
            AlarmManager.RTC_WAKEUP,
            System.currentTimeMillis() + ensurePositiveTime,
            pendingIntent
    );

    Log.e("Alarm", "Alarm updated..!");

}

/**
 * Use this to cancel alarm
 */
private void cancelAlarm() {


    if (pendingIntent != null) {
        alarmManager.cancel(pendingIntent);
    }
    if (broadcastReceiver != null) {
        unregisterReceiver(broadcastReceiver);
        broadcastReceiver = null;
    }

    Log.e("Alarm", "Alarm has been canceled..!");
}
}

BroadCastReceiver class

class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
    WakeLocker.acquire(context);
    createNotification(context);
    WakeLocker.release();
}

/***
 * It creates notification
 * @param context
 */
private void createNotification(Context context) {
    String channelId = "fcm_default_channel";
    String channelName = "notification";

    Uri defaultSoundUri =
            RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
    NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, channelId)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setContentText("I am alarm from my activity")
            .setContentTitle(context.getString(R.string.app_name))
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setDefaults(NotificationCompat.DEFAULT_ALL)
            .setPriority(NotificationCompat.PRIORITY_HIGH);

    NotificationManager mNotificationManager =
            (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel =
                new NotificationChannel(
                        channelId,
                        channelName,
                        NotificationManager.IMPORTANCE_HIGH
                );
        notificationBuilder.setChannelId(channelId);
        mNotificationManager.createNotificationChannel(channel);
    }

    Notification notification = notificationBuilder.build();
    mNotificationManager.notify(0, notification);
}

}

Wake Locker class 也可以在后台保持 运行ning,但是为此你必须从应用程序设置中启用后台服务并禁用省电模式

public abstract class WakeLocker {
private static PowerManager.WakeLock wakeLock;

public static void acquire(Context c) {
    if (wakeLock != null) wakeLock.release();

    PowerManager pm = (PowerManager) c.getSystemService(Context.POWER_SERVICE);
    wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
            PowerManager.ACQUIRE_CAUSES_WAKEUP |
            PowerManager.ON_AFTER_RELEASE, AlarmActivity.APP_TAG);
    wakeLock.acquire();
}

public static void release() {
    if (wakeLock != null){
        wakeLock.release();
    }
    wakeLock = null;
}
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.VIBRATE" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.Alarmdemo">
    <activity android:name=".AlarmActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <receiver android:name="com.example.alarmdemo.MyReceiver" />
</application>

您的这部分代码似乎有误:

notificationIntent.setAction("com.example.basicalarmsetter.MatchNotification");

您在这里使用的是 class 名称。你需要使用广播接收器的动作,你放在你的意图过滤器中的那个,a.k.a:

notificationIntent.setAction("com.example.notificationtest.MatchNotification");

另一个问题:您在此处创建了两个不必要的警报:

AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, futureInMillis, pendingIntent);
assert alarmManager != null;
alarmManager.setExact(AlarmManager. ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);

在此部分,以下几行是不必要的:

alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, futureInMillis, pendingIntent);
assert alarmManager != null;

值 RTC_WAKEUP 应该与 System.currentTimeMillis() 一起使用,而不是 SystemClock.elapsedRealtime()

您的 MainActivity.java 的最终结果将如下所示:

package com.example.basicalarmsetter;

import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.SystemClock;

public class MainActivity extends AppCompatActivity {
    private int uniqueId = 0;

    // Schedules a notification in the future given the delay
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void scheduleNotification(int matchId, long delay) {
        // Construct the PendingIntent which will trigger our alarm to go off
        Intent notificationIntent = new Intent();
        notificationIntent.setAction("com.example.notificationtest.MatchNotification");

        PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), matchId, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT) ;
        long futureInMillis = SystemClock.elapsedRealtime() + delay;

        // Set off our PendingIntent
        AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        alarmManager.setExact(AlarmManager. ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
    }

    // Sets an Alarm at a future specified date
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void setAlarm(long notificationDelay) {
        try {
            System.out.println("Setting alarm at " + notificationDelay + " seconds");

            // Sets off a notification after 5 seconds
            scheduleNotification(uniqueId, notificationDelay);

            uniqueId++;

        } catch (Exception ex) {
            System.out.println("Cannot print alarm!");
            System.out.println("Exception: " + ex.toString());
        }
    }

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

        setAlarm(15000);
    }
}

我有一个适合我的解决方案。设置警报如下:

    public class MainActivity extends AppCompatActivity {

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

        long delayInMillis = 5000;//your delay in millisecond

        Intent myIntent = new Intent(this, AlertReceiver.class);
        //any data you want to pass to your receiver class
        myIntent.putExtra(AlertReceiver.TITLE, "Scheduled Alert");
        myIntent.putExtra(AlertReceiver.CONTENT, "You have scheduled alert. Tap here to view continue...");
        AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, myIntent, 0);
        alarmManager.set(AlarmManager.RTC_WAKEUP, delayInMillis, pendingIntent);
    }
}

在您的清单中添加接收者

<receiver android:name=".utils.AlertReceiver" />//path to your receiver

您的警报接收器 class 可以是这样的

public class AlertReceiver extends BroadcastReceiver {
private static final String TAG = "AlertReceiver";

public static final String TITLE = "title";
public static final String CONTENT = "content";

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

    String title = receivedIntent.getStringExtra(TITLE);
    String message = receivedIntent.getStringExtra(CONTENT);
    Log.e(TAG, "onReceive: " + title + ":" + message);

    //you can show your notification or anything you want to do once you receive your alert here...



   }

}

希望这对您有所帮助。编码愉快!!