高德地图:解决android系统在锁屏条件下无法持续定位

本文介绍了一种针对Android高版本锁屏下无法定位的问题解决方案,通过双服务唤醒机制,即使在锁屏状态下也能保持定位服务运行,提升用户体验。文章详细讲解了创建AIDL文件、回调接口和定位服务实现步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、简介

之前公司在app中以android5.0为适用版本添加了轨迹功能用于记录用户健走路线。由于现在用户手机系统普遍更新到android8.0甚至更高。有一些问题就凸显出来,最突出的问题便是android系统现有版本在锁屏条件下无法继续定位。用户如果使用必须要保持手机前台亮屏。这样用户体验就非常差了。所以新一轮的优化也很有必然了。

道长看过一些方案后,最后选择使用的双服务唤醒方法实现。道长这篇博客重点是双服务唤醒的实现以及一些需要注意的地方。大佬的博客列举了几种方案以及实现方法,十分详细,希望可以转发扩散,让更多小伙伴解决自己的问题:https://www.jianshu.com/p/956cbba64c53

二、实现步骤

2.1创建AIDL文件

首先这两个AIDL文件必须是创建的(copy是没有灵魂的),然后创建位置要对。如下图所示:
在这里插入图片描述

内容如下:

interface ILocationHelperServiceAIDL {

    /**
    * 定位service绑定完毕后通知helperservice自己绑定的notiId
    * @param notiId 定位service的notiId
    */
    void onFinishBind(int notiId);
}
interface ILocationServiceAIDL {

    /** 当其他服务已经绑定时调起 */
    void onFinishBind();
}

2.2回调接口

public interface IWifiAutoCloseDelegate {

    boolean isUseful(Context context);

    void initOnServiceStarted(Context context);

    /**
     * 定位成功时,如果移动网络无法访问,而且屏幕是点亮状态,则对状态进行保存
     */
    void onLocateSuccess(Context context, boolean isScreenOn, boolean isMobileable);

    /**
     * 对定位失败情况的处理
     */
    void onLocateFail(Context context, int errorCode, boolean isScreenOn, boolean isWifiable);

}

2.3定位服务实现

实现的逻辑大概为:启动LocationService开启定位,定位成功后在手机添加一个前台的通知,让通知的优先级尽可能的提高。在锁屏后,powermanager判断获取如果定位失败唤醒服务。
1.定位服务基类,代码如下:

public class NotificationService extends Service {


    private static int NOTICE_ID = 12138;
    private final String mHelperServiceName = "com.yushan.background.locationservice.LocationHelperService";
    private Utils.CloseServiceReceiver mCloseReceiver;
    public Binder mBinder;
    private ILocationHelperServiceAIDL mHelperAIDL;
    private ServiceConnection connection;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        mCloseReceiver = new Utils.CloseServiceReceiver(this);
        registerReceiver(mCloseReceiver, Utils.getCloseServiceFilter());
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        if (mCloseReceiver != null) {
            unregisterReceiver(mCloseReceiver);
            mCloseReceiver = null;
        }

        super.onDestroy();
    }

    /**
     * 触发利用notification增加进程优先级
     */
    protected void applyNotiKeepMech() {
        startForeground(NOTICE_ID, Utils.buildNotification(getBaseContext()));
        startBindHelperService();
    }

    public void unApplyNotiKeepMech() {
        stopForeground(true);
    }

    public class LocationServiceBinder extends ILocationServiceAIDL.Stub {

        @Override
        public void onFinishBind(){
        }
    }

    private void startBindHelperService() {
        connection = new ServiceConnection() {

            @Override
            public void onServiceDisconnected(ComponentName name) {
                //doing nothing
            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                ILocationHelperServiceAIDL l = ILocationHelperServiceAIDL.Stub.asInterface(service);
                mHelperAIDL = l;
                try {
                    l.onFinishBind(NOTICE_ID);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };
        Intent intent = new Intent();
        intent.setAction(mHelperServiceName);
        intent.setPackage("com.wanbu.dascom");
        Intent explicitIntent = Utils.getExplicitIntent(getApplicationContext(), intent);
        bindService(explicitIntent, connection, Service.BIND_AUTO_CREATE);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null) {
            mBinder = new LocationServiceBinder();
        }
        return mBinder;
    }

}

2.定位服务类,代码如下:

public class LocationService extends NotificationService {

