Android 锁屏无法继续定位问题(1)

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

}

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 {

intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);

intent.setData(Uri.parse(“package:” + packageName));

}

}

getActivity().startActivity(intent);

文章最后我给大家推荐一个最简单也是最有效的学习提升方法:脑图 + 视频 + 资料

在这也分享一份自己收录整理的阿里P6P7【安卓】进阶资料分享+加薪跳槽必备面试题 ,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

如果你有需要的话,可以点赞+评论关注我加Vx:q1607947758(备注简书,需要进阶资料)



自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

Android高级架构师

由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。

  • 330页PDF Android学习核心笔记(内含上面8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT部分大厂面试题(有解析)

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

XV0by1vcmllbnQvc3RyaXB8aW1hZ2VWaWV3Mi8yL3cvNDc1L2Zvcm1hdC93ZWJw?x-oss-process=image/format,png)

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-XanZ7Emn-1711773229171)]

[外链图片转存中…(img-kLQKPcuh-1711773229172)]

[外链图片转存中…(img-kDpOht3h-1711773229172)]

[外链图片转存中…(img-wr55ljwo-1711773229172)]

[外链图片转存中…(img-pJ51olMU-1711773229172)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

Android高级架构师

由于篇幅问题,我呢也将自己当前所在技术领域的各项知识点、工具、框架等汇总成一份技术路线图,还有一些架构进阶视频、全套学习PDF文件、面试文档、源码笔记。

  • 330页PDF Android学习核心笔记(内含上面8大板块)

[外链图片转存中…(img-0oKWzAnZ-1711773229173)]

[外链图片转存中…(img-E0jO2UGi-1711773229173)]

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

[外链图片转存中…(img-g45JJNbG-1711773229173)]

  • Android BAT部分大厂面试题(有解析)

[外链图片转存中…(img-64aclbGh-1711773229173)]

好了,以上便是今天的分享,希望为各位朋友后续的学习提供方便。觉得内容不错,也欢迎多多分享给身边的朋友哈。

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 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() { Loca
Android锁屏无法继续定位的问题可能是由于以下原因导致的: 1. 系统设置限制:Android系统在锁屏状态下会对某些功能进行限制,包括定位服务。这是为了保护用户隐私和降低耗电量。如果发现无法继续定位,可以进入系统设置中的"安全与隐私"或"锁屏与密码"等选项,检查是否有相关的设置限制。如果有,可以尝试解除限制或者调整相关设置。 2. 电池优化:Android系统的电池优化功能会对一些应用进行限制,限制它们在后台运行的能力,从而可能影响定位服务的正常运行。可以在系统设置中找到"电池"或"电池优化"选项,检查是否有对定位服务的限制。如果有,可以尝试将相关应用排除在电池优化之外或调整相关设置。 3. 应用权限:某些应用在获取定位信息时需要用户授权,但如果用户在锁屏状态下无法授权,就会导致无法继续定位。可以在系统设置中找到"应用管理"或"应用权限"选项,检查相关应用的定位权限是否被禁用或限制。如果有,可以尝试启用相应的权限。 4. 第三方应用冲突:某些第三方应用可能会对系统定位服务产生冲突,从而导致无法继续定位。可以尝试关闭或卸载一些可能与定位服务冲突的应用,然后重新尝试定位。 以上是Android锁屏无法继续定位的可能原因和解决方法,希望能对你有帮助。如果问题仍然存在,建议联系相关厂商或社区寻求更专业的技术支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值