如何在安卓设备上的应用未运行时,在特定时间发送通知?

huangapple go评论61阅读模式
英文:

How to send notification in a specific time when the app is not running in android devices?

问题

I will provide a translation of the code portion you provided:

这是我的BROADCASTRECEIVER:

public class AlarmReceiver extends BroadcastReceiver {

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

        Log.d("NOTIF","YESSSS, IT's WORKING");

        NotifJobIntentService.enqueueWork(context, intent);

    }
}

这是我的JobIntentService类:

public class NotifJobIntentService extends JobIntentService {

    private static final String TAG = "NotifJobIntentService";
    private Context context;

    static void enqueueWork(Context context, Intent intent) {
        enqueueWork(context, NotifJobIntentService.class, 123 , intent);
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate");
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        context = getApplicationContext();

        Log.d(TAG, "OnHandleWork");

        String input = intent.getStringExtra("inputExtra");

        intent.setFlags(intent.FLAG_INCLUDE_STOPPED_PACKAGES);
        Intent intent1 = new Intent(context , MainFragment.class);
        PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent, 0);

        // TODO: Make this accessible to exterior projects, such as web interface.
        Notification.Builder builder = new Notification.Builder(context)
                .setTicker("Notification")
                .setContentTitle("Important Message")
                .setContentText("This is an example of a push notification using a Navigation Manager")
                .setSmallIcon(R.drawable.ic_add)
                .setContentIntent(pIntent);

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelId = "Your_channel_id";
            NotificationChannel channel = new NotificationChannel(
                    channelId,
                    "Reminder to remind to review your notes",
                    NotificationManager.IMPORTANCE_HIGH);
            channel.setDescription("Hello Dear friends"); //this is to test what this is
            notificationManager.createNotificationChannel(channel);
            builder.setChannelId(channelId);
        }

        Notification notification = builder.build();
        notification.flags = Notification.FLAG_AUTO_CANCEL;

        notificationManager.notify(0, notification);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }

    @Override
    public boolean onStopCurrentWork() {
        Log.d(TAG, "onStopCurrentWork");
        return false;
    }
}

这是设置闹钟管理器的主要活动:

private void setNotificationAlarm(){

    //Alarm Manager

    Calendar time = Calendar.getInstance();

    time.set(Calendar.HOUR_OF_DAY,11);//set the alarm time
    time.set(Calendar.MINUTE, 30);
    time.set(Calendar.SECOND,0);

    AlarmManager am =( AlarmManager)getContext().getSystemService(Context.ALARM_SERVICE);
    Intent i = new Intent(getActivity(), AlarmReceiver.class);
    i.setAction("android.intent.action.NOTIFY");
    PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, i, PendingIntent.FLAG_ONE_SHOT);

    if (Build.VERSION.SDK_INT >= 23){
        am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pi);
    } else{
        am.set(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pi);
    }
}

这是我的清单:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ir.zima.schema">

    <application
        android:name=".CustomApplication"
        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.AppCompat.Light.NoActionBar">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".ListActivity"></activity>
        <activity android:name=".MenuItemActivity"></activity>
        <service
            android:name=".NotifJobIntentService"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:exported="true"
            android:enabled="true"/>
        <receiver android:name=".AlarmReceiver" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.NOTIFY"/>
            </intent-filter>
        </receiver>
    </application>
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
</manifest>

I've provided translations for the code-related parts you shared. If you have any specific questions or need further assistance, please let me know.

英文:

I literally read all threads about this subject but none of them worked. (None of them also has a accepted valid answer that everyone says yay! reading it)

What I am trying to do is to send notification to the user at specific time that user specifies. these are what I tried so far:

  • Alarm manager + Broadcast receiver: this work on the emulator, but not on my device (Xiaomi Redmi note 8). It actually works for short periods like 1 or 2 minutes, but doesnt work for longer periods like 10 or 15 minutes. there is a funny bug about alarms that I will explain later.

  • wrokmanager: again works on the emulator, but not on my device.

  • Alarm manager + Broadcast receiver + JobServiceIntent: this doesn't change anything. again it works fine on emulator as expected. But on my phone, it works for short periods but not when the alarm is set for 15 minutes later.

P.S: I also tried many variation, like different types of alarm (RTC, RTC_WAKEUP). or different methods to set it like setExact, setReapiting etc. did the same with workmanger.

Final Result(Important):

after days of research I reached to this thread and I was convinced that these are all forced by manufacturers and there is no way to solve it rather than asking the user to do some manual changes (like auto start, battery saver etc). However, I found an app that is not one of those whitelisted apps(like facebook, whatsapp etc), it's a local app and is sending notifications on time. First I though it is using FCM, then I turned off my internet connection and I still received notification exactly on time.
Now I can not really think of any other solutions, and couldn't find any on other threads that actually work. Any helps would be appreciated from the bottom of my heart.

this is my BROADCASTRECEIVER:

public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(&quot;NOTIF&quot;,&quot;YESSSS, IT&#39;s WORKING&quot;);
NotifJobIntentService.enqueueWork(context,intent);
}}