    private AMapLocationClient mLocationClient;
    private AMapLocationClientOption mLocationOption;
    private IWifiAutoCloseDelegate mWifiAutoCloseDelegate = new WifiAutoCloseDelegate();
    private boolean mIsWifiCloseable = false;

    AMapLocationListener locationListener = new AMapLocationListener() {

        @Override
        public void onLocationChanged(AMapLocation aMapLocation) {
            //发送结果的通知
            sendLocationBroadcast(aMapLocation);

            if (!mIsWifiCloseable) {
                return;
            }

            if (aMapLocation.getErrorCode() == AMapLocation.LOCATION_SUCCESS) {
                mWifiAutoCloseDelegate.onLocateSuccess(getApplicationContext(), PowerManagerUtil.getInstance().isScreenOn(getApplicationContext()), NetUtil.getInstance().isMobileAva(getApplicationContext()));
            } else {
                mWifiAutoCloseDelegate.onLocateFail(getApplicationContext() , aMapLocation.getErrorCode() , PowerManagerUtil.getInstance().isScreenOn(getApplicationContext()), NetUtil.getInstance().isWifiCon(getApplicationContext()));
            }
        }

        private void sendLocationBroadcast(AMapLocation aMapLocation) {
            if (null != aMapLocation) {
                Intent mIntent = new Intent(LocationChangBroadcastReceiver.RECEIVER_ACTION);
                mIntent.putExtra(LocationChangBroadcastReceiver.RECEIVER_DATA, aMapLocation);
                sendBroadcast(mIntent);
            }
        }
    };

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);
        //开启利用notification提高进程优先级的机制
        applyNotiKeepMech();
        if (mWifiAutoCloseDelegate.isUseful(getApplicationContext())) {
            mIsWifiCloseable = true;
            mWifiAutoCloseDelegate.initOnServiceStarted(getApplicationContext());
        }

        startLocation();
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        unApplyNotiKeepMech();
        stopLocation();
        super.onDestroy();
    }

    void startLocation() {
        stopLocation();

        if (null == mLocationClient) {
            mLocationClient = new AMapLocationClient(this.getApplicationContext());
        }

        mLocationOption = new AMapLocationClientOption();
        mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);
        // 使用连续
        mLocationOption.setOnceLocation(false);
        mLocationOption.setLocationCacheEnable(false);
        // 每2秒定位一次
        mLocationOption.setInterval(2 * 1000);
        // 地址信息
        mLocationOption.setNeedAddress(true);
        mLocationClient.setLocationOption(mLocationOption);
        mLocationClient.setLocationListener(locationListener);
        mLocationClient.startLocation();
    }

    void stopLocation() {
        if (null != mLocationClient) {
            mLocationClient.stopLocation();
        }
    }
  
}

3.定位守护类,代码如下

public class LocationHelperService extends Service {
    private Utils.CloseServiceReceiver mCloseReceiver;
    private HelperBinder mBinder;
    private ServiceConnection mInnerConnection;
    
    @Override
    public void onCreate() {
        super.onCreate();
        startBind();
        mCloseReceiver = new Utils.CloseServiceReceiver(this);
        registerReceiver(mCloseReceiver, Utils.getCloseServiceFilter());
    }

