Android中如何通過程序打開或關閉GPS

手機應用中最酷的可能就是位置服務相關的了,如何讀取GPS信息,在官方文檔上有相當詳細的說明,後面如果有機會,我也會專門寫例子來介紹(教程已完成,請參見:教程:實現Android的不同精度的定位(基於網絡和GPS))。但今天,我們先來看下如何以編程的方式來開啓關閉GPS
官方的API中,android.provider.Settings.Secure類有2個靜態方法:
public static final void setLocationProviderEnabled (ContentResolver cr, String provider, boolean enabled)

public static final boolean isLocationProviderEnabled (ContentResolver cr, String provider)
不過遺憾的是,這2個方法都註明了從API Level 8(即Android 2.2)纔開始提供,那麼在2.2之前又該如何編程實現GPS的開關呢?

山重水複疑無路

首先,我們要知道,Android系統的設置畫面中就可以進行GPS的開關,那麼它是如何實現的呢?
由於我的機器上的android source是2.3版本的,所以直接啓動了一個2.1的模擬器,用adb pull將Settings.apk抓下來,反編譯之後,在SecuritySettings類中找到如下代碼:

package,com.android.settings.SecuritySettings.java

[java] view plaincopy
  1. CheckBoxPreference localCheckBoxPreference3 = this.mGps;  
  2.       if (paramPreference == localCheckBoxPreference3)  
  3.       {  
  4.         ContentResolver localContentResolver3 = getContentResolver();  
  5.         boolean bool6 = this.mGps.isChecked();  
  6.         Settings.Secure.setLocationProviderEnabled(localContentResolver3, "gps", bool6);  
  7.         continue;  
  8.       }  

可以看到2.1系統中已經存在有Settings.Secure.setLocationProviderEnabled方法了,只是該方法沒有開放而已,事實上讀過Android源碼的人都對/*hide*/很反感吧,看得到,摸不到!

既然Setting畫面中的用法,我們不能使用,那麼再換1種方法,我們去看一下Settings.Secure.setLocationProviderEnabled的寫法,然後直接套用。
這次,我們直接去看Android 2.3的源碼,找到Setting.java之後,找到相關的方法,代碼如下:
core, android.provider.Setting.java

[java] view plaincopy
  1. public static final void setLocationProviderEnabled(ContentResolver cr,  
  2.                 String provider, boolean enabled) {  
  3.             // to ensure thread safety, we write the provider name with a '+' or '-'  
  4.             // and let the SettingsProvider handle it rather than reading and modifying  
  5.             // the list of enabled providers.  
  6.             if (enabled) {  
  7.                 provider = "+" + provider;  
  8.             } else {  
  9.                 provider = "-" + provider;  
  10.             }  
  11.             putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider);  
  12.         }  

原來這個方法只是1個包裝,事實上調用的還是Settings.Secure中的putString方法,我們直接借用過來:
在自己的onClick事件中寫上
Settings.Secure.putString(getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED, “network,gps”);
然後執行,WOW,發生了什麼,需要android.permission.WRITE_SETTINGS權限?在Manifest文件中加上,再運行,還是出錯,不過這次需要的是android.permission.WRITE_SECURE_SETTINGS,再次加上。
滿懷希望的再次運行,結果還是一樣的問題:
java.lang.SecurityException: Permission denial: writing to secure settings requires android.permission.WRITE_SECURE_SETTINGS
看來,Google封死了直接調用Settings的路了,事實上我又試着使用反射來直接調用setLocationProviderEnabled方法,結果也是一樣的告訴我需要權限

柳暗花明又一村

難道沒有別的辦法了嗎?那些2.1中可以運行的App Widget是如何做到的呢?
再次檢視2.1的Settings.apk中的代碼,發現有1個widget包,裏面有1個類叫SettingsAppWidgetProvider,這就是用來提供App Widget的類,繼續研究這裏的代碼,發現構造RemoteView的代碼中用到如下方法:

