如何在后台运行 android 服务 24x7 以获取用户的位置
Posted
技术标签:
【中文标题】如何在后台运行 android 服务 24x7 以获取用户的位置【英文标题】:How to run service 24x7 in android in background to get location of user 【发布时间】:2019-01-23 03:36:57 【问题描述】:我已经尝试了很多不同的方法来运行后台服务来获取用户的位置,但是 android 系统会在一段时间后自动终止服务。 虽然它适用于某些设备,但不适用于大多数设备。 我正在构建类似于 UBER 的应用程序。我希望在一段时间后更新驱动程序的位置,即使应用程序处于后台或前台,直到用户没有将状态设置为离线
【问题讨论】:
你试过前台服务吗? 【参考方案1】:您可以使用Foreground Service,这只是一个普通服务,在前台有一个持续的通知,它会阻止操作系统停止/终止您的服务进程。
顺便说一句,这并不能保证您的服务会在设备进入打盹或应用待机模式时给予 CPU/处理时间。
虽然您无法绕过这些打瞌睡、待机和电池优化,但我已经测试了一种技巧来避免这些问题,方法是在前台服务中创建唤醒锁并在单独的进程中启动该服务。
希望对你有帮助。
【讨论】:
前台服务唤醒锁有什么好处?? 是的前台服务有效。我在前台服务中使用 FusedLocationProviderClientApi 来获取位置更新。但它只有在打开 gps 时才有效。如何要求用户从前台服务打开位置?? 如果我获得了唤醒锁并在单独的进程中启动服务,那么不要求用户为我的应用禁用电池优化是可以的。电池优化和打盹模式不会影响我的应用程序吗?您是否在 Android 30 上对此进行了测试?谢谢【参考方案2】:您可以使用 Android 提供的一些机制。
-
对于运行 pre-Oreo 的设备,您可以直接使用后台服务,它应该大部分时间都存在,通过在清单文件中声明它来将其保留在单独的进程中。您还可以注册到设备启动完成广播,这样您将在设备重启时收到回电,然后您将有机会重新启动后台服务。对于运行 oreo+ 的设备,最可靠的方法是使用前台服务。确保您的服务在任何情况下都具有粘性。
设置 fire base 调度作业以在服务停止时重新启动服务
获取更多反馈的地理围栏政策
安排警报管理器以在服务停止时重新启动您的服务
使用 google 活动识别 api 还可以获得回调,从而有更多机会获取更多位置信息
推送通知
我建议将所有这些与针对您的应用程序量身定制的策略结合使用,它们应该可以随时为您提供足够的用户位置信息。
【讨论】:
【参考方案3】:是的,您可以这样做,但可能会消耗更多的电池电量。 找到下面的代码,它会帮助你,
还有另一种使用移动位置的方法,Google 发布了融合的位置提供程序,这对您的情况更有帮助。
1.在您的 Build.gradle 文件中添加 Google Location gradle 行
实现 'com.google.android.gms:play-services-location:15.0.1'
2.使用定位服务获取用户位置
import android.Manifest;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.NotificationCompat;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationAvailability;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.uffizio.taskeye.BuildConfig;
import com.uffizio.taskeye.R;
import com.uffizio.taskeye.extra.Constants;
import com.uffizio.taskeye.ui.activity.MainActivity;
/**
* Created by Kintan on 16/8/18.
*/
public class LocationServiceDemo extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener
public static LocationServiceDemo locationService;
private static GoogleApiClient mGoogleApiClient;
private static LocationRequest mLocationRequest;
private FusedLocationProviderClient mFusedProviderClient;
private MyLocationCallback mMyLocationCallback;
private Location curLocation;
@Nullable
@Override
public IBinder onBind(Intent intent)
return new MyBinder();
@Override
public void onCreate()
super.onCreate();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
showNotificationAndStartForegroundService();
locationService = this;
init();
//Google location Api build
protected synchronized void buildGoogleApiClient()
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
protected void createLocationRequest()
mMyLocationCallback = new MyLocationCallback();
mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(5000);
mLocationRequest.setFastestInterval(3000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(5.0f);
requestUpdate();
//Start Foreground Service and Show Notification to user for Android O and higher Version
private void showNotificationAndStartForegroundService()
final String CHANNEL_ID = BuildConfig.APPLICATION_ID.concat("_notification_id");
final int REQUEST_CODE = 1;
PendingIntent pendingIntent = PendingIntent.getActivity(this,
REQUEST_CODE, new Intent(this, MainActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this,
CHANNEL_ID)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle(getString(R.string.app_name))
.setAutoCancel(false)
.setContentIntent(pendingIntent);
startForeground(Constants.NOTIFICATION_ID, notificationBuilder.build());
public void requestUpdate()
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED)
return;
mFusedProviderClient.requestLocationUpdates(mLocationRequest, mMyLocationCallback,
Looper.myLooper());
public void removeUpdate()
mFusedProviderClient.removeLocationUpdates(mMyLocationCallback);
@Override
public void onConnected(@Nullable Bundle bundle)
createLocationRequest();
@Override
public void onConnectionSuspended(int i)
buildGoogleApiClient();
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult)
buildGoogleApiClient();
private void init()
buildGoogleApiClient();
@Override
public int onStartCommand(Intent intent, int flags, int startId)
mFusedProviderClient = LocationServices.getFusedLocationProviderClient(LocationServiceDemo.this);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION)
!= PackageManager.PERMISSION_GRANTED)
return START_STICKY;
mFusedProviderClient.getLastLocation().addOnSuccessListener(location ->
if (location != null)
curLocation = location;
);
if (mGoogleApiClient.isConnected())
createLocationRequest();
else
buildGoogleApiClient();
return START_STICKY;
@Override
public void onTaskRemoved(Intent rootIntent)
super.onTaskRemoved(rootIntent);
startService();
@Override
public void onLowMemory()
super.onLowMemory();
startService();
@Override
public void onDestroy()
super.onDestroy();
startService();
public void startService()
startService(new Intent(LocationServiceDemo.this, LocationServiceDemo.class));
public class MyLocationCallback extends LocationCallback
@Override
public void onLocationResult(LocationResult locationResult)
//get your location here
if (locationResult.getLastLocation() != null)
for (Location location : locationResult.getLocations())
curLocation = location;
@Override
public void onLocationAvailability(LocationAvailability locationAvailability)
super.onLocationAvailability(locationAvailability);
private class MyBinder extends Binder
LocationServiceDemo getService()
return LocationServiceDemo.this;
3.最终授予访问管理员权限,Android系统不会在一段时间后自动终止服务。
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
public class LocationHome extends AppCompatActivity
private static final int REQUEST_CODE = 0;
private DevicePolicyManager mDPM;
private ComponentName mAdminName;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState)
super.onCreate(savedInstanceState);
mDPM = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
mAdminName = new ComponentName(this, DeviceAdmin.class);
Button button = new Button();
button.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
//Grant Admin Permission
if (mDPM.isAdminActive(mAdminName))
startService(new Intent(this, LocationService.class));
else
adminPermission();
);
public void adminPermission()
try
if (!mDPM.isAdminActive(mAdminName))
try
Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mAdminName);
intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION, "Click on Activate button to secure your application.");
startActivityForResult(intent, REQUEST_CODE);
catch (Exception e)
e.printStackTrace();
catch (Exception e)
e.printStackTrace();
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
super.onActivityResult(requestCode, resultCode, data);
//Check Permission is granted or not
if (requestCode == REQUEST_CODE)
if (resultCode == RESULT_OK)
startService(new Intent(this, LocationService.class));
else
if (!mDPM.isAdminActive(mAdminName))
adminPermission();
创建 XML 文件夹添加 device_admin.xml
<device-admin>
<uses-policies>
</uses-policies>
</device-admin>
最后修改您的清单并享受。
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature
android:name="android.hardware.location.gps"
android:required="false" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:name=".common.MyApplication"
android:theme="@style/AppTheme">
<receiver
android:name=".ui.DeviceAdmin"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data
android:name="android.app.device_admin"
android:resource="@xml/device_admin" />
<intent-filter>
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
</application>
【讨论】:
如何在后台启动? DeviceAdmin 中应该有什么以上是关于如何在后台运行 android 服务 24x7 以获取用户的位置的主要内容,如果未能解决你的问题,请参考以下文章