    @Override
    public void onDestroy() {
        if (mInnerConnection != null) {
            unbindService(mInnerConnection);
            mInnerConnection = null;
        }

        if (mCloseReceiver != null) {
            unregisterReceiver(mCloseReceiver);
            mCloseReceiver = null;
        }

        super.onDestroy();
    }

    
    private void startBind() {
        final String locationServiceName = "com.yushan.background.locationservice.LocationService";
        mInnerConnection = new ServiceConnection() {

            @Override
            public void onServiceDisconnected(ComponentName name) {
                Intent intent = new Intent();
                intent.setAction(locationServiceName);
                startService(Utils.getExplicitIntent(getApplicationContext(), intent));
            }

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                ILocationServiceAIDL l = ILocationServiceAIDL.Stub.asInterface(service);
                try {
                    l.onFinishBind();
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };

        Intent intent = new Intent();
        intent.setAction(locationServiceName);
        intent.setPackage("com.wanbu.dascom");
        bindService(Utils.getExplicitIntent(getApplicationContext(), intent), mInnerConnection, Service.BIND_AUTO_CREATE);
    }



    private class HelperBinder extends ILocationHelperServiceAIDL.Stub {

        @Override
        public void onFinishBind(int notiId) throws RemoteException {
            startForeground(notiId, Utils.buildNotification(LocationHelperService.this.getApplicationContext()));
            stopForeground(true);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        if (mBinder == null) {
            mBinder = new HelperBinder();
        }

        return mBinder;
    }
}

4.添加全局广播
由于道长公司app是组件化框架,在当前模块的Application注册全局广播就可以实现唤醒服务。

public class HealthApplication extends BaseApplication {

    private static LocationChangBroadcastReceiver locationChangBroadcastReceiver;

    public HealthApplication(Application application, int tinkerFlags, boolean tinkerLoadVerifyFlag, long applicationStartElapsedTime, long applicationStartMillisTime, Intent tinkerResultIntent) {
        super(application, tinkerFlags, tinkerLoadVerifyFlag, applicationStartElapsedTime, applicationStartMillisTime, tinkerResultIntent);
    }

    public static LocationChangBroadcastReceiver getlocationChangeBoardcase() {
        if (null == locationChangBroadcastReceiver) {
            locationChangBroadcastReceiver = new LocationChangBroadcastReceiver();
        }
        Log.e("yushan","一切有我");
        return locationChangBroadcastReceiver;
    }
}

这个方案由道长亲测可以。小米手机后台运行锁屏可能会被杀死,但是打开自启动后可以长时间运行,但是重新唤醒期间不能定位,期间会丢失部分数据。华为手机没有问题。其他机型小伙伴们可以测试一下。

小米手机(6.0.1)app后台息屏运行时间30s(未打开自启动)
	app后台息屏运行时间不确定(5小时)(打开自启动)
	app前台息屏运行时间超长(大于5个小时,具体未测出)(未打开自启动)

华为手机(8.0.0)app后台息屏运行时间大于20小时(未上软件锁)
	app后台息屏运行时间(打开软件锁)
	app前台息屏运行时间(未上软件锁)

由于道长是在公司app上实现的,代码无法分享。如果有人需要的话可以去上面大佬博客下载Demo,如果还是不明白的话可以留言。希望这篇博客给小伙伴们解决问题的思路。(这个方案暂时无法在android9.0及上实现,如果适配9.0需要对上面的代码做部分改动)

Android无法继续定位问题 产生问题的原因: 手机后,Android系统为了省电以及减少CPU消耗,在一段时间后会将手机进入休眠状态。此时的服务以及线程等都会停止。 最近就这个问题,阅读了很多代码以及官方文档,下面就说下最近都尝试过的方式,可能其中有些您实现了,我这边没实现,望见谅。本文采用的高德定位。 一、PowerManager.WakeLock (1)直接强制当前页面cpu运行 private PowerManager pm; private PowerManager.WakeLock wakeLock; @Override public void onCreate() { super.onCreate(); //创建PowerManager对象 pm = (PowerManager) getSystemService(Context.POWER_SERVICE); //保持cpu一直运行,不管幕是否黑 wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "CPUKeepRunning"); wakeLock.acquire(); } @Override public void onDestroy() { wakeLock.release(); super.onDestroy(); } 这个写法我表示并没有什么用,并不能强制cpu持续运行。 (2)WakefulBroadcastReceiver public class WLWakefulReceiver extends WakefulBroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // String extra = intent.getStringExtra("msg"); Intent serviceIntent = new Intent(context, MyIntentService.class); serviceIntent.putExtra("msg", extra); startWakefulService(context, serviceIntent); } } WakefulBroadcastReceiver 内部的原理也是PowerManager,注册广播时8.0的请动态注册,静态没有用。广播注册完了之后,写一个服务用来与广播互动。 public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override public void onCreate() { super.onCreate(); } @Override protected void onHandleIntent(@Nullable final Intent intent) { //子线程中执行 Log.i("MyIntentService", "onHandleIntent"); String extra = intent.getStringExtra("msg"); new Thread(new Runnable() { @Override public void run() { LocationUtil.getInstance().startLocation(LocationUtil.NULL, new LocationUtil.OnLocationBack() { @Override public void back(AMapLocation aMapLocation, String backString) { Log.e("定位結果", aMapLocation.getAddress()+""); 定位结果操作,我这边是把定位的时候保存到数据库里面。 } }); } }).start(); Log.i("MyIntentService", "onHandleIntent:"+extra); //调用completeWakefulIntent来释放唤醒。 // WLWakefulReceiver.completeWakefulIntent(intent); } } 注册
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值