2024年3月7日发(作者:)

手机应用中最酷的可能就是位置服务相关的了,如何读取GPS信息,在官方文档上有相当详细的说明,后面如果有机会,我也会专门写例子来介绍(教程已完成,请参见:教程:实现Android的不同精度的定位(基于网络和GPS))。但今天,我们先来看下如何以编程的方式来开启或关闭GPS。

官方的API中,类有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将抓下来,反编译之后,在SecuritySettings类中找到如下代码:

package,

1

CheckBoxPreference localCheckBoxPreference3 = ;

2

if (paramPreference == localCheckBoxPreference3)

3

{

4

ContentResolver localContentResolver3 = getContentResolver();

5

boolean bool6 = ked();

6

ationProviderEnabled(localContentResolver3, "gps", bool6);

7

continue;

8

}

可以看到2.1系统中已经存在有ationProviderEnabled方法了,只是该方法没有开放而已,事实上读过Android源码的人都对/*hide*/很反感吧,看得到,摸不到!

既然Setting画面中的用法,我们不能使用,那么再换1种方法,我们去看一下ationProviderEnabled的写法,然后直接套用。

这次,我们直接去看Android 2.3的源码,找到之后,找到相关的方法,代码如下:

core,

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, ON_PROVIDERS_ALLOWED, provider);

12

}

原来这个方法只是1个包装,事实上调用的还是中的putString方法,我们直接借用过来:

在自己的onClick事件中写上

ing(getContentResolver(),

ON_PROVIDERS_ALLOWED, “network,gps”);

然后执行,WOW,发生了什么,需要_SETTINGS权限?在Manifest文件中加上,再运行,还是出错,不过这次需要的是_SECURE_SETTINGS,再次加上。

满怀希望的再次运行,结果还是一样的问题:

tyException: Permission denial: writing to secure settings requires

_SECURE_SETTINGS

看来,Google封死了直接调用Settings的路了,事实上我又试着使用反射来直接调用setLocationProviderEnabled方法,结果也是一样的告诉我需要权限。

柳暗花明又一村

难道没有别的办法了吗?那些2.1中可以运行的App Widget是如何做到的呢?

再次检视2.1的中的代码,发现有1个widget包,里面有1个类叫SettingsAppWidgetProvider,这就是用来提供App Widget的类,继续研究这里的代码,发现构造RemoteView的代码中用到如下方法:

1

private static PendingIntent getLaunchPendingIntent(Context paramContext, int

2

paramInt1, int paramInt2)

3

{

4

Intent localIntent1 = new Intent();

5

Intent localIntent2 = ss(paramContext,

6

);

7

Intent localIntent3 = egory("ATIVE");

8

Uri localUri = ("custom:" + paramInt2);

9

Intent localIntent4 = a(localUri);

return adcast(paramContext, 0, localIntent1, 0);

}

由于这是反编译的结果,略微有点混乱,但还是可以看出思路,目的是通过PendingIntent

来扔出1个Intent,接受者是,接受的参数有2个,1个是Category:(正是这个类自身),另1个是Data:(“custom:” + paramInt2),这个paramInt2是Widget中的图标按钮的序号。所有的序号在类的首部都有定义:

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类来帮我们调用ationProviderEnabled方法了。

说到做到,在Activity中添加如下方法:

1

private void toggleGPS() {

2

Intent gpsIntent = new Intent();

3

ssName("gs",

4

"gsAppWidgetProvider");

5

egory("ATIVE");

6

a(("custom:3"));

7

try {

8

adcast(this, 0, gpsIntent, 0).send();

9

}

10

catch (CanceledException e) {

11

tackTrace();

12

}

13

}

这个方法是1个纯开关,如果当前是开启的,那么就会关闭它,反之亦然。

检查GPS开关状态

那么,如何查看当前的GPS开关状态呢?可以用上面提到的反射方式调用isLocationProviderEnabled,代码片断如下:

1

secureClass = ass("gs$Secure");

2

isMethod = hod("isLocationProviderEnabled",

3

, );

4

Boolean ret = (Boolean) (secureClass, this

5

.getContentResolver(), "gps");

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

1

private void isGPSEnable() {

2

/* 用来读取也可以,只是这是更旧的用法

3

String str = ing(getContentResolver(),

4

ON_PROVIDERS_ALLOWED);

5

*/

6

String str = ing(getContentResolver(),

7

ON_PROVIDERS_ALLOWED);

8

Log.v("GPS", str);

9

if (str != null) {

10

return ns("gps");

11

}

12

else{

13

return false;

14

}

15

}

这2种方法的原理都是一样的,方法2其实也就是isLocationProviderEnabled实际调用的代码,只是Google未对读取操作进行权限限制。

总结

如果目标手机是运行Android 2.2的话,那么最好还是使用2.2开放的类中的2个方法来操作。但如果目标手机运行的版本是2.1或以下的话,那么就只能使用变通的方法来实现了。这1方法在Android官方的Wiki上已经有人提出了,详情请见:Issue 7890。但可能是2.1版本已经古旧不再维护的原因,官方并未进行任何的Fix。

© 2011, Bing. 版权所有。 所有转载请以链接方式进行。