写点什么

Android 锁屏无法继续定位问题,android 实战教程

作者:嘟嘟侠客
  • 2021 年 11 月 27 日
  • 本文字数:12237 字

    阅读完需:约 40 分钟

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.setOnceLocation(false);


mLocationOption.setLocationCacheEnable(false);


// 每 5 秒定位一次


mLocationOption.setInterval(5 * 1000);


// 地址信息


mLocationOption.setNeedAddress(true);


mLocationClient.setLocationOption(mLocationOption);


mLocationClient.setLocationListener(locationListener);


mLocationClient.startLocation();


}


/**


  • 停止定位


*/


void stopLocation() {


if (null != mLocationClient) {


mLocationClient.stopLocation();


}


}


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);


ToastUtils.show("获取到定位信息");


String string = System.currentTimeMillis() + ","+aMapLocation.getLatitude() + "," + aMapLocation.getLongitude();


Utils.saveFile(string, "backlocation.txt", true);


}


}


};


}


定位服务的基类


/**


  • 利用双 service 进行 notification 绑定,进而将 Service 的 OOM_ADJ 提高到 1

  • 同时利用 LocationHelperService 充当守护进程,在 NotificationService 被关闭后,重启他。

  • 如果 LocationHelperService 被停止,NotificationService 不负责唤醒


*/


public class NotificationService extends Service {


/**


  • startForeground 的 noti_id


*/


private static int NOTI_ID = 123321;


private Utils.CloseServiceReceiver mCloseReceiver;


@Override


public int onStartCommand(Intent intent, int flags, int startId) {


Log.e("background location", "远程服务调用成功");


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();


}


private final String mHelperServiceName = "com.hdsx.background.locationservice.LocationHelperService";


/**


  • 触发利用 notification 增加进程优先级


*/


protected void applyNotiKeepMech() {


startForeground(NOTI_ID, Utils.buildNotification(getBaseContext()));


startBindHelperService();


}


public void unApplyNotiKeepMech() {


stopForeground(true);


}


public Binder mBinder;


public class LocationServiceBinder extends ILocationServiceAIDL.Stub {


@Override


public void onFinishBind(){


}


}


private ILocationHelperServiceAIDL mHelperAIDL;


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(NOTI_ID);


} catch (RemoteException e) {


e.printStackTrace();


}


}


};


Intent intent = new Intent();


intent.setAction(mHelperServiceName);


bindService(Utils.getExplicitIntent(getApplicationContext(), intent), connection, Service.BIND_AUTO_CREATE);


}


private ServiceConnection connection;


@Nullable


@Override


public IBinder onBind(Intent intent) {


if (mBinder == null) {


mBinder = new LocationServiceBinder();


}


return mBinder;


}


}


另外一个服务:


public class LocationHelperService extends Service {


private Utils.CloseServiceReceiver mCloseReceiver;


@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 ServiceConnection mInnerConnection;


private void startBind() {


final String locationServiceName = "com.hdsx.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);


bindService(Utils.getExplicitIntent(getApplicationContext(), intent), mInnerConnection, Service.BIND_AUTO_CREATE);


}


private HelperBinder mBinder;


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;


}


}


双服务进行捆绑, 之前测试的过程中,发现这个 powermanager 配合服务他是可实现部分手机的,这边也做一个 power 的封装。


/**


  • 获得 PARTIAL_WAKE_LOCK , 保证在息屏状体下,CPU 可以正常运行


*/


public class PowerManagerUtil {


private static class Holder {


public static PowerManagerUtil instance = new PowerManagerUtil();


}


private PowerManager pm = null;


private PowerManager.WakeLock pmLock = null;


/**


  • 上次唤醒屏幕的触发时间


*/


private long mLastWakupTime = System.currentTimeMillis();


/**


  • 最小的唤醒时间间隔,防止频繁唤醒。默认 5 分钟


*/


private long mMinWakupInterval = 10 * 1000;


private InnerThreadFactory mInnerThreadFactory = null;


public static PowerManagerUtil getInstance() {


return Holder.instance;


}


/**


  • 判断屏幕是否处于点亮状态

  • @param context


*/


public boolean isScreenOn(final Context context) {


try {


Method isScreenMethod = PowerManager.class.getMethod("isScreenOn",


new Class[]{});


if (pm == null) {


pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);


}


boolean screenState = (Boolean) isScreenMethod.invoke(pm);


return screenState;


} catch (Exception e) {


return true;


}


}


