不知道大家在用高德地图的时候有没有发现,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这个方法里重复调用造成可以循环启动。定位成功后通过广播发送,在有需要的地方接收广播,如果还有其他方式发送和接收的可以跟我说说,最后注意服务记得注册,不用的时候取消服务,注销广播。接收就不放了,不知道的可以问我
8.0以上无效的话只能后台开一个服务,在服务里面循环播放一个无声的音乐,也只能管几个小时。完全跟微信一样的话只能跟厂商加入白名单。
————————————————
可以看看这一篇:android 熄屏 后台运行,Android进程保活/息屏后后台保持定位、网络运行_今人不见古时月,今月曾经照古人的博客-CSDN博客
原文链接:https://blog.csdn.net/qq_22718203/article/details/86571635