42

我有一个应用程序,它的功能 A 应该每分钟在后台运行。功能 A 是应用程序应该连接到数据库,读取一些数据然后获取设备的当前位置并根据它们检查条件,如果条件为真,它应该向用户发送状态栏通知,以便当用户单击通知,将显示应用程序的 UI 并发生一些事情。
这个后台任务应该每分钟永久运行一次,无论应用程序被使用、关闭、终止(如 facebook 或 Whatsapp,无论它们是否在应用程序堆栈中都会向我们显示通知)。
现在我搜索并发现 Android 提供Job SchedulerBackground ServiceAlarmManager处理程序
但我对它们的了解越多,这些陈述在我看来就越矛盾。

  1. 关于处理程序我读过它们不会长时间延迟存在,并且将在系统重新启动后终止。所以它们不适合我的任务。
  2. 但是 AlarmManager 似乎是解决该问题的一个很好的候选者,因为即使在系统重新启动后它们也存在并且可以重新运行应用程序。但在Android 文档中,警报管理器旨在用于必须在特定时间运行的任务(如闹钟)。但是我的任务必须每分钟运行一次。
  3. 然后是后台服务。这更适用于我已经阅读过的诸如在后台下载之类的任务,而不是为了做我已经解释过的事情。
  4. JobScheduler 似乎不是用于必须永久完成的任务,而是用于满足特定约束(如空闲或没有网络)的任务......所以您建议使用其中哪些(或其他如果存在)对于我在第一部分中解释的任务
4

7 回答 7

35

我有一个应用程序,它的功能 A 应该每分钟在后台运行。

由于打盹模式(可能还有应用程序待机,取决于应用程序的其余部分),这不会在数亿台运行 Android 6.0 及更高版本的 Android 设备上发生。

但是 AlarmManager 似乎是解决该问题的一个很好的候选者,因为即使在系统重新启动后允许它们也存在

不,他们没有。您需要在重新启动后重新安排所有已安排的警报AlarmManager

警报管理器旨在用于必须在特定时间运行的任务

AlarmManager支持重复选项。

这更适用于我已经阅读过的诸如在后台下载之类的任务,而不是为了做我已经解释过的事情。

AService对于您最终使用的任何解决方案都是必不可少的。

JobScheduler 似乎不是针对必须永久完成的任务,而是针对满足特定约束的任务,例如空闲或没有网络

JobScheduler,与 一样AlarmManager,支持重复作业。

因此,您建议将其中哪些(或其他如果存在)用于我在第一部分中解释的任务

不使用它们,因为一旦设备进入打盹模式(屏幕关闭后一小时内),您就无法在 Android 6.0+ 上每分钟运行一次。相反,要么重新设计应用程序,使其每天只需要几次后台工作,要么不费心编写应用程序。

于 2017-03-22T22:28:55.557 回答
14

如果您的 minSdkVersion=21,您可以使用JobSchedulerAndroid 5.0 中引入的现代 API。

还有https://github.com/firebase/firebase-jobdispatcher-android需要安装 Google Play minSdkVersion=9

但我建议使用这个库https://github.com/evernote/android-job取决于 Android 版本JobSchedulerGcmNetworkManager或者AlarmManager将被使用。

使用这些 API,您可以安排您的作业并运行描述任务的服务。

更新 现在最好使用新的WorkManager文档)。android-job将很快被弃用

于 2017-03-22T22:15:09.313 回答
7

首先,JobService 是一项服务。后台服务是模棱两可的,我猜你的意思是在后台线程中运行的服务。Job Service 在 ui 线程上运行,但您可以在其中创建一个异步任务对象以使其在后台运行。

从你的问题来看,JobService 不是要走的路。我的建议是:

  1. 您可以在该类的 onDestroy 方法中创建一个扩展 IntentService 的类(这在后台线程上运行),发送广播并使广播重新启动服务。

     @onDestroy(){
     Intent broadcastIntent = new 
     Intent("com.example.myapp.serviceRestarted");
     sendBroadcast(broadcastIntent);}
    
  2. 创建一个扩展广播接收器的类

     public class RestartServiceReceiver extends BroadcastReceiver {
     @Override
     public void onReceive(Context context, Intent intent) {
     context.startService(new Intent(context, 
     MyService.class));
    } 
    }
    
    1. 在您的清单中,注册您的服务和接收器
<receiver
            android:name=".RestartServiceReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.myapp.serviceRestarted" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

启动权限是在系统启动完成后才可以调用reciever,一旦reciever被调用,服务会再次被调用。

于 2017-05-12T11:06:39.630 回答
2

在 Lollipop 之上,即 API 版本 21,您可以使用 aJobScheduler来安排 a JobService。要每分钟重复一次作业,您必须通过将最小延迟设置为 60*1000 毫秒来安排作业每次完成。

