Job Scheduler与后台服务

时间:2017-03-22 22:02:52

标签: android alarmmanager background-process android-handler android-jobscheduler

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

  1. 关于处理程序我已经读过它们因长时间延迟而不存在 并将在系统重启后终止。所以他们不会 适合我的任务。
  2. 但是AlarmManager似乎是这个问题的一个很好的候选者,因为当它被允许时它们甚至在系统重启后也存在并且可以 重新运行该应用程序。但是在警报的Android Documentation中 Manager旨在用于必须在a上运行的任务 特定时间(如闹钟)。但我的任务必须运行 每一分钟。
  3. 然后是后台服务。这更适用于在后台下载的任务,因为我已阅读并且不打算这样做 做我已解释过的事情。
  4. JobScheduler似乎不适用于必须永久完成的任务,但适用于满足特定约束的任务 闲置,或没有网络...所以这些(或其他如果他们 你建议使用我在第一个中解释的任务吗? 部分

7 个答案:

答案 0 :(得分:25)

  

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

由于Doze模式(以及可能的应用待机状态,取决于应用的其余部分),数百万台Android设备(运行Android 6.0及更高版本)不会发生这种情况。

  

但是AlarmManager似乎是这个问题的一个很好的候选者,因为即使在系统重启后它们仍然存在

不,他们没有。您需要重新安排重启后使用AlarmManager计划的所有警报。

  

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

AlarmManager支持重复选项。

  

这更像是我在阅读后在后台下载的任务,而不是用于我已经解释过的事情。

Service对于您最终使用的解决方案至关重要。

  

JobScheduler似乎不适用于必须永久完成的任务,但适用于满足特定约束(如空闲或无网络)的任务

JobScheduler一样,

AlarmManager支持重复工作。

  

那么你建议使用我在第一部分中解释的任务中的哪一个(或其他如果存在的话)

不使用它们,因为一旦设备进入打盹模式,您将无法在Android 6.0+上每分钟运行一次,这将在屏幕关闭后一小时内完成。相反,要么重新设计应用程序,每天只需要几次背景工作,或者不打扰编写应用程序。

答案 1 :(得分:12)

如果你的minSdkVersion = 21,你可以使用Android 5.0中引入的现代JobScheduler API。

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

但我建议使用此库https://github.com/evernote/android-job,根据Android版本,将使用JobSchedulerGcmNetworkManagerAlarmManager

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

<强>更新 现在最好使用新的WorkManagerdocs)。 android-job将很快弃用

答案 2 :(得分:6)

首先,JobService是一种服务。后台服务不明确,让我猜你的意思是在后台线程中运行的服务。作业服务在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. 在您的清单中,注册您的服务和接收方
  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>
    

    启动权限是为了在系统完成启动后调用接收器,一旦调用接收器,将再次调用该服务。

答案 3 :(得分:2)

根据this 以及下面评论1中的其他链接

您应该使用AlarmManager完成任务。

如果您需要设置在Doze中触发的警报,请使用:

 setAndAllowWhileIdle() or setExactAndAllowWhileIdle().

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

祝你好运!

答案 4 :(得分:1)

在以前的Android版本中,人们使用Handler或后台服务来实现此目的。过了一会儿,他们宣布了警报管理员课程,用于永久性的预定作品。

Whatsapp,脸书或一些社交媒体应用程序大多使用谷歌云消息传递通知目的,这对你没用。

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

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

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

答案 5 :(得分:0)

你可以通过使用service来做到这一点,返回start_sticky in“START_STICKY告诉操作系统在有足够内存后重新创建服务并再次使用null intent调用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>
    
  4. 此外,您可以使用AlarmManager,如果您需要设置在Doze中触发的闹钟,请使用:

      

    setAndAllowWhileIdle()或setExactAndAllowWhileIdle()。

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

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

    此外,您可以在重启设备后启动服务或AlarmManager 只需在“RECEIVE_BOOT_COMPLETED”

    时使用brodcastReciever

    并提出此权限:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    

答案 6 :(得分:-1)

在Lollipop之上,即API版本21,您可以使用JobScheduler来安排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");
        }
    }
}