序文

定位干事是挪动配备上最经常运用的功用之一,下文以 Android 源码为根蒂根抵,具体剖析了 Android 零星中定位干事的架构和完成。
定位干事是 Android 零星供应的一项零星干事,在 Android 零星中,一切零星干事的架构都是相反的。只需邃晓个中一个,然后再去理解其他是很随意疏忽的。
对 Android 的运用垦荒人员来说,本文可以匡助他们意见他们所行使的 API 面前的完成。
对 Android 的零星垦荒人员来说,本文可以匡助他们更好的意见 Android 零星架构。
关于若何获得 Android 源码,请参阅 Android Source 的官方网站:http://source.android.com/source/downloading.html
Android 源码中包括了除夜量的文件,有些源文件甚至是同名的。为了了了的指明我们所谈论的文件,本文在提到源码文件的时辰都邑指明其在 Android 源码树中的途径。

android.location 包与 API 代码示例

定位干事供应给运用层的 API 位于 android.location 包中,它个中包括的类和接口如表 1 所示:

表 1. android.location 包中的类和接口
称号 类型 声名
GpsStatus.Listener 接口 用于收受接管 GPS 形态修改时的告知。
GpsStatus.NmeaListener 接口 用于收受接管 Nmea(为海用电子配备制定的花招)信息。
LocationListener 接口 用于收受接管位置信息修改时的告知。
Address 用于描写地址信息。
Criteria 用于选择 LocationProvider。
Geocoder 用于处置责罚地舆位置的编码。
GpsSatellite 用于描写 GPS 卫星的形态。
GpsStatus 用于描写 GPS 配备的形态。
Location 用于描写地舆位置信息,包括经度,纬度,海拔,倾向等信息。
LocationManager 用于获得和挪用定位干事。
LocationProvider 描写 Location Provider 的超类,Location Provider 是真正用来获得位置信息的组件。Location Provider 的完成次要可以分为两类:一种依托于 GPS 配备,其他一种依托汇集形态。

在表 1 中,最主要的类是 LocationManager,这是悉数定位干事的出口类。
清单 1 是行使定位干事 API 的代码示例:

清单 1. 行使定位干事供应的 API
public class MainActivity extends Activity { // 测试行使的日志 Tag  private static final String TAG = "LocationService API Demo"; // 将在 onCreate 中被初始化 private LocationManager locationManager; // 收受接管位置更新的监听器 protected final LocationListener locationListener = new LocationListener() { // 当位置发生发火转变时,输出位置信息 public void onLocationChanged(Location location) { Log.d(TAG, "Location changed to: " + getLocationInfo(location)); } public void onProviderDisabled(String provider) { Log.d(TAG, provider + " disabled."); } public void onProviderEnabled(String provider) { Log.d(TAG, provider + " enabled."); } public void onStatusChanged(String provider, int status, Bundle extras){ Log.d(TAG, provider + " status changed."); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 获得 LocationManager  locationManager = (LocationManager)getSystemService(LOCATION_SERVICE); } @Override protected void onResume() { super.onResume(); // 指定一个 Provider  String currentProvider = LocationManager.NETWORK_PROVIDER; Log.d(TAG, "CurrentProvider: " + currentProvider); // 获得 Provider 最初一个记载的地址信息 Location lastKnownLocation = locationManager .getLastKnownLocation(currentProvider); if (lastKnownLocation != null) { Log.d(TAG, "LastKnownLocation: " + getLocationInfo(lastKnownLocation)); } else { Log.d(TAG, "Last Location Unkown!"); } // 注册监听器收受接管位置更新 locationManager.requestLocationUpdates(currentProvider, 0, 0, locationListener); } @Override protected void onPause() { super.onPause(); // 移除监听器 locationManager.removeUpdates(locationListener); Log.d(TAG, "LocationListener: " + locationListener + " removed."); } /** * 将 Location 对象转换成字符串方法随意显示 * * @param location * Location 对象 * @return 字符串方法的泄漏表现 */ private String getLocationInfo(Location location) { String info = ""; info += "Longitude:" + location.getLongitude(); info += ", Latitude:" + location.getLatitude(); if (location.hasAltitude()) { info += ", Altitude:" + location.getAltitude(); } if (location.hasBearing()) { info += ", Bearing:" + location.getBearing(); } return info; } }

这段代码的声名以下:
在 Activity 显示的时辰首先考试考试获得经由进程汇集定位的 Location Provider 记载的最初一次定位信息,然后在零星中注册一个监听器来监听位置信息的交换,这里的 API 都是行使定位干事最经常运用的。

定位干事的完成架构图

悉数定位干事的架构如图 1 所示。该组织共分为四层:

