Markdown版本筆記 | 個人GitHub首頁 | 個人博客 | 個人微信 | 個人郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
參考:
容許模擬位置在Android M下的坑
Android手機模擬GPS位置
android 模擬位置信息Location使用示例
參考這個工程java
不少文章中說,M以上選擇模擬位置信息應用只能影響當前應用的定位信息,實際測試後發現這個結論是徹底錯誤的,這個定位結果是能夠影響全部APP的。android
開啓模擬位置的方法git
須要的權限github
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
判斷是否開啓了系統模擬位置
Android 6.0 如下:使用Settings.Secure.ALLOW_MOCK_LOCATION
判斷。微信
boolean canMockPosition = Settings.Secure.getInt(getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0
Android 6.0 及以上:沒有【容許模擬位置】選項,同時棄用了Settings.Secure.ALLOW_MOCK_LOCATION
,沒法經過上面的方法判斷。增長了【選擇模擬位置信息應用】的方法,須要選擇使用模擬位置的應用。可是不知道怎麼獲取當前選擇的應用。app
開始、中止模擬位置dom
manager.setTestProviderLocation(PROVDER_NAME, location);// 添加並啓動GpsMockProvider manager.removeTestProvider(PROVDER_NAME);//移除GpsMockProvider
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/> <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
public class SimpleMockActivity extends ListActivity { private static final String CHHJ = "30.557622,114.337485,0,0";//楚河漢街【30.557622,114.337485,0,0】 protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] array = {"startService", "stopService", "打開GPS設置", "進入測試頁面",}; setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array)); String[] permissions = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}; ActivityCompat.requestPermissions(this, permissions, 0); } @Override protected void onDestroy() { super.onDestroy(); stopService(new Intent(this, MockIntentService.class)); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { switch (position) { case 0: Intent intent = new Intent(this, MockIntentService.class); intent.putExtra("location", Utils.parseLocation(LocationManager.GPS_PROVIDER, CHHJ.split(","))); String name = "任務" + new Random().nextInt(100); intent.putExtra("name", name); startService(intent); Log.i("bqt", "【startService】" + name); break; case 1: stopService(new Intent(this, MockIntentService.class)); break; case 2: startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));//打開GPS設置 break; case 3: stopService(new Intent(this, MockIntentService.class)); startActivity(new Intent(this, MockTestActivity.class)); break; } } }
public class MockIntentService extends IntentService { private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER; private LocationManager manager; private boolean hasAddTestProvider; private boolean stop; private boolean hasNewIntent; public MockIntentService() { super("【MockIntentService】"); } @Override public void onCreate() { super.onCreate(); Log.i("bqt", "【onCreate】"); manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //位置服務 if (!manager.isProviderEnabled(PROVDER_NAME)) { //Returns the current enabled/disabled status of the given provider. Log.i("bqt", "【準備啓用Gps Provider】"); //false, false, false, false, true, false, false, 0, 5 或 false, false, false, false, true, true, true, 0, 5 manager.addTestProvider(PROVDER_NAME, true, true, false, false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_FINE); manager.setTestProviderEnabled(PROVDER_NAME, true); hasAddTestProvider = true; } } @Override public int onStartCommand(@Nullable Intent intent, int flags, int startId) { Log.i("bqt", "【onStartCommand】"); hasNewIntent = true;//先把舊的任務停掉,若是不停掉的話,onHandleIntent是不會執行的 return super.onStartCommand(intent, flags, startId); } @Override protected void onHandleIntent(@Nullable Intent intent) { Log.i("bqt", "【onHandleIntent】"); hasNewIntent = false;//執行到這裏代表前一個任務已經執行完畢了 if (intent != null && intent.hasExtra("location")) { Location location = intent.getParcelableExtra("location"); String name = intent.getStringExtra("name"); if (location != null) { while (!hasNewIntent && !stop) {//若是隻在有限的時間內發一次,會致使其餘APP獲取不到定位信息,必須持續刷新 location.setTime(System.currentTimeMillis()); //當前時間 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); } Log.i("bqt", name + "【模擬位置】經度 " + location.getLongitude() + ",維度 " + location.getLatitude()); manager.setTestProviderLocation(PROVDER_NAME, location);// 添加並啓動GpsMockProvider SystemClock.sleep(1000); Log.i("bqt", "臥槽,若是不加這一行日誌,會致使休眠時間不肯定!");//絕對不能刪 } Log.i("bqt", "【任務結束】"); } } } @Override public void onDestroy() { super.onDestroy(); Log.i("bqt", "【onDestroy】"); if (hasAddTestProvider) { try { manager.clearTestProviderEnabled(PROVDER_NAME); manager.removeTestProvider(PROVDER_NAME);//若未成功addTestProvider,或者系統模擬位置已關閉則會出錯 } catch (Exception e) { e.printStackTrace(); } hasAddTestProvider = false; } stop = true; } }
public class MockTestActivity extends ListActivity implements ServiceConnection, LocationListener { private Location currentLocation; private IMyBinder mIBinder; private TextView textView; private EditText editText; private LocationManager manager; private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER; private boolean stop; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); String[] array = {"獲取當前位置", "打開開發者模式,請在【選擇模擬位置信息應用】中選擇本應用", "bindService", "unbindService", "模擬步行軌跡,請打開高德地圖APP查看效果", "模擬當前位置,模擬的當前位置多是模擬位置", "模擬輸入的位置", "模擬在輸入位置四周亂轉",}; setListAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, array)); editText = new EditText(this); editText.setText("30.557622,114.337485,0,0"); getListView().addFooterView(editText); textView = new TextView(this); textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); textView.setTextColor(Color.BLUE); getListView().addFooterView(textView); manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); //模擬位置服務 requestLocationUpdates(); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { switch (position) { case 0://獲取當前位置 Location location = Utils.getCurrentLocatioon(this);//經度 114.337675,維度 30.558067 if (location != null) { textView.setText(String.format("【當前位置】經度 %s,維度 %s\n", location.getLongitude(), location.getLatitude())); currentLocation = location; } break; case 1://打開開發者模式 startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)); break; case 2: bindService(new Intent(this, MockService.class), this, Context.BIND_AUTO_CREATE); break; case 3: if (mIBinder != null) { unbindService(this); mIBinder = null; } break; case 4://模擬步行 //百度地圖和騰訊地圖都屏蔽了模擬位置,可是若是你的應用中集成了這些地圖SDK,在你不明確禁用的狀況下,仍是能使用的 if (mIBinder != null) mIBinder.mockMovingLocation(); else Toast.makeText(this, "服務未開啓", Toast.LENGTH_SHORT).show(); break; case 5://模擬當前位置 if (mIBinder != null) { if (currentLocation != null) mIBinder.mockFixLocation(currentLocation); else Toast.makeText(this, "獲取位置失敗", Toast.LENGTH_SHORT).show(); } else Toast.makeText(this, "服務未開啓", Toast.LENGTH_SHORT).show(); break; case 6: if (mIBinder != null) { mIBinder.mockFixLocation(Utils.parseLocation(PROVDER_NAME, editText.getText().toString().split(","))); } else Toast.makeText(this, "服務未開啓", Toast.LENGTH_SHORT).show(); break; case 7: mockCircle(); break; } } private void mockCircle() { if (mIBinder != null) { new Thread(() -> { stop = !stop; while (stop) { String[] datas = editText.getText().toString().split(","); double lat = Double.parseDouble(datas[0]); double lon = Double.parseDouble(datas[1]); Location location = new Location(PROVDER_NAME);//the name of the provider that generated this location location.setLatitude(lat - 0.001 + 0.002 * new Random().nextDouble());// 維度(度) location.setLongitude(lon - 0.001 + 0.002 * new Random().nextDouble());// 經度(度) location.setAccuracy(20f); // 精度(米) SystemClock.sleep(1000); mIBinder.mockFixLocationOnce(location); } }).start(); } else Toast.makeText(this, "服務未開啓", Toast.LENGTH_SHORT).show(); } private void requestLocationUpdates() { if (manager.isProviderEnabled(PROVDER_NAME)) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "沒有權限", Toast.LENGTH_SHORT).show(); return; } manager.requestLocationUpdates(PROVDER_NAME, 0, 0, this); } else { Toast.makeText(this, "Gps Provider 不可用", Toast.LENGTH_SHORT).show(); } } //region ServiceConnection @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i("bqt", "【onServiceConnected】" + name.getClassName()); mIBinder = (IMyBinder) service; } @Override public void onServiceDisconnected(ComponentName name) { Log.i("bqt", "【onServiceDisconnected】" + name.getClassName()); } //endregion //region LocationListener @Override public void onLocationChanged(Location location) { textView.append("經度 " + location.getLongitude() + ",維度 " + location.getLatitude() + ",速度" + location.getSpeed() + ",方向" + location.getBearing() + "\n"); } @Override public void onStatusChanged(String provider, int status, Bundle extras) { Log.i("bqt", "【onStatusChanged】" + provider + "," + status); } @Override public void onProviderEnabled(String provider) { Log.i("bqt", "【onProviderEnabled】" + provider); } @Override public void onProviderDisabled(String provider) { Log.i("bqt", "【onProviderDisabled】" + provider); } //endregion }
public class MockService extends Service { private static final String PROVDER_NAME = LocationManager.GPS_PROVIDER; private LocationManager manager; private ScheduledExecutorService executor;//定時修改位置信息 private boolean hasAddTestProvider; @Override public void onCreate() { super.onCreate(); manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); if (!manager.isProviderEnabled(PROVDER_NAME)) { manager.addTestProvider(PROVDER_NAME, true, true, false, false, true, true, true, Criteria.POWER_LOW, Criteria.ACCURACY_FINE); manager.setTestProviderEnabled(PROVDER_NAME, true); } } @Override public IBinder onBind(Intent intent) { return new MyMyBinder(); } @Override public void onDestroy() { super.onDestroy(); stopExecutor(); if (hasAddTestProvider) { try { manager.clearTestProviderEnabled(PROVDER_NAME); manager.removeTestProvider(PROVDER_NAME);//若未成功addTestProvider,或者系統模擬位置已關閉則會出錯 } catch (Exception e) { e.printStackTrace(); } hasAddTestProvider = false; } } private class MyMyBinder extends Binder implements IMyBinder { @Override public void stopMockLocation() { stopExecutor(); } @Override public void mockMovingLocation() { stopExecutor(); executor = Executors.newScheduledThreadPool(2); ConcurrentLinkedQueue<Location> locations = Utils.getGaodeTestLocations(MockService.this, PROVDER_NAME); executor.scheduleWithFixedDelay(() -> mockTask(locations.poll()), 1, 1, TimeUnit.SECONDS); } @Override public void mockFixLocation(Location location) { stopExecutor(); executor = Executors.newScheduledThreadPool(2); executor.scheduleWithFixedDelay(() -> mockTask(location), 1, 1, TimeUnit.SECONDS); } @Override public void mockFixLocationOnce(Location location) { mockTask(location); } private void mockTask(Location location) { if (location != null) { location.setTime(System.currentTimeMillis()); //當前時間 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); } Log.i("bqt", "【模擬位置】經度 " + location.getLongitude() + ",維度 " + location.getLatitude()); manager.setTestProviderLocation(PROVDER_NAME, location);// 添加並啓動GpsMockProvider } } } private void stopExecutor() { if (executor != null && !executor.isShutdown()) { executor.shutdownNow(); } } }
public interface IMyBinder { void stopMockLocation(); void mockMovingLocation(); void mockFixLocation(Location location); void mockFixLocationOnce(Location location); }
public class Utils { private static double PI = 3.14159265358979324;//圓周率 GCJ_02_To_WGS_84 public static ConcurrentLinkedQueue<Location> getGaodeTestLocations(Context context, String name) { ConcurrentLinkedQueue<Location> locations = new ConcurrentLinkedQueue<>(); try { InputStreamReader reader = new InputStreamReader(context.getResources().getAssets().open("test_tji.csv")); BufferedReader bufferedReader = new BufferedReader(reader); String line; while ((line = bufferedReader.readLine()) != null) { String[] datas = line.split(","); Location location = Utils.initGaodeLocaton(name, datas); locations.add(location); } reader.close(); } catch (IOException e) { e.printStackTrace(); } return locations; } private static Location initGaodeLocaton(String name, String[] datas) { Location location = new Location(name);//the name of the provider that generated this location double latitude = Double.parseDouble(datas[0]); double longitude = Double.parseDouble(datas[1]); double[] transformLocation = Utils.delta(latitude, longitude); location.setLatitude(transformLocation[0]);// 維度(度) location.setLongitude(transformLocation[1]);// 經度(度) location.setSpeed(Float.parseFloat(datas[2]) / 3.6f); //速度(米/秒) location.setBearing(Float.parseFloat(datas[3]));// 方向(度) location.setAltitude(30); // 海拔(米) location.setAccuracy(20f); // 精度(米) return location; } //此方法能夠將高德地圖SDK獲取到的GPS經緯度轉換爲真實的經緯度,能夠用於解決安卓系統使用高德SDK獲取經緯度的轉換問題。 private static double[] delta(double lat, double lon) { double a = 6378245.0;//克拉索夫斯基橢球參數長半軸a double ee = 0.00669342162296594323;//克拉索夫斯基橢球參數第一偏愛率平方 double dLat = transformLat(lon - 105.0, lat - 35.0); double dLon = transformLon(lon - 105.0, lat - 35.0); double radLat = lat / 180.0 * PI; double magic = Math.sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = Math.sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * PI); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * PI); return new double[]{lat - dLat, lon - dLon}; } //轉換經度 private static double transformLon(double x, double y) { double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0; ret += (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0; return ret; } //轉換緯度 private static double transformLat(double x, double y) { double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)); ret += (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0; ret += (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0; ret += (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0; return ret; } public static Location getCurrentLocatioon(Context context) { if (ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { return null; } LocationManager manager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); //模擬位置服務 List<String> prodiverlist = manager.getProviders(true); if (prodiverlist == null || prodiverlist.size() == 0) { Toast.makeText(context, "沒有可用的位置提供器", Toast.LENGTH_SHORT).show(); return null; } Log.i("bqt", "【可用位置服務】" + prodiverlist); //[passive, network, GpsMockProvider, gps] Location location; for (String prodiver : prodiverlist) { location = manager.getLastKnownLocation(prodiver); if (location != null) { //經度 114.337675,維度 30.558067,速度 0.0,方向 0.0 Log.i("bqt", "【當前位置】類型 " + location.getProvider() + ",經度 " + location.getLongitude() + ",維度 " + location.getLatitude() + ",速度 " + location.getSpeed() + ",方向 " + location.getBearing()); return location; } } Toast.makeText(context, "獲取位置失敗", Toast.LENGTH_SHORT).show(); return null; } public static Location parseLocation(String provider, String[] datas) { Location location = new Location(provider);//the name of the provider that generated this location location.setLatitude(Double.parseDouble(datas[0]));// 維度(度) location.setLongitude(Double.parseDouble(datas[1]));// 經度(度) location.setSpeed(Float.parseFloat(datas[2])); //速度(米/秒) location.setBearing(Float.parseFloat(datas[3]));// 方向(度) location.setAltitude(30); // 海拔(米) location.setAccuracy(20f); // 精度(米) Bundle bundle = new Bundle(); bundle.putString("info", "來自包青天模擬的位置"); location.setExtras(bundle); //額外的信息 return location; } }
2019-4-11ide