Android O(8.0) 前臺位置服務
為降低功耗,無論應用的目標 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); } });