为降低功耗,无论应用的目标 SDK 版本为何,Android 8.0
都会对后台应用检索用户当前位置的频率进行限制。
系统会对前台应用和后台应用进行区分。应用满足以下任一条件即视为前台应用:
- 它具有可见的
Activity
,无论Activity
处于启动还是暂停状态。 - 它具有前台服务。
- 另一个前台应用通过绑定到应用的其中一个服务或使用应用的其中一个内容提供程序与应用相连。
如果以上所有条件均不满足,应用即视为后台应用。
如果应用在运行Android 8.0
的设备上处于前台,其位置更新行为将与Android 7.1.1
(API 级别 25)及更低版本上相同。
一、前台服务
/**
* 前台服务
*/
public class FrontService extends Service {
private static final int NOTIFY_ID = 1;
public static void startService(Activity context) {
Intent intent = new Intent(context, FrontService.class);
context.startService(intent);
}
public static void stopService(Activity context) {
Intent intent = new Intent(context, FrontService.class);
context.stopService(intent);
}
@Override
public void onCreate() {
super.onCreate();
startForegroundNotification();
}
/**
* 开启前台服务,并显示通知
*/
private void startForegroundNotification() {
FrontNotificationHelper noHelper = FrontNotificationHelper.getIstance(this);
if (noHelper.getNotification() != null)
startForeground(NOTIFY_ID, noHelper.getNotification());
LocationHelper.startLocation(this, 10000, new LocationCallback() {
@Override
public void onLocation(String latitude, String longitude) {
Log.i("===FrontService===", "纬度:" + latitude + ";经度:" + longitude);
}
});
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
LocationHelper.stopLocation();
super.onDestroy();
}
}
调用startForeground(NOTIFY_ID, noHelper.getNotification());
,让服务运行于前台,此方法采用两个参数:唯一标识通知的整型数和状态栏的 Notification
。
但是,如果您在服务正在前台运行时将其停止,则通知也会被移除。
二、前台通知
public class FrontNotificationHelper {
private final Context mContext;
private NotificationManager mNotificationManager;
private NotificationCompat.Builder mBuilder;
private static final String CHANNEL_ID = "front_service";
private static final CharSequence CHANNEL_NAME = "front_service_channel";
private FrontNotificationHelper(Context mContext) {
this.mContext = mContext;
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
setUpNotification();
}
public static FrontNotificationHelper getIstance(Context mContext) {
return new FrontNotificationHelper(mContext);
}
/**
* 创建通知
*/
private void setUpNotification() {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(channel);
}
mBuilder = new NotificationCompat.Builder(mContext, CHANNEL_ID);
}
/**
* 获取通知
* @return
*/
public Notification getNotification() {
return mBuilder == null ? null : mBuilder.build();
}
}
FrontNotificationHelper
类构建一个通知,并通过getNotification()
将Notification
返回到FrontService
。
三、定位功能
目前主要使用的定位功能是GPS
定位和百度地图的定位服务。
public class GPSLocationProvider {
private LocationManager locationManager;
private LocationCallback listener;
private static GPSLocationProvider instance;
private long intervalTime = 10 * 1000;
public static synchronized GPSLocationProvider getInstance() {
if (instance == null) {
instance = new GPSLocationProvider();
}
return instance;
}
private GPSLocationProvider() {
}
public void setIntervalTime(long intervalTime) {
this.intervalTime = intervalTime;
}
/**
* 开始定位
*/
public void startLocation(Context mContext, LocationCallback listener) {
locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(true);
criteria.setPowerRequirement(Criteria.POWER_LOW);
String best = locationManager.getBestProvider(criteria, true);
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
this.listener = listener;
locationManager.requestLocationUpdates(best, intervalTime, 0, MyLocationListener);
}
/**
* 停止定位服务
*/
public void stopLocation() {
if (locationManager == null || MyLocationListener == null)
return;
locationManager.removeUpdates(MyLocationListener);
instance = null;
}
/**
* 位置监听
*/
private LocationListener MyLocationListener = new LocationListener() {
@Override
public void onLocationChanged(Location location) {
if (listener != null && location != null) {
listener.onLocation(location.getLatitude() + "", location.getLongitude() + "");
}
}
@Override
public void onStatusChanged(String s, int i, Bundle bundle) {
}
@Override
public void onProviderEnabled(String s) {
}
@Override
public void onProviderDisabled(String s) {
}
};
}
public class BaiduLocationProvider {
public LocationClient mLocationClient = null;
private MyLocationListener myListener = new MyLocationListener();
private LocationCallback listener;
private static BaiduLocationProvider instance;
private int intervalTime = 10 * 1000;
public static synchronized BaiduLocationProvider getInstance() {
if (instance == null) {
instance = new BaiduLocationProvider();
}
return instance;
}
private BaiduLocationProvider() {
}
public void setIntervalTime(int intervalTime) {
this.intervalTime = intervalTime;
}
/**
* 开始定位
*/
public void startLocation(Context mContext, LocationCallback listener) {
mLocationClient = new LocationClient(mContext.getApplicationContext());
//声明LocationClient类
mLocationClient.registerLocationListener(myListener);
LocationClientOption option = new LocationClientOption();
option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
//定位SDK能够返回三种坐标类型的经纬度(国内),分别是GCJ02(国测局坐标)、BD09(百度墨卡托坐标)和BD09ll(百度经纬度坐标)。
option.setCoorType("bd09ll");
//可选,设置发起定位请求的间隔,int类型,单位ms
option.setScanSpan(intervalTime);
option.setOpenGps(true);
option.setLocationNotify(true);
option.setIgnoreKillProcess(false);
option.SetIgnoreCacheException(false);
option.setWifiCacheTimeOut(5 * 60 * 1000);
option.setEnableSimulateGps(false);
mLocationClient.setLocOption(option);
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(mContext, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
this.listener = listener;
mLocationClient.start();
}
/**
* 停止定位服务
*/
public void stopLocation() {
if (mLocationClient == null)
return;
mLocationClient.stop();
instance = null;
}
private class MyLocationListener extends BDAbstractLocationListener {
@Override
public void onReceiveLocation(BDLocation location) {
if (location != null && listener != null) {
listener.onLocation(location.getLatitude() + "", location.getLongitude() + "");
}
}
}
}
我们不在FrontService
直接调用GPSLocationProvider
或者BaiduLocationProvider
,而是另外再封装一层LocationHelper
,在LocationHelper
中调用位置服务。
public class LocationHelper {
/**
* @param mContext 上下文
* @param intervalTime 间隔时间
* @param listener 位置监听
*/
public static void startLocation(Context mContext, long intervalTime, LocationCallback listener) {
GPSLocationProvider provider = GPSLocationProvider.getInstance();
if (intervalTime != 0)
provider.setIntervalTime(intervalTime);
provider.startLocation(mContext, listener);
}
/**
* 停止定位,主要是为了省电
*/
public static void stopLocation() {
GPSLocationProvider.getInstance().stopLocation();
}
//=========================百度定位======分割线===============//
/**
* @param mContext 上下文
* @param intervalTime 间隔时间
* @param listener 位置监听
*/
// public static void startLocation(Context mContext, int intervalTime, LocationCallback listener) {
// BaiduLocationProvider provider = BaiduLocationProvider.getInstance();
// if (intervalTime != 0)
// provider.setIntervalTime(intervalTime);
// provider.startLocation(mContext, listener);
// }
/**
* 停止定位,主要是为了省电
*/
// public static void stopLocation() {
// BaiduLocationProvider.getInstance().stopLocation();
// }
}
这样做的好处是可以在FrontService
切换使用GPSLocationProvider
或者BaiduLocationProvider
,而且FrontService
中调用不变。
LocationHelper.startLocation(this, 10000, new LocationCallback() {
@Override
public void onLocation(String latitude, String longitude) {
Log.i("===FrontService===", "纬度:" + latitude + ";经度:" + longitude);
}
});