this is my JobIntentSerice class:

public class NotifJobIntentService extends JobIntentService {
private static final String TAG = &quot;NotifJobIntentService&quot;;
private Context context;
static void enqueueWork(Context context, Intent intent) {
enqueueWork(context, NotifJobIntentService.class, 123 , intent);
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, &quot;onCreate&quot;);
}
@Override
protected void onHandleWork(@NonNull Intent intent) {
context = getApplicationContext();
Log.d(TAG, &quot;OnHandleWork&quot;);
String input = intent.getStringExtra(&quot;inputExtra&quot;);
intent.setFlags(intent.FLAG_INCLUDE_STOPPED_PACKAGES);
Intent intent1 = new Intent(context , MainFragment.class);
PendingIntent pIntent = PendingIntent.getActivity(context, 0, intent, 0);
// TODO: Make this accessible to exterior projects, such as web interface.
Notification.Builder builder = new Notification.Builder(context)
.setTicker(&quot;Notification&quot;)
.setContentTitle(&quot;Important Message&quot;)
.setContentText(&quot;This is an example of a push notification using a Navigation Manager&quot;)
.setSmallIcon(R.drawable.ic_add)
.setContentIntent(pIntent);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.O)
{
String channelId = &quot;Your_channel_id&quot;;
NotificationChannel channel = new NotificationChannel(
channelId,
&quot;Reminder to remind to review your notes&quot;,
NotificationManager.IMPORTANCE_HIGH);
channel.setDescription(&quot;Hello Dear friends&quot;); //this is to test what this is
notificationManager.createNotificationChannel(channel);
builder.setChannelId(channelId);
}
Notification notification = builder.build();
notification.flags = Notification.FLAG_AUTO_CANCEL;
notificationManager.notify(0, notification);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, &quot;onDestroy&quot;);
}
@Override
public boolean onStopCurrentWork() {
Log.d(TAG, &quot;onStopCurrentWork&quot;);
return false;
}

}

this is my Main activity that sets the alarm manager:

private void setNotificationAlarm(){
//Alarm Manager
Calendar time = Calendar.getInstance();
time.set(Calendar.HOUR_OF_DAY,11);//set the alarm time
time.set(Calendar.MINUTE, 30);
time.set(Calendar.SECOND,0);
AlarmManager am =( AlarmManager)getContext().getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(getActivity(), AlarmReceiver.class);
i.setAction(&quot;android.intent.action.NOTIFY&quot;);
PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, i, PendingIntent.FLAG_ONE_SHOT);
if (Build.VERSION.SDK_INT &gt;= 23){
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pi);
}
else{
am.set(AlarmManager.RTC_WAKEUP,time.getTimeInMillis(),pi);
}
}

and here is my manifest:

&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
package=&quot;ir.zima.schema&quot;&gt;
&lt;application
android:name=&quot;.CustomApplication&quot;
android:allowBackup=&quot;true&quot;
android:icon=&quot;@mipmap/ic_launcher&quot;
android:label=&quot;@string/app_name&quot;
android:roundIcon=&quot;@mipmap/ic_launcher_round&quot;
android:supportsRtl=&quot;true&quot;
android:theme=&quot;@style/Theme.AppCompat.Light.NoActionBar&quot;&gt;
&lt;activity android:name=&quot;.MainActivity&quot;&gt;
&lt;intent-filter&gt;
&lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
&lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
&lt;/intent-filter&gt;
&lt;/activity&gt;
&lt;activity android:name=&quot;.ListActivity&quot;&gt;&lt;/activity&gt;
&lt;activity android:name=&quot;.MenuItemActivity&quot;&gt;&lt;/activity&gt;
&lt;service
android:name=&quot;.NotifJobIntentService&quot;
android:permission=&quot;android.permission.BIND_JOB_SERVICE&quot;
android:exported=&quot;true&quot;
android:enabled=&quot;true&quot;/&gt;
&lt;receiver android:name=&quot;.AlarmReceiver&quot; android:exported=&quot;true&quot;&gt;
&lt;intent-filter&gt;
&lt;action android:name=&quot;android.intent.action.NOTIFY&quot;&gt;
&lt;/action&gt;
&lt;/intent-filter&gt;
&lt;/receiver&gt;
&lt;/application&gt;
&lt;uses-permission android:name=&quot;android.permission.WAKE_LOCK&quot; /&gt;
&lt;uses-permission android:name=&quot;android.permission.SYSTEM_ALERT_WINDOW&quot; /&gt;

</manifest>

and finally the funny bug:
There are many threads that say alarm set by alarm manager is cleared when you swipe away your app and that actually happens (i figured that with adb shell dumpsys...). However after days of research I finally found it in this thread accidently that there is something like a bug. If you run your app via android studio (clicking "run app" button) and then swipe the app away, all alarms will be cleared(adb shell dumpsys returns nothing). But if you run the same app using the launcher Icon and then swipe it away, the alarms are set and adb shell dumpsys also shows it. (which those alarms still work fine on emulator but not on my device).

答案1

得分: 1