/**


  • 唤醒屏幕


*/


public void wakeUpScreen(final Context context) {


try {


acquirePowerLock(context, PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.SCREEN_DIM_WAKE_LOCK);


String string = System.currentTimeMillis() +"唤醒";


Utils.saveFile(string, "huanxinglocation.txt", true);


} catch (Exception e) {


throw e;


}


}


/**


  • 根据 levelAndFlags,获得 PowerManager 的 WaveLock

  • 利用 worker thread 去获得锁,以免阻塞主线程

  • @param context

  • @param levelAndFlags


*/


private void acquirePowerLock(final Context context, final int levelAndFlags) {


if (context == null) {


throw new NullPointerException("when invoke aquirePowerLock , context is null which is unacceptable");


}


long currentMills = System.currentTimeMillis();


if (currentMills - mLastWakupTime < mMinWakupInterval) {


return;


}


mLastWakupTime = currentMills;


if (mInnerThreadFactory == null) {


mInnerThreadFactory = new InnerThreadFactory();


}


mInnerThreadFactory.newThread(new Runnable() {


@Override


public void run() {


if (pm == null) {


pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);


}


if (pmLock != null) {


// release


pmLock.release();


pmLock = null;


}


pmLock = pm.newWakeLock(levelAndFlags, "MyTag");


pmLock.acquire();


pmLock.release();


}


}).start();


}


private class InnerThreadFactory implements ThreadFactory {


@Override


public Thread newThread(Runnable runnable) {


return new Thread(runnable);


}


}


}


整体的逻辑呢就是说, 启动 LocationService 开启定位,定位成功后在手机添加一个前台的通知,让通知的优先级尽可能的提高。在锁屏后,powermanager 判断获取如果定位失败唤醒服务。


public void onLocateFail(Context context, int errorCode, boolean isScreenOn, boolean isWifiable) {


//如果屏幕点亮情况下,因为断网失败,则表示不是屏幕点亮造成的断网失败,并修改参照值


if (isScreenOn && errorCode == AMapLocation.ERROR_CODE_FAILURE_CONNECTION && !isWifiable) {


LocationStatusManager.getInstance().resetToInit(context);


return;


}


if (!LocationStatusManager.getInstance().isFailOnScreenOff(context, errorCode, isScreenOn, isWifiable)) {


return;


}


PowerManagerUtil.getInstance().wakeUpScreen(context);


}


代码有点多,就不一一介绍了,下面把我包含的工具类都发出来。


  • LocationStatusManager


/**


  • 在定位失败的情况下,用于判断当前定位错误是否是由于息屏导致的网络关闭引起的。

  • 判断逻辑仅限于处理设备仅有 wifi 信号的情况下


*/


