如何在后台 运行 android 提供 24x7 服务以获取用户位置
How to run service 24x7 in android in background to get location of user
我已经尝试了很多不同的方法来 运行 后台服务来获取用户的位置,但是 android 系统会在一段时间后自动终止服务。
虽然它适用于某些设备但不适用于大多数设备。
我正在构建类似于 UBER 的应用程序。我希望 driver 的位置在一段时间后更新,即使应用程序处于后台或前台,直到用户未将状态设置为离线
您可以使用 Foreground Service,这只是一个正常的服务,在前台有一个持续的通知,它会停止 OS 到 stop/kill 您的服务进程。
顺便说一句,当设备进入“打瞌睡”或“应用程序待机”模式时,这不能保证您的服务会给予 CPU/processing 时间。
虽然您无法绕过这些打瞌睡、待机和电池优化,但我已经测试了一种破解方法,通过在前台服务中创建唤醒锁并在单独的进程中启动该服务来避免这些。
希望对您有所帮助。
您可以使用 Android 提供的一些机制。
- 对于 运行 Oreo 之前的设备,您可以按原样使用后台服务,它应该大部分时间都在运行,通过在清单文件中声明它来将其保持在单独的进程中。您还可以注册到设备启动完成广播,这样您将在设备重新启动时收到回电,然后您将有机会重新启动后台服务。对于 运行 oreo+ 设备,最可靠的方法是使用前台服务。确保您的服务在任何情况下都具有粘性。
- 设置 fire base schedule 作业以在服务停止时重新启动您的服务
- 地理围栏政策以获得更多反馈
- 安排警报管理器在服务停止时重新启动您的服务
- 使用googleactivity识别api你也可以得到回调有额外的机会拉取更多的位置信息
- 推送通知
我建议将所有内容与为您的应用程序量身定制的策略结合使用,它们一起应该可以随时为您提供足够的用户位置信息。
是的,您可以这样做,但可能会消耗更多电池电量。
找到下面的代码,它会对你有所帮助,
有另一种使用移动位置的方法,Google 发布了融合位置提供程序,这对您的情况更有帮助。
1.Add Google 您的 Build.gradle 文件中的位置 gradle 行
实施'com.google.android.gms:play-services-location:15.0.1'
2.Get 使用位置服务的用户位置
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.Finaly授予访问管理员权限,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>
我已经尝试了很多不同的方法来 运行 后台服务来获取用户的位置,但是 android 系统会在一段时间后自动终止服务。 虽然它适用于某些设备但不适用于大多数设备。 我正在构建类似于 UBER 的应用程序。我希望 driver 的位置在一段时间后更新,即使应用程序处于后台或前台,直到用户未将状态设置为离线
您可以使用 Foreground Service,这只是一个正常的服务,在前台有一个持续的通知,它会停止 OS 到 stop/kill 您的服务进程。
顺便说一句,当设备进入“打瞌睡”或“应用程序待机”模式时,这不能保证您的服务会给予 CPU/processing 时间。
虽然您无法绕过这些打瞌睡、待机和电池优化,但我已经测试了一种破解方法,通过在前台服务中创建唤醒锁并在单独的进程中启动该服务来避免这些。
希望对您有所帮助。
您可以使用 Android 提供的一些机制。
- 对于 运行 Oreo 之前的设备,您可以按原样使用后台服务,它应该大部分时间都在运行,通过在清单文件中声明它来将其保持在单独的进程中。您还可以注册到设备启动完成广播,这样您将在设备重新启动时收到回电,然后您将有机会重新启动后台服务。对于 运行 oreo+ 设备,最可靠的方法是使用前台服务。确保您的服务在任何情况下都具有粘性。
- 设置 fire base schedule 作业以在服务停止时重新启动您的服务
- 地理围栏政策以获得更多反馈
- 安排警报管理器在服务停止时重新启动您的服务
- 使用googleactivity识别api你也可以得到回调有额外的机会拉取更多的位置信息
- 推送通知
我建议将所有内容与为您的应用程序量身定制的策略结合使用,它们一起应该可以随时为您提供足够的用户位置信息。
是的,您可以这样做,但可能会消耗更多电池电量。 找到下面的代码,它会对你有所帮助,
有另一种使用移动位置的方法,Google 发布了融合位置提供程序,这对您的情况更有帮助。
1.Add Google 您的 Build.gradle 文件中的位置 gradle 行
实施'com.google.android.gms:play-services-location:15.0.1'
2.Get 使用位置服务的用户位置
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.Finaly授予访问管理员权限,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>