我希望每天都能同时收到通知 我已经阅读了一些帖子和教程/示例,但它无法正常工作。
11-07 07:33:05.725 4611 6121 I ActivityManager: Process at.htl3r.appmosphere (pid 5238) has died.
11-07 07:33:05.725 4611 6121 W ActivityManager: Scheduling restart of crashed service at.htl3r.appmosphere/.notify.NotifyService in 14648ms
11-07 07:33:20.400 4611 4632 I ActivityManager: Start proc at.htl3r.appmosphere for service at.htl3r.appmosphere/.notify.NotifyService: pid=5463 uid=10096 gids={50096}
---
11-07 07:33:41.580 4611 4623 I ActivityManager: Process at.htl3r.appmosphere (pid 5463) has died.
11-07 07:33:41.580 4611 4623 W ActivityManager: Scheduling restart of crashed service at.htl3r.appmosphere/.notify.NotifyService in 73293ms
11-07 07:33:44.310 4611 5385 F ProcessStats: Starting service ServiceState{43760cf0 at.htl3r.appmosphere.notify.NotifyService pkg=at.htl3r.appmosphere proc=43760cf0} without owner
这两种方式(在最后一行有和没有所有者)
这个bug只在我的S3上如此极端,在我的N7(2013)上有点好看
每次重启后我都会收到通知。 (只是一个想法:如果我删除它,崩溃的可能性会更高。)
每3分钟收到一次通知有点烦人^ - ^
像 Larry Schiefer 这样的更新代码告诉了 new full log
NotifyManager
请参阅下面的最新版本
version from this update
NotifyReceiver
public class NotifyReceiver extends BroadcastReceiver {
private static final String TAG = "NotifyReceiver";
public static final int ID_NEWHINTAVAILABLE = 1;
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
SharedPreferences spref = PreferenceManager.getDefaultSharedPreferences(context);
NotificationManager mNM = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Intent i = new Intent(context.getApplicationContext(), MainActivity.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, i, 0);
Notification.Builder mNotifyBuilder = new Notification.Builder(context);
mNotifyBuilder.setSmallIcon(R.drawable.ic_stat_name);
mNotifyBuilder.setContentTitle(context.getString(R.string.app_name));
mNotifyBuilder.setContentText(context.getString(R.string.notification_contenttext));
mNotifyBuilder.setContentIntent(pIntent);
mNotifyBuilder.setAutoCancel(true);
// has to have an icon - now the app icon
// auto cancel after click: in main use cancel(int id);
// mNotifyBuilder.addAction(R.drawable.ic_stat_name, getString(R.string.notification_action), pIntent);
// mNotifyBuilder.setTicker(getString(R.string.app_name));
// mNotifyBuilder.setTicker(getString(R.string.app_name)+" "+getString(R.string.notification_contenttext));
// mNotifyBuilder.setWhen(System.currentTimeMillis());
// mNotifyBuilder.setDefaults(Notification.DEFAULT_SOUND | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_LIGHTS);
// http://stackoverflow.com/questions/2724871/how-to-bring-up-list-of-available-notification-sounds-on-android
String sound = spref.getString(SettingsFragment.pref_notify_sound, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION).toString());
mNotifyBuilder.setSound(Uri.parse(sound));
if (spref.getBoolean(SettingsFragment.pref_notify_vibrate, true)) {
// mNotifyBuilder.setVibrate(new long[] { 0, 1000 });
mNotifyBuilder.setDefaults(Notification.DEFAULT_VIBRATE);
}
if (spref.getBoolean(SettingsFragment.pref_notify_light, true)) {
mNotifyBuilder.setLights(Color.GREEN, 3000, 3000);
}
Notification mNotify = mNotifyBuilder.build();
mNM.notify(ID_NEWHINTAVAILABLE, mNotify);
NotifyManager.startAlarm(context, true);
// wenn aktiviert: ausgeführt & neu gestartet
// bei Deaktiviertung: abgebrochen - demnach kein Neustart
}
}
自动启动工作..
但现在,它也死了
此代码中没有任何变化;只有上面的代码
<receiver android:name="at.htl3r.appmosphere.notify.Autostart" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
Autostart.java
public class Autostart extends BroadcastReceiver {
private static final String TAG = "autostart";
@Override
public void onReceive(Context context, Intent intent) {
if (NotifyManager.isNotificationEnabled(context)) {
NotifyManager.startAlarm(context);
Log.i(TAG, "started");
}
}
}
目录下载
s3 - full
N7
12-14 23:15:19.227 1452 1679 I ActivityManager: Start proc at.htl3r.appmosphere for broadcast at.htl3r.appmosphere/.notify.Autostart: pid=5837 uid=10391 gids={50391, 3003}
12-14 23:15:42.300 1452 4109 I ActivityManager: Killing 5837:at.htl3r.appmosphere/u0a391 (adj 15): empty #17
12-15 06:43:47.501 18799 18819 D JsonParser: at.htl3r.appmosphere: publishState=6
12-15 06:43:47.501 18799 18819 D JsonParser: Skipping app 0 with state != 1: package name=at.htl3r.appmosphere: state=6
NotifyManager
public class NotifyManager {
private static final String TAG = "NotifyManager";
/**
* {@link #startAlarm(Context, boolean)}<br>
* default: restart: true
*
* @param context Context of activity
* @return alarm started: true<br>
* is running: false
*/
public static boolean startAlarm(Context context) {
return startAlarm(context, false);
}
/**
* @param context Context of activity
* @param restart start the alarm even when already running
* @return true if started | false if running and not started
*/
public static boolean startAlarm(Context context, boolean restart) {// todo restart alarm on settings change
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
SharedPreferences spref = PreferenceManager.getDefaultSharedPreferences(context);
String time = spref.getString(SettingsFragment.pref_notify_time, TimePreference.notify_default);
int hour = Integer.parseInt(time.split("\\:")[0]);
int minute = Integer.parseInt(time.split("\\:")[1]);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.HOUR_OF_DAY, hour);
// alternative: HOUR and AM_PM
if (calendar.getTimeInMillis() < Calendar.getInstance().getTimeInMillis()) {
calendar.add(Calendar.DAY_OF_MONTH, 1);
}
// String time = new SimpleDateFormat("hh:mm", Locale.getDefault()).format(calendar.getTime());
if (!isAlarmRunning(context) || restart) {
alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), getPendingIntent(context));
Log.d(TAG, "Start Alarm at " + time);
// Toast.makeText(context, "Start Alarm at " + time, Toast.LENGTH_LONG).show();
return true;
}
Log.d(TAG, "Service already running");
return false;
}
/**
* @param context Context of activity
* @return true if running and canceled
*/
public static boolean cancelAlarm(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (isAlarmRunning(context)) {
alarmManager.cancel(getPendingIntent(context));
Log.d(TAG, "Cancel Alarm");
NotifyManager.isAlarmRunning(context);
// Toast.makeText(context, "Cancel Alarm from " + time, Toast.LENGTH_LONG).show();
return true;
}
Log.d(TAG, "Service already canceled");
return false;
}
/**
* @param context Context of activity
* @return if alarm is running
*/
public static boolean isAlarmRunning(Context context) {
Intent intent_service = new Intent(context, NotifyReceiver.class);
Log.d(TAG, "isAlarmRunning:" + (PendingIntent.getBroadcast(context, 0, intent_service, PendingIntent.FLAG_NO_CREATE) != null));
return (PendingIntent.getBroadcast(context, 0, intent_service, PendingIntent.FLAG_NO_CREATE) != null);
}
/**
* @param context Context of activity
* @return PendingIntent
*/
public static PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, NotifyReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
// If it exists return it
if (pi != null)
return pi;
// It doesn't exist, make it (last parameter to 0 for reusable):
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
/**
* @return yyMMdd
*/
public static String getCurrentTimeStamp() {
SimpleDateFormat sdfDate = new SimpleDateFormat("yyMMdd", Locale.getDefault());
Date now = new Date();
String strDate = sdfDate.format(now);
return strDate;
}
/**
* @param context Context of the activity
* @return if notification is enabled or not
*/
public static boolean isNotificationEnabled(Context context) {
return PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.pref_notify, true);
}
}
答案 0 :(得分:3)
A点:服务代码缺少关键组件
在上面的代码中,服务有一个onCreate
和onDestroy
,它们将在创建和销毁服务时触发。但是,如果服务已触发并且已在运行,则它将不会通过onCreate
。但是,它将通过onstartCommand
(onStart
pre android 2.0)。代码的实际结构应该是:
onCreate() {
// Stuff you only do when this class is instantiated the first time
// and don't need to do if it is called (started in android terminology)
// thereafter
}
// The next two are >=2.0 and then <2.0
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startHandleIntent(intent);
return START_STICKY; // If you want the service to hang around
}
@Override
public void onStart(Intent intent, int startId) {
startHandleIntent(intent);
}
void startHandleIntent(Intent intent) {
// Do things that shiould happen every time here
// eg. in your case, the notification
}
B点:这不是一项服务的设计
你不能依赖这种长期存在的服务。通常会删除非活动服务以为其他事物腾出空间。鉴于该服务的功能很少,使用BroadcastReceiver可能会更好,因为BroadcastReceiver是专门为偶尔需要触发的东西而设计的,但实际上并不需要在那里。所以:
使用BroadcastRecevier
来捕获触发器并发出通知。像这样:
class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// Issue the notidfication
<...>
// Reissue a request for a future alarm call here if needed
<...>
}
}
请记住将其设置为在清单中接收广播:
<application>
... other stuff ...
<receiver android:name=".MyBroadcastReceiver" android:enabled="true">
<intent-filter>
<action android:name="com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION"/>
</intent-filter>
</receiver>
</application>
要触发此操作,您需要一个会触发广播的意图:
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
context.sendBroadcast(intent);
如果您要将其设置为稍后通过PendingIntent调用(如果您希望可重复使用的PendingIntent
重复发生事件,则将最终标志更改为零):
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT)
如果您希望稍后更改或取消某些内容,或者您只是需要知道系统的观点是否存在待处理意图:
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
if (pi != null) {
// It exists. If you want then to cancel the alarm that triggers it:
alarmManager.cancel(pi);
}
else {
// It doesn't exist. If you need to create a reusable PendingIntent:
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
}
就个人而言,我会使用这种方法而不是initializePendingIntent
,即:
public static PendingIntent getPendingIntent() {
Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
// If it exists return it
if (pi != null) return pi;
// It doesn't exist, make it (last parameter to 0 for reusable):
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT);
}
使用SharedPreferences(正如您所做)来跟踪发生的事情(发出警报的时间)
我的偏好是只创建一次性警报,一次性警报应该发出下一个警报。如果更改,请删除此警报并创建一个新警报。当它触发时,创建一个新的。这样,您可以最大限度地减少必须长时间保持活动状态的数量。
答案 1 :(得分:0)
检查logcat以查找堆栈跟踪。它将在您提供的活动管理器服务条目之前。这条线看起来很可疑,特别是setAction
,因为它没有为图标提供适当的资源值:
mNotifyBuilder.setContentTitle(getString(R.string.app_name)).setContentText(getString(R.string.notification_contenttext)).setContentIntent(pIntent).addAction(0, getString(R.string.notification_action), pIntent).setAutoCancel(true)