public class LocationStatusManager {


/**


  • 上一次的定位是否成功


*/


private boolean mPriorSuccLocated = false;


/**


  • 屏幕亮时可以定位


*/


private boolean mPirorLocatableOnScreen = false;


static class Holder {


public static LocationStatusManager instance = new LocationStatusManager();


}


public static LocationStatusManager getInstance() {


return Holder.instance;


}


/**


  • 由于仅仅处理只有 wifi 连接的情况下,如果用户手机网络可连接,那么忽略。

  • 定位成功时,重置为定位成功的状态

  • @param isScreenOn 当前屏幕是否为点亮状态

  • @param isMobileable 是否有手机信号


*/


public void onLocationSuccess(Context context, boolean isScreenOn, boolean isMobileable) {


if (isMobileable) {


return;


}


mPriorSuccLocated = true;


if (isScreenOn) {


mPirorLocatableOnScreen = true;


saveStateInner(context, true);


}


}


/**


  • reset 到默认状态

  • @param context


*/


public void resetToInit(Context context) {


this.mPirorLocatableOnScreen = false;


this.mPriorSuccLocated = false;


saveStateInner(context, false);


}


/**


  • 由 preference 初始化。特别是在定位服务重启的时候会进行初始化


*/


public void initStateFromPreference(Context context) {


if (!isLocableOnScreenOn(context)) {


return;


}


this.mPriorSuccLocated = true;


this.mPirorLocatableOnScreen = true;


}


/**


  • 判断是否由屏幕关闭导致的定位失败。

  • 只有在 网络可访问 &&errorCode==4&&(priorLocated&&locatableOnScreen) && !isScreenOn 才认为是有息屏引起的定位失败

  • 如果判断条件较为严格,请按需要适当修改

  • @param errorCode 定位错误码, 0=成功, 4=因为网络原因造成的失败

  • @param isScreenOn 当前屏幕是否为点亮状态


*/


public boolean isFailOnScreenOff(Context context, int errorCode, boolean isScreenOn, boolean isWifiable) {


return !isWifiable && errorCode == AMapLocation.ERROR_CODE_FAILURE_CONNECTION && (mPriorSuccLocated && mPirorLocatableOnScreen) && !isScreenOn;


}


/**


  • 是否存在屏幕亮而且可以定位的情况的 key


*/


private String IS_LOCABLE_KEY = "is_locable_key";


/**


  • IS_LOCABLE_KEY 的过期时间


*/


private String LOCALBLE_KEY_EXPIRE_TIME_KEY = "localble_key_expire_time_key";


/**


  • 过期时间为 10 分钟


*/


private static final long MINIMAL_EXPIRE_TIME = 30 * 60 * 1000;


private static final String PREFER_NAME = LocationStatusManager.class.getSimpleName();


private static final long DEF_PRIOR_TIME_VAL = -1;


/**


  • 如果 isLocable,则存入正确的过期时间,否则存默认值

  • @param context

  • @param isLocable


*/


public void saveStateInner(Context context, boolean isLocable) {


SharedPreferences sharedPreferences = context.getSharedPreferences(PREFER_NAME, MODE_PRIVATE);


SharedPreferences.Editor editor = sharedPreferences.edit();


editor.putBoolean(IS_LOCABLE_KEY, isLocable);


editor.putLong(LOCALBLE_KEY_EXPIRE_TIME_KEY, isLocable ? System.currentTimeMillis() : DEF_PRIOR_TIME_VAL);


editor.commit();


}


/**


  • 从 preference 读取,判断是否存在网络状况 ok,而且亮屏情况下,可以定位的情况


*/


public boolean isLocableOnScreenOn(Context context) {


SharedPreferences sharedPreferences = context.getSharedPreferences(PREFER_NAME, MODE_PRIVATE);


boolean res = sharedPreferences.getBoolean(IS_LOCABLE_KEY, false);


long priorTime = sharedPreferences.getLong(LOCALBLE_KEY_EXPIRE_TIME_KEY, DEF_PRIOR_TIME_VAL);


if (System.currentTimeMillis() - priorTime > MINIMAL_EXPIRE_TIME) {


saveStateInner(context, false);


return false;


}


return res;


}


}


  • LocationChangBroadcastReceiver


做最后定位的结果,保存到数据库里


public class LocationChangBroadcastReceiver extends BroadcastReceiver {


public static final String RECEIVER_ACTION = "location_in_background";


public static final String RECEIVER_DATA = "location_data";


@Override


public void onReceive(Context context, Intent intent) {


String action = intent.getAction();


if (action.equals(RECEIVER_ACTION)) {


AMapLocation location = (AMapLocation) intent.getParcelableExtra(RECEIVER_DATA);


if (null != location) {


String string = System.currentTimeMillis() + ","+location.getLatitude() + "," + location.getLongitude();


Utils.saveFile(string, "broadcastlocation.txt", true);


Log.v("定位数据", "经度:" + location.getLongitude() + " 纬度:" + location.getLatitude());


SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


java.util.Date date = new Date(location.getTime());


String tracktime = sdf.format(date);


Map map = new HashMap();


String userid = SpUtils.getString(USER_ID);


map.put("userid", userid);


double[] loc = CoordinateTransformUtil.gcj02towgs84(location.getLongitude(),location.getLatitude());


map.put("tracktime", tracktime);


map.put("latitude", loc[1]);


map.put("lontitude", loc[0]);


Frame.getInstance().getDao().insert("trackbean.insert_track", map);


}


}


}


}


  • NetUtil


