如何每 5 分钟运行 10 秒的前台服务?

Posted

技术标签:

【中文标题】如何每 5 分钟运行 10 秒的前台服务?【英文标题】:How to run a foreground service for 10 seconds, every 5 minutes? 【发布时间】:2020-03-06 22:04:35 【问题描述】:

我想运行Foreground Service,无论应用程序是关闭还是打开,例如 10 秒。在这 10 秒过去后,Foreground Service 应该被销毁并在 5 分钟后再次调用。 Foreground Service 我找到了用户的位置,然后将其保存到 SQLite 数据库中。所以基本上我想每 5 分钟存储一次新坐标,这样我可以提高电池消耗。

我的服务等级:

public class LocationService extends Service

private final static int UPDATE_TIME = 10000;
private final static int UPDATE_DISTANCE = 0;

private LocationListener locationListener;
private LocationManager locationManager;

private PowerManager.WakeLock mWakeLock;

private String latitude, longitude;

private DatabaseHelper myDb;

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

    myDb = new DatabaseHelper(this);
    locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);

    createWakeLock(); //Creates a wakelock
    createNotification(); //Creates a notification if SDK version is > 26



@SuppressLint("MissingPermission")
@Override
public int onStartCommand(Intent intent, int flags, int startId) 

    mWakeLock.acquire(10*60*500L /*5 minutes*/);

    locationListener = new LocationListener() 

        @Override
        public void onLocationChanged(Location location) 

            Log.d("Location", "Updated");

            getCoordinates(location); //Gets coordinates
            insertCoordinates(latitude, longitude); //Sends coordinates to SQLite database
            sendDataToMainActivity(latitude, longitude); //Sends coordinates to MainActivity

        

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) 

        

        @Override
        public void onProviderEnabled(String provider) 

        

        @Override
        public void onProviderDisabled(String provider) 

            Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);

        

    ;

    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, UPDATE_TIME, UPDATE_DISTANCE, locationListener);

    return START_NOT_STICKY;



@Override
public void onDestroy() 

    super.onDestroy();

    Log.d("Service", "Destroyed");

    if(locationManager != null)
        locationManager.removeUpdates(locationListener);

    mWakeLock.release();



private void createWakeLock() 

    PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);

    if(pm != null)
        mWakeLock = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "myApp:myWakeLock");



private void createNotification() 

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Service")
            .setContentText("Coordinates Location Running")
            .setContentIntent(pendingIntent)
            .build();

    startForeground(1, notification);



private void getCoordinates(Location location) 

    this.latitude = String.valueOf(location.getLatitude());
    this.longitude = String.valueOf(location.getLongitude());



private void insertCoordinates(String latitude, String longitude) 

    boolean inserted = myDb.insertData(latitude, longitude); //Insert coordinates

    //Check if insertion is completed
    if(inserted)
        Toast.makeText(LocationService.this, "Coordinates Inserted", Toast.LENGTH_SHORT).show();
    else
        Toast.makeText(LocationService.this, "Coordinates Not Inserted", Toast.LENGTH_SHORT).show();



private void sendDataToMainActivity(String latitude, String longitude) 

    Intent i = new Intent("location_update");
    i.putExtra("latitude", latitude);
    i.putExtra("longitude",longitude);

    sendBroadcast(i);



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

【问题讨论】:

【参考方案1】:

使用 Fused Location Api 请求位置更新,这将在指定的重复间隔(给定 5 分钟)内触发待处理的意图(假设启动一个意图服务)。因此 Fused Location Api 每 5 分钟读取一次位置并发送它到意图服务,您可以在其中进行处理。

详情请参考this。

【讨论】:

【参考方案2】:

您可以使用警报管理器设置准确的警报,在警报的接收功能中启动服务运行前台服务 5 分钟,然后再设置下一个警报

【讨论】:

【参考方案3】:

由于电源管理限制,“Fused Location Api”可能无法在后台运行。 您应该使用警报管理器,但在最新的 android 版本中有一些 limitations。更多关于打瞌睡here

    AlarmManager alarm =
            (AlarmManager)getSystemService(Context.ALARM_SERVICE);
    if (alarm == null) return;  // for instant apps

    Intent intent = new Intent(this, MyBroadcastReceiver.class)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 
        alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerInMillis,
                PendingIntent.getBroadcast(this, intent, PendingIntent.FLAG_UPDATE_CURRENT));

     else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) 
        AlarmManager.AlarmClockInfo ac=
                new AlarmManager.AlarmClockInfo(triggerInMillis, null);
        alarm.setAlarmClock(ac, PendingIntent.getBroadcast(this, intent, PendingIntent.FLAG_UPDATE_CURRENT));

     else 
        alarm.setExact(AlarmManager.RTC_WAKEUP, triggerInMillis,
                PendingIntent.getBroadcast(this, intent, PendingIntent.FLAG_UPDATE_CURRENT));
    

【讨论】:

上下文指的是什么?当我输入 AlarmManager alarm = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE); 时出现错误,但是当我将其更改为 AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 时它可以工作。我不确定在上下文中放置什么变量。请注意,我没有BroadcastReceiver 课程,我只有Service Class 我在上面的问题中包含。 context 表示 Context 类的任何实例(Activity、Service、Application - 它们都扩展了 Context)。您必须将您的 MyBroadcastReceiver 声明为单独的文件公共类 MyBroadcastReceiver 扩展 BaseBroadcastReceiver 并添加到 AndroidManifest BroadcastReceiver example 并且您的MyBroadcastReceiver 将在方法onReceive 中启动您的服务 我创建了一个MyBroadcastReceiver 类并在onReceive 方法上写了这个Intent serviceIntent = new Intent(context, LocationService.class); ContextCompat.startForegroundService(context, serviceIntent); 所以我得到了AlarmManager 的工作。我想要的是在我将坐标插入我的SQLite 数据库后销毁Service。目前该服务无限期运行。所以基本上我希望我的AlarmManager 触发我的Service 10 秒来完成所有任务,销毁它,然后在 5 分钟内再次调用它。

以上是关于如何每 5 分钟运行 10 秒的前台服务?的主要内容,如果未能解决你的问题,请参考以下文章

用于位置跟踪的前台服务与 WorkManager

在前台服务中运行的 ScheduledThreadPoolExecutor 的 poolsize

如何每 10 秒在前台和后台更新用户位置? [复制]

应用程序进入前台后停止调用 setKeepAliveTimeout 处理程序?

Android - 前台在 Oreo 中不起作用。操作系统在一段时间后终止服务

Camerax 在服务中运行。如何在前台服务中获取生命周期所有者或在没有它的情况下运行?