GPS代码学习---Framework代码学习
Android基于位置的服务,不得不提到andriod.location包,它提供了很方便的API来实现基于位置的服务。和其他android系统服务一样,我们不能直接实例化一个LocationManager,而是通过getSystemService(Context.LOCATION_SERVICE)获取LocationManager实例。在获得LocationManager实例后,我们可以做三件事情:
1、查询上次已知所有用户位置列表;
2、注册或取消注册用户位置阶段性从LocationProvider获得的更新;
3、注册或取消注册一个指定的Intent,如果手机设备接近一个指定经纬度指定范围(由半径米指定)。
相关组件。和location相关的GoogleMap API是MapView,在聚焦MapView时,可捕获按键,触屏手势去放大缩小地图。
相关权限设置。
<manifest ... ><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> ...</manifest>
ACCESS_COARSE_LOCATION只提供NETWORK_PROVIDER的权限,ACCESS_FINE_LOCATION则提供NETWORK_PROVIDER和GPS_PROVIDER。
调试。可通过Eclipse中DDMS调试,或者命令方式调试。打开DDMS的EmulatorControls中有LocationControls部分,选择设备后,可向设备发送经纬度信息。
Android.Location包的目录结构分析。
frameworks/base/location/java/android/location
-----Address.aidl提供一个parcelable类
-----Address.java提供地址信息,包含featurename, postal code, country code。
-----Country.aidl提供一个parcelable类
-----Country.java提供ISO标准两个字符国家码(CN仅限于大陆,不含港澳台)
-----ICountryDetector.aidl获取国家码接口类
-----CountryDetector.java获取用户所在地国家码
-----ICountryListener.aidl检测国家码监听
----CountryListener.java检测国家码监听
-----Criteria.aidl提供一个parcelable类
-----Criteria.java提供精确度信息
-----GeocoderParams.aidl提供一个parcelable类
-----GeocoderParams.java获取Geocoder参数
-----Geofence.aidl提供一个parcelable类
-----Geofence.java对获取的经纬度判断保护
-----GpsSatellite.java描述GPS卫星状态,主要在GpsStatus.java中使用
-----GpsStatus.java描述GPSengine的状态,通过伪随机码初始化卫星得到mSatelliteList
-----IGeocodeProvider.aidl接口类提供两个方法
-----IGpsStatusListener.aidlGPS的状态监听。
-----IGpsStatusProvider.aidl增加和移除GPS状态监听
-----ILocationListener.aidl定义接口,在LocationManager中ListenerTransport实现
-----LocationListener.java定义接口
-----ILocationManager.aidl定义接口,在LocationManagerService中实现
-----INetInitiatedListener.aidl定义接口GpsLocationProvider中实现
(从多个aidl文件分析看,aidl并非仅在service类中使用,另一种使用方法(Binder)是:定义aidl文件后,在其他文件中只需newIXXX.Stub并实现aidl中定义的方法,然后该实例即可作为其他类的成员,并最终调用方法)
-----Location.aidl提供一个parcelable类
-----Location.javaLocation类,实现Parcelable的类
-----LocationProvider.java提供诸如是否需要网络,卫星等信息
-----LocationRequest.aidl提供一个parcelable类
-----LocationRequest.java请求对应精度的位置更新服务
ACCURACY_FINE:1M,ACCURACY_BLOCK100M,ACCURACY_CITY10KM
其中可设置位置更新请求间隔时间setInterval(),最快间隔时间setFastestInterval()等。
-----LocationManager.java其他类或接口大多和这个文件有关,下面篇幅重点介绍
frameworks/base/location/java/com/android/internal/location
-----GpsNetInitiatedHandler.javaGPS网络初始化的处理类,弹提示框等,在GpsLocationProvider.java中用到。
扩展:frameworks/base/services/jni/下有个Onload.cpp,JNI_OnLoad注册了一些系统服务如:
PowerManagerService,SystemServer,location_GpsLocationProvider等,其中location_GpsLocationProvider对应注册JNI接口的文件是com_android_server_location_GpsLocationProvider.cpp
-----ILocationProvider.aidl
-----ProviderProperties.aidl
-----ProviderProperties.java
-----ProviderRequest.aidl
-----ProviderRequest.java
frameworks/base/location/lib/java/com/android/location/provider
-----GeocodeProvider.java
-----LocationProviderBase.java
-----LocationRequestUnbundled.java
-----ProviderPropertiesUnbundled.java
-----ProviderRequestUnbundled.java
frameworks/base/services/java/com/android/server/location
-----ComprehensiveCountryDetector.java
-----CountryDetectorBase.java
-----GeocoderProxy.java
-----GeofenceManager.java
-----GeofenceState.java
-----GpsLocationProvider.java
-----GpsXtraDownloader.java
-----LocationBasedCountryDetector.java
-----LocationBlacklist.java
-----LocationFudger.java
-----LocationProviderInterface.java
-----LocationProviderProxy.java
-----MockProvider.java
-----PassiveProvider.java
问题1:CountryDetector类中的成员ICountryDetectormService调用detectCountry()接口时,怎么和CountryDetectorService.java中的detectorCountry()联系在一起呢?
在系统启动时,SystemServer起来时,在run()函数中会加载不少系统服务,代码如:
try{ Slog.i(TAG,"Country Detector"); countryDetector= new CountryDetectorService(context); ServiceManager.addService(Context.COUNTRY_DETECTOR,countryDetector);}catch (Throwable e) { reportWtf("startingCountry Detector", e);}
而之后运行到ContextImpl.java中时,我们看到有个static代码块,代码片段如下:
static{ ...... registerService(COUNTRY_DETECTOR,new StaticServiceFetcher() { publicObject createStaticService() { IBinderb = ServiceManager.getService(COUNTRY_DETECTOR); returnnew CountryDetector(ICountryDetector.Stub.asInterface(b)); }}); ......}
从上面两个代码片段就可以看出,在SystemServer加载服务后,在ContextImpl中获取到对应的Binder后,将CountryDetectorService作为参数传给CountryDetector的构造函数中。这样,就完成了CountryDetectorService和成员变量mService的绑定。从而我们也看出,aidl实现Service的数据通信,并非一定要使用bindService将ServiceConnection和IXXX.aidl绑定在一起。
问题2:IGeocodeProvider作为aidl文件,如果将虚函数看作插槽。为什么GeocodeProvider不直接将接口实现?仍然作为虚函数使用呢?
搜了一圈,终于在LocationManagerService中找到一个成员mGpsStatusProvider,有如下代码:
@Overridepublic boolean addGpsStatusListener(IGpsStatusListener listener) { if(mGpsStatusProvider == null) { return false; } checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),LocationManager.GPS_PROVIDER); try{ mGpsStatusProvider.addGpsStatusListener(listener); }catch (RemoteException e) { Slog.e(TAG,"mGpsStatusProvider.addGpsStatusListener failed", e); returnfalse; } return true;}
该函数的调用流程实际上是从LocationManager中addGpsStatusListener调起的。为什么这么说呢?和问题1流程相同,在LocationManager中有个变量ILocationManagermService,从问题1分析可得出结论,实际上mService即LocationManagerService。那下面的这段代码就好理解了:
public boolean addGpsStatusListener(GpsStatus.Listener listener) { boolean result; if(mGpsStatusListeners.get(listener) != null) { //listener is already registered return true; } try{ GpsStatusListenerTransporttransport = new GpsStatusListenerTransport(listener); result = mService.addGpsStatusListener(transport); if(result) { mGpsStatusListeners.put(listener,transport); } }catch (RemoteException e) { Log.e(TAG,"RemoteException in registerGpsStatusListener: ", e); result = false; } return result;}Location 相关内容主要包括 Geocoder 和 LocationManager 。
一、Geocoder
Geocoder可以在街道地址和经纬度地图坐标之间进行转换。它提供了对两种地理编码功能的访问:
ForwardGeocoding(前向地理编码):查找某个地址的经纬度
ReverseGeocoding(反向地理编码):查找一个给定的经纬度所对应的街道地址。
对应的API是:
List<Address>getFromLocationName(StringlocationName,intmaxResults)
List<Address>getFromLocation(doublelatitude,doublelongitude,intmaxResults);
二、LocationManager相关的两个知识点
provider:LocationManager获取位置信息的途径,常用的有两种:GPS和NETWORK。GPS定位更精确,缺点是只能在户外使用,耗电严重,并且返回用户位置信息的速度远不能满足用户需求。NETWORK通过基站和Wi-Fi信号来获取位置信息,室内室外均可用,速度更快,耗电更少。为了获取用户位置信息,我们可以使用其中一个,也可以同时使用两个。
LocationListener:位置监听器接口,定义了常见的provider状态变化和位置的变化的方法,我们需要实现此接口,完成自己的处理逻辑,然后LocationManager注册此监听器,完成对各种状态的监听。
在locationManager.java中,可以看到PROVIDER有几种,就会对应有几种类型的LocationListener。GPS_PROVIDER对应mGpsListener。例如:在MTK写的一个apk rcse中,AndroidManifest.xml中有如下代码:
<service android:name=".core.ims.service.presence.pidf.geoloc.GPSLocationService"> <intent-filter> <actionandroid:name="com.orangelabs.rcs.GPS" /> </intent-filter></service>GPSLocationService 继承自 Service ,其中有个 onBind() 函数,注册了对应 GPS_PROVIDER 的监听:
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,5000, 0, mGpsListener);先来看下 requestLocationUpdates , mLocationManager 是 LocationManager 类型,在 LocationManager.java 中找到对应接口后,发现最终调用的是如下接口:
private void requestLocationUpdates(LocationRequest request, LocationListenerlistener, Looperlooper, PendingIntent intent) { String packageName = mContext.getPackageName(); //wrap the listener class ListenerTransport transport = wrapListener(listener, looper); try{ mService.requestLocationUpdates(request,transport, intent, packageName); }catch (RemoteException e) { Log.e(TAG, "RemoteException",e); }}
首先,WrapListener将传入的mGpsListener放入hashmapmListeners中。在try块代码中,mLocationManagerService是LocationManagerService类型,最终调用到LocationManagerService.java中的requestLocationUpdates函数。
GoogleMaps V2简单应用
GoogleMaps Android API V2改进:
1、API作为GooglePlay servicesSDK一部分
2、Maps装入MapFragment类,该类继承自android的Fragment类,取代MapView
3、允许扩展标准的Activity,而不再必须是V1中的MapActivity
4、使用矢量贴图,所以地图显示更快,占用带宽更少
5、改良的缓存,用户不会再看到有空白区域
6、使用3D地图,移动用户的视角,可以看透视图
开发局限性:
官网上说V1的APIKey 2013年3月18号开始停止申请了,而且V1Key不适用V2。
运行局限性:
设备安装有GooglePlay service APK;
版本限制:
android2.2或更高版本
1、SDK配置
打开Eclipse的AndroidSDK Manager,安装更新Extras下面的googleplay services,这点很关键,否则无法使用。按照google官网的步骤执行后出现程序异常,需要加载库文件解决。
在Eclipse里面选择:File>Import>Android>ExistingAndroid Code Into Workspace然后点击Next.之后Browse...,找到路径下的<android-sdk-folder>/extras/google/google_play_services/libproject/google-play-services_lib,然后选择Finish。
第二步是添加对这个库的引用:在自己的项目上右键,选Properties,左边选Android,然后在下面的Library里面Add刚才的google-play-services_lib。
有传言说V2不能在AVD上运行,可能Google还会对此问题进行更新。在Stackoverflow上已经有过讨论,简单的采用以下方法即可在emulator中跑起来。
在AVD上安装两个包(GooglePlay Store和GooglePlayservices):vending.apk和gms.apk,(给一个网盘链接:http://pan.baidu.com/share/link?shareid=190602&uk=2701745266)
2、获取AndroidMaps Api Key
想要获取googlemap api服务,需要在应用中添加AndroidMaps ApiKey。先找到本地的debug.keystore文件。如果不知道路径,可打开Eclipse,在Window\Preference\Android\Build中可以看到,一般路径为~/.android/debug.keystore。然后用keytool工具获取唯一的指纹认证码:
linux中命令如下:
keytool-list -v -keystore ~/.android/debug.keystore -alias androiddebugkey-storepass android -keypass android
window系统命令如下:
keytool-list -v -keystore "C:\Users\your_user_name\.android\debug.keystore"-alias androiddebugkey -storepass android -keypass android
以上命令其实并无太大差别,只是linux和window存储debug.keystore的路径不同。
生成的SHA-1指纹认证码如下:
别名名称:androiddebugkey
创建日期:2012-8-28
项类型:PrivateKeyEntry
认证链长度:1
认证[1]:
所有者:CN=AndroidDebug, O=Android, C=US
签发人:CN=AndroidDebug, O=Android, C=US
序列号:503c9c42
有效期:Tue Aug 28 18:24:02 CST 2012 至ThuAug 21 18:24:02 CST 2042
证书指纹:
MD5:3A:D9:CA:87:53:A7:38:1C:56:77:32:18:5B:D7:EB:72
SHA1:39:03:D5:1E:0F:F1:E0:AC:12:11:2F:D8:8D:88:0F:EF:58:07:EE:4A
签名算法名称:SHA1withRSA
版本:3
为了获取APIKey,需要用你的gmail帐号登录,进入后点击CreateProject按钮,左边导航栏点Services。在其中找到GoogleMaps Android Api v2,选择打开on,在进入的页面中接受协议内容并点Accept。然后再在导航栏中选择ApiAccess,在页面中找到CreateNew Android Key....然后在以下弹出框中输入以上指纹及应用包名,中间以分号隔开,例如:39:03:D5:1E:0F:F1:E0:AC:12:11:2F:D8:8D:88:0F:EF:58:07:EE:4A;com.dewav.locationtest
生成40位字符的APIkey码:
APIkey: | AIzaSyB3fUxGpH15Y_4y53VBHIps4IueTuTQJnY |
在androidManifest.xml中<application>标签中间填入apikey,例如:
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyB3fUxGpH15Y_4y53VBHIps4IueTuTQJnY"/>
添加权限控制,例如:
<permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE" android:protectionLevel="signature"/><uses-permission android:name="com.example.mapdemo.permission.MAPS_RECEIVE"/>
另外,添加其他权限:
<uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/><!-- The following two permissions are not required to use Google Maps Android API v2, but are recommended. --><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
因为googlemap v2 api要求使用OpenglES Version 2,所以,我们必须加入
<uses-feature android:glEsVersion="0x00020000" android:required="true"/>
3、MapFragment和sampleCode
首先在布局文件中加入MapFragment,
<?xml version="1.0" encoding="utf-8"?><fragment xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/map" android:layout_width="match_parent" android:layout_height="match_parent" android:name="com.google.android.gms.maps.MapFragment"/>在mapActivity中,添加如下代码:public class MapActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map); }}除了用Google Map Android Api V1和Google Map Android Api V2外,还可用百度的SDK实现地图app,详细实例可参考百度SDK的介绍及sample code。
参考资料:
GoogleMap Android APIV1:https://developers.google.com/maps/documentation/android/v1/hello-mapview?hl=zh-CN
GoogleMap Android API V2:
https://developers.google.com/maps/documentation/android/?hl=zh-CN
APIKEY request:https://code.google.com/apis/console/#project:470805776290:access
介绍百度地图:http://blog.csdn.net/lyq8479/article/details/6384428
百度地图SDK:http://developer.baidu.com/map/sdk-android.htm
http://www.cnblogs.com/mengdd/archive/2013/01/01/2841390.html
http://wiki.eoe.cn/page/Using_the_Location_Manager
http://blog.csdn.net/liuhe688/article/details/6566505
http://www.cnblogs.com/feisky/archive/2010/01/20/1652230.html
更多相关文章
- 用百度地图API实现Android定位功能(2.6版本为例)
- Android使用include标签无法设置具体位置的解决
- Android(安卓)Aidl 的使用方法
- wifi 架构
- Android获取验证码倒计时实现代码
- Android应用开发SharedPreferences存储数据的使用方法 以及与 ge
- Android中创建Message两种方法比较,new Message和obtainMessage
- Android(安卓)BaseAdapter 简单封装
- View 的 android:visibility属性的讨论