Android LBS
前言
本文以项目AOSP的源码为参考,描述基于Android的定位服务的实现流程,包括GPS定位,网络定位,GeoCoder等服务。以代码执行流程为主线,一步步分析定位的实现流程。由于Android Binder采用C/S的架构模式,所以本文中就以客户端和服务器来称呼服务获取实体和服务实体。
定位流程图如下:
图 1-1
APP Layer Service
在使用user location之前,先了解如下一些概念:
多路定位
位置提供者可以是GPS、WiFi、Cell-ID等,它们对定位的精度、速度、能耗等各不相同。用户移动
由于用户可能是在不断的移动,所以需要不断更新用户的位置。定位频率
位置提供者按照指定的频率向位置需求者报告定位数据。
1.1 获取定位服务
得到用户位置,首先要获取LocationManager对象,该对象可以通过getSystemService(Context.LOCATION_SERVICE)获取,如图1-1所以。
上图所以,获得LocationManager对象实例locationManager,调用该对象的requestLocationUpdates()方法注册位置更新。该方法需要传递指定位置提供者、最小时间间隔报告位置、最小移动距离报告位置以及被回调的LocationListener。
1.2 权限需求
从NETWORK_PROVIDER 或者GPS_PROVIDER获取位置更新,需要ACCESS_COARSE_LOCATION或者ACCESS_FINE_LOCATION 权限,如果同时用到NETWORK_PROVIDER 和GPS_PROVIDER,仅需要ACCESS_–FINE_LOCATION权限即可,因为该权限包含NETWORK_PROVIDER的权限。
到此,注册位置监听已完成,当有报告位置等变化时,LocationManager回调LocationListener方法。
Frameworks Layer Service
2.1 LocationListener分析
承接上文,分析LocationListener,这是非常简单的一个过程,代码实现也非常简洁,下图所示。
图 2-1
上图中,LocationManager回调onLocationChanged()方法,报告位置数据,传递参数Location对象,接着看Location类
图 2-2
位置信息被封装到Location的对象中,开发者便可从该对象中提取数据。其它回调方法也比较简单,读者可自行阅读。Location对象的创建过程将在下文中分析。
2.2 LocationManager分析
在本文1.1中提到获取LocationManager对象,该对象实际是怎样操作定位的呢?下面,将深入分析。
2.2.1 LocationManager对象的实例化
以读者阅读源码的嗅觉,LocationManager对象通过get–SystemService(Context.LOCATION_SERVICE)方法获取,可知实际上这是一个基于Android Binder机制的C/S架构。通过阅读Activity的实例化过程可知道该过程会注册一系列的Service,读者如果对这一过程不了解,可自行先阅读相关知识。Service的注册过程在ContextImpl中实现,看下图。
图 2-3
上述代码中,ContextImpl实例化时,会注册LocationManager服务,ServiceManager.getService(LOCATION_SERVICE)方法通过Binder机制获取IBinder对象,如果读者不清楚ServiceManager、Binder、IBinder这一个过程,这是一件槽糕的事情,读者需要先行阅读Android Binder机制的知识。继续跟踪阅读ServiceManager.getService(LOCATION_SERVICE)中的LOCATION_SERVICE,如下图。
图 2-4
LOCATION_SERVICE的值是从Context中继承的,和getSystemService(Context.LOCATION_SERVICE)中的Context.LOC–ATION_SERVICE是指向同一个变量,因此,客户端和服务器端通过LOCATION_SERVICE关联,从图2-1中创建LocationManager对象可知,客户端对象被封装在LocationManager对象中。关于客户端和服务器端将在下文中进行分析。
小节总结
本小节谈论到Android LBS服务实现基于Binder机制,Location–Manager通过封装客户端对象,从而实现对服务器端的操作。
2.2.2 注册位置服务
获取到LocationManager对象的实例后,在1.1节中,该实例通过如下方式注册LocationListener监听位置变化。
图 2-5
跟踪LocationManager的requestLocationUpdates方法,直接看代码。
图 2-6
该方法中,会创建一个LocationRequest的实例,不妨再看看LocationRequest是干什么用的,如下图
图 2-7
LocationRequest的实例创建过程非常简单,封装一些参数,是一个数据类型对象,这个对象携带的数据最终会传到Service使用,下文将会分析。
继续往下分析注册监听位置的过程,LocationRequest对象实例化完成后,调用如下方法
图 2-8
该方法的详情如下图
图 2-9
此过程会把LocationListener对象和ListenerTransport对象进行一个转换,把LocationListener对象保存到ILocationListener对象中。其中ILocationListener的实现过程也是通过Binder机制,也就是说LocationManager通过Binder请求服务,ListenerTransport通过Binder返回结果,感兴趣的读者可自行阅读。接着代码会调用mService的requestLocationUpdates方法,mService又是个什么东东呢?看下图
图 2-10
mService实质是一个ILocationManager对象,ILocationManager对象是做什么的呢?又是在什么时候被实例化的呢?前面2.2.1 中LocationManager对象的实例化过程中,如下图
图 2-11
继续查看LocationManager的构造方法,如下图
图 2-12
上图可见,在实例化LocationManager时mService被赋值,而从图2-11可知,mService实质是一个客户端对象,通过IBinder句柄从服务器端获取相对应的服务,对于ILocationManager.Stub.asInterface不了解的读者,可以自行先了解Android Binder机制。
本节小结
到此LocationManager的注册位置服务过程已经结束。在LocationManager的实现过程非常简单,实例化一些定位相关的对象后,最终调用客户端对象以服务器端就行交互。
2.2.3 Binder通信过程
上文提到mService调用requestLocationUpdates方法,通过Binder机制的原理可知,mService实质是ILocationManager.Stub.Proxy的对象实例,mService调用requestLocationUpdates方法实质是调用Proxy的requestLocationUpdates方法,查看该方法的实现如下图
图 2-13
图 2-14
Note:图2-13和图2-14是一个方法中的内容。
该方法接收一个LocationRequest对象和一个ILocationListener对象,这两个对象实例在上文中都有描述,通过request.writeToParcel方法将LocationRequest的实例中的数据转化到Parcel对象中,从而实现Java和C/C++通过JNI进行数据对象的转换。最终调用mRemote.transact方法把_data和_reply推送到底层,同时传递Stub.TRANSACTION_requestLocationUpdates参数,这个参数在后续内容用到,读者暂行记住。mRemote.transact方法后会推送数据到Binder驱动,触发相应的服务,如果读者不了解这个过程,可自行先了解Binder机制原理。
本节小结
本节描述了请求位置变化的Binder通信过程,LocationRequest和ILocationListener的转化过程,最终触发相应的服务器端,下面将分析服务器端的实现。
2.3 LocationManagerService的创建过程
在Android启动的过程中,会初始化一系列服务,这些服务运行在system_process进程中,如果读者不了解这个过程,可自行学习Android启动流程分析和Binder机制的知识。在SystemServer中初始化LocationManagerService,如下图
图 2-15
实例化LocationManagerService后,通过Service–Manager.addService方法把定位管理服务添加到service manager管理,注意Context.LOCATION_SERVICE参数,正好与上文中的2.2.1和2.2.2中的参数对应,所以LocationManager实质是与Location–ManagerService通过Binder进行通信。
2.3.1 LocationManagerService的实例化
LocationManagerService的实例化非常简单,如下图
图 2-16
LocationManagerService的实例化只是实例化一个AppOpsManager对象对应用进行跟踪,如果读者感兴趣可自行了解AppOpsManager的功能。
2.3.2 实例化Location Provider
LocationManagerService服务初始化完成后,将会继续执行下图的代码
图 2-17
locationF是LocationManagerService的对象,查看systemRunning方法,如下图
图 2-18
该方法中会创建LocationWorkerHandler对象,如下图
图 2-19
LocationWorkerHandler是一个工作线程,处理GPS Provider和Network Provider位置变化发送的message,这个处理过程在后面的内容再进行描述。接着调用loadProvidersLocked加载location provider(下文简称LP),过程如下
图 2-20
1) GpsLocationProvider
Gps LP是GPS定位Java层的最后通道,该LP创建过程非常简单
看看构造函数
图 2-21
图 2-22
图 2-23
首先是加载GPS设备的配置,启动ProviderHandler接收LP的数据变化报告,监听SUPL的数据短信或WAP_PUSH。配置加载分两种,一种是Android默认的配置,如下图
图 2-24
当然,实质上更多的是用设备厂商的配置,如下图
图 2-25
配置数据一般就是SUPL的相关参数,这些参数是为后面AGPS定位服务,主要包括SUPL的服务器地址以及端口号。配置数据加载完毕后,会调用setSuplHostPort方法,如下图
图 2-26
最终通过JNI调用本地代码,native_set_agps_server的方法如下
图 2-27
上图可以看到,sAGpsInterface不为空时,调用该对象的set_server函数,把agps参数传递给gps driver。关于sAGpsInterface将在下文中赘述。
图 2-23中提到listenForBroadcasts方法中会接收处理发往127.0.0.1:7275的数据短信和mime类型为application/vnd.–omaloc-supl-init的WAP_PUSH,收到信息后在Broadcast中处理,处理过程如下
图 2-28
接收到Intents.DATA_SMS_RECEIVED_ACTION)或Intents.WAP_–PUSH_RECEIVED_ACTION时分别调用checkSmsSuplInit(intent)和checkWapSuplInit(intent),如下图
图 2-29
两个方法最终统一调用native_agps_ni_message,再看看该函数
图 2-30
最后也是调用sGpsNiInterface接口,和上文提到的sAGpsInterface同理,都是HAL与gps driver的通道,在下文在赘述它们。到此可见,SUPL数据的初始化通过Data SMS或WAP_PUSH的方式来实现。上文提到数据短信接收来此127.0.0.1:7275的消息,关于7275端口可参考OMA-SUPL的协议。
到此GpsLocationProvider的构造方法基本分析完毕,但是该lP的初始化还没有完成,因为还有一个static方法,如下图
图 2-31
该方法只调用本地的函数,该函数的部分截图如下
图 2-32
图 2-33
图 2-34
从图2-32到图2-34,init_native主要实现三个功能,第一个加载本地代码回调Java的方法,用于底层向上层报告定位的服务,其中报告位置变化的方法为reportLocation,这一过程在下文定位流程中分析。第二个过程是GPS定位中非常关键的一步,首先是通过hw_get_module函数加载GPS模块,GPS_HARDWARE_MODULE_ID的值是“gps”,了解Android的HAL可知,JNI通过HAL的hw_get_module函数建立JNI与HAL的通道,加载硬件模块代码,接着调用GPS模块的open函数打通HAL与硬件驱动的通道device,然后通过get_gps_interface函数返回sGpsInterface的对象,GpsInterface在HAL的gps.h中定义,如下图
图 2-35
到此可见,取得sGpsInterface对象,HAL与GPS driver的位置服务通道完全打通,sGpsInterface初始化完成后,如图2-34,将会加载sGpsInterface扩展功能,例如sAgpsInterface,这些Interface和sGpsInterface一样,都是HAL与driver通信的通道,实现不同的功能。
2)LocationProviderProxy
上文论述到GpsLocationProvider初始化的过程,下面将阐述网络LP(LocationProviderProxy)的初始化,从名字上可知这是代理设计模式,那么可以让Android不需要知道当前定位用的是什么网络定位方式,不管是WiFi,GSM,CDMA等等都可以实现定位。下面看LocationProviderProxy的创建过程,如下图:
图 2-36
上图中NetWork Provider并不像Gps Provider那样new出一个对象,而是调用static的createAndBind方法,方法中会传递一个Handler的对象,该对象实质是LocationWorkerHandler,和Gps LP一样,会接收后面位置变化的Message。倒数第二个参数config_locationProviderPackageNames,指定了Network Location的提供者,看下图
<string-array name="config_locationProviderPackageNames" translatable="false">
<!-- The standard AOSP fused location provider -->
<item>com.android.location.fused</item>
<item>com.google.android.gms</item>
<item>com.amap.android.ams</item>
<item>com.baidu.location.fused</item>
</string-array>
图 2-37
可知,上图中配置了LP为com.android.location.fused、com.google.android.gms、com.baidu.map.location,这三个包名是怎样和系统NetWork LP以及LMS建立通信并提供服务呢?接着看createAndBind方法。
图 2-37
该方法很简单,直接new一个LocationProviderProxy对象,生成LPProxy后调用bind方法,该方法很重要,后面再析它,先看Location–ProviderProxy的实例化做了些什么,如下图
图 2-38
构造方法里面又创建了一个ServiceWatcher对象mService–Watcher,该对象很重要,是LPProxy和上面图2-37提到的应用通信的桥梁,ServiceWatcher实现了ServiceConnection接口,ServiceWatcher实现了什么功能呢,下面来分析它
图 2-39
初始化很简单,所做内容就是把传进来的包名取出来放到一个ArrayList对象initialPackageNames中。回到图2-38中,LPProxy和ServiceWatcher初始化完成后,执行bind方法,接着执行Service–Watcher的start方法,start方法部分代码如下:
图 2-40
接着调用bindBestPackageLocked方法,该方法主要实现目标应用程序的(图 2-37中的程序)的检测,检测成功后,调用bind–ToPackageLocked方法,方法实现如下
图 2-41
该方法是bind的最后执行方法,这个方法最重要,从上图可以看到,packageName是目标应用程序的包名,mAction是createAndBind方法的第三个参数,参数值为com.android.location.–service.v3.NetworkLocationProvider,然后调用bindServerAsUser方法,至此,网络定位LP的实现思路非常清晰了,网络LP实质提供的数据的是第三方应用,这些应用必须有一个Service,且该Service的名字必须是com.android.location.service.v3.NetworkLocationProvider。上文有提到ServiceWatcher实现了ServiceConnection接口,由Android Service组件可知,bindServerAsUser连接服务成功后,会回调ServiceConnection的onServiceConnected,直接上该方法的实现
图 2-42
该方法的代码很少,主要功能是把binder赋值给ServiceWatcher的变量mBinder,并执行Runnable mNewServiceWork,mNewService–Work在调用createAndBind时由倒数第二个参数传入,mNewServiceWork的实现如下
图 2-43
图 2-44
图2-44声明一个ILocationProvider对象servive,该对象通过调用getService方法实例化,看看getService的实现
图 2-45
图 2-46
由图2-43可知,mBinder对象是onServiceConnected方法回调的IBinder对象,由图2-46可知,通过句柄IBinder返回一个ILocation–Provider对象实例,到此,可知,目标应用程序的Service不仅必须命名为com.android.location.service.v3.NetworkLocationProvider,且必须有实现ILocationProvider的对象实例,LPProxy通过句柄IBinder获取到的ILocationProvider对象实例,通过Binder机制和应用程序端的ILocationProvider进行通信,这就是我们熟悉的Binder机制了。继续看mNewServiceWork的实现,图2-45中可以看到,ILocationProvider的实例service调用setRequest方法请求位置服务,ILocationProvider和Android的LocationProviderInterface一样,关于setRequest的作用在下文中分析。
本节小结
至此,Gps LP和NetWork LP的实现到此已经结束,回顾内容,Gps LP在初始化时做了很多底层操作,实现过程尤为复杂,主要是打开HAL和芯片通道,检测GPS芯片,初始化芯片,设置定位参数,开始定位。LocationProviderProxy实现LBS服务过程先绑定应用程序的服务,通过服务返回的句柄IBinder,通过句柄获取目标应用程序的ILocationProvider的对象实例,通过该对象实例向目标程序发送LBS服务请求,然后通过WorkSource实现数据报告,最终传送给上层应用程序。
2.4 定位流程在LocationManagerService中的过程
到这里,GPS LP和NetWork LP的创建和运行阐述完毕了,LP的叙述边幅这么长,不知读者是否还记得2.3节前面的内容,上层应用调用requestLocationUpdates到图2-14的mRemote.transact后,通过binder驱动,触发服务,最终执行到服务器端的onTransact方法,如下图(下面的代码是通过Binder机制运行在不同的进程中)
图 2-47
图 2-48
Note:图2-15和图2-16是一个方法中的内容
不知读者是否记得上面提到的Stub.TRANSACTION–_requestLocationUpdates参数,所以最终会执行图2-16中case TRANSACTION_requestLocationUpdates的代码。上图中的Location–Request正是LocationManager通过Binder传过来的LocationRequest对象实例,ILocationListener也正是上层应用传递过来的对象实例,从本地代码经过对象转换后,调用LocationManagerService的requestLocationUpdates方法, 最终会执行如下代码
图 2-49
图 2-50
接着调用LocationProviderListener的setRequest方法,和LocationProviderProxy中提到ILocationProvider实现一样,下面将以GpsLocationProvider为例阐述setRequest的工作过程。直接看代码
图 2-51
这里进行一个转发,最终执行下面的代码
图 2-52
继续往下看代码
图 2-53
匹配GPS定位的方式,然后调用本地的native_set_position_mode把定位模式和参数设置到GPS芯片,如下图
图 2-54
在上文提到Gps模块的初始化,在这里设置了定位模式,终于可以调用本地的native_start启动GPS芯片进行定位了,看本地的native_start的实现过程,如下
图 2-55
上文中有提到参数在本地方法的初始化,所以调用native_start时,实质执行了android_location_GpsLocationProvider_start的方法,sGpsInterface在GpsLocationProvider的创建过程中有阐述,是HAL和GPS驱动的通道,然后调用sGpsInterface的start方法开始定位,关于定位的过程,是硬件厂商不公开的代码,这些代码在上文中打开GPS模块时HAL加载gps模块的so库。定位成功后会回调location_callback函数,如下
图 2-56
在这里会回调到Java代码,如图method_reportLocation会回调到Java的什么方法呢,在上文GpsLocationProvider的创建过程的初始化是有提到,如图2-32,method_reportLocation函数调用实质是调用了Java的reportLocation方法,该方法如下
图 2-57
图 2-58
然后包定位的数据封装到Location的对象mLocation中,通过mILocationManager的reportLocation方法,mILocationManager是ILocationManager对象实例,该实例在GpsLocationProvider创建时被实例化,上文提到,GpsLocationProvider的创建过程在LocationManagerService中开始,回顾GpsLocationProvider的new过程,如下图
图 2-59
上图中的this对象正是mILocationManager的实例,所以,reportLocation.reportLocation是执行LocationManagerService的reportLocation方法,如下
图 2-60
这里在再把Locaton对象传出去,mLocationHandler在上文中有描述,以一个工作线程,是LocationWorkerHandler的对象,接收位置的变化。再经过一些处理和判断,最终执行下面的代码
图 2-61
上图中mListener正是上层应用通过Binder传送过来的ILocationListener对象实例,然后调用该对象的onLocationChanged方法,即通过Binder回调到图1-1中的方法。
本节小结
Gps定位到此终于结束,本节描述了上层应用通过发送定位请求到LocationManagerService后,LocationManagerService通过GpsLocationProvider调用GPS芯片进行定位的过程。
3 总结
本文详细描述了Android定位的过程,从APP层获取定位的方法,到Frameworks层,在通过Binder机制,连接到LocationManagerService,通过LP获取定位的数据,然后通过层层回调到APP层的过程。通过上文可知,GPS的定位在GpsLocationProvider就可以完成,然而网络定位是依赖第三方应用,实际中设备更多的是用到融合定位,即网络和GPS同时进行定位,这样可以提高定位速度和精度。在GPS定位中,目前大多设备并不是用标准的GPS模式进行定位,而是用AGPS来提高定位的速度和精度。上文中定位过程只是取得经纬度、海拔、速度、精度等数据,然而我们生活中更多的是用地理编码(道路、街道、邮政编号等)来描述位置,GeoCoder正是可以实现把GPS定位的数据转换成生活中通俗的表示。当然,实际中人们使用的都是地图类的应用,当用户看到地图上自己的位置时,地图应用可能已经做了很多工作,包括定位,GeoCoder,POI等众多服务。
感谢读者的信赖,由于作者知识有限,如发现文中有错误的地方,欢迎及时提出来以便更正!
上一篇: cookie的自动获取以及自动更新的方法
下一篇: 批处理脚本应用——attrib