Android - 周期性后台服务 - 建议

33

我正在开发一个应用,它将向远程服务器中继定位信息。我打算通过简单的HTTP post方式将信息发送到Web服务器,一切都很简单和顺利。

但根据规范,该应用程序需要定期执行自身,例如每30分钟执行一次。同时,它需要独立于界面运行,即使应用程序已关闭也要能够运行。

我查找了一些资料,发现Android服务是需要使用的内容。什么可以用来实现这样的系统呢?当手机重新启动时,服务(或其他机制)会重新启动吗?

提前感谢。

4个回答

33
创建一个 Service 来将你的信息发送到服务器。假定你已经控制好了这个过程。
你的 Service 应该由 AlarmManager 触发的警报启动,你可以在那里指定一个时间间隔。除非你需要每30分钟精确上报数据,否则你可能想要使用不精确的警报以节省一些电池寿命。
最后,你可以通过设置像下面这样的 BroadcastReceiver 来注册应用程序以获得开机广播:
public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {  
        if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
            // Register your reporting alarms here.            
        }
    }
}
你需要在AndroidManifest.xml中添加以下权限才能使其正常工作。不要忘记在正常运行应用程序时注册你的闹钟,否则它们只会在设备启动时注册。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

19

这里有一种半不同的方法来让服务永远运行。如果你愿意,可以在代码中找到终止它的方式。

后台服务:

package com.ex.ample;

import android.app.Service;
import android.content.*;
import android.os.*;
import android.widget.Toast;

public class BackgroundService extends Service {

    public Context context = this;
    public Handler handler = null;
    public static Runnable runnable = null;

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

    @Override
    public void onCreate() {
        Toast.makeText(this, "Service created!", Toast.LENGTH_LONG).show();

        handler = new Handler();
        runnable = new Runnable() {
            public void run() {
                Toast.makeText(context, "Service is still running", Toast.LENGTH_LONG).show();
                handler.postDelayed(runnable, 10000);
            }
        };

        handler.postDelayed(runnable, 15000);
    }

    @Override
    public void onDestroy() {
        /* IF YOU WANT THIS SERVICE KILLED WITH THE APP THEN UNCOMMENT THE FOLLOWING LINE */
        //handler.removeCallbacks(runnable);
        Toast.makeText(this, "Service stopped", Toast.LENGTH_LONG).show();
    }

    @Override
    public void onStart(Intent intent, int startid) {
        Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show();
    }
}

以下是如何从您的主要活动或任何您想要的地方开始:

startService(new Intent(this, BackgroundService.class));

onDestroy()会在应用程序关闭或被杀死时调用,但是这个可运行对象会立即重新启动它。

我希望这能帮助到某些人。

一些人之所以这样做,是因为在企业应用程序中,有些情况下用户/员工必须无法停止某些事物 :)

http://i.imgur.com/1vCnYJW.png


编辑

自从Android O(8.0)以后,您必须使用JobManager进行定期任务。有一个名为Android-Job by Evernote的库,可以使所有Android版本上的定期后台工作变得轻松。我还制作了此库的Xamarin绑定

然后,您只需要执行以下操作:

在您的应用程序类中:

public class MyApp extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        JobManager.create(this).addJobCreator(new MyJobCreator());
    }
}
创建以下两个类:YourJobCreatorYourSyncJob(所有的工作都将在其中完成。Android会为所有后台作业分配时间以运行。对于Android版本小于8.0,仍会像往常一样使用Alarm Manager和后台服务运行)。
public class MyJobCreator implements JobCreator {

    @Override
    @Nullable
    public Job create(@NonNull String tag) {
        switch (tag) {
            case MySyncJob.TAG:
                return new MySyncJob();
            default:
                return null;
        }
    }
}

public class MySyncJob extends Job {

    public static final String TAG = "my_job_tag";

    @Override
    @NonNull
    protected Result onRunJob(Params params) {
        //
        // run your job here
        //
        //
        return Result.SUCCESS;
    }

    public static void scheduleJob() {
        new JobRequest.Builder(MySyncJob.TAG)
                .setExecutionWindow(30_000L, 40_000L) //Every 30 seconds for 40 seconds
                .build()
                .schedule();
    }
}

1
不错的解决方法,但是这会在一段时间后继续启动处理程序,因为服务将在一段时间后重新启动。 - Dev
1
为了防止重启,请将代码从onCreate()移动到 @Override public int onStartCommand(Intent intent, int flags, int startId) { //runnable的代码 return START_NOT_STICKY; }返回START_NOT_STICKY将确保服务不会重新启动,并且在onDestroyed()之后您的处理程序将保持停止状态。 - Dev
2
不要忘记将服务添加到您的 AndroidManifest.xml 文件中:<service android:name=".services.MyService" android:label="MyService" ></service> - Mr. B.
@KTWorks 这在旧版的 Android 上曾经可行。你需要使用类似 https://github.com/evernote/android-job 的工具。通过正确的设置,你可以让任务无限期地运行而不会耗尽用户的电池。从 Android O 开始,你不能再在后台使用 StartService 了。所有的处理程序都会在 Doze 模式下暂停。 - Pierre

8
您需要使用Alarm Manager来安排服务,首先创建服务的Pending Intent:
Intent ii = new Intent(getApplicationContext(), MyService.class);
PendingIntent pii = PendingIntent.getService(getApplicationContext(), 2222, ii,
PendingIntent.FLAG_CANCEL_CURRENT);

然后使用闹钟管理器进行调度:
//getting current time and add 5 seconds to it
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
//registering our pending intent with alarmmanager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), pi);

这将在当前时间的5秒后启动您的服务,您可以使您的闹钟重复。


可能更容易做一些像 System.currentTimeMillis() + TIMEOUT_MS 这样的事情,而不是在 Calendar 上胡乱操作,以获得您的初始延迟。 - Ionoclast Brigham
5
你们应该知道在AlarmManager中有静态值分别为1天、12小时、1小时、半小时和15分钟吗? public static final long INTERVAL_DAY表示一天 public static final long INTERVAL_FIFTEEN_MINUTES表示15分钟 public static final long INTERVAL_HALF_DAY表示半天 public static final long INTERVAL_HALF_HOUR表示半个小时 public static final long INTERVAL_HOUR表示1小时 - DaMachk

0

您可以使用Alarm Manager在指定的时间开始服务,然后按照指定的间隔重复警报。当闹钟响起时,您可以启动服务并连接到服务器以完成您想要的操作。


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