[java] view plaincopy
  1. private static PendingIntent getLaunchPendingIntent(Context paramContext, int paramInt1, int paramInt2)  
  2.   {  
  3.     Intent localIntent1 = new Intent();  
  4.     Intent localIntent2 = localIntent1.setClass(paramContext, SettingsAppWidgetProvider.class);  
  5.     Intent localIntent3 = localIntent1.addCategory("android.intent.category.ALTERNATIVE");  
  6.     Uri localUri = Uri.parse("custom:" + paramInt2);  
  7.     Intent localIntent4 = localIntent1.setData(localUri);  
  8.     return PendingIntent.getBroadcast(paramContext, 0, localIntent1, 0);  
  9.   }  

由於這是反編譯的結果,略微有點混亂,但還是可以看出思路,目的是通過PendingIntent來扔出1個Intent,接受者是SettingsAppWidgetProvider.class,接受的參數有2個,1個是Category:SettingsAppWidgetProvider.class(正是這個類自身),另1個是Data:Uri.parse(“custom:” + paramInt2),這個paramInt2是Widget中的圖標按鈕的序號。所有的序號在類的首部都有定義:

[java] view plaincopy
  1. private static final int BUTTON_BLUETOOTH = 4;  
  2.   private static final int BUTTON_BRIGHTNESS = 1;  
  3.   private static final int BUTTON_GPS = 3;  
  4.   private static final int BUTTON_SYNC = 2;  
  5.   private static final int BUTTON_WIFI = 0;  

那麼我們只要送出custom:3的Uri給SettingsAppWidgetProvider.class,就應該可以由SettingsAppWidgetProvider類來幫我們調用Settings.Secure.setLocationProviderEnabled方法了。
說到做到,在Activity中添加如下方法:
[java] view plaincopy
  1. private void toggleGPS() {  
  2.         Intent gpsIntent = new Intent();  
  3.         gpsIntent.setClassName("com.android.settings",  
  4.                 "com.android.settings.widget.SettingsAppWidgetProvider");  
  5.         gpsIntent.addCategory("android.intent.category.ALTERNATIVE");  
  6.         gpsIntent.setData(Uri.parse("custom:3"));  
  7.         try {  
  8.             PendingIntent.getBroadcast(this0, gpsIntent, 0).send();  
  9.         }  
  10.         catch (CanceledException e) {  
  11.             e.printStackTrace();  
  12.         }  
  13.     }  

這個方法是1個純開關,如果當前是開啓的,那麼就會關閉它,反之亦然。

檢查GPS開關狀態

[java] view plaincopy
  1. secureClass = cl.loadClass("android.provider.Settings$Secure");  
  2.     isMethod = secureClass.getMethod("isLocationProviderEnabled",  
  3.             ContentResolver.class, String.class);  
  4.     Boolean ret = (Boolean) isMethod.invoke(secureClass, this  
  5.             .getContentResolver(), "gps");  

也可以直接用下面的方法:

[java] view plaincopy
  1. private void isGPSEnable() {  
  2.         /* 用Setting.System來讀取也可以,只是這是更舊的用法 
  3.         String str = Settings.System.getString(getContentResolver(), 
  4.                 Settings.Secure.LOCATION_PROVIDERS_ALLOWED); 
  5.         */  
  6.         String str = Settings.Secure.getString(getContentResolver(),  
  7.                 Settings.Secure.LOCATION_PROVIDERS_ALLOWED);  
  8.         Log.v("GPS", str);  
  9.         if (str != null) {  
  10.             return str.contains("gps");  
  11.         }  
  12.         else{  
  13.             return false;  
  14.         }  
  15.     }  

這2種方法的原理都是一樣的,方法2其實也就是isLocationProviderEnabled實際調用的代碼,只是Google未對讀取操作進行權限限制。

總結

如果目標手機是運行Android 2.2的話,那麼最好還是使用2.2開放的Settings.Secure類中的2個方法來操作。但如果目標手機運行的版本是2.1或以下的話,那麼就只能使用變通的方法來實現了。這1方法在Android官方的Wiki上已經有人提出了,詳情請見:Issue 7890。但可能是2.1版本已經古舊不再維護的原因,官方並未進行任何的Fix。



轉自:http://www.itivy.com/android/archive/2011/6/17/android-open-or-close-gps.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章