我曾经制作过一个应用程序,从服务器获取数据(每小时一次),然后根据数据显示通知。我基本上创建了一个AlarmManager,它每小时触发一次,并启动一个IntentService,该Service将发起网络调用并显示通知。你可以在这里看到如何启动AlarmManager。然后你可以查看Journaldev上的这篇文章,这应该可以解决你的问题。但我想提到的一件事是,正如你提到的,AlarmManager有可能会被清除。在我的情况下,我创建了3个AlarmManager实例,一个是主要的,另外两个用于检查是否触发了主要的那个。对我来说效果还不错,尽管这不是一个好的解决方案。

英文:

Once I had made an app that gets data from server (one hour interval) then show notifications based on the data. I basically created an AlarmManager which will trigger each hour and start an IntentService which will initiate the network call and show notifications. You can see here how to start the AlarmManager. Then you can see this nice articale from Journaldev. That should work. But one thing I want to mention that, it is possible that the alarmmanger may be cleared as you mentioned. In my case I created 3 alarmanager instances, one is the main, and the other 2 was for checking that 1 was triggered or not. Worked just fine for me though it is not a good solution.

答案2

得分: 1

以下是翻译好的部分:

I am sharing the code. But I should tell you that it is not the most efficient and recommended way. This will only serve your needs.

For setting and canceling alarm:

private void setAlarmTask(AlarmManager alarmManager, long time, Intent intent, int request_code){
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, request_code, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, time, AlarmManager.INTERVAL_HOUR, pendingIntent);
}
private void cancelAlarmTask(Intent intent, int request_code){
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, request_code, intent, 0);
pendingIntent.cancel();
}

For starting scheduler:

private void setScheduler(long time_in_miliseconds){
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(time_in_miliseconds);
AlarmManager mainAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
AlarmManager failSafeAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
AlarmManager failSafeAlarmManager_2 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent scheduleServiceExecuterIntent = new Intent(this, ScheduledServiceExecuter.class);
scheduleServiceExecuterIntent.putExtra("state", "Main");
setAlarmTask(mainAlarmManager, time_in_miliseconds, scheduleServiceExecuterIntent, 0);
calendar.add(Calendar.MINUTE, 5);
scheduleServiceExecuterIntent.putExtra("state", "Fail Safe");
setAlarmTask(failSafeAlarmManager, calendar.getTimeInMillis(), scheduleServiceExecuterIntent, 1);
calendar.add(Calendar.MINUTE, 5);
scheduleServiceExecuterIntent.putExtra("state", "Fail Safe 2");
setAlarmTask(failSafeAlarmManager_2, calendar.getTimeInMillis(), scheduleServiceExecuterIntent, 2);
}

For stopping scheduler:

private void stopScheduler(){
Intent scheduleServiceExecuterIntent = new Intent(this, ScheduledServiceExecuter.class);
cancelAlarmTask(scheduleServiceExecuterIntent, 0);
cancelAlarmTask(scheduleServiceExecuterIntent, 1);
cancelAlarmTask(scheduleServiceExecuterIntent, 2);
}
英文:

I am sharing the code. But I should tell you that it is not the most efficient and recommended way. This will only serve your needs.

For setting and canceling alarm:

private void setAlarmTask(AlarmManager alarmManager, long time, Intent intent, int request_code){
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, request_code, intent, 0);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, time, AlarmManager.INTERVAL_HOUR, pendingIntent);
}
private void cancelAlarmTask(Intent intent, int request_code){
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, request_code, intent, 0);
pendingIntent.cancel();
}

For starting scheduler:

private void setScheduler(long time_in_miliseconds){
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(time_in_miliseconds);
AlarmManager mainAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
AlarmManager failSafeAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
AlarmManager failSafeAlarmManager_2 = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent scheduleServiceExecuterIntent = new Intent(this, ScheduledServiceExecuter.class);
scheduleServiceExecuterIntent.putExtra(&quot;state&quot;, &quot;Main&quot;);
setAlarmTask(mainAlarmManager, time_in_miliseconds, scheduleServiceExecuterIntent, 0);
calendar.add(calendar.MINUTE, 5);
scheduleServiceExecuterIntent.putExtra(&quot;state&quot;, &quot;Fail Safe&quot;);
setAlarmTask(failSafeAlarmManager, calendar.getTimeInMillis(), scheduleServiceExecuterIntent, 1);
calendar.add(calendar.MINUTE, 5);
scheduleServiceExecuterIntent.putExtra(&quot;state&quot;, &quot;Fail Safe 2&quot;);
setAlarmTask(failSafeAlarmManager_2, calendar.getTimeInMillis(), scheduleServiceExecuterIntent, 2);
}

For Stopping scheduler:

private void stopScheduler(){
Intent scheduleServiceExecuterIntent = new Intent(this, ScheduledServiceExecuter.class);
cancelAlarmTask(scheduleServiceExecuterIntent,0);
cancelAlarmTask(scheduleServiceExecuterIntent,1);
cancelAlarmTask(scheduleServiceExecuterIntent,2);
}

huangapple
  • 本文由 发表于 2020年8月3日 15:08:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/63225115.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定