问题
I'm trying to create a reminder application that pops up a reminder in the status bar. As I understand, the Android NotificationManager is the standard tool to use to schedule notifications however a BootService is needed in order to re-schedule notifications as scheduled events do not survive across boots.
I've pieced together an application below which schedules a single reminder at boot time, and pops up a notification in the status bar. If you click the notification, it launches MainActivity which, in the future, will have options to add more reminders, delete them, or whatever.
The problem I'm having is that the reminder works correctly the first time however it seems to be re-scheduling itself and popping up again at random times for some reason. Should I be launching another activity rather than the one which installs the BootAlarmService?
UPDATE: So I think I found some clues with logcat. Apparently the Service is crashing and being restarted which is resetting the Notification. Any ideas why this is?
UPDATE II : code changed to working model
ActivityManager I No longer want com.example.alarm_boot_test (pid 1428): hidden #16
ActivityManager W Scheduling restart of crashed service com.example.alarm_boot_test/.BootAlarmService in 5000ms
ActivityManager W Scheduling restart of crashed service com.example.alarm_boot_test/.NotificationAlarmService in 15000ms
ActivityManager I Start proc com.example.alarm_boot_test for service com.example.alarm_boot_test/.BootAlarmService: pid=2321 uid=10069 gids={}
dalvikvm D Debugger has detached; object registry had 1 entries
szipinf D Initializing inflate state
BootAlarmService D oncreate()
BootAlarmService D alarm set for Thu Jan 17 08:03:00 CST 2013
MainActivity.java
package com.example.alarm_boot_test;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
@Override
protected void onCreate (Bundle savedInstanceState)
{
super.onCreate (savedInstanceState);
setContentView (R.layout.activity_main);
}
}
BootAlarmService.java
package com.example.alarm_boot_test;
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class BootAlarmService extends Service
{
private final String TAG = this.getClass ().getName ();
@Override
public void onCreate ()
{
Log.d (TAG, "oncreate()");
super.onCreate ();
}
@Override
public int onStartCommand (Intent intent, int flags, int startId)
{
Log.d (TAG, "alarm_test: BootAlarmService.onStartCommand() Received start id " + startId + ": " + intent);
// if intent == null, service has been killed/restarted by system
if (intent != null)
createNotificationOnBoot();
else
Toast.makeText (getBaseContext (), "Intent was null in BootAlarmService.", Toast.LENGTH_LONG).show();
return START_STICKY;
}
private void createNotificationOnBoot ()
{
Intent inotify = new Intent(this , NotificationAlarmService.class);
inotify.setFlags (Intent.FLAG_ACTIVITY_NEW_TASK);
AlarmManager amgr = (AlarmManager)getSystemService(ALARM_SERVICE);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, inotify, 0);
// go off two mins from now
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, calendar.get (Calendar.MINUTE) + 2);
amgr.set (AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis (), pendingIntent);
Log.d (TAG, "alarm set for " + calendar.getTime ().toString ());
}
@Override
public IBinder onBind (Intent intent)
{
return null;
}
}
BootReceiver.java
package com.example.alarm_boot_test;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class BootReceiver extends BroadcastReceiver
{
@Override
public void onReceive (final Context context, final Intent bootintent)
{
Intent i = new Intent ();
i.setAction ("com.example.alarm_boot_test.BootAlarmService");
context.startService (i);
}
}
NotificationAlarmService.java
package com.example.alarm_boot_test;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class NotificationAlarmService extends Service
{
private final String TAG = this.getClass().getName ();
@Override
public void onCreate ()
{
super.onCreate ();
}
@Override
public int onStartCommand (Intent intent, int flags, int startId)
{
Log.d (TAG, "alarm_test: NotificationAlarmService.onStartCommand()");
if (intent != null)
createNotification ();
else
Toast.makeText (getBaseContext (), "Intent was null in NotificationAlarmService.", Toast.LENGTH_LONG).show();
return super.onStartCommand (intent, flags, startId);
}
private void createNotification()
{
NotificationManager notificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
Notification notification = new Notification(android.R.drawable.stat_sys_warning, "Note from AlarmService", System.currentTimeMillis());
Intent i = new Intent(this, ViewReminder.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, i, 0);
notification.setLatestEventInfo(this, "New Notification", "You have been notified by AlarmService", pendingIntent);
notificationManager.notify(10001, notification);
}
@Override
public IBinder onBind (Intent intent)
{
return null;
}
}
*activity_main.xml*
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="Service started! Reboot!" />
</RelativeLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.alarm_boot_test"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-sdk
android:minSdkVersion="9"
android:targetSdkVersion="9" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service android:name="com.example.alarm_boot_test.BootAlarmService" >
<intent-filter>
<action android:name="com.example.alarm_boot_test.BootAlarmService" >
</action>
</intent-filter>
</service>
<receiver android:name="com.example.alarm_boot_test.BootReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" >
</action>
</intent-filter>
</receiver>
<service android:name="com.example.alarm_boot_test.NotificationAlarmService" >
</service>
<activity
android:name="com.example.alarm_boot_test.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
回答1:
In case it helps someone else, I think I needed to check the passed Intent for NULL in the onStartCommand() method both in BootAlarmService.java as well as NotificationAlarmService.java. Only been testing it a couple days but it looks like the Intent is NULL if the system kills/restarts the service. Simply testing for this allowed me to create the notification only when the service is started at Boot-time (when the passed Intent is NOT null).
来源:https://stackoverflow.com/questions/14381165/android-alarmmanager-randomly-scheduling-notifications