  • 最上面是运用层,即 android.location 包中包括的内容,是以 Java 措辞供应的 API。
  • 第二层是框架层,这一层包括了零星干事的完成,次要由 Java 措辞来完成。
  • 第三层是同享库层,本层由 C 和 C++ 措辞完成 , 框架层与同享库层行使 JNI 中止跟尾。
  • 最上面一层是 Linux 内核层 , 悉数 Android 零星都是以 Linux 内核为根蒂根抵的。

从上至下它们是逐层依托的关系,每层依托上面一层完成其所需供应的干事。

图 1. 定位干事的完成架构图


零星干事的启动与注册

从图 1 中可以看出,在框架层,完成位置干事的类是 LocationManagerService,这是一个零星干事。
那末 LocationManager 和 LocationManagerService 两者是什么关系,它们是若何联络关系起来的呢?
想要理解这一点就要先引见一下 Android 中的 Binder 机制。
在 Android 零星中,零星干事运转在一个专门的进程中,这个进程称号为 system_server。该进程在零星启动的时辰便被加载和启动。零星中有一个专门用来治理零星干事的类,它叫做 ServiceManager。这个类担负注册并治理一切的零星干事。
当运用轨范想要行使零星干事时,需求经由进程干事的署理来挪用干事。因为客户运用轨范运转在本人的进程中,这和 system_server 是两个自力的进程,是以署理需求经由进程进程间通讯将要求发送到 system_server 进程,由该进程来呼应干事,然后再前往效果。悉数这个机制称之为 Binder 机制。Binder 机制在 Android 零星中运用异常之广,几近一切的进程间通讯都是行使该进制完成的。

图 2 描写了 Binder 机制的恳乞降呼应进程:

图 2. Binder 机制的恳乞降呼应进程

Binder Driver 作为 Binder 机制的焦点部份完成底层进程间通讯的责任。被要求的进程(这里是零星干事进程)常日会缓存一些线程,当有要求时,在这些线程中完成要求。

意见了 Binder 机制之后,我们连续来看 LocationManager,在 LocationManager.java( 位于:frameworks/base/location/java/android/location/) 中可以看到,LocationManager 类中一切功用的完成都是依托于一个称号为 mService 的字段来完成的,这个字段的类型是 ILocationManager。而这个对象就是我们上面所说的署理。mService 字段是在 LocationManager 的组织函数中被初始化的,是以找到 LocationManager 组织函数被挪用的地方就可以晓得这个署理对象是哪里来的了。
在 ContextImpl.java(frameworks/base/core/java/android/app) 中有这样一段代码,如清单 2 所示:

清单 2. registerService 方法中的代码片断
registerService(LOCATION_SERVICE, new StaticServiceFetcher() { public Object createStaticService() { IBinder b = ServiceManager.getService(LOCATION_SERVICE); return new LocationManager(ILocationManager.Stub.asInterface(b)); }});

在这里,我们见到了 ServiceManager 这个类,上文说了,这个类是专门用来治理零星干事的。经由进程它,我们即可以获得定位干事的完成类对象,然后又经由进程 ILocationManager.Stub.asInterface(b) 将其转换成干事的署理寄存到了 LocationManager 中。

猎奇心重的读者一定要问:那 ServiceManager 中所治理的零星干事对象又是从哪里来的呢?要弄清这个成就,还需求比拟多的查询访问。上面我们来具体讲授:

在 Android 零星启动进程傍边,需求完成一系列的初始化举动。源码中有一个专门的剧副本治理启动时需求完成的责任,该剧本文件称号为 init.rc(位于:system/core/rootdir)。这个剧本文件由 init 进程读取。
在这个剧本文件中有这样一段代码(关于 init 进程和 init.rc 剧本的语法,请拜见参考资估中的链接):

清单 3. init.rc 中的代码片断
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

该行剧本的寄义以下:
“service”指明要启动一个零星干事,“zygote”是该干事的称号,“/system/bin/app_process”是该干事的可实行文件途径。“-Xzygote /system/bin”是可实行文件的责任目录,“–zygote – start-system-server”是供应给可实行文件的参数。

想要弄清这里现实发生发火了什么,我们还需求检查 app_process 的源码。该源码位于:frameworks/base/cmds/app_process/app_main.cpp 中。
在 app_main.cpp 中,剖断因为参数同时包括了“— zygote”和“–start-system-server”,是以启动 com.android.internal.os.ZygoteInit 类,并传递“start-system-server”作为其 main 函数的参数。
而在 ZygoteInit.java(frameworks/base/core/java/com/android/internal/os)中,剖断假定参数为“start-system-server”则挪用 startSystemServer 方法来启动零星干事。启动的方法是 fork 一个新的进程,然后在个中加载 com.android.server.SystemServer 类。
在SystemServer 中,有一个名为 ServerThread 的类,这是一个线程类,在这个类中真正实行了零星干事的树立和注册 。以 LocationManagerService 为例,在 ServerThread 的 run 方法中有以下代码:

清单 4. ServerThread 中 run 方法的代码片断
try { Slog.i(TAG, "Location Manager"); location = new LocationManagerService(context); ServiceManager.addService(Context.LOCATION_SERVICE, location); } catch (Throwable e) { reportWtf("starting Location Manager", e); }

这段代码真正完成了 LocationManagerService 的树立和注册。
悉数挪用关系如图 3 所示:

图 3. LocationManagerService 注册进程

检查除夜图

是以,在零星启动完成之后,这些零星干事也一一的被启动并注册在 ServiceManager 中了。

既然一切零星都是注册在 ServiceManager 中的,有些读者可以要问,可弗成以弗成使署理,直接经由进程 ServiceManager 来获得零星干事对象,然后挪用呢?谜底能否是定的。
在 Android 中,将悍然的 API 和非悍然的 API 做了划分。一切非悍然的 API 在垦荒运用轨范的 SDK 中是没法行使的,而 ServiceManager 就是属于非悍然的 API。所以对运用轨范垦荒人员,基本没法接触到 ServiceManager 类。

LocationManagerService

现在,我们终于可以来看看定位干事的真正完成类:LocationManagerService。
该类位于 frameworks/base/services/java/com/android/server/LocationManagerService.java 中。
这个类的文件有 2400 多行,第一次看到可以会感想沾染这个类太除夜了,致使于无从下手。其实,在 Android 的源码,这个文件远不算除夜,WindowManagerService 源文件有 10000 多行,ActivityManagerService 源文件有 15000 多行。不管类有多除夜,只需遵照一定的剖析方法,总能慢慢的将这个类理解上去。
笔者以为,要剖析一个类只需遵照以下几个步骤即可:

