Xamarin Android:在特定时间重新打开应用程序

Xamarin Android: Reopen app at specific time

我正在努力尝试在 Xamarin 中创建一个闹钟类型的应用程序,我现在正在做的是尝试让该应用程序在特定时间重新打开,无论如何(我知道不包括重启)那是 onbootcompleted)但是当我现在关闭应用程序时,AlarmManager 广播永远不会被触发并且应用程序永远不会重新打开。

现在我正在尝试通过将 alarmmanager 设置为在我单击按钮 1 分钟后弹出广播来进行测试。

广播接收器

using Android.Content;

namespace (removed)
{
    [BroadcastReceiver]
    public class ServiceEventHandler : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
            Intent temp = new Intent();
            temp.SetClass(context, typeof(MainActivity));
            temp.SetFlags(ActivityFlags.NewTask);
            context.StartActivity(temp);
        }
    }
}

清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="(removed)" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="24" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.PERSISTENT_ACTIVITY" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application android:icon="@drawable/Icon" android:label="(removed)">
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
        <meta-data android:name="com.google.android.geo.API_KEY" android:value="(removed)" />
    </application>
</manifest>

启动闹钟的方法

private void Test_Click(object sender, EventArgs e)
        {
            //StartService(new Intent(this, typeof(TestService)));
            Intent wake = new Intent(this, typeof(ServiceEventHandler));
            PendingIntent pending = PendingIntent.GetBroadcast(this, 0, wake, 0);

            var alarmMgr = (AlarmManager)GetSystemService(AlarmService);

            Calendar cal = Calendar.GetInstance(Java.Util.TimeZone.Default);
            cal.Set(CalendarField.HourOfDay, DateTime.Now.Hour);
            cal.Set(CalendarField.Minute, DateTime.Now.Minute + 1);
            cal.Set(CalendarField.Minute, cal.Get(CalendarField.Minute) + 1);//One minute for test

            alarmMgr.Set(AlarmType.RtcWakeup, cal.TimeInMillis, pending);
        }

1) 您的日历可能有问题。添加:

cal.Set(CalendarField.Year, DateTime.Now.Year);
cal.Set(CalendarField.Month, DateTime.Now.Month - 1);
cal.Set(CalendarField.DayOfMonth, DateTime.Now.Day);

删除这个:

cal.Set(CalendarField.Minute, cal.Get(CalendarField.Minute) + 1);

2) 对于不同的 API 级别,您可能无法使用正确的方法。使用这个:

if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop)
        {
            var launchIntent = new Intent(Application.Context, typeof(SplashActivity)); //use your starting activity
            var launchPendingIntent = PendingIntent.GetActivity(Application.Context, requestCode, launchIntent,
                PendingIntentFlags.CancelCurrent); //use the same request code as for 'pending'
            var alarmInfo = new AlarmManager.AlarmClockInfo(cal.TimeInMillis, launchPendingIntent);
            alarmMgr.SetAlarmClock(alarmInfo, pending);
        }
else if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
        alarmMgr.SetExact(AlarmType.RtcWakeup, cal.TimeInMillis, pending);
else
        alarmMgr.Set(AlarmType.RtcWakeup, cal.TimeInMillis, pending);

最终,在尝试了几周之后,我终于发现了一种无论如何都能让闹钟响起的可靠方法。总的来说,有很多关于这个过程的文档,但我发现很多文档并不全部集中在一个地方。

这就是我所做的:

AlarmManager 中设置的警报只有在设置它的应用程序始终为 运行 时才会持久存在。

所以解决方案是拥有一个运行和管理所有警报的后台服务。

代码:

我发现使用 Cal 留下了很大的错误空间,尤其是在使用 xamarin 时,所以我改为使用当前系统时间,并添加了现在和我希望闹钟响起之间的时间间隔

TimeSpan span = time - DateTime.Now;
long schedule = (long)(Java.Lang.JavaSystem.CurrentTimeMillis() + span.TotalMilliseconds);

这是完整的报警代码:

    private void SetAlarm(DateTime time, string alarmID, int eventID, bool final)
    {
        if (!final)
        {
            Intent wake = new Intent(this, typeof(Check//Removed//Event));
            wake.PutExtra("alarmID", alarmID);
            PendingIntent temp = PendingIntent.GetBroadcast(this, eventID, wake, PendingIntentFlags.NoCreate);
            if (temp != null)
            {
                temp.Cancel();
            }
            PendingIntent pending = PendingIntent.GetBroadcast(this, eventID, wake, PendingIntentFlags.UpdateCurrent);

            var alarmMgr = (AlarmManager)GetSystemService(AlarmService);

            TimeSpan span = time - DateTime.Now;

            long schedule = (long)(Java.Lang.JavaSystem.CurrentTimeMillis() + span.TotalMilliseconds);

            alarmMgr.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, schedule, pending);
        }
        else
        {//Set Alarm
            Intent wake = new Intent(this, typeof(//Removed//Event));
            wake.PutExtra("alarmID", alarmID);
            PendingIntent temp = PendingIntent.GetBroadcast(this, eventID, wake, PendingIntentFlags.NoCreate);
            if (temp != null)
            {
                temp.Cancel();
            }
            PendingIntent pending = PendingIntent.GetBroadcast(this, eventID, wake, PendingIntentFlags.UpdateCurrent);

            var alarmMgr = (AlarmManager)GetSystemService(AlarmService);

            TimeSpan span = time - DateTime.Now;
            long schedule = (long)(Java.Lang.JavaSystem.CurrentTimeMillis() + span.TotalMilliseconds);

            alarmMgr.SetExactAndAllowWhileIdle(AlarmType.RtcWakeup, schedule, pending);

        }
    }
}

准确设置闹钟。至于让一切持久化,我让服务变得有粘性,并将其设置为每次重置时重置所有警报。这是通过检查广播是否已经存在,然后将其删除并在存在时重新安排来完成的。

PendingIntent temp = PendingIntent.GetBroadcast(this, eventID, wake, PendingIntentFlags.NoCreate);
if (temp != null)
{
    temp.Cancel();
}

这个和接收启动通知的广播接收器的组合使警报尽可能持久。