总结
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
this.onLocationBack = onLocationBack;
this.differenceFlag = differenceFlag;
Log.e(“开始定位”,“开始定位”);
}
public void startLocationTrain(String differenceFlag, OnLocationTrain onLocationTrain) {
init();
mLocationClient.startLocation();//开始
this.onLocationTrain = onLocationTrain;
this.differenceFlag = differenceFlag;
Log.e(“开始定位”,“开始定位”);
}
public void stopLocation() {
if (null == mLocationClient) {
return;
}
mLocationClient.unRegisterLocationListener(this);
mLocationClient.stopLocation();//关闭
mLocationClient.onDestroy();//销毁
mLocationClient = null;
Log.e(“开始定位”,“开始定位”);
}
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
Log.e("定位到当前位置: " , aMapLocation.getAddress());
if (aMapLocation == null) {
onLocationTrain.LocationFail(“定位失败”);
return;
}
if (null != aMapLocation.getCity()
&& !“null”.equals(aMapLocation.getCity())
&& !“”.equals(aMapLocation.getCity())
&& 0 != aMapLocation.getLatitude()) {
cityNameString = aMapLocation.getCity();
latitude = “” + aMapLocation.getLatitude();
longitude = “” + aMapLocation.getLongitude();
saveLocation(aMapLocation);
} else {
onLocationTrain.LocationFail(“定位失败”);
return;
}
}
public interface OnLocationBack {
void back(AMapLocation aMapLocation, String backString);
}
public interface OnLocationTrain {
void back(AMapLocation aMapLocation, String backString);
void LocationFail(String error);
}
private void saveLocation(AMapLocation aMapLocation) {
switch (differenceFlag) {
case NULL:
onLocationBack.back(aMapLocation, “返回的是定位到的所有信息”);
break;
}
}
}
使用 记得把权限加上。
Intent intent = new Intent(“startlocation”); intent.putExtra(“msg”, “定位定位定位”); sendBroadcast(intent);
就这个写法而言,我拿demo测试的时候,小米,oppo,高精度gps都可以,gps需要室外定位,室内没有信号。在适配华为手机的时候,有点坑。华为并不行,只支持亮屏,之后我用的双服务唤醒通讯方式实现的华为手机,适配8.0,但其实我觉得就是因为一个电量管理中保持锁屏后继续运行权限导致的, 但这个我没有测试过开了权限这个方式行不行,小伙伴测试的时候,可以给我反馈一下,谢谢。
二、AlarmManager
这个我一开始看的时候,觉得不错,设置重复的闹钟唤醒服务,让服务持续。闹钟设置重复定时的方法变化还是蛮大的,阅读官方api的时候,不同的版本基本都有不同的设置方式。
使用的服务:
public class PollingService extends Service { public static final String ACTION = “com.hdsx.service.PollingService”; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Log.e(TAG, "onCreate:onCreate "); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG, “Service:onStartCommand “); return super.onStartCommand(intent, flags, startId); } @Override public void onStart(Intent intent, int startId) { new PollingThread().start(); } int count = 0; class PollingThread extends Thread { @Override public void run() { count ++; Log.e(“polling”, count+”” ); } } @Override public void onDestroy() { super.onDestroy(); startService(new Intent(PollingService.this,PollingService.class)); Log.e(TAG, "Service:onDestroy "); }}注册
服务里面起个线程,就是为了测试是否能持续的log输出日志。这块提醒一下,调试状态的时候手机是处于充电状态的,cpu不会休眠,所以会一直log,不过经过我后面添加代码测试,是可行的。我这块只是提供了原始的demo,后面的加的代码找不到了。
闹钟:
public static void startPollingService(Context context, int seconds, Class<?> cls,String action) { long triggerAtTime = SystemClock.elapsedRealtime(); 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.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, seconds, pendingIntent); }使用PollingUtils.startPollingService(MainActivity.this, 2000, PollingService.class, PollingService.ACTION);
这种方式,也可以实现, 但是测试的过程中有个问题,就是闹钟重复定时它的间隔有时候并不能完全按照我设置的时间来运行,周期感觉有点不太确定,而且大多时间返回间隔较长。最主要的是,这个我华为手机并不行。。。。。
三、JobScheduler执行任务调度保活
JobScheduler
它的宗旨是把一些不是特别紧急的任务放到更合适的时机批量处理,这样可以有效的节省电量。这个我自己也写了个demo啥的测试,应该是我写的有问题,也可能是别的,我觉得这个不太适合定位这个事,最终没有实现。有实现的小伙伴可以分享一下,谢谢。
四、WorkManager
一开始看这个的时候,我表示这个的描述有点牛X而且还是新特性
不管是断网,还是将进程杀死,他都会执行。有一些需求用这个来实现是相当的不错,比如 手机端向服务器请求数据,当没有网络时,不要请求,有网络时自动恢复请求。如果网络处于断开状态,则将请求保存在队列当中,监听网络状态,一旦网络连接上,则将队列当中的请求一个一个的执行。很强,主要是这个可以设置重复的任务,我主要看这块。
简单介绍:WorkManager 可以轻松地让异步任务延迟执行以及何时运行它们,API需要我们创建个worker任务并将其交给WorkManager,用来控制在什么时间运行这个任务。
.setRequiredNetworkType(NetworkType.CONNECTED) // 网络状态
.setRequiresBatteryNotLow(true) // 不在电量不足时执行
.setRequiresCharging(true) // 在充电时执行
.setRequiresStorageNotLow(true) // 不在存储容量不足时执行
.setRequiresDeviceIdle(true) // 在待机状态下执行,需要 API 23
我就不做深究了, 有兴趣的朋友可以自己研究, 最终我没有用这个来实现我的需求,因为重复的任务执行时间间隔最少15分钟。
五、实现了的方式。双服务唤醒
经过测试,小米,oppo,华为等手机 6.0 - 8.0版本没问题,可完美实现。
先把AIDL写了:
interface ILocationHelperServiceAIDL {
/**
-
定位service绑定完毕后通知helperservice自己绑定的notiId
-
@param notiId 定位service的notiId
*/
void onFinishBind(int notiId);
}
interface ILocationServiceAIDL {
/** 当其他服务已经绑定时调起 */
void onFinishBind();
}
一共两个。定义的回调接口,也先写了。
/**
- 代理类,用于处理息屏造成wifi被关掉时再重新点亮屏幕的逻辑
*/
public interface IWifiAutoCloseDelegate {
/**
-
判断在该机型下此逻辑是否有效。目前已知的系统是小米系统存在(用户自助设置的)息屏断掉wifi的功能。
-
@param context
-
@return
*/
boolean isUseful(Context context);
/**
-
点亮屏幕的服务有可能被重启。此处进行初始化
-
@param context
-
@return
*/
void initOnServiceStarted(Context context);
/**
- 定位成功时,如果移动网络无法访问,而且屏幕是点亮状态,则对状态进行保存
*/
void onLocateSuccess(Context context, boolean isScreenOn, boolean isMobileable);
/**
- 对定位失败情况的处理
*/
void onLocateFail(Context context, int errorCode, boolean isScreenOn, boolean isWifiable);
}
- 定位服务
/**
-
后台服务定位
-
- 只有在由息屏造成的网络断开造成的定位失败时才点亮屏幕
-
- 利用notification机制增加进程优先级
*/
public class LocationService extends NotificationService {
private AMapLocationClient mLocationClient;
private AMapLocationClientOption mLocationOption;
/**
- 处理息屏关掉wifi的delegate类
*/
private IWifiAutoCloseDelegate mWifiAutoCloseDelegate = new WifiAutoCloseDelegate();
/**
- 记录是否需要对息屏关掉wifi的情况进行处理
*/
private boolean mIsWifiCloseable = false;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
applyNotiKeepMech(); //开启利用notification提高进程优先级的机制
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;
最后
总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。
在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
.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;
最后
总而言之,Android开发行业变化太快,作为技术人员就要保持终生学习的态度,让学习力成为核心竞争力,所谓“活到老学到老”只有不断的学习,不断的提升自己,才能跟紧行业的步伐,才能不被时代所淘汰。
在这里我分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司20年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
[外链图片转存中…(img-Fhf7QF25-1715116448991)]
[外链图片转存中…(img-yo2KqQv2-1715116448992)]
[外链图片转存中…(img-HAWlef14-1715116448992)]
还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!