  1. 理解该类的次要浸染
  2. 剖析类中的次要字段
  3. 理解类的组织方法和初始化进程
  4. 理解类中的次要营业逻辑方法
  5. 剖析类中的其他成员:例如内部类
  6. 剖析与这个类慎密相关的其他类

上面,我们就以这样的方法来慢慢理睬 LocationManagerService。

LocationManagerService 最次要的浸染自然是供应定位干事,在刚最先的内容中我们已看到,获得位置信息可以选择不合的 Location Provider,每一个 Location Provider 可以会记载比来一次的定位信息。同时,我们也可以行使监听器来自动获得位置更新告知。一切的这些功用,都是在 LocationManagerService 中完成的。

次要字段

LocationManagerService 中包括次要字段如表 2 所示:

表 2. LocationManagerService 中的次要字段
类型 称号 声名
HashMap mLastWriteTime 记载最初一次位置更新的时辰,这是一个以 Location Provider 称号为键的映照。
Set mEnabledProviders 有用的 Location Provider。
Set mDisabledProviders 无效的 Location Provider。
boolean sProvidersLoaded Location Provider 能否是已被加载。
String mNetworkLocationProviderPackageName 供应予搜小我式格式中止定位干事的包名。
String mGeocodeProviderPackageName 供应地舆位置码干事的包名。
LocationWorkerHandler mLocationHandler 这是一个 Handler,用来处置责罚位置信息更新和包更新两种旧事。
LocationProviderProxy mNetworkLocationProvider 以搜小我式格式供应定位干事的 Location Provider 的署理。
LocationProviderInterface mGpsLocationProvider 依托于 GPS 模块完成定位的 Location Provider。
ArrayList mProviders 一切的 Location Provider。
HashMap mProvidersByName 一切的 Location Provider,以名字为键存储在映照中。
Object mLock 作为内部完成的锁行使。
HashMap mLastKnownLocation 比来一次的定位信息,以 Location Provider 的称号为键的映照。
PackageMonitor mPackageMonitor 监测器,监测干事包更新事宜,并发送旧事给 mLocationHandler。

从这些字段中我们可以看出,LocationManagerService 中的次要内容都是围绕着 Location Provider 而完成的。

组织函数

上面我们来看一下 LocationManagerService 的组织方法:其代码如清单 5 所示:

清单 5. LocationManagerService 组织方法
public LocationManagerService(Context context) { super(); mContext = context; Resources resources = context.getResources(); mNetworkLocationProviderPackageName = resources.getString( com.android.internal.R.string.config_networkLocationProviderPackageName); mGeocodeProviderPackageName = resources.getString( com.android.internal.R.string.config_geocodeProviderPackageName); mPackageMonitor.register(context, null, true); if (LOCAL_LOGV) { Slog.v(TAG, "Constructed LocationManager Service"); } }

这个组织方法很庞杂,这里初始化了 mContext,mNetworkLocationProviderPackageName 和 mGeocodeProviderPackageName 三个字段,然先行使 mPackageMonitor 注册包更新的 context。
个中,mNetworkLocationProviderPackageName,mGeocodeProviderPackageName 这两个字段是经由进程读取成本文件的内容来初始化的。关于这两个字段的声名在表 2 中已提到过。

这里之所以将这两个包名放在内部成本文件中,同时经由进程 LocationProviderProxy 以署理的方法来行使这个干事,目的很显著:这样做可以在运转的时辰静态的交流干事。而 PackageMonitor 和 LocationWorkerHandler 就是呼应完成这一机制的类。

LocationProviderInterface

上文中我们说了,Location Provider 是真正获得位置信息的模块。在 android.location 包中,用 LocationProvider 这个接口来描写。而这一接口是供应给运用层 API 行使的,在 LocationManagerService 中,Location Provider 行使其他一个接口来描写,这就是 com.android.location.provider. LocationProviderInterface,LocationManagerService 对定位干事的完成均是经由进程挪用 LocationProviderInterface 来完成的。

LocationProviderInterface 对象存储在称号为 mProviders 和 mProvidersByName 的字段中(见表 2)。
LocationProviderInterface 接口的声名如表 3 所示:

表 3. LocationProviderInterface 接口声名
称号 声名
getName 取适合前 Location Provider 的称号
requiresNetwork 该 Location Provider 能否是需求汇集
requiresSatellite 该 Location Provider 能否是需求卫星
requiresCell 该 Location Provider 能否是需求手机蜂窝旗子暗记
hasMonetaryCost 该 Location Provider 能否是需求花消金钱
supportsAltitude 该 Location Provider 能否是支持海拔高度信息
supportsSpeed 该 Location Provider 能否是支持速度信息
supportsBearing 该 Location Provider 能否是支持方位信息
getPowerRequirement 获得该 Location Provider 的耗电量级别
meetsCriteria 该 Location Provider 能否是能相符指定的 Criteria
getAccuracy 获得该 Location Provider 的精度级别
isEnabled 查询有用形态
enable 使该 Location Provider 有用
disable 使该 Location Provider 无效
getStatus 获得该 Location Provider 的形态
getStatusUpdateTime 获得该 Location Provider 的形态更新时辰
enableLocationTracking 使该 Location Provider 位置追踪有用
requestSingleShotFix 要求 Single Shot Fix
getInternalState 获得该 Location Provider 的内部形态
setMinTime 设置最小时辰
updateNetworkState 使该 Location Provider 更新汇集形态
updateLocation 使该 Location Provider 更新位置
sendExtraCommand 使该 Location Provider 发送辅佐的敕令
addListener 增加监听器
removeListener 移除监听器

在 Android 源码中,完成 LocationProviderInterface 接口的类有四个,它们如表 4 所示:

表 4. LocationProviderInterface 的完成类
称号 声名
GpsLocationProvider 行使 Gps 卫星定位,最准确的定位体式格式。
PassiveProvider 该 Provider 其实不真正触发定位的更新,而是行使其他 Provider 来完成位置申报。
LocationProviderProxy 行使汇集完成定位的干事的署理。汇集定位依托于手机旗子暗记的基站或 Wifi 接入点作为定位的根蒂根抵。留意该类只是个署理,其实不包括真实的完成逻辑。
MockProvider 为了辅佐测试的模拟完成类。

那末,在 LocationManagerService 中,对 LocationProviderInterface 的加载是在什么时辰完成的呢?上面我们已看过 LocationManagerService 的组织方法了,并没有看到这部份内容。
其实,LocationManagerService 是一个线程类,除组织函数之外,在其 run 方法中又完成了其他一部份的初始化责任,主假定挪用其 initialize 方法。
在 initialize 方法中挪用了 loadProviders 方法,loadProviders 这个方法中完成了 Location Provider 的加载责任。
该方法又经由同步加锁和异常的包装,最终的完成方法是 _loadProvidersLocked。
_loadProvidersLocked 方法的代码如清单 5 所示:

清单 5. _loadProvidersLocked 方法代码
private void _loadProvidersLocked() { // Attempt to load "real" providers first  if (GpsLocationProvider.isSupported()) { // Create a gps location provider  GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this); mGpsStatusProvider = gpsProvider.getGpsStatusProvider(); mNetInitiatedListener = gpsProvider.getNetInitiatedListener(); addProvider(gpsProvider); mGpsLocationProvider = gpsProvider; } // create a passive location provider, which is always enabled  PassiveProvider passiveProvider = new PassiveProvider(this); addProvider(passiveProvider); mEnabledProviders.add(passiveProvider.getName()); // initialize external network location and geocoder services.  // The initial value of mNetworkLocationProviderPackageName and  // mGeocodeProviderPackageName is just used to determine what  // signatures future mNetworkLocationProviderPackageName and  // mGeocodeProviderPackageName packages must have. So alternate  // providers can be installed under a different package name  // so long as they have the same signature as the original  // provider packages.  if (mNetworkLocationProviderPackageName != null) { String packageName = findBestPackage( LocationProviderProxy.SERVICE_ACTION, mNetworkLocationProviderPackageName); if (packageName != null) { mNetworkLocationProvider = new LocationProviderProxy( mContext, LocationManager.NETWORK_PROVIDER, packageName, mLocationHandler); mNetworkLocationProviderPackageName = packageName; addProvider(mNetworkLocationProvider); } } if (mGeocodeProviderPackageName != null) { String packageName = findBestPackage( GeocoderProxy.SERVICE_ACTION, mGeocodeProviderPackageName); if (packageName != null) { mGeocodeProvider = new GeocoderProxy(mContext, packageName); mGeocodeProviderPackageName = packageName; } } updateProvidersLocked(); }

这段代码首先剖断当前配备能否是支持 Gps,假定支持,则会树立 GpsLocationProvider。
接着,树立了一个 PassiveProvider 对象。
然后,凭证 mNetworkLocationProviderPackageName 字段树立 LocationProviderProxy 对象。(在树立 LocationProviderProxy 的时辰,packageName 参数是依托于 mNetworkLocationProviderPackageName 的。这个字段是在 LocationManagerService 的组织函数中初始化的。)
最初,凭证 mGeocodeProviderPackageName 字段树立 GeocoderProxy 对象(mGeocodeProviderPackageName 一样是在 LocationManagerService 的组织函数中初始化的)。
这里需求留意的是,LocationProviderProxy 和 GeocoderProxy 两个对象能否是会树立,是依托于零星情形的。在树立它们之前,都经由进程 findBestPackage 去检查最适合的包,而且查找的进程指定了 Intent 的 Action。只需在零星中已有 Service 支持呼应的 Intent 的 Action 时,才会找到适合的包,才会树立这两个对象,否则,假定零星没有找到适合的 Service 就不会树立这两个对象,因为零星基本没法行使这两项干事。

上文我们提到 LocationProviderInterface 有四个完成类。而在 LocationManager 中,界说了三个常量来标示定位干事的供应者:

  • public static final String NETWORK_PROVIDER = “network”;
  • public static final String GPS_PROVIDER = “gps”;
  • public static final String PASSIVE_PROVIDER = “passive”;

显著,这三个常量分离对应着三种 LocationProviderInterface 的完成类(不包括 MockProvider,因为该类仅仅是供应给测试用的)。

内部类

除此之外,LocationManagerService 中还包括了一些内部类,它们的声名如表 5 所示:

表 5. LocationManagerService 中包括的内部类
类名 声名
LocationWorkerHandler 旧事处置责罚器,次要处置责罚位置交换和汇集定位的包更新两种旧事。例如,当有 Provider 发明有位置更新时,会首先发送旧事到 LocationManagerService,而在 LocationManagerService 中该旧事最终就是在 LocationWorkerHandler 被处置责罚的。
LpAccuracyComparator 对 LocationProviderInterface 按精度排序的比拟器。
LpCapabilityComparator 对 LocationProviderInterface 按能力排序的比拟器。
LpPowerComparator 对 LocationProviderInterface 按耗电量排序的比拟器。
ProximityAlert 临近距离位置的警报器。
ProximityListener 临近距离位置的警报监听器。
Receiver 包装器,用来包括一个 ILocationListener 或 PendingIntent 来收受接管位置更新。个中,告知位置更新的方法为 callLocationChangedLocked。
SettingsObserver 针对零星设置的监听器。
UpdateRecord 留存更新记载的数据。

GpsLocationProvider 的完成

上文已提到,LocationProviderInterface 的完成类有四个。而理想上,在挪动配备上我们可真正用于定位干事的完成常日只需两种:一种是经由进程 Gps 模块,一种是经由进程汇集。
在剖析 LocationManagerService 的代码的时辰我们已看到,对经由进程汇集定位的完成理论上是经由进程署理的体式格式来完成的,面前的完成是可以在运转时静态的交流的,是纷歧定的(在 Android 源码中,经由进程搜小我式格式定位的默许干事包名是:com.谷歌.android.location, 很显著,这是由 Google 供应完成的干事,但这部份代码是不包括在 Android 源码中的,经由进程包名的设置配备铺排,很随意疏忽的就做到了将完成与依托中止隔离了,这是一种异常好的软件设计)。
相反,Gps 模块的定位完成是一定的,是我们可以参考的。
所以,上面我就来看看经由进程 Gps 模块来完成定位的完成类:GpsLocationProvider(位于:frameworks/base/services/java/com/android/server/location/GpsLocationProvider.java)。
GpsLocationProvider 类包括了除夜量的常量界说,这些常量除夜部份是和 HAL 层(关于 HAL 层,我们稍后会讲授)中的界说绝对应的,表 6 列出了比拟主要的一些常量:

表 6. GpsLocationProvider.java 中包括的主要的常量
称号 声名
GPS_POSITION_MODE_STANDALONE 0 GPS 零丁运转方法
GPS_POSITION_MODE_MS_BASED 1 AGPS MS-Based 方法
GPS_POSITION_MODE_MS_ASSISTED 2 AGPS MS-Assisted 方法
GPS_POSITION_RECURRENCE_PERIODIC 0 以固定的距离重复收受接管 GPS 调解
GPS_POSITION_RECURRENCE_SINGLE 1 一次性收受接管 GPS 调解
GPS_STATUS_NONE 0 GPS 形态未知
GPS_STATUS_SESSION_BEGIN 1 最先导航
GPS_STATUS_SESSION_END 2 导航停止
GPS_STATUS_ENGINE_ON 3 GPS 引擎最先责任
GPS_STATUS_ENGINE_OFF 4 GPS 引擎关闭
GPS_REQUEST_AGPS_DATA_CONN 1 GPS 模块为 AGPS 要求数据衔接
GPS_RELEASE_AGPS_DATA_CONN 2 AGPS 数据衔接关闭
GPS_AGPS_DATA_CONNECTED 3 AGPS 数据衔接最先
GPS_AGPS_DATA_CONN_DONE 4 AGPS 数据衔接完成
GPS_AGPS_DATA_CONN_FAILED 5 AGPS 数据衔接
LOCATION_INVALID 0 无效位置
LOCATION_HAS_LAT_LONG 1 位置信息中包括了经度和纬度信息
LOCATION_HAS_ALTITUDE 2 位置信息中包括了海拔信息
LOCATION_HAS_SPEED 4 位置信息中包括了速度信息
LOCATION_HAS_BEARING 8 位置信息中包括了方位信息
LOCATION_HAS_ACCURACY 16 位置信息中包括了准确度信息
GPS_CAPABILITY_SCHEDULING 0x0000001 GPS 支持设计能力
GPS_CAPABILITY_MSB 0x0000002 GPS 支持 MS-Based AGPS
GPS_CAPABILITY_MSA 0x0000004 GPS 支持 MS-Assisted
GPS_CAPABILITY_SINGLE_SHOT 0x0000008 GPS 支持 single-shot
GPS_CAPABILITY_ON_DEMAND_TIME 0x0000010 GPS 支持 demand time injection

GpsLocationProvider 挪用 JNI 层为上层供应干事。它行使了 Android 供应的 Looper 和 Handler 机制,这使得它可以在一个自力的线程中完成要求的处置责罚,这些要求的呼应在 Looper 所在的线程,而不是要求所在的线程,是以不会壅塞要求的线程。
为了便于理解,我们将 GpsLocationProvider 中的方法分为几类来谈论(某些方法可以不止属于一类):

初始化方法

GpsLocationProvider 的组织函数代码内容较多,这里就不贴出了。总的来说,组织函数中次要完成了以下几个责任:

  • 初始化了一系列的字段,包括获得处置责罚时辰,电源,闹钟,汇集衔接等功用的零星干事
  • 在零星中注册了一个 BroadcastReceiver,这个 BroadcastReceiver 的浸染是担负外行使 Gps 模块时呼应对闹钟和短旧事的事宜处置责罚。
  • 读取 Gps 模块的内部设置配备铺排文件,这是一个属性文件,该文件是用来设置配备铺排 Gps 模块扩展年夜功用的干事器信息,例如 XTRA 干事器,NTP 干事器等信息。该文件的位置记载在 PROPERTIES_FILE 字段中,它的值是”/etc/gps.conf”。
  • 树立并启动 GpsLocationProviderThread,这是一个线程类,对 Gps 模块功用的要求都是在这个线程中完成的。

GpsLocationProviderThread 的 run 方法代码如清单 7 所示:

清单 7. GpsLocationProviderThread 类中 run 方法代码
public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); initialize(); Looper.prepare(); mHandler = new ProviderHandler(); // signal when we are initialized and ready to go  mInitializedLatch.countDown(); Looper.loop(); }

这个方法的处置责罚逻辑以下:
首先,将当提高程级别设置为后台级别,这是一个绝对较低的级别。然后挪用 initialize 方法,在这个方法中,将 mBroadcastReciever 注册到 GpsLocationProviderThread 所在的线程中。
接着,行使 Looper.prepare(); 将当前哨程初始化为 Looper。然后,树立了一个 ProviderHandler 对象。ProviderHandler 是 GpsLocationProvider 的内部类,它连续自 Handler,它担负处置责罚经由进程 Message 发送到当前哨程的要求。Looper 和 Handler 的合营使适合前哨程可以自力于主线程完成恳乞降处置责罚。ProviderHandler 是一个很主要的类,GpsLocationProvider 中许多要求都是依托 Message 机制完成的,例如当挪用其 updateNetworkState,updateLocation 等方法时,都是向 GpsLocationProviderThread 线程发送旧事,这些旧事的处置责罚都是在 ProviderHandler 中完成的(即 GpsLocationProviderThread 所在线程)。
初始化的最初,为了告知主线程初始化已完成,挪用了 mInitializedLatch.countDown(),这行代码和 GpsLocationProvider 组织函数中:mInitializedLatch.await() 是对应的。挪用 mInitializedLatch.await() 会致使线程壅塞,直到有其他一个线程挪用 mInitializedLatch.countDown() 为止。这里这样做的启事是因为有部份初始化责任在 GpsLocationProviderThread 线程中完成,这和主线程是互相自力的。为了在担保只需在一切初始化责任完成之后 GpsLocationProvider 组织函数才华前往,所以行使了 CountDownLatch 来担保。

完成 LocationProviderInterface 接口的方法

这些方法是完成 LocationProviderInterface 接口的方法,它们已在表 3 中声名。

向 GpsLocationProviderThread 发送要求的方法

这些方法经由进程 Message 机制发送要求到 GpsLocationProviderThread,它们如表 7 所示:

表 7. GpsLocationProviderThread 的次要方法
称号 声名
enable 见表 2
disable 见表 2
enableLocationTracking 见表 2
requestSingleShotFix 见表 2
updateNetworkState 见表 2
requestUtcTime 要求 Utc 时辰信息
xtraDownloadRequest XTRA 下载要求
updateLocation 见表 2
addListener 见表 2
removeListener 见表 2

ProviderHandler 挪用的处置责罚方法

ProviderHandler 对要求的处置责罚逻辑并没有直接写在 handleMessage 方法中,而是对每一个要求专门用一个方法来处置责罚,这些方法如表 8 所示。这些方法的完成常日都是依托于表 9 的外埠方法的。

表 8. ProviderHandler 的方法
称号 声名
handleEnable 使该 Provider 有用
handleDisable 使该 Provider 无效
handleEnableLocationTracking 使该 Provider 最先记载位置追踪信息
handleRequestSingleShot 使该 Provider 完成 singleShot 要求
handleUpdateNetworkState 处置责罚汇集形态更新
handleInjectNtpTime 处置责罚 Ntp 时辰注入
handleDownloadXtraData 处置责罚下载 Xtra 数据
handleUpdateLocation 处置责罚位置
更新
handleAddListener 增加监听器
handleRemoveListener 删除监听器

外埠方法

Gps 模块的功用完成最终需求挪用硬件来完成,这些完成必需经由进程 C/C++ 措辞才华完成。为了能在 GpsLocationProvider.java 中挪用到这些功用,GpsLocationProvider 中包括了许多的 native 方法,这些方法如表 9 所示,这些方法都是以 JNI 的体式格式来完成的。这些 JNI 的完成方法位于 com_android_server_location_GpsLocationProvider.cpp(位于:frameworks/base/services/jni)中。

表 9. GpsLocationProvider 中包括的 native 方法
称号 声名
class_init_native 类的初始化方法
native_is_supported 能否是支持 Gps 模块
native_init 初始化方法
native_cleanup 担负清算责任
native_set_position_mode 设置位置方法
native_start 最先导航
native_stop 住手导航
native_delete_aiding_data 删除辅佐信息
native_read_sv_status 读取 SV 形态
native_read_nmea 读取 nmea 信息
native_inject_location 注入位置信息
native_inject_time 注入时辰信息
native_supports_xtra 能否是支持 XTRA
native_inject_xtra_data 注入 XTRA 数据
native_get_internal_state 获得内部形态
native_agps_data_conn_open 掀开 AGps 数据衔接
native_agps_data_conn_closed 关闭 AGps 数据衔接
native_agps_data_conn_failed AGps 数据衔接丢失落败
native_agps_ni_message AGps NI(Network-initiated)旧事
native_set_agps_server 设置 AGPS 干事器
native_send_ni_response 发送 NI 呼应
native_agps_set_ref_location_cellid AGPS 设置引用位置
native_agps_set_id AGPS 设置 id
native_update_network_state 更新汇集形态

被 JNI 方法回调的方法

GpsLocationProvider 中最初一类方法是被 JNI 方法回调的方法。在 JNI 的完成中,经由进程这些方法的回调来传递 JNI 层的实行效果。它们如表 10 所示:

表 10. GpsLocationProvider 中被 JNI 回调的方法
称号 声名
reportLocation 申报位置
reportStatus 报起诉态
reportSvStatus 申报 SV 形态
reportAGpsStatus 申报 AGps 形态
reportNmea 申报 Nmea
setEngineCapabilities 设置引擎能力
xtraDownloadRequest XTRA 下载要求
reportNiNotification 申报 NI 告知
requestRefLocation 要求引用位置
requestSetID 要求设置 id
requestUtcTime 要求 Utc 时辰

JNI 层与 HAL 层

JNI(Java Native Interface) 层依托于 HAL 层为上层供应干事。
HAL(Hardware Abstract Layer) 层是对硬件的笼统,这是悉数模块完成的最底层。

JNI 层

上文中我们已提到,Gps 模块 JNI 层的完成在 com_android_server_location_GpsLocationProvider.cpp(位于:frameworks/base/services/jni)文件中。该层依托 HAL 层接口,供应对 GpsLocationProvider.java 中外埠方法的完成。这些外埠方法和 JNI 方法是一一对应(关于 GpsLocationProvider.java 中的外埠方法请参阅表 9。)。
这类对应关系是在 register_android_server_location_GpsLocationProvider 方法中,经由进程 jniRegisterNativeMethods 函数竖立的。
两个文件中函数的对应关系如表 11 所示:

表 11. GpsLocationProvider 中的 native 方法及其 JNI 完成方法的对应关系
GpsLocationProvider.java 中的方法名 com_android_server_location_GpsLocationProvider.cpp 中对应的完成方法
class_init_native android_location_GpsLocationProvider_class_init_native
native_is_supported android_location_GpsLocationProvider_is_supported
native_init android_location_GpsLocationProvider_init
native_cleanup android_location_GpsLocationProvider_cleanup
native_set_position_mode android_location_GpsLocationProvider_set_position_mode
native_start android_location_GpsLocationProvider_start
native_stop android_location_GpsLocationProvider_stop
native_delete_aiding_data android_location_GpsLocationProvider_delete_aiding_data
native_read_sv_status android_location_GpsLocationProvider_read_sv_status
native_read_nmea android_location_GpsLocationProvider_read_nmea
native_inject_location android_location_GpsLocationProvider_inject_location
native_inject_time android_location_GpsLocationProvider_inject_time
native_supports_xtra android_location_GpsLocationProvider_supports_xtra
native_inject_xtra_data android_location_GpsLocationProvider_inject_xtra_data
native_get_internal_state android_location_GpsLocationProvider_get_internal_state
native_agps_data_conn_open android_location_GpsLocationProvider_agps_data_conn_open
native_agps_data_conn_closed android_location_GpsLocationProvider_agps_data_conn_closed
native_agps_data_conn_failed android_location_GpsLocationProvider_agps_data_conn_failed
native_agps_ni_message android_location_GpsLocationProvider_send_ni_response
native_set_agps_server android_location_GpsLocationProvider_set_agps_server
native_send_ni_response android_location_GpsLocationProvider_send_ni_response
native_agps_set_ref_location_cellid android_location_GpsLocationProvider_agps_set_reference_location_cellid
native_agps_set_id android_location_GpsLocationProvider_agps_set_id
native_update_network_state android_location_GpsLocationProvider_update_network_state

这个中,最为主要的是两个初始化方法:
一个是 android_location_GpsLocationProvider_class_init_native 方法。这个方法在 GpsLocationProvider 类中的静态初始化块中被挪用,它的浸染有三个:

