Android闹钟管理器在重启/从最近任务管理器中删除后如何继续?

5

我希望能够使用AlarmManager在用户预设的时间从服务器向用户发送通知。代码如下:

MainActivity:

public void set_Retrival_then_notifcation_Alarm(Context context, int year, int month, int day, int hour, int min, int sec) 
    {
        Calendar updateTime = Calendar.getInstance();
        updateTime.setTimeZone(TimeZone.getDefault());
        updateTime.set(Calendar.YEAR, year);
        updateTime.set(Calendar.MONTH, month-1);
        updateTime.set(Calendar.DATE, day);
        updateTime.set(Calendar.HOUR_OF_DAY, hour);
        updateTime.set(Calendar.MINUTE, min);
        updateTime.set(Calendar.SECOND, sec);
        Intent downloader = new Intent(context, AlarmBroadcastReceiver.class);
        downloader.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
        AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);           
        alarmManager.set(AlarmManager.RTC_WAKEUP, updateTime.getTimeInMillis(), pendingIntent);                        
    }

ParseService:

public class ParseService extends IntentService 
{
    static String Parse_AppID = "abc";  
    static String Parse_ClientKey = "abckey";
    String notification_next_price = "";
    String notification_next_date = "";
    SharedPreferences settings;

    public class LocalBinder extends Binder 
    {
        public ParseService getService() 
        {
            return ParseService.this;
        }
    }

    public ParseService() 
    {
        super("ParseService");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent,flags,startId);
        return START_STICKY ;
    }

    @Override
    protected void onHandleIntent(Intent intent) 
    {
        Log.d("MyService", "About to execute MyTask");
        new MyTask().execute();
    }

    private class MyTask extends AsyncTask<String, Void, Boolean> 
    {
        @Override
        protected Boolean doInBackground(String... strings) 
        {
            Log.d("MyService - MyTask", "Calling doInBackground within MyTask");
            initParse(ParseService.this);
            get_notification_info(ParseService.this);
            return false;
        }
    }

    private void sendNotification(Context context, String title, String content) 
    {
        Log.d("MyService - MyTask", "A - sendNotification");
        settings = context.getSharedPreferences("MyApp",0);
        boolean notify_is_on = settings.getBoolean("notify_is_on", true);
        int saved_amount = settings.getInt("saved_amount", 800);

        NumberFormat numberFormat = NumberFormat.getInstance();
        int k = 0;
        try
        {
            k = numberFormat.parse(notification_next_price).intValue();
            if (notify_is_on && (k >= (saved_amount*10000)))
            {
                setNotificationContent(context, k, ""+title, content + "");
            }
            else
            {
                Toast.makeText(getApplicationContext(), "No need notification", Toast.LENGTH_SHORT).show();
            }
        }
        catch (Exception ex)
        {
            setNotificationContent(context, k, "Title2", "Content2");
        }
    } 

    public void setNotificationContent(Context context, int k, String title, String content)
    {
        Intent notificationIntent = new Intent(context, CurrentResult.class);
        PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent, 0);
        Notification noti = new Notification.Builder(context)
            .setTicker("Hello")
            .setSmallIcon(R.drawable.b06)
            .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_launcher))
            .setAutoCancel(true)
            .setOngoing(true) 
            .setOnlyAlertOnce(true)
            .setContentTitle(""+title)
            .setContentText(""+content)
            .setContentIntent(pi)
         .build();

        startForeground(1337, noti);

        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, noti);

        stopSelf();
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    public void initParse(Context context) 
    {
        try 
        {
            ...connection to Parse.com
        } 
        catch (Exception e) 
        {
            e.printStackTrace();
        }
    }

    public void get_notification_info(Context context)
    {       
        ParseQuery<ParseObject> query = ParseQuery.getQuery("Record_db");
        //....getting records from server
        sendNotification(ParseService.this, ""+notification_next_price, ""+notification_next_date);
                    }                   
                } 
                else 
                {
                    Toast.makeText(getApplicationContext(), "Getting server content error", Toast.LENGTH_SHORT).show();
                }
            }
        });
    }
}

AlarmBroadcastManager

public class AlarmBroadcastReceiver extends BroadcastReceiver 
{
    public AlarmBroadcastReceiver () {
    }

    @Override
     public void onReceive(Context context, Intent intent) 
     {
         Intent dailyUpdater = new Intent(context, ParseService.class); 
         context.startService(dailyUpdater);
         Log.d("AlarmReceiver", "Called context.startService from AlarmReceiver.onReceive");
     }
}

清单:

<service android:name="com.abc.abc.ParseService" 
                android:enabled="true"
                android:exported="true" />
        <receiver 
            android:name="com.abc.abc.AlarmBroadcastReceiver"
            android:enabled="true"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>         

问题:

当应用程序在最近的任务列表中时,从服务器提取记录并发送到通知是正常工作的。

然而,如果应用程序被手动从最近的任务列表中删除,则alarmManager和通知都将被取消,并且将无法接收到通知。

我已经搜索了很多解决方案,大多数解决方案都是在onStartCommand中返回START_STICKY,在清单中注册,但我尝试过没有成功。

请问有什么问题吗?为什么应用程序在手动从最近任务列表中删除后无法重新启动通知?是否有任何示例可以使此ParseService在用户预设的时间工作?

