需求描述
通过代码控制当前手机GPS定位位置修改,并测试用于其他定位app
分析
android要实现对定位的修改,那么就必须要使用开发者模式中的模拟位置信息,这里必须要引导用户手动选择,然后在通过代码传入需要定位的位置经纬度(可以接入地图等sdk来获取对应点位的经纬度)。已知andorid定位方式有很多种,如gps、wifi、基站等,这里修改的不支持全部渠道,也就是说只能操控一些定位规则不严格的app的定位方式。测试可修改QQ、高德,微信不受影响
权限获取
首先需要在AndroidManifest.xml中静态获取定位权限
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 通过GPS得到精确位置 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 允许程序创建模拟位置提供用于测试 -->
<uses-permission
android:name="android.permission.ACCESS_MOCK_LOCATION"
tools:ignore="MockLocation,ProtectedPermissions" />
然后需要在代码中动态获取权限
通过activity的requestPermissions方法和onRequestPermissionsResult回调来申请Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION定位权限,或者通过其他封装方式。
而ACCESS_MOCK_LOCATION模拟位置信息权限则需要跳转开发者选项页面后用户自行找到模拟位置信息应用后选择当前app,这里是无法监听到用户的选择结果的,所以一定要做好引导,而且如果没有静态申请那么是不会在选择列表中找到当前app的。
修改定位
这里就不接入地图app了,可以自己找一个地址的经纬度直接测试。通过开启服务来进行定位的修改。
需要使用LocationManager进行管理,而且需要一个线程去不停地发布定位,才能保证定位的准确性,在setLocationNetwork和setLocationGPS方法中填写经纬度数据。
public class ServiceGo extends Service {
// 定位相关变量
public static final double DEFAULT_LAT = 36.667662;
public static final double DEFAULT_LNG = 117.027707;
public static final double DEFAULT_ALT = 55.0D;
public static final float DEFAULT_BEA = 0.0F;
//自己对经纬度及各变量进行赋值,可写其他方法也可在这里初始化固定参数
private double mCurLat = DEFAULT_LAT;
private double mCurLng = DEFAULT_LNG;
private double mCurAlt = DEFAULT_ALT;
private float mCurBea = DEFAULT_BEA;
private double mSpeed = 1.2; /* 默认的速度,单位 m/s */
private LocationManager mLocManager;
private HandlerThread mLocHandlerThread;
private Handler mLocHandler;
private boolean isStop = false;
private static final int HANDLER_MSG_ID = 0;
private static final String SERVICE_GO_HANDLER_NAME = "ServiceGoLocation";
@Override
public void onCreate() {
super.onCreate();
mLocManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
removeTestProviderNetwork();
addTestProviderNetwork();
removeTestProviderGPS();
addTestProviderGPS();
initGoLocation();
}
private void removeTestProviderNetwork() {
try {
if (mLocManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
mLocManager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, false);
mLocManager.removeTestProvider(LocationManager.NETWORK_PROVIDER);
}
} catch (Exception e) {
XLog.e("SERVICEGO: ERROR - removeTestProviderNetwork");
}
}
// 注意下面临时添加 @SuppressLint("wrongconstant") 以处理 addTestProvider 参数值的 lint 错误
@SuppressLint("wrongconstant")
private void addTestProviderNetwork() {
try {
// 注意,由于 android api 问题,下面的参数会提示错误(以下参数是通过相关API获取的真实NETWORK参数,不是随便写的)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
mLocManager.addTestProvider(LocationManager.NETWORK_PROVIDER, true, false,
true, true, true, true,
true, ProviderProperties.POWER_USAGE_LOW, ProviderProperties.ACCURACY_COARSE);
} else {
mLocManager.addTestProvider(LocationManager.NETWORK_PROVIDER, true, false,
true, true, true, true,
true, Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
}
if (!mLocManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
mLocManager.setTestProviderEnabled(LocationManager.NETWORK_PROVIDER, true);
}
} catch (SecurityException e) {
XLog.e("SERVICEGO: ERROR - addTestProviderNetwork");
}
}
private void removeTestProviderGPS() {
try {
if (mLocManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
mLocManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, false);
mLocManager.removeTestProvider(LocationManager.GPS_PROVIDER);
}
} catch (Exception e) {
XLog.e("SERVICEGO: ERROR - removeTestProviderGPS");
}
}
// 注意下面临时添加 @SuppressLint("wrongconstant") 以处理 addTestProvider 参数值的 lint 错误
@SuppressLint("wrongconstant")
private void addTestProviderGPS() {
try {
// 注意,由于 android api 问题,下面的参数会提示错误(以下参数是通过相关API获取的真实GPS参数,不是随便写的)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
mLocManager.addTestProvider(LocationManager.GPS_PROVIDER, false, true, false,
false, true, true, true, ProviderProperties.POWER_USAGE_HIGH, ProviderProperties.ACCURACY_FINE);
} else {
mLocManager.addTestProvider(LocationManager.GPS_PROVIDER, false, true, false,
false, true, true, true, Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
}
if (!mLocManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
mLocManager.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true);
}
} catch (Exception e) {
XLog.e("SERVICEGO: ERROR - addTestProviderGPS");
}
}
private void initGoLocation() {
// 创建 HandlerThread 实例,第一个参数是线程的名字
mLocHandlerThread = new HandlerThread(SERVICE_GO_HANDLER_NAME, Process.THREAD_PRIORITY_FOREGROUND);
// 启动 HandlerThread 线程
mLocHandlerThread.start();
// Handler 对象与 HandlerThread 的 Looper 对象的绑定
mLocHandler = new Handler(mLocHandlerThread.getLooper()) {
// 这里的Handler对象可以看作是绑定在HandlerThread子线程中,所以handlerMessage里的操作是在子线程中运行的
@Override
public void handleMessage(@NonNull Message msg) {
try {
Thread.sleep(100);
if (!isStop) {
setLocationNetwork();
setLocationGPS();
sendEmptyMessage(HANDLER_MSG_ID);
}
} catch (InterruptedException e) {
XLog.e("SERVICEGO: ERROR - handleMessage");
Thread.currentThread().interrupt();
}
}
};
mLocHandler.sendEmptyMessage(HANDLER_MSG_ID);
}
private void setLocationNetwork() {
try {
// 尽可能模拟真实的 NETWORK 数据
Location loc = new Location(LocationManager.NETWORK_PROVIDER);
loc.setAccuracy(Criteria.ACCURACY_COARSE); // 设定此位置的估计水平精度,以米为单位。
loc.setAltitude(mCurAlt); // 设置高度,在 WGS 84 参考坐标系中的米
loc.setBearing(mCurBea); // 方向(度)
loc.setLatitude(mCurLat); // 纬度(度)
loc.setLongitude(mCurLng); // 经度(度)
loc.setTime(System.currentTimeMillis()); // 本地时间
loc.setSpeed((float) mSpeed);
loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
mLocManager.setTestProviderLocation(LocationManager.NETWORK_PROVIDER, loc);
} catch (Exception e) {
XLog.e("SERVICEGO: ERROR - setLocationNetwork");
}
}
private void setLocationGPS() {
try {
// 尽可能模拟真实的 GPS 数据
Location loc = new Location(LocationManager.GPS_PROVIDER);
loc.setAccuracy(Criteria.ACCURACY_FINE); // 设定此位置的估计水平精度,以米为单位。
loc.setAltitude(mCurAlt); // 设置高度,在 WGS 84 参考坐标系中的米
loc.setBearing(mCurBea); // 方向(度)
loc.setLatitude(mCurLat); // 纬度(度)
loc.setLongitude(mCurLng); // 经度(度)
loc.setTime(System.currentTimeMillis()); // 本地时间
loc.setSpeed((float) mSpeed);
loc.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
Bundle bundle = new Bundle();
bundle.putInt("satellites", 7);
loc.setExtras(bundle);
mLocManager.setTestProviderLocation(LocationManager.GPS_PROVIDER, loc);
} catch (Exception e) {
XLog.e("SERVICEGO: ERROR - setLocationGPS");
}
}
@Override
public void onDestroy() {
isStop = true;
mLocHandler.removeMessages(HANDLER_MSG_ID);
mLocHandlerThread.quit();
removeTestProviderNetwork();
removeTestProviderGPS();
unregisterReceiver(mActReceiver);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE);
}else {
stopForeground(true);
}
super.onDestroy();
}
}