网上搜了半天,试了很多方法,大同小异,当然报错也是大同小异……
最经典的报错就是 IllegalArgumentException: Provider "gps" already exists
这里介绍一下如何解决这个问题,并且给出正确的模拟定位方法。
首先当然是先在AndroidManifest里申请权限了
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"
tools:ignore="MockLocation,ProtectedPermissions"/>
在Android 10里定位是危险权限,如果不是在编译系统的时候打包的apk,就需要动态申请权限:
private void initPermissions(Context context) {
RequestPermissions(context, "android.permission.ACCESS_FINE_LOCATION");
RequestPermissions(context, "android.permission.ACCESS_COARSE_LOCATION");
RequestPermissions(context,"android.permission.ACCESS_MOCK_LOCATION");
}
public static boolean RequestPermissions(Context context, String permission) {
if (ContextCompat.checkSelfPermission(context, permission) != PackageManager.PERMISSION_GRANTED) {
Log.i("requestMyPermissions", ": 【 " + permission + " 】没有授权,申请权限");
ActivityCompat.requestPermissions((Activity) context, new String[]{permission}, 100);
return false;
} else {
Log.i("requestMyPermissions", ": 【 " + permission + " 】有权限");
return true;
}
}
然后就是获取LocationManager、构建location 注意location参数必须设满,否则会报不完整错误:
java.lang.IllegalArgumentException: Incomplete location object
mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
Location mockLocation = new Location(GPS_PROVIDER);
mockLocation.setLatitude(116.397128);
mockLocation.setLongitude(39.916527);
mockLocation.setAltitude(0);
mockLocation.setTime(System.currentTimeMillis());
mockLocation.setSpeed(0.01f);
mockLocation.setBearing(1f);
mockLocation.setAccuracy(3f);
mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
mockLocation.setBearingAccuracyDegrees(0.1f);
mockLocation.setVerticalAccuracyMeters(0.1f);
mockLocation.setSpeedAccuracyMetersPerSecond(0.01f);
}
下一步就是直接mock location了:
这里需要在addTestProvider之前remove一次,否则会报 Provider "gps" already exists 。
源码:frameworks/base/services/core/java/com/android/server/LocationManagerService.java
@Override
public void addTestProvider(String name, ProviderProperties properties, String opPackageName) {
if (!canCallerAccessMockLocation(opPackageName)) {
return;
}
if (PASSIVE_PROVIDER.equals(name)) {
throw new IllegalArgumentException("Cannot mock the passive location provider");
}
synchronized (mLock) {
long identity = Binder.clearCallingIdentity();
try {
LocationProvider oldProvider = getLocationProviderLocked(name);
if (oldProvider != null) {
if (oldProvider.isMock()) {
throw new IllegalArgumentException(
"Provider \"" + name + "\" already exists");
}
removeProviderLocked(oldProvider);
}
mock代码:
public void mockGps(Location location) throws SecurityException {
location.setProvider(GPS_PROVIDER);
try{
// @throws IllegalArgumentException if a provider with the given name already exists
mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
} catch (IllegalArgumentException ignored){}
try{
// @throws IllegalArgumentException if no provider with the given name exists
mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
} catch (IllegalArgumentException ignored){
mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
}
try{
// @throws IllegalArgumentException if no provider with the given name exists
mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
} catch (IllegalArgumentException ignored){
// IllegalArgumentException: Provider "gps" already exists
mLocationManager.removeTestProvider(GPS_PROVIDER);
mLocationManager.addTestProvider(GPS_PROVIDER, false, false, false, false, false, true, true, 0, 5);
mLocationManager.setTestProviderEnabled(GPS_PROVIDER, true);
mLocationManager.setTestProviderLocation(GPS_PROVIDER, location);
}
}
如此一来就模拟成功了,当然安装apk成功后,记得在开发者模式里设置 “选择模拟位置信息位置 ”为当前app。