用于判断设备是否可以访问网络。


public class NetUtil {


private static class Holder {


public static NetUtil instance = new NetUtil();


}


public static NetUtil getInstance() {


return Holder.instance;


}


/**


  • 是否手机信号可连接

  • @param context

  • @return


*/


public boolean isMobileAva(Context context) {


boolean hasMobileCon = false;


ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);


NetworkInfo[] netInfos = cm.getAllNetworkInfo();


for (NetworkInfo net : netInfos) {


String type = net.getTypeName();


if (type.equalsIgnoreCase("MOBILE")) {


if (net.isConnected()) {


hasMobileCon = true;


}


}


}


return hasMobileCon;


}


/**


  • 是否 wifi 可连接

  • @param context

  • @return


*/


public boolean isWifiCon(Context context) {


boolean hasWifoCon = false;


ConnectivityManager cm = (ConnectivityManager) context.getSystemService(context.CONNECTIVITY_SERVICE);


NetworkInfo[] netInfos = cm.getAllNetworkInfo();


for (NetworkInfo net : netInfos) {


String type = net.getTypeName();


if (type.equalsIgnoreCase("WIFI")) {


if (net.isConnected()) {


hasWifoCon = true;


}


}


}


return hasWifoCon;


}


}


  • Utils


辅助工具类