谢谢!


这意味着用户有意从最近任务列表中删除了该应用程序(可以通过长按Home按钮启动最近任务列表)。 - pearmak
在普通的Android设备上,这不应该影响到预定的闹钟。由于您的问题似乎没有任何“AlarmManager”代码,因此很难对此发表评论。 - CommonsWare
相关的AlarmManager代码已添加到上面的问题中。有关详细信息,fetchServer和发送通知操作每2天执行一次。是的,我正在普通的Android设备上进行测试,随着时间的推移,应用程序通常会被系统从最近任务列表中自动删除。如果它被系统删除,闹钟会被删除吗?我已经尝试手动从列表中删除应用程序,闹钟不再起作用。 - pearmak
现在大多数应用程序都有通知功能,例如,在某些游戏中,每隔3个小时就可以免费获得一些奖励,并且当奖励解锁时会有通知...应该有标准的方法来解决这个问题吧?上面漏掉了什么? - pearmak
1
顺便提一下:当onHandleIntent方法执行完毕后,你的Intent服务可能会消失,并且你的应用程序可能会被杀死。不能保证异步任务一定会运行。如果您希望在后台执行操作,则可以删除异步任务并在Intent服务中执行操作。 - JohanShogun
显示剩余3条评论
4个回答

5
  • 您需要将已注册的警报存储在本地数据库中
  • 然后创建一个开机完成接收器
    • 在此接收器中加载所有警报并重新注册它们
  • 在AlarmBroadcastReceiver类中从数据库中删除此警报

就是这样


1
请问您能详细说明如何将注册的闹钟存储在本地数据库中吗? - pearmak

3

为了得到更好的体验,你应该使用GcmListenerService来代替AlarmManager。这样即使你的应用程序被从任务管理器中删除,也可以收到通知GcmListenerService


我以前没有使用过GCM。但是,我的当前数据库在其他第三方提供商那里,所以我需要将数据库转移到GCM云上? - pearmak
不完全是这样... 你需要在两个方面使用GCM(Google Cloud Messaging)(服务器:存储数据库的地方和应用程序:接收推送通知的地方)。这很简单,只需在网络上搜索即可。 - Sanat Chandravanshi

2
我已经修改了。
set_Retrival_then_notifcation_Alarm 

以下为测试内容。在当前时间上添加30秒并设置闹钟。同时从最近任务中关闭应用以进行测试。
 Calendar cur_cal = Calendar.getInstance();
        cur_cal.setTimeInMillis(System.currentTimeMillis());
        cur_cal.add(Calendar.SECOND, 30);
        Log.d("Testing", "Calender Set time:"+cur_cal.getTime());
        Intent intent = new Intent(this, ParseService.class);
        Log.d("Testing", "Intent created");
        PendingIntent pi = PendingIntent.getService(this, 0, intent, 0);
        AlarmManager alarm_manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
        alarm_manager.set(AlarmManager.RTC, cur_cal.getTimeInMillis(), pi);
        Log.d("Testing", "alarm manager set");
        Toast.makeText(this, "ServiceClass.onCreate()", Toast.LENGTH_LONG).show();

您可以通过 PendingIntent 触发服务。这将解决当用户从最近任务中清除应用程序时所遇到的问题。
正如 Hossam Alaa 在 链接 中所述,您可以解决引导问题。 此外,在 IntentService 中不需要 AsyncTask 。它有一个工作线程。
以下是 ParseService 的示例(根据您的需求进行修改,当前它将等待 5000 毫秒并触发通知)。
public class ParseService extends IntentService {


public ParseService() {
    super("ParseService");
}


@Override
protected void onHandleIntent(Intent intent) {
    if (intent != null) {
        try {
            Thread.sleep(5000);
            setNotificationContent("HELLO","THERE");
            //stopSelf();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
public void setNotificationContent( String title, String content)
{
    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pi = PendingIntent.getActivity(this, 0, notificationIntent, 0);
    Notification noti = new Notification.Builder(this)
            .setTicker("Hello")
            .setAutoCancel(true)
            .setOngoing(true)
            .setSmallIcon(R.mipmap.ic_launcher)
            .setOnlyAlertOnce(true)
            .setContentTitle(""+title)
            .setContentText(""+content)
            .setContentIntent(pi).build();

    startForeground(1337, noti);

    NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(0, noti);


}
}

请问您能解释一下“您可以从PendingIntent触发服务。如果用户从最近任务中清除了应用程序,则可以解决此问题。”吗?您是否找到了一种避免在用户从最近任务中删除任务的情况下完全删除警报的方法? - android developer

0

只有在您的应用程序被删除或禁用(在应用程序管理器中)时,警报才会被删除,它们肯定会在常规重新启动或挂起后继续存在。

但我不确定您的清单是否设置正确: 该警报正在向“AlarmBroadcastReceiver.class”发送广播,并且如果应用程序在特定时刻处于活动状态,则会响应。

我建议使用警报广播自定义操作并在接收器中注册。

在清单中添加到您的接收器:

<intent-filter>
    <action android:name="com.abc.abc.action.downloader" />
</intent-filter>

而对于闹钟,可以使用另一个意图:

Intent downloader = new Intent(context, "com.abc.abc.action.downloader");
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接