  • 在 JNI 层初始化对 GpsLocationProvider.java 中回调方法的引用。
  • 考试考试掀开 Gps 配备。
  • 假定 Gps 配备掀开胜利,则获得 Gps 扩展年夜接口的指针,它们一共有五种,分离是:GpsXtraInterface,AGpsInterface,GpsNiInterface,GpsDebugInterface,AGpsRilInterface。这些组织的声名见表 12。

其他一个是 android_location_GpsLocationProvider_init 方法。这个方法的浸染是:

  • 考试考试初始化 Gps 配备模块,假定初始化丢失落败,直接前往 false。
  • 考试考试初始化 XTRA 扩展年夜接口,假定初始化丢失落败,则使 sGpsXtraInterface 指向 NULL。
  • 考试考试初始化 AGpsInterface,GpsNiInterface,AGpsRilInterface 扩展年夜组件。

HAL 层

HAL 层是与硬件相接触的一层,该层行使 C 措辞完成。
Gps 模块的 HAL 层的头文件是 gps.h(位于:hardware/libhardware/include/hardware)。gps.h 中包括了许多的常量(这些常量是和 GpsLocationProvider 中的内容绝对应的,请拜见表 6)和组织体的界说。这个中,最主要的组织体就是 GpsInterface,这是对 Gps 模块的笼统。它的内容如清单 7 所示:

清单 6. GpsInterface 组织体界说
typedef struct { // 设置为 sizeof(GpsInterface)  size_t size; // 掀开 Gps 模块,供应接口完成的回调函数 int (*init)( GpsCallbacks* callbacks ); // 最先导航 int (*start)( void ); // 住手导航 int (*stop)( void ); // 关闭 Gps 模块 void (*cleanup)( void ); // 注入当前时辰 int (*inject_time)(GpsUtcTime time, int64_t timeReference, int uncertainty); // 注入当前位置 int (*inject_location)(double latitude, double longitude, float accuracy); // 指定下一次启动时,弗成使 flags 所界说的信息 void (*delete_aiding_data)(GpsAidingData flags); // 设置位置方法 int (*set_position_mode)(GpsPositionMode mode, GpsPositionRecurrence recurrence, uint32_t min_interval, uint32_t preferred_accuracy, uint32_t preferred_time); // 获得扩展年夜信息的指针 const void* (*get_extension)(const char* name); } GpsInterface;

除此之外,该头文件中还界说了与 Gps 模块相关的其他组织体,它们如表 12 所示:

表 12. gps.h 中界说的其他组织体
称号 声名
GpsLocation 描写 Gps 位置,包括经度,维度,海拔,速度,方位,精度等信息。
GpsStatus 描写 Gps 形态
GpsSvInfo 描写 Gps SV 信息
GpsSvStatus 描写 Gps SV 形态
AGpsRefLocationCellID 描写 AGps 引用位置单元 Id
AGpsRefLocationMac 描写 AGps 引用位置 Mac
AGpsRefLocation 描写 AGps 引用位置
GpsCallbacks 描写 Gps 回调函数
GpsXtraCallbacks 描写 XTRA 接口回调函数
GpsXtraInterface 供应 XTRA 支持的扩展年夜接口
GpsDebugInterface 供应 DEBUG 支持的扩展年夜接口
AGpsStatus 描写 AGps 形态
AGpsCallbacks 描写 AGps 接口回调函数
AGpsInterface 供应 AGps 支持的扩展年夜接口
GpsNiNotification 描写一次 NI(Network-initiated)要求
GpsNiCallbacks 描写 Gps NI 回调函数组织体
GpsNiInterface 供应 NI 支持的扩展年夜接口
AGpsRilCallbacks 描写 AGps Ril 回调函数组织体
AGpsRilInterface 供应 AGPS-RIL 支持的扩展年夜接口

因为篇幅所限,这里就不将这些内容睁开讲授,请读者自行参阅 Android 源码。
同时,Android 源码中包括了对高通公司的 Gps 模块的完成,其代码位于 hardware/qcom/gps 中。

总结

综合以上常识,我们来看一下当硬件接遭到位置更新之后,为了告知这个信息,悉数挪用关系是若何的 ( 为了描写随意,下文将 com_android_server_location_GpsLocationProvider.cpp 简称为 GpsLocationProvider.cpp)。

  1. 当硬件检测到有位置更新之后,最初挪用的是 GpsLocationProvider.cpp 中的 location_callback 函数。
  2. location_callback 函数中对应的是挪用 GpsLocationProvider.java 中的 reportLocation 方法。
  3. GpsLocationProvider.java 中的 reportLocation 方法会挪用 ILocationManager 的 reportLocation 方法,然后是挪用 LocationManagerService 的 reportLocation 方法。
  4. LocationManagerService 的 reportLocation 方法中会对 LocationWorkerHandler 发送旧事 MESSAGE_LOCATION_CHANGED。该旧事在 LocationWorkerHandler 的 handleMessage 方法中被处置责罚。处置责罚方法中会挪用 LocationProviderInterface 的 updateLocation 方法和 LocationManagerService 的 handleLocationChangedLocked 的方法。前者对 Gps 模块来说就是挪用 GpsLocationProvider 的 updateLocation 方法。
  5. GpsLocationProvider 的 updateLocation 方法会对 ProviderHandler 发送旧事 UPDATE_LOCATION,该旧事在 ProviderHandler 的 handler 方法中被处置责罚,处置责罚的方法是挪用 handleUpdateLocation 方法,该方法中会挪用 native_inject_location 方法以注入。
  6. 而 LocationManagerService 的 handleLocationChangedLocked 的方法会将最新的位置寄存到 mLastKnownLocation 中。至此,即可以经由进程 LocationManagerService 的 getLastKnownLocation 方法取获得最新更新的位置信息了。

上面是上述的逻辑递次图。因为挪用进程比拟庞杂,所以分红了两部份。

图 4 描写了上述步骤的 1 ~ 4,图 5 描写了上述步骤的 4 ~ 6

图 4. 位置更新后的挪用关系(前半部份)

检查除夜图

图 5. 位置更新后的挪用关系(后半部份)

检查除夜图

最初,我们来总结一下定位干事的完成牵扯到的文件及所处途径,它们如表 13 所示。

表 13. 定位干事完成中牵扯到的文件一览
途径 声名
frameworks/base/location/java/android 包括供应给运用垦荒的 API
frameworks/base/services/java/com/android/server 包括 LocationManagerService.java
frameworks/base/core/java/android/os 包括 ServiceManager.java
frameworks/base/core/java/android/app 包括 ContextImpl.java
system/core/rootdir 包括 init.rc
frameworks/base/services/java/com/android/server/location 包括 LocationManagerService.java
frameworks/base/services/jni 包括
com_android_server_location_GpsLocationProvider.cpp
hardware/libhardware/include/hardware 包括了 HAL 层的接口
hardware/qcom/gps 包括了对高通公司的 Gps 模块的完成

后面我们说了,在 Android 零星中,一切的零星干事的完成组织都是相反的。信任读者在理解了定位干事的完成之后再去理解其他的零星干事是比拟随意疏忽的。

点赞 作者:Noblog
原文地址http://ju.outofmemory.cn/entry/111182 写的很详细,很多函数的作用都标明了,学习了。

Android 系统中 Location Service 的实现与架构

Android 系统中 Location Service 的实现与架构

更多相关文章

  1. 浅谈Java中Collections.sort对List排序的两种方法
  2. Python list sort方法的具体使用
  3. python list.sort()根据多个关键字排序的方法实现
  4. android 沉浸式状态栏的三种方法
  5. Android菜单实现两种方式
  6. Android消息处理机制1——Handler
  7. Android(安卓)Studio 初体验
  8. Android常用的工具类
  9. android sqlist中游标下标越界问题解决方案

随机推荐

  1. 服务应用突然宕机了?别怕,Dubbo 会帮你自动
  2. DoDAF2.0方法论探究
  3. 什么时候不能在 Node.js 中使用 Lock Fil
  4. IP地址简介与配置
  5. java并发之TimeUnit理解
  6. 函数式编程思维在三行代码情书中的应用
  7. 跨年游-四姑娘山大峰/二峰初级雪山攀登、
  8. 用 Vue 开发自己的 Chrome 扩展[每日前端
  9. 我还在生产玩 JDK7,JDK 15 却要来了!|新特
  10. 这才是GraphQL最详细的解释[每日前端夜话