public class Utils {


private static String CLOSE_BRODECAST_INTENT_ACTION_NAME="com.hdsx.background.locationservice.CloseService";


/**


  • 开始定位


*/


public final static int MSG_LOCATION_START = 0;


/**


  • 定位完成


*/


public final static int MSG_LOCATION_FINISH = 1;


/**


  • 停止定位


*/


public final static int MSG_LOCATION_STOP = 2;


public final static String KEY_URL = "URL";


public final static String URL_H5LOCATION = "file:///android_asset/location.html";


private static SimpleDateFormat sdf = null;


private static NotificationManager mNotificationManager;


private final static String PRIMARY_CHANNEL = "default";


/**


  • 根据定位结果返回定位信息的字符串

  • @param location

  • @return


*/


public synchronized static String getLocationStr(AMapLocation location) {


if (null == location) {


return null;


}


StringBuffer sb = new StringBuffer();


//errCode 等于 0 代表定位成功,其他的为定位失败,具体的可以参照官网定位错误码说明


if (location.getErrorCode() == 0) {


sb.append("定位成功" + "\n");


sb.append("定位类型: " + location.getLocationType() + "\n");


sb.append("经 度 : " + location.getLongitude() + "\n");


sb.append("纬 度 : " + location.getLatitude() + "\n");


sb.append("精 度 : " + location.getAccuracy() + "米" + "\n");


sb.append("提供者 : " + location.getProvider() + "\n");


sb.append("海 拔 : " + location.getAltitude() + "米" + "\n");


sb.append("速 度 : " + location.getSpeed() + "米/秒" + "\n");


sb.append("角 度 : " + location.getBearing() + "\n");


if (location.getProvider().equalsIgnoreCase(


android.location.LocationManager.GPS_PROVIDER)) {


// 以下信息只有提供者是 GPS 时才会有


// 获取当前提供定位服务的卫星个数


sb.append("星 数 : "


  • location.getSatellites() + "\n");


}


//逆地理信息


sb.append("国 家 : " + location.getCountry() + "\n");


sb.append("省 : " + location.getProvince() + "\n");


sb.append("市 : " + location.getCity() + "\n");


sb.append("城市编码 : " + location.getCityCode() + "\n");


sb.append("区 : " + location.getDistrict() + "\n");


sb.append("区域 码 : " + location.getAdCode() + "\n");


sb.append("地 址 : " + location.getAddress() + "\n");


sb.append("兴趣点 : " + location.getPoiName() + "\n");


//定位完成的时间


sb.append("定位时间: " + formatUTC(location.getTime(), "yyyy-MM-dd HH:mm:ss") + "\n");


} else {


//定位失败


sb.append("定位失败" + "\n");


sb.append("错误码:" + location.getErrorCode() + "\n");


sb.append("错误信息:" + location.getErrorInfo() + "\n");


sb.append("错误描述:" + location.getLocationDetail() + "\n");


}


//定位之后的回调时间


sb.append("回调时间: " + formatUTC(System.currentTimeMillis(), "yyyy-MM-dd HH:mm:ss") + "\n");


return sb.toString();


}


public synchronized static String formatUTC(long l, String strPattern) {


if (TextUtils.isEmpty(strPattern)) {


strPattern = "yyyy-MM-dd HH:mm:ss";


}


if (sdf == null) {


try {


sdf = new SimpleDateFormat(strPattern, Locale.CHINA);


} catch (Throwable e) {


}


} else {


sdf.applyPattern(strPattern);


}


return sdf == null ? "NULL" : sdf.format(l);


}


public static Intent getExplicitIntent(Context context, Intent implicitIntent) {


if (context.getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {


return implicitIntent;


}


// Retrieve all services that can match the given intent


PackageManager pm = context.getPackageManager();


List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);


// Make sure only one match was found


if (resolveInfo == null || resolveInfo.size() != 1) {


return null;


}


// Get component info and create ComponentName


ResolveInfo serviceInfo = resolveInfo.get(0);


String packageName = serviceInfo.serviceInfo.packageName;


String className = serviceInfo.serviceInfo.name;


ComponentName component = new ComponentName(packageName, className);


// Create a new intent. Use the old one for extras and such reuse


Intent explicitIntent = new Intent(implicitIntent);


// Set the component to be explicit


explicitIntent.setComponent(component);


return explicitIntent;


}


public static void saveFile(String toSaveString, String fileName, boolean append) {


try {


String sdCardRoot = Environment.getExternalStorageDirectory()


.getAbsolutePath();


File saveFile = new File(sdCardRoot + "/" + fileName);


if (!saveFile.exists()) {


File dir = new File(saveFile.getParent());


dir.mkdirs();


saveFile.createNewFile();


}


FileOutputStream outStream = new FileOutputStream(saveFile, append);


outStream.write(toSaveString.getBytes());


outStream.write("\r\n".getBytes());


outStream.close();


} catch (FileNotFoundException e) {


e.printStackTrace();


} catch (IOException e) {


e.printStackTrace();


}


}


public static Notification buildNotification(Context context) {


Notification notification = null;


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {


NotificationChannel channel = new NotificationChannel(PRIMARY_CHANNEL,


context.getString(R.string.default_channel), NotificationManager.IMPORTANCE_DEFAULT);


channel.setLightColor(Color.GREEN);


channel.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);


getNotificationManager(context).createNotificationChannel(channel);


Notification.Builder builder = new Notification.Builder(context,


PRIMARY_CHANNEL)


.setContentText("水源地轨迹记录中...")


.setSmallIcon(R.drawable.ic_launcher)


.setAutoCancel(true);


notification = builder.build();


} else {


Notification.Builder builder = new Notification.Builder(context);


builder.setSmallIcon(R.drawable.ic_launcher);


builder.setContentText("水源地轨迹记录中..." )


.setWhen(System.currentTimeMillis());


notification = builder.build();


}


return notification;


}


private static NotificationManager getNotificationManager(Context context) {


if (mNotificationManager == null) {


mNotificationManager = (NotificationManager)context.getSystemService(


Context.NOTIFICATION_SERVICE);


}


return mNotificationManager;


}


public static void startWifi(Context context) {


WifiManager wm = (WifiManager) context


.getSystemService(Context.WIFI_SERVICE);


wm.setWifiEnabled(true);


wm.reconnect();


}


