標籤:
轉載請標明出處:一片楓葉的專欄
本文我們將講解允許類比位置在Android M下的坑。做地圖類應用的同學應該都知道為了避免軟體類比位置影響正常流程的進行我們一般都會判斷使用者手機是否開啟了類比位置設定,若開啟了則終止使用者流程,提醒使用者關閉類比位置設定。在android系統的開發人員選項中有一個類比位置的選項,其作用是允許使用者通過代碼類比裝置的當前位置,比如地圖類應用需要測試在外地的使用方式,通過開啟此項選項可以通過代碼類比位置,具體可參考我的:Android中的開發人員選項
允許類比位置的設定選項在手機的開發人員選項設定中:
產品實踐:
在我們的產品下單用車中有一個取車的環節,通過手機控制開車門,而這個時候會判斷當前手機是否開啟的類比位置的功能,若開啟則,提示使用者並關閉該類比位置的功能。(若是允許使用者開啟類比位置功能,則惡意使用者可以通過第三方的類比位置App修改手機的定位資訊,進而影響我們App的定位資訊,當需要使用者在中關村還車時,在十裡堡就可以通過類比位置屏蔽這個操作了)
判斷使用者是否開啟類比位置的代碼如下:
/** * 判斷是否開啟了允許虛擬位置,如果開啟了 則彈窗讓他去關閉 */ public static boolean isAllowMockLocation(final Activity context) { /** * 判斷使用者是否開啟了類比位置功能 */ boolean isOpen = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0; if (isOpen) { Config.showTiplDialog(context, null, "定位失敗,需要關閉【允許類比位置】功能後才能使用友友用車查看附近的車輛。", "去設定", new View.OnClickListener() { @Override public void onClick(View view) { context.startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)); } }); } return isOpen; }
在開車門頁面中,點擊開車們按鈕,判斷使用者是否開啟了類比位置開關,若開啟則提示使用者關閉:
這時候點擊去設定按鈕,則會跳轉到開發人員選項中,並允許使用者關閉開發人員選項。
出現的問題:
但是在Android M的機型中判斷邏輯出現了問題,部分三星手機開啟車載模式的話,這時候再次點擊開車門的話,上述代碼會判斷出使用者開啟了類比位置功能,這時候就會阻塞使用者的操作,並指引使用者關閉類比位置開關。但是Android M手機上已經沒有了允許類比位置的設定開關了,取而代之的是選擇類比位置資訊應用設定按鈕。
按道理來說,即便使用者開啟了車載模式這時候通過上述判斷是否開啟類比位置的代碼傳回值應該是false(沒有開啟類比位置),但是這時候用於彈出了定位失敗,需要關閉類比位置的彈窗,說明通過代碼判斷使用者是否開啟了類比位置返回了true。
後來經過排查得知像這種允許類比位置等資訊都是儲存在系統底層的一個資料庫中,而我們的判斷代碼返回了true,則說明使用者底層的允許類比位置資料庫值為true。
但是這時候Android M中由於已經不存在允許類比位置取而代之的是選擇類比位置資訊應用設定,相當於這是兩個設定底層資料庫變數的開關了,而我們的代碼判斷的是允許類比位置的資料庫值,在Android M中並沒有更改允許類比位置的開關,所以這樣就沒辦法更改Android M下的允許類比位置的值了。但是Android M上不是使用了選擇類比資訊應用設定嗎?這又是什麼鬼呢?
在Android M中已經沒有了允許類比位置的開發,取而代之的是:選擇類比位置資訊應用:
在Android M下預設的應用是無法顯示在選擇類比資訊應用中的,需要經過如下的操作才可以:
- 添加debug-AndroidManifest許可權
這樣經過設定之後我們的應用資訊就可以顯示在類比位置中了,其中經過測試當為我們的應用設定了類比位置資訊之後,其只可以影響我們自己應用的定位資訊,而無法影響其他應用的定位資訊。這也算android系統解決的類比位置資訊的bug吧。
允許類比位置的BUG:
在Android M之前如果我們為自己的應用選擇了允許類比位置,則可以通過一個應用的類比位置操作影響其他應用的定位資訊,而這種操作Google認為是不正確的。類比位置資訊的初衷是為了方便App的調試操作,而當這種操作影響其他應用時就可以做一些黑操作了。
比如通過類比位置,在使用滴滴的時候類比位置資訊搶單等等。
所以為瞭解決這個問題,android M中升級了允許類比位置設定,取而代之的是選擇類比位置資訊應用設定,通過設定這個選項,只能影響當前應用,而不能影響其他應用的定位資訊。
比如,這時候我們在通過一些App類比當前手機的定位資訊,這時候就不可以影響滴滴的定位資訊了。
執行Android M下的類比位置操作:
public class RunnableMockLocation implements Runnable { @Override public void run() { try { // 類比位置(addTestProvider成功的前提下) String providerStr = LocationManager.GPS_PROVIDER; Location mockLocation = new Location(providerStr); mockLocation.setLatitude(22); // 維度(度) mockLocation.setLongitude(113); // 經度(度) mockLocation.setAltitude(30); // 高程(米) mockLocation.setBearing(180); // 方向(度) mockLocation.setSpeed(10); //速度(米/秒) mockLocation.setAccuracy(0.1f); // 精度(米) mockLocation.setTime(new Date().getTime()); // 本地時間 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { mockLocation.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos()); } locationManager.setTestProviderLocation(providerStr, mockLocation); } catch (Exception e) { // 防止使用者在軟體運行過程中關閉類比位置或選擇其他應用 stopMockLocation(); } } }
//位置監聽 private LocationListener locationListener=new LocationListener() { /** * 位置資訊變化時觸發 */ public void onLocationChanged(Location location) { double lat = location.getLatitude(); double lot = location.getLongitude(); String str= "Latitude"+lat+"\r\nLongitude:"+lot; textView.setText(str); Log.i(TAG, "時間:"+location.getTime()); Log.i(TAG, "經度:"+location.getLongitude()); Log.i(TAG, "緯度:"+location.getLatitude()); Log.i(TAG, "海拔:"+location.getAltitude()); } /** * GPS狀態變化時觸發 */ public void onStatusChanged(String provider, int status, Bundle extras) { switch (status) { //GPS狀態為可見時 case LocationProvider.AVAILABLE: Log.i(TAG, "當前GPS狀態為可見狀態"); break; //GPS狀態為服務區外時 case LocationProvider.OUT_OF_SERVICE: Log.i(TAG, "當前GPS狀態為服務區外狀態"); break; //GPS狀態為暫停服務時 case LocationProvider.TEMPORARILY_UNAVAILABLE: Log.i(TAG, "當前GPS狀態為暫停服務狀態"); break; } } /** * GPS開啟時觸發 */ public void onProviderEnabled(String provider) { } /** * GPS禁用時觸發 */ public void onProviderDisabled(String provider) { } };
我們開啟我們的其它應用發現其定位資訊並未受到影響。也就是說android M中的選擇類比位置資訊應用與Android M以下的手機中的允許類比位置區別。
允許類比位置與選擇類比位置資訊應用的區別:
而我們的手機中判斷的是允許類比位置開關,在android M中若判斷開啟了這個開關,但是系統已經關閉這個開關的設定作業,所以我們只需要屏蔽這個值即可。
最後的解決方案:
/** * 判斷是否開啟了允許虛擬位置,如果開啟了 則彈窗讓他去關閉 */ public static boolean isAllowMockLocation(final Activity context) { boolean isOpen = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION, 0) != 0; /** * 該判斷API是androidM以下的API,由於Android M中已經沒有了關閉允許類比位置的入口,所以這裡一旦檢測到開啟了類比位置,並且是android M以上,則 * 預設設定為未有開啟類比位置 */ if (isOpen && Build.VERSION.SDK_INT > 22) { isOpen = false; } if (isOpen) { Config.showTiplDialog(context, null, "定位失敗,需要關閉【允許類比位置】功能後才能使用友友用車查看附近的車輛。", "去設定", new View.OnClickListener() { @Override public void onClick(View view) { context.startActivity(new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)); } }); } return isOpen; }
也就是說,當我們判斷出當前裝置開啟允許類比位置時,在判斷一下手機系統的版本,若為Android M以及以上,就屏蔽不管。可能部分同學會問那麼android M上的選擇類比位置資訊應用有影響嗎?答案是否定的,由於我們的App沒有添加允許類比位置的許可權,所以其根本不會出現在選擇類比位置應用列表,進而不會執行類比位置的操作。
所以最終的解決方案就是,檢測裝置是否開啟了類比位置選項,若開啟了,則判斷當前裝置是否為Android M即以上,若是,則屏蔽不管,否則阻塞使用者操作,引導使用者關閉類比位置選項。
Android tips(十)-->允許類比位置在Android M下的坑