不知道大家在用高德地圖的時候有沒有發現,7.0以上的手機屏幕關閉後如果還在定位的話間隔兩分鐘後定位就停止了,8.0以上就更加了 用官方的通知也沒用。下面說重點:
用服務定位通過廣播發送在頁面裏面接收,這種能在後臺定位2個小時左右,兩個小時後還是會熄滅一切操作,不過兩個小時已經可以滿足大多數要求了吧。如果還需要更久那就定時重啓服務,前提是應用沒被殺死 只針對熄屏操作。下面是代碼:
package com.swq.mcsrefine.service;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.swq.mcsrefine.activity.R;
import com.swq.mcsrefine.utils.ContantUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
public class LocationService extends Service {
public AMapLocationClient locationClient;
//聲明mLocationOption對象
public AMapLocationClientOption locationOption = null;
//android 8.0後臺定位權限
private static final String NOTIFICATION_CHANNEL_NAME = "Location";
private NotificationManager notificationManager = null;
boolean isCreateChannel = false;
private Intent locationIntent = new Intent();
public LocationService() {
}
@Override
public void onCreate() {
initLocation();
Log.e("234", "3334455");
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startLocation();
Log.e("234", "33344");
return super.onStartCommand(intent, flags, startId);
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
@Override
public void onDestroy() {
super.onDestroy();
destroyLocation();
}
/**
* 銷燬定位
*
* @author hongming.wang
* @since 2.8.0
*/
private void destroyLocation() {
if (null != locationClient) {
/**
* 如果AMapLocationClient是在當前Activity實例化的,
* 在Activity的onDestroy中一定要執行AMapLocationClient的onDestroy
*/
locationClient.disableBackgroundLocation(true);
locationClient.stopLocation();
locationClient.unRegisterLocationListener(locationListener);
locationClient.onDestroy();
locationClient = null;
locationOption = null;
}
}
/**
* 初始化定位
*
* @author hongming.wang
* @since 2.8.0
*/
private void initLocation() {
//初始化client
locationClient = new AMapLocationClient(this.getApplicationContext());
locationOption = getDefaultOption();
//設置定位參數
locationClient.setLocationOption(locationOption);
// 設置定位監聽
locationClient.setLocationListener(locationListener);
// 設置是否單次定位
locationOption.setOnceLocation(false);
// 設置是否需要顯示地址信息
locationOption.setNeedAddress(true);
// 設置是否開啓緩存
locationOption.setLocationCacheEnable(true);
//設置定位模式爲高精度模式,Battery_Saving爲低功耗模式,Device_Sensors是僅設備模式
locationClient.enableBackgroundLocation(2004, buildNotification());
locationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
}
private void startLocation() {
// 啓動定位
if (ContantUtils.XUNCAHSERVICE) {
startAlarm();
locationClient.startLocation();
} else {
locationClient.stopLocation();
}
}
/**
* 默認的定位參數
*
* @author hongming.wang
* @since 2.8.0
*/
private AMapLocationClientOption getDefaultOption() {
AMapLocationClientOption mOption = new AMapLocationClientOption();
mOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);//可選,設置定位模式,可選的模式有高精度、僅設備、僅網絡。默認爲高精度模式
mOption.setGpsFirst(true);//可選,設置是否gps優先,只在高精度模式下有效。默認關閉
mOption.setHttpTimeOut(30000);//可選,設置網絡請求超時時間。默認爲30秒。在僅設備模式下無效
mOption.setInterval(1000);//可選,設置定位間隔。默認爲2秒
mOption.setNeedAddress(true);//可選,設置是否返回逆地理地址信息。默認是true
mOption.setOnceLocation(false);//可選,設置是否單次定位。默認是false
mOption.setOnceLocationLatest(false);//可選,設置是否等待wifi刷新,默認爲false.如果設置爲true,會自動變爲單次定位,持續定位時不要使用
AMapLocationClientOption.setLocationProtocol(AMapLocationClientOption.AMapLocationProtocol.HTTP);//可選, 設置網絡請求的協議。可選HTTP或者HTTPS。默認爲HTTP
mOption.setSensorEnable(false);//可選,設置是否使用傳感器。默認是false
mOption.setWifiScan(true); //可選,設置是否開啓wifi掃描。默認爲true,如果設置爲false會同時停止主動刷新,停止以後完全依賴於系統刷新,定位位置可能存在誤差
mOption.setLocationCacheEnable(true); //可選,設置是否使用緩存定位,默認爲true
mOption.setGeoLanguage(AMapLocationClientOption.GeoLanguage.DEFAULT);//可選,設置逆地理信息的語言,默認值爲默認語言(根據所在地區選擇語言)
return mOption;
}
/**
* 定位監聽
*/
AMapLocationListener locationListener = new AMapLocationListener() {
@Override
public void onLocationChanged(AMapLocation location) {
if (null != location) {
StringBuffer sb = new StringBuffer();
//errCode等於0代表定位成功,其他的爲定位失敗,具體的可以參照官網定位錯誤碼說明
if (location.getErrorCode() == 0) {
double lat = location.getLatitude();
double lon = location.getLongitude();
locationIntent.setAction("com.swq.mcsrefine.activity.mylocation");
locationIntent.putExtra("location", location);
sendBroadcast(locationIntent);
} else {
//定位失敗
sb.append("定位失敗" + "\n");
sb.append("錯誤碼:" + location.getErrorCode() + "\n");
sb.append("錯誤信息:" + location.getErrorInfo() + "\n");
sb.append("錯誤描述:" + location.getLocationDetail() + "\n");
}
} else {
}
}
};
/**
* 獲取時間
*
* @return
*/
public String getTime() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss ");
Date curDate = new Date(System.currentTimeMillis());//獲取當前時間
String str = formatter.format(curDate);
return str;
}
private Notification buildNotification() {
Notification.Builder builder = null;
Notification notification = null;
if (android.os.Build.VERSION.SDK_INT >= 26) {
//Android O上對Notification進行了修改,如果設置的targetSDKVersion>=26建議使用此種方式創建通知欄
if (null == notificationManager) {
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
}
String channelId = getPackageName()+"001";
if (!isCreateChannel) {
NotificationChannel notificationChannel = new NotificationChannel(channelId,
NOTIFICATION_CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
notificationChannel.enableLights(true);//是否在桌面icon右上角展示小圓點
notificationChannel.setLightColor(Color.BLUE); //小圓點顏色
notificationChannel.setShowBadge(true); //是否在久按桌面圖標時顯示此渠道的通知
notificationManager.createNotificationChannel(notificationChannel);
isCreateChannel = true;
}
builder = new Notification.Builder(getApplicationContext(), channelId);
} else {
builder = new Notification.Builder(getApplicationContext());
}
builder.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("定位")
.setContentText("")
.setWhen(System.currentTimeMillis());
if (android.os.Build.VERSION.SDK_INT >= 16) {
notification = builder.build();
} else {
return builder.getNotification();
}
return notification;
}
/**
* 防止後臺2個小時後就休眠
*/
public void startAlarm() {
AlarmManager am;
Intent intentAlarm;
PendingIntent pendSender;
//首先獲得系統服務
am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
//設置鬧鐘的意圖,我這裏是去調用一個服務,該服務功能就是獲取位置並且上傳
intentAlarm = new Intent(this, LocationService.class);
pendSender = PendingIntent.getService(this, 0, intentAlarm, PendingIntent.FLAG_UPDATE_CURRENT);
// am.cancel(pendSender);
//AlarmManager.RTC_WAKEUP ;這個參數表示系統會喚醒進程;設置的間隔時間是20分鐘
long triggerAtTime = System.currentTimeMillis() + 20 * 60 * 1000;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
am.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtTime,
pendSender);
// am.setWindow(AlarmManager.RTC_WAKEUP, triggerAtTime, 1000, pendSender);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
am.setExact(AlarmManager.RTC_WAKEUP, triggerAtTime,
pendSender);
// am.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtTime, 1000, pendSender);
} else {
am.set(AlarmManager.RTC_WAKEUP, triggerAtTime,
pendSender);
}
}
}
至於定位的操作就不多說了 可以看高德的官方文檔,這裏用的是系統的鬧鐘,對於4.4以上的set方法定時鬧鐘有點不準所以對不同版本有不同的定時啓動鬧鐘的方法。setExactAndAllowWhileIdle方法和setExact都是一次性的不會循環,所以在onStartCommand這個方法裏重複調用造成可以循環啓動。定位成功後通過廣播發送,在有需要的地方接收廣播,如果還有其他方式發送和接收的可以跟我說說,最後注意服務記得註冊,不用的時候取消服務,註銷廣播。接收就不放了,不知道的可以問我