本文介绍了每天同时通知的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想每天都在同一时间发送通知.
我已经阅读了一些帖子和教程/示例,但无法正常工作.

I want a notification every day at the same time.
I already read some posts and tutorials/examples but it won't work correctly.

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

这是的两种方式(在最后一行中有和没有所有者)
这个错误仅在我的S3上如此极端,在我的N7(2013)上好一点了

每次重新启动后,我都会收到通知. (只是一个想法:如果我删除它,崩溃的可能性就更高.)

After every restart I get a notification. (just a thought: And if I delete it, the possibility is higher to make a crash.)

有点讨厌每3分钟收到一次通知^-^

A bit annoying to receive a notification every 3 minutes ^-^

版本1-提供服务

Larry Schiefer 这样的更新代码告诉了
完整日志

updated code like Larry Schiefer told
new full log

NotifyManager
有关最新版本,请参见下文
此更新的版本

NotifyManager
see below for newest version
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
    }
}

更新3

自动启动有效..
但是现在,它也死了此代码未做任何更改;仅上面的代码

Update 3

Autostart worked..
but now, it dies toonothing changed in this code; only the code above

<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");
        }
    }
}

CatLog
s3-完整
n7

CatLog
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

更新4

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);
    }
}

推荐答案

要点A:服务代码缺少关键组件

在上面的代码中,该服务具有一个onCreateonDestroy,将在创建和销毁该服务时触发它们.但是,如果服务被触发并且已经在运行,则它将不会通过onCreate.但是,它将通过onstartCommand(在Android 2.0之前的onStart).您的代码的实际结构应为:

In the code above, the service has an onCreate and onDestroy, which will be triggered when the service is created and destroyed. However, if a service is triggered and it is already running, then it will not go through onCreate. It will, however, go through onstartCommand (onStart pre android 2.0). The actual structure of your code should be:

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,该广播接收器是专门为需要偶尔触发但实际上并不需要触发的事物而设计的.所以:

You cannot rely on a service hanging around for that long. Inactive services will often be removed to make space for other things. Given that the the service does very little, it would probably be better to use a BroadcastReceiver, which was designed specifically for things that need triggering occasionally but don't really need to be there otherwise. So:

  1. 使用BroadcastRecevier捕获触发器并发出通知.像这样:

  1. Use a BroadcastRecevier to catch the triggers and issue a notification. Something like this:

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
        <...>
    }
}

请记住将其设置为接收清单中的广播:

Remember to set it up to receive broadcasts in the manifest:

<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>

  • 要触发该操作,您需要一个意图来触发广播:

  • To trigger that, you need an intent that will trigger a broadcast:

    Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
    context.sendBroadcast(intent);
    

    如果您将其设置为稍后通过PendingIntent进行调用(如果您希望重复发生的事件可重复使用PendingIntent,则将最终标志更改为零):

    If you are setting it up to call later via a PendingIntent (change the final flag to zero if you want a reusable PendingIntent for a recurring event):

    Intent intent = new Intent("com.mystuff.coolapp.ACTION_TIME_FOR_NOTIFICATION");
    PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_ONE_SHOT)
    

    如果以后您希望更改或取消某项操作,或者仅从系统角度了解挂起意图是否存在,就可以了:

    If later on you wish to change, or cancel somehting, or if you simply need to know if the Pending Intent exists from the system's point of view:

    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,即:

    Personally, I would use this approach instead of initializePendingIntent, ie:

    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(如您已经做过的)来跟踪发生的情况(警报时间)

  • Use SharedPreferences (as you already do) to keep track of what is going on (time of alarm)

    我的喜好是只创建一个单发警报,意图是在下一声警报响起时发出一声.如果更改,请删除此警报并创建一个新警报.当它触发时,创建一个新的.这样一来,您可以将必须​​存活一段时间的事物的数量减至最少.

    My preference would be to only create a one shot alarm with a one shot intent for when the next alarm should sound. If it changes, remove this alarm and create a new one. When it triggers, crate a new one. This way you minimise the number of things that have to stay alive for lengths of time.

    这篇关于每天同时通知的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

  • 07-28 01:43