以前公司需求作一款相似滴滴打车派单的app,其中须要app退到后台后能一直上传定位的坐标,当有任务派发时,能够进行语音提醒和dialog弹窗提醒。根据以上的需求,这个需求应用长期在后台定位,必须保证应用是存活的,以前作过一款滑雪测速的项目,也须要后台不停的采集gps坐标。关于应用保活我尝试过过一下几种方案:git
我先说下百度导航的方案和思路,我反复查看了百度导航app在后台运行的效果,发现百度导航在后台播放音频文件,来让系统得知它在运行,提高了应用的优先级,不让系统kill掉它。在不播放路线的时候,它在后台播放无声的音频文件。在得知它的保活方式后,我也开始仿照它的作法来进行实现,发现这样的方式确实可让应用存活的时间更长,可是这样会比较费电,系统会进行提示高耗电。web
首先进入应用我会判断当前状态是否为可接收任务状态,若是可接收则开启service,在后台进行定位,每两秒上传一次定位坐标给后台。直接上代码:缓存
管理是否开启定位服务bash
public class PollingUtils {
//开启轮询服务
public static void startPollingService(Context context, int seconds, Class<?> cls, String action) {
//获取AlarmManager系统服务
AlarmManager manager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
//包装须要执行Service的Intent
Intent intent = new Intent(context, cls);
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
//触发服务的起始时间
long triggerAtTime = SystemClock.elapsedRealtime();
//使用AlarmManger的setRepeating方法设置按期执行的时间间隔(seconds秒)和须要执行的Service
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime,
seconds * 1000, pendingIntent);
}
//中止轮询服务
public static void stopPollingService(Context context, Class<?> cls, String action) {
AlarmManager manager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, cls);
intent.setAction(action);
PendingIntent pendingIntent = PendingIntent.getService(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
//取消正在执行的服务
manager.cancel(pendingIntent);
context.stopService(intent);
}
}
复制代码
定位servicewebsocket
public class CourierLocationService extends BaseService implements AMapLocationListener {
// // 定位相关
// private LocationClient mLocClient;
// //
private int msgId = -1;
private PowerManager.WakeLock wakeLock = null;
private AMapLocation mLocation = null;
private MediaPlayer mediaPlayer = null;
//
// @Override
// public void onCreate() {
// super.onCreate();
// // 定位初始化
//// mLocClient = new LocationClient(this);
//// mLocClient.registerLocationListener(this);
//// LocationClientOption option = new LocationClientOption();
//// option.setIsNeedAddress(true);// 设置之后,请求结果 BDLocation#getCity 就不为null了
//// option.setOpenGps(true);// 打开gps
//// option.setCoorType("bd09ll"); // 设置坐标类型
//// option.setScanSpan(10000);// 定位频率
//// mLocClient.setLocOption(option);
//// mLocClient.start();
// initBaiDu();
// //
// isStarted = true;
// }
//
// @Override
// public int onStartCommand(Intent intent, int flags, int startId) {
// if (intent != null) {
// msgId = intent.getIntExtra("msgId", -1);
// }
// // 刷新定位
// if (mLocClient != null && mLocClient.isStarted()) {
// mLocClient.requestLocation();
// }
// return super.onStartCommand(intent, flags, startId);
// }
//
// /**
// * 初始化百度地图
// */
// private void initBaiDu() {
// // 定位初始化
// mLocClient = new LocationClient(this);
// mLocClient.registerLocationListener(locationListener);
// LocationClientOption option = new LocationClientOption();
// option.setIsNeedAddress(true);// 设置之后,请求结果 BDLocation#getCity 就不为null了
//// option.setOpenGps(true);// 打开gps
// option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy);
// option.setCoorType("bd09ll"); // 设置坐标类型
// option.setScanSpan(20000);// 定位频率
// //可选,定位SDK内部是一个service,并放到了独立进程。
// //设置是否在stop的时候杀死这个进程,默认(建议)不杀死,即setIgnoreKillProcess(true)
// option.setIgnoreKillProcess(true);
// mLocClient.setLocOption(option);
// mLocClient.start();
// }
//
// /**
// * 定位SDK监听函数
// */
// LocationListener locationListener = new LocationListener() {
// @Override
// public void onReceiveLocation(BDLocation location) {
// int errorCode = location.getLocType();
// Log.d("33333", "错误码:" + errorCode);
// if (location == null || Str.isEmpty(location.getCity())) {
// // 刷新定位
// if (mLocClient != null) {
// SDKInitializer.initialize(BeeApplication.getContext());
// mLocClient.unRegisterLocationListener(locationListener);
// mLocClient.stop();
// mLocClient = null;
// initBaiDu();
// } else {
// initBaiDu();
// }
// } else {
// Message msg = Message.obtain();
// if (msgId == -1) {
// msg.what = MsgID.location_baidu;
// } else {
// msg.what = msgId;
// if (msgId != MsgID.courier_location_upload_data) {
// msgId = -1;// reset
// }
// }
// msg.obj = location;
// HandlerMgr.sendMessage(msg, 0);
// }
// //
// // int userId = ((BeeApplication) getApplication()).getUser().getId();
// // AppHttp.getInstance().beat(userId, location.getLatitude(), location.getLongitude());
// }
// };
//
// @Override
// public void onDestroy() {
// super.onDestroy();
// msgId = -1;// reset
// mLocClient.unRegisterLocationListener(locationListener);
// // 退出时销毁定位
// mLocClient.stop();
// //
// isStarted = false;
// }
//声明AMapLocationClient类对象
private AMapLocationClient mLocationClient = null;
@Override
public void onCreate() {
super.onCreate();
Notify.getInstance().startForeground(this);
initGaoDe();
}
private void initGaoDe() {
//初始化定位
mLocationClient = new AMapLocationClient(getApplicationContext());
//设置定位回调监听
mLocationClient.setLocationListener(this);
AMapLocationClientOption option = new AMapLocationClientOption();
/**
* 设置定位场景,目前支持三种场景(签到、出行、运动,默认无场景)
*/
option.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);
//设置定位模式为AMapLocationMode.Device_Sensors,仅设备模式。
// option.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);
//获取一次定位结果:
//该方法默认为false。
option.setOnceLocation(false);
//获取最近3s内精度最高的一次定位结果:
//设置setOnceLocationLatest(boolean b)接口为true,启动定位时SDK会返回最近3s内精度最高的一次定位结果。若是设置其为true,setOnceLocation(boolean b)接口也会被设置为true,反之不会,默认为false。
// option.setOnceLocationLatest(true);
//设置定位间隔,单位毫秒,默认为2000ms,最低1000ms。
option.setInterval(10000);
//设置是否返回地址信息(默认返回地址信息)
option.setNeedAddress(true);
//设置是否容许模拟位置,默认为true,容许模拟位置
option.setMockEnable(true);
// option.setGpsFirst(true);
//单位是毫秒,默认30000毫秒,建议超时时间不要低于8000毫秒。
// option.setHttpTimeOut(20000);
//关闭缓存机制
// option.setLocationCacheEnable(false);
if (null != mLocationClient) {
mLocationClient.setLocationOption(option);
//设置场景模式后最好调用一次stop,再调用start以保证场景模式生效
mLocationClient.stopLocation();
mLocationClient.startLocation();
}
}
//
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
msgId = intent.getIntExtra("msgId", -1);
}
flags = START_STICKY;
acquireWakeLock();
// 刷新定位
if (mLocationClient != null && mLocationClient.isStarted()) {
mLocationClient.startLocation();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onLocationChanged(AMapLocation location) {
int errorCode = location.getErrorCode();
// DealTaskService.writerLogToFile("定位的错误码:" + errorCode + ",定位的 MSGID:" + msgId);
Log.d("33333", "错误码:" + errorCode);
if (location == null || Str.isEmpty(location.getCity()) || errorCode != 0) {
mLocationClient.stopLocation();
mLocationClient.startLocation();
} else {
mLocation = location;
}
if (mLocation != null) {
Message msg = Message.obtain();
if (msgId == -1) {
msg.what = MsgID.location_baidu;
} else {
msg.what = msgId;
if (msgId != MsgID.courier_location_upload_data) {
msgId = MsgID.courier_location_upload_data;// reset
}
}
msg.obj = location;
HandlerMgr.sendMessage(msg, 0);
}
//
// int userId = ((BeeApplication) getApplication()).getUser().getId();
// AppHttp.getInstance().beat(userId, location.getLatitude(), location.getLongitude());
}
@Override
public void onDestroy() {
super.onDestroy();
releaseWakeLock();
if (mLocationClient != null) {
mLocationClient.unRegisterLocationListener(this);
mLocationClient.stopLocation();//中止定位后,本地定位服务并不会被销毁
mLocationClient.onDestroy();//销毁定位客户端,同时销毁本地定位服务。
}
pausePlayer();
}
/**
* PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有多是关闭的。
* SCREEN_DIM_WAKE_LOCK:保持CPU 运转,容许保持屏幕显示但有多是灰的,容许关闭键盘灯
* SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,容许保持屏幕高亮显示,容许关闭键盘灯
* FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
* ACQUIRE_CAUSES_WAKEUP:强制使屏幕亮起,这种锁主要针对一些必须通知用户的操做.
* ON_AFTER_RELEASE:当锁被释放时,保持屏幕亮起一段时间
*/
private void acquireWakeLock() {
if (null == wakeLock) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, getClass()
.getCanonicalName());
if (null != wakeLock) {
// Log.i(TAG, "call acquireWakeLock");
Log.d("33333", "call acquireWakeLock");
wakeLock.acquire();
}
}
}
// 释放设备电源锁
private void releaseWakeLock() {
if (null != wakeLock && wakeLock.isHeld()) {
Log.d("33333", "call releaseWakeLock");
// Log.i(TAG, "call releaseWakeLock");
wakeLock.release();
wakeLock = null;
}
}
// /**
// * 设置应用进入后台,播放音频来进行cpu不休眠,进行应用保活
// */
// private void setAppBackgroundPlayer() {
// MediaPlayerUtils.getInstance().playerMusic("courier_silence.mp3", true);
// }
private void pausePlayer() {
MediaPlayerUtils.getInstance().destoryPlayer();
}
}
复制代码
后台播放音视频文件工具类app
public class MediaPlayerUtils {
private static final String TAG = MediaPlayerUtils.class.getSimpleName();
private static MediaPlayer mediaPlayer = null;
private static MediaPlayerUtils mediaPlayerUtils = null;
private PlayerMediaAsync playerMediaAsync = null;
private boolean isLooping = true;
public static MediaPlayerUtils getInstance() {
if (mediaPlayerUtils == null) {
synchronized (MediaPlayerUtils.class) {
if (mediaPlayerUtils == null) {
mediaPlayerUtils = new MediaPlayerUtils();
mediaPlayer = new MediaPlayer();
}
}
}
return mediaPlayerUtils;
}
public MediaPlayer getMediaPlayer() {
if (mediaPlayer == null) {
mediaPlayer = new MediaPlayer();
}
return mediaPlayer;
}
public synchronized void playerMusic(String fileName, boolean isLooping) {
this.isLooping = isLooping;
if (playerMediaAsync != null) {
playerMediaAsync.cancel(true);
playerMediaAsync = null;
}
playerMediaAsync = new PlayerMediaAsync();
playerMediaAsync.execute(fileName);
}
class PlayerMediaAsync extends AsyncTask<String, String, String> {
@Override
protected String doInBackground(String... params) {
try {
getMediaPlayer();
if (mediaPlayer.isPlaying()) {
mediaPlayer.stop();
mediaPlayer.reset();
}
AssetFileDescriptor fileDescriptor = BeeApplication.getContext().getAssets().openFd(params[0]);
mediaPlayer.setDataSource(fileDescriptor.getFileDescriptor(),
fileDescriptor.getStartOffset(),
fileDescriptor.getLength());
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);//STREAM_ALARM
mediaPlayer.prepare();
mediaPlayer.setLooping(isLooping);
mediaPlayer.start();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
public void destoryPlayer() {
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer = null;
}
if (playerMediaAsync != null) {
playerMediaAsync.cancel(true);
playerMediaAsync = null;
}
}
}
复制代码
使用定时器进行cpu唤醒socket
public class TimerService extends Service {
public static final String ACTION = "com.iseastar.guojiang.app.TimerService";
private Timer timer = null;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Notify.getInstance().startForeground(this);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (timer == null) {
timer = new Timer(true);
}
timer.schedule(new TimerTask() {
@Override
public void run() {
Log.d("33333", "TimerService:onHandleIntent()");
Log.d("33333", ActivityMgr.isServiceRunning(BeeApplication.getContext(), "com.iseastar.guojiang.newcabinet.DealTaskService"));
if (!ActivityMgr.isServiceRunning(BeeApplication.getContext(), "com.iseastar.guojiang.newcabinet.DealTaskService")) {
Intent intent = new Intent(getApplication(), DealTaskService.class);
intent.setPackage(getPackageName());
intent.putExtra("isStop", false);
startService(intent);
DealTaskService.startWorkLocation(false);
}
}
}, new Date(), 10000);
flags = START_STICKY;
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
if (timer != null) {
timer.cancel();
timer = null;
}
super.onDestroy();
}
}
复制代码
通过我屡次验证,发现使用timer唤醒cpu并很差使,你们能够选择不实用TimerService和PollingUtils这两个类,使用上后效果并不明显。ide
经过作这个项目,我也思考过一些问题,好比使用websocket长连接,考虑到定位是基于百度或者高德的,也是每隔几秒定位一次,就直接使用了普通的接口。gps定位也不是每秒都定位一次的,而且还容易受到干扰,因此选择了第三方的定位方式,增长定位的准确度。以前我作过一款基于gps定位进行滑雪测速相关项目,对于gps定位进行过一些研究,因此这里不采用手机原始的gps定位。函数