編輯:關於Android編程
本文我們將講解允許模擬位置在Android M下的坑。做地圖類應用的同學應該都知道為了避免軟件模擬位置影響正常流程的進行我們一般都會判斷用戶手機是否打開了模擬位置設置,若打開了則終止用戶流程,提醒用戶關閉模擬位置設置。在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下的模擬位置操作:
在debug-AndroidManifest中添加模擬位置的權限權限
在開發者選項,選擇模擬位置信息應用中,選擇自身App
通過代碼模擬位置
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) { } };查看其它App的定位信息是否收到了影響
我們打開我們的其它應用發現其定位信息並未受到影響。也就是說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即以上,若是,則屏蔽不管,否則阻塞用戶操作,引導用戶關閉模擬位置選項。
今天這篇文章,我們來看看Canvas。Canvas 是畫布,來響應繪畫(Draw)的調用(並將其寫入Btmap)。我們先看看官方文檔對Canvas的描述:The Canv
前面講到Vitamio可以支持一些流媒體,在這裡就用Vitamio來播放網絡上的一些流媒體,如:mms、rtsp、http,參考前輩的一些文章來寫一個網絡收音機程序,對於
一、前言在Android客戶端開發中,使用網絡請求是非常常見的事情,一般我們使用HttpURLConnection是可以滿足需求的,不過隨著業務邏輯復雜,依然還是有很多不
首先需要有網絡權限,然後我們這裡匹配的網絡請求是之前封裝好的Okhttp。非常的簡單方便,直接復制進去,依賴一下包,然後調用方法即可。 這裡是把圖片轉換成Base64.d