public static boolean isWifiEnabled(Context context) {


WifiManager wm = (WifiManager) context


.getSystemService(Context.WIFI_SERVICE);


return wm.isWifiEnabled();


}


public static String getManufacture(Context context) {


return Build.MANUFACTURER;


}


public static Intent getCloseBrodecastIntent() {


return new Intent(CLOSE_BRODECAST_INTENT_ACTION_NAME);


}


public static IntentFilter getCloseServiceFilter() {


return new IntentFilter(CLOSE_BRODECAST_INTENT_ACTION_NAME);


}


public static class CloseServiceReceiver extends BroadcastReceiver {


Service mService;


public CloseServiceReceiver(Service service) {


this.mService = service;


}


@Override


public void onReceive(Context context, Intent intent) {


if (mService == null) {


return;


}


mService.onDestroy();


}


}


}


  • WifiAutoCloseDelegate


接口实现类,回调一下结果


public class WifiAutoCloseDelegate implements IWifiAutoCloseDelegate {


/**


  • 请根据后台数据自行添加。此处只针对小米手机

  • @param context

  • @return


*/


@Override


public boolean isUseful(Context context) {


String manName = Utils.getManufacture(context);


Pattern pattern = Pattern.compile("xiaomi", Pattern.CASE_INSENSITIVE);


Matcher m = pattern.matcher(manName);


return m.find();


}


@Override


public void initOnServiceStarted(Context context) {


LocationStatusManager.getInstance().initStateFromPreference(context);


}


@Override


public void onLocateSuccess(Context context, boolean isScreenOn, boolean isMobileable) {


LocationStatusManager.getInstance().onLocationSuccess(context, isScreenOn, isMobileable);


}


@Override


public void onLocateFail(Context context, int errorCode, boolean isScreenOn, boolean isWifiable) {


//如果屏幕点亮情况下,因为断网失败,则表示不是屏幕点亮造成的断网失败,并修改参照值


if (isScreenOn && errorCode == AMapLocation.ERROR_CODE_FAILURE_CONNECTION && !isWifiable) {


LocationStatusManager.getInstance().resetToInit(context);


return;


}


《Android 学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享


if (!LocationStatusManager.getInstance().isFailOnScreenOff(context, errorCode, isScreenOn, isWifiable)) {


return;


}


PowerManagerUtil.getInstance().wakeUpScreen(context);


}


}


基本就这些, 代码比较多, 有兴趣的朋友可以自行阅读,注解基本都有介绍。记录一下此类问题。


更新:2019-01-03 附张效果图


yes.png

上述图片仅用设备 GPS 定位的。


demo 链接如下:https://download.csdn.net/download/binbinxiaoz/10892551 https://download.csdn.net/download/binbinxiaoz/10890248


测试前,请检查手机 对 app 的电量管理权限以及限制后台运行权限及锁屏后针对 app 的权限。若无可手动更改请忽略。


测试时,请室外测试,室内 gps 信号差。若室内测试请切换别的定位模式,保证可以定位但不保证定位的精准度,本身流量等定位偏差就相当的大。


demo 中只需关注养护巡查模块即可,别的模块练手加的,无关联。


android studio 版本 3.1.4


下载 demo 后,评个分谢谢。


小米 9.0 手机处理:禁止电池优化, 华为手机会因为耗电过强直接终止进程, 别的手机暂时还为测试。推荐接推送唤醒·


Intent intent = new Intent();


String packageName = getActivity().getPackageName();


PowerManager pm = (PowerManager) getActivity().getSystemService(Context.POWER_SERVICE);


if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {


if (pm.isIgnoringBatteryOptimizations(packageName))


intent.setAction(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);


else {

学习福利

【Android 详细知识点思维脑图(技能树)】



其实 Android 开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。


虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。


这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司 19 年的面试题。把技术点整理成了视频和 PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。


由于篇幅有限,这里以图片的形式给大家展示一小部分。



网上学习 Android 的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。


本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

用户头像

嘟嘟侠客

关注

还未添加个人签名 2021.03.19 加入

还未添加个人简介

评论

发布
暂无评论
Android 锁屏无法继续定位问题,android实战教程