@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MyJobService extends JobService {

    boolean isWorking = false;
    boolean jobCancelled = false;

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d("_____TAG_____", "MyJobService started!");
        isWorking = true;

        doWork(params);

        return isWorking;
    }

    private void doWork(JobParameters params) {

        if (jobCancelled)
            return;

        //Create a new thread here and do your work in it. 
        //Remember, job service runs in main thread

        Log.d("_____TAG_____", "MyJobService finished!");
        isWorking = false;
        boolean needsReschedule = false;
        jobFinished(params, needsReschedule);

        scheduleRefresh();

    }

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d("_____TAG_____", "MyJobService cancelled before being completed.");
        jobCancelled = true;
        boolean needsReschedule = isWorking;
        jobFinished(params, needsReschedule);
        return needsReschedule;
    }

    private void scheduleRefresh() {

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            ComponentName componentName = new ComponentName(getApplicationContext(), MyJobService.class);
            JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
            builder.setMinimumLatency(60*1000);  //1 minute
            JobInfo jobInfo = builder.build();

            JobScheduler jobScheduler = (JobScheduler)getApplicationContext().getSystemService(JOB_SCHEDULER_SERVICE);
            int resultCode = jobScheduler.schedule(jobInfo);
            if (resultCode == JobScheduler.RESULT_SUCCESS) {
                Log.d("_____TAG_____", "MyJobService scheduled!");
            } else {
                Log.d("_____TAG_____", "MyJobService not scheduled");
            }
        }
    }

}

您可以在任何您喜欢的地方编写一个通用函数来安排第一次作业 -

public void scheduleMyJobService() {

    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        ComponentName componentName = new ComponentName(context, MyJobService.class);
        JobInfo.Builder builder = new JobInfo.Builder(5, componentName);
        builder.setMinimumLatency(60*1000);
        JobInfo jobInfo = builder.build();

        JobScheduler jobScheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
        int resultCode = jobScheduler.schedule(jobInfo);
        if (resultCode == JobScheduler.RESULT_SUCCESS) {
            Log.d("_____TAG_____", "MyJobService scheduled!");
        } else {
            Log.d("_____TAG_____", "MyJobService not scheduled");
        }
    }
}
于 2019-09-28T15:06:21.627 回答
2

根据下面评论1中的这个 和其他链接

您应该使用 AlarmManager 来完成您的任务。

如果您需要设置在打瞌睡时触发的警报,请使用:

 setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

有关在后台执行操作的不同方式的完整易于理解的解释,请阅读: https ://www.bignerdranch.com/blog/choosing-the-right-background-scheduler-in-android/

祝你好运!

于 2017-06-21T10:42:50.820 回答
1

在之前的 Android 版本中,人们为此使用 Handler 或后台服务。过了一会儿,他们宣布了永久性的预定工作的警报管理器课程。

Whatsapp、facebook 或一些社交媒体应用程序大多使用谷歌云消息来进行通知目的,这对您没有用处。

我会建议您为此使用警报管理器。在 KitKat 版本(4.2)之后,操作系统会阻止后台处理程序以更好地使用电池。

后台服务主要用于图片上传或一些有结束时间的繁重过程。当您在 Whatsapp 上向您的朋友发送视频时,后台进程会启动并将视频上传到后端服务器。

我不确定 JobScheduler api 是否支持旧版本的支持,但它与 Alarm Manager 一样好。

于 2017-03-22T22:20:26.057 回答
0

您可以通过使用服务来做到这一点,返回 start_sticky 在“START_STICKY 告诉操作系统在它有足够的内存后重新创建服务,并再次以空意图调用 onStartCommand()。START_NOT_STICKY 告诉操作系统不要再费心重新创建服务。那里也是第三个代码 START_REDELIVER_INTENT,它告诉操作系统重新创建服务并将相同的意图重新传递给 onStartCommand()"

并设置一个周期为 1 分钟的 TIMER 并执行您的代码。

同样,如果您想在用户强制停止服务时重新启动服务,您可以“作为以前的答案”执行此操作

  1. 您可以在该类的 onDestroy 方法中创建一个扩展 IntentService 的类(这在后台线程上运行),发送广播并使广播重新启动服务。

    @onDestroy(){
        Intent broadcastIntent = new Intent("com.example.myapp.serviceRestarted");
        sendBroadcast(broadcastIntent);
    }
    
  2. 创建一个扩展广播接收器的类

    public class RestartServiceReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.startService(new Intent(context, MyService.class));
        } 
    }
    
  3. 在您的清单中,注册您的服务和接收器

    <receiver
       android:name=".RestartServiceReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="com.example.myapp.serviceRestarted" />
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    

此外,您可以使用 AlarmManager,如果您需要设置在打瞌睡时触发的警报,请使用:

setAndAllowWhileIdle() 或 setExactAndAllowWhileIdle()。

将其设置为“以秒为单位的当前时间 + 60 秒”,以便您在下一分钟进行设置。

并执行您的代码,最后在下一分钟重置 AlarmManager。

此外,您可以在重新启动设备后启动您的服务或 AlarmManager,只需在“RECEIVE_BOOT_COMPLETED”时使用 brodcastReciever

并把这个权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
于 2017-10-16T23:34:15.280 回答