更新数据的开始都是源于requestLocationUpdates或者requestSingleUpdate接口,那么接下来让我们来看看下面的解析吧。
Application层
提供给开发者的接口可以认为两种requestSingleUpdate和requestLocationUpdates,前者获取一次,而或者可以根据大于多少时间再更新,大于多少距离再更新。但是不论差别多大,它们的底层都是调用requestLocationUpdates(LocationRequest request, LocationListener listener,
Looper looper, PendingIntent intent)
实现的。
frameworks/base/location/java/android/location/LocationManager.java
private void requestLocationUpdates(LocationRequest request, LocationListener listener,
Looper looper, 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) {
throw e.rethrowFromSystemServer();
}
}
先来说说LocationManagerService,在SystemServer启动的时候调用ServiceManager.addService(Context.LOCATION_SERVICE, location);
注册了LocationManagerService服务,当客户端调用Context.getSystemService(Context.LOCATION_SERVICE)
时,最终LocationManager对象会通过ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE);
获得服务端的LocationManagerService对象mService,而接下来LocationManager和LocationManagerService这两者就可以通过binder实现了app进程和framework进程间的通信。
知道这些我们再来看看LocationManagerService#requestLocationUpdates方法,其注入了四个参数,如下几个。
- request: LocationRequest类对象
- transport:ILocationListener类对象
- intent:PendingIntent类对象
- packageName:使用定位功能的应用包名
1.LocationRequest类对象
该类是一个可持久化的Parcelable,为了通过binder到达LocationManagerService,就必须这样做,所以基本可以把它看成一个运输参数的运输车。
private int mQuality = POWER_LOW;
private long mInterval = 60 * 60 * 1000; // 60 minutes
private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR); // 10 minutes
private boolean mExplicitFastestInterval = false;
private long mExpireAt = Long.MAX_VALUE; // no expiry
private int mNumUpdates = Integer.MAX_VALUE; // no expiry
private float mSmallestDisplacement = 0.0f; // meters
private WorkSource mWorkSource = null;
private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps
private String mProvider = LocationManager.FUSED_PROVIDER; // for deprecated APIs that explicitly request a provider
这些参数的值都是外部调用者提供。见名只意,我们就不做细讲,等在LocationManagerService服务端用到再来将有啥用途。
2.ILocationListener类对象
该类是客户端用来响应服务端的回调接口。其回调接口如下:
package android.location;
import android.location.Location;
import android.os.Bundle;
/**
* {@hide}
*/
oneway interface ILocationListener
{
void onLocationChanged(in Location location);
void onStatusChanged(String provider, int status, in Bundle extras);
void onProviderEnabled(String provider);
void onProviderDisabled(String provider);
}
每一个LocationListener对象对应着一个响应服务端的ILocationListener对象,通过ILocationListener的回调方法将底层的响应传给实现了LocationListener接口的应用。这里说一下服务端的处理方式,采用锁机制同步处理来至客户端并发下发的请求。
不过有个地方值得我们关注一下。
ListenerTransport(LocationListener listener, Looper looper) {
mListener = listener;
if (looper == null) {
mListenerHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
_handleMessage(msg);
}
};
} else {
mListenerHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
_handleMessage(msg);
}
};
}
}
ILocationListener的实现类ListenerTransport通过外部调用者提供的Looper,来决定handleMessage是在主线程还是在工作线程,这也就意味着允许来自于底层的定位数据可以在在主线程刷新。
3.PendingIntent类对象
该类用于也是用于获取来自底层的定位数据,不过与ILocationListener不同的是,注册ILocationListener的界面会收到更新的定位数据,而PendingIntent则是将更新的定位数据传给其他的界面。还有一点需要注意的ILocationListener和PendingIntent不能同时存在,请求数据更新时,只能二选一。
Framework-Java层
frameworks/base/services/core/java/com/android/server/LocationManagerService.java
@Override
public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
PendingIntent intent, String packageName) {
...
// providers may use public location API's, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
// We don't check for MODE_IGNORED here; we will do that when we go to deliver
// a location.
checkLocationAccess(pid, uid, packageName, allowedResolutionLevel);
synchronized (mLock) {
Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
packageName, workSource, hideFromAppOps);
requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
服务端通过锁机制来处理客户端并发下发请求,所以这样就会导致请求在服务端像队列一样按照顺序被处理,而一个请求对应一个Receiver,Receiver是用来介绍底层上报的定位数据,并回调给客户端。
在锁代码块里面除了创建Receiver还有发送请求的逻辑代码,我们接着往下看。
private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
int pid, int uid, String packageName) {
// Figure out the provider. Either its explicitly request (legacy use cases), or
// use the fused provider
if (request == null) request = DEFAULT_LOCATION_REQUEST;
String name = request.getProvider();
...
boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid);
if (isProviderEnabled) {
applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
receiver.callProviderEnabledLocked(name, false);
}
// Update the monitoring here just in case multiple location requests were added to the
// same receiver (this request may be high power and the initial might not have been).
receiver.updateMonitoring(true);
}
requestLocationUpdatesLocked方法核心方法就是applyRequirementsLocked方法。为了下面的分析方便,我们这边还需要知道一些背景知识。
1.LocationProviderInterface接口实现类有以下三种:
- GnssLocationProvider: 定位数据来源于gps
- LocationProviderProxy:它其实是个proxy,真正的provider是NetworkLocationProvider,该类的实现需要一些服务厂商提供。国内的服务厂商有高德、百度,国外有google等
- PassiveProvider:定位数据来自于其他应用
这三者在启动Location服务时(SystemServer阶段),就通过在systemRunning方法中调用loadProvidersLocked方法初始化了这三个provider。
2.Receiver
在Receiver类里面有个成员变量mUpdateRecords,是Map类型,用来映射provider和UpdateRecord,即一种provider对应一个UpdateRecord。而对LocationManagerService的成员变量mRecordsByProvider,也是个Map类型,不过是一个provider映射一个UpdateRecord集合。
private class UpdateRecord {
final String mProvider;
final LocationRequest mRealRequest; // original request from client
LocationRequest mRequest; // possibly throttled version of the request
final Receiver mReceiver;
boolean mIsForegroundUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
...
}
UpdateRecord用来处理一些请求的变化,比如发送请求的组件不是前台,那么就会让本来要求2分钟间隔更新数据,变成默认要求的半小时,并且将原来的请求节流改成新的请求。
罗里吧嗦讲完了上面的一些知识,现在终于要来讲最为重要的applyRequirementsLocked方法。
private void applyRequirementsLocked(String provider) {
...
if (records != null) {
for (UpdateRecord record : records) {
if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
if (checkLocationAccess(
record.mReceiver.mIdentity.mPid,
record.mReceiver.mIdentity.mUid,
record.mReceiver.mIdentity.mPackageName,
record.mReceiver.mAllowedResolutionLevel)) {
LocationRequest locationRequest = record.mRealRequest;
long interval = locationRequest.getInterval();
if (!isThrottlingExemptLocked(record.mReceiver.mIdentity)) {
if (!record.mIsForegroundUid) {
interval = Math.max(interval, backgroundThrottleInterval);
}
if (interval != locationRequest.getInterval()) {
locationRequest = new LocationRequest(locationRequest);
locationRequest.setInterval(interval);
}
}
record.mRequest = locationRequest;
providerRequest.locationRequests.add(locationRequest);
if (interval < providerRequest.interval) {
providerRequest.reportLocation = true;
providerRequest.interval = interval;
}
}
}
}
if (providerRequest.reportLocation) {
...
}
}
...
p.setRequest(providerRequest, worksource);
}
在applyRequirementsLocked方法中会先判断请求是在前台发出还是后台发出。
在Android 8.0提出来后台限制的规定,只允许后台应用每小时接收几次位置更新,前台不受影响。上面的代码就是这个限制的实现。如果想了解更多这个限制可以参考这一篇文章Background Location Limits。然后在判断请求的interval是否小临界值,用一个临界值来限制请求的interval,如果由于频繁就会被流失掉。最后调用LocationProviderInterface#setRequest方法,之前我们已经说过LocationProviderInterface接口的实现类有三个,这里我们挑选GnssLocationProvider和LocationProviderProxy来进行分析。
先来看看GnssLocationProvider
@Override
public void setRequest(ProviderRequest request, WorkSource source) {
sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
}
sendMessage方式是对Handler发送消息的包装,GnssLocationProvider的内部类ProviderHandler是在工作线程处理来自其他线程的消息的类,这样可以避免framework主线程因执行太多任务而造成响应慢。我们来继续看看ProviderHandler到底能处理哪些消息。
@Override
public void handleMessage(Message msg) {
int message = msg.what;
switch (message) {
case ENABLE:
if (msg.arg1 == 1) {
handleEnable();
} else {
handleDisable();
}
break;
case SET_REQUEST:
GpsRequest gpsRequest = (GpsRequest) msg.obj;
handleSetRequest(gpsRequest.request, gpsRequest.source);
break;
case UPDATE_NETWORK_STATE:
handleUpdateNetworkState((Network) msg.obj);
break;
case REQUEST_SUPL_CONNECTION:
handleRequestSuplConnection((InetAddress) msg.obj);
break;
case RELEASE_SUPL_CONNECTION:
handleReleaseSuplConnection(msg.arg1);
break;
case INJECT_NTP_TIME:
handleInjectNtpTime();
break;
case DOWNLOAD_XTRA_DATA:
handleDownloadXtraData();
break;
case INJECT_NTP_TIME_FINISHED:
mInjectNtpTimePending = STATE_IDLE;
break;
case DOWNLOAD_XTRA_DATA_FINISHED:
mDownloadXtraDataPending = STATE_IDLE;
break;
case UPDATE_LOCATION:
handleUpdateLocation((Location) msg.obj);
break;
case SUBSCRIPTION_OR_SIM_CHANGED:
subscriptionOrSimChanged(mContext);
break;
case INITIALIZE_HANDLER:
handleInitialize();
break;
}
if (msg.arg2 == 1) {
// wakelock was taken for this message, release it
mWakeLock.release();
if (Log.isLoggable(TAG, Log.INFO)) {
Log.i(TAG, "WakeLock released by handleMessage(" + messageIdAsString(message)
+ ", " + msg.arg1 + ", " + msg.obj + ")");
}
}
}
ProviderHandler除了能够处理SET_REQUEST消息还能够处理下列消息
- GnssLocationProvider初始化的INITIALIZE_HANDLER消息
- SIM卡发生变化的SUBSCRIPTION_OR_SIM_CHANGED消息
- 网络可用的UPDATE_NETWORK_STATE消息
- 来自底层协议supl的RELEASE_SUPL_CONNECTION释放消息
- 请求supl连接的REQUEST_SUPL_CONNECTION连接消息
- 注入同步UTC时间的NTP的INJECT_NTP_TIME消息
- 下载同AGPS一样功能的XTRA的数据的DOWNLOAD_XTRA_DATA消息
- UPDATE_LOCATION消息
对于这么多的消息,这里我们挑INITIALIZE_HANDLER消息来讲一下。
在GnssLocationProvider的构造时,会发送INITIALIZE_HANDLER消息在工作线程中初始化一些东西,比如gps的一些配置属性;注册广播,注册网络是否可用的监听器
可以从config.xml读取
<!-- Values for GPS configuration -->
<string-array translatable="false" name="config_gpsParameters">
<item>SUPL_HOST=supl.google.com</item>
<item>SUPL_PORT=7275</item>
<item>NTP_SERVER=north-america.pool.ntp.org</item>
<item>SUPL_VER=0x20000</item>
<item>SUPL_MODE=1</item>
</string-array>
也可以从file(/etc/gps_debug.conf)读取.
#Uncommenting these urls would only enable
#the power up auto injection and force injection(test case).
#XTRA_SERVER_1=http://xtrapath1.izatcloud.net/xtra2.bin
#XTRA_SERVER_2=http://xtrapath2.izatcloud.net/xtra2.bin
#XTRA_SERVER_3=http://xtrapath3.izatcloud.net/xtra2.bin
#Version check for XTRA
#DISABLE = 0
#AUTO = 1
#XTRA2 = 2
#XTRA3 = 3
XTRA_VERSION_CHECK=0
# Error Estimate
# _SET = 1
# _CLEAR = 0
ERR_ESTIMATE=0
#Test
NTP_SERVER=time.gpsonextra.net
#Asia
# NTP_SERVER=asia.pool.ntp.org
#Europe
# NTP_SERVER=europe.pool.ntp.org
#North America
# NTP_SERVER=north-america.pool.ntp.org
# DEBUG LEVELS: 0 - none, 1 - Error, 2 - Warning, 3 - Info
# 4 - Debug, 5 - Verbose
# If DEBUG_LEVEL is commented, Android's logging levels will be used
DEBUG_LEVEL = 3
# Intermediate position report, 1=enable, 0=disable
INTERMEDIATE_POS=0
# Below bit mask configures how GPS functionalities
# should be locked when user turns off GPS on Settings
# Set bit 0x1 if MO GPS functionalities are to be locked
# Set bit 0x2 if NI GPS functionalities are to be locked
# default - non is locked for backward compatibility
#GPS_LOCK = 0
# supl version 1.0
SUPL_VER=0x20000
# Emergency SUPL, 1=enable, 0=disable
SUPL_ES=0
#Choose PDN for Emergency SUPL
#1 - Use emergency PDN
#0 - Use regular SUPL PDN for Emergency SUPL
USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL=0
#SUPL_MODE is a bit mask set in config.xml per carrier by default.
#If it is uncommented here, this value will overwrite the value from
#config.xml.
#MSA=0X2
#MSB=0X1
SUPL_MODE=3
# GPS Capabilities bit mask
# SCHEDULING = 0x01
# MSB = 0x02
# MSA = 0x04
# ON_DEMAND_TIME = 0x10
# GEOFENCE = 0x20
# default = ON_DEMAND_TIME | MSA | MSB | SCHEDULING | GEOFENCE
CAPABILITIES=0x37
# Accuracy threshold for intermediate positions
# less accurate positions are ignored, 0 for passing all positions
# ACCURACY_THRES=5000
################################
##### AGPS server settings #####
################################
# FOR SUPL SUPPORT, set the following
SUPL_HOST=supl.qxwz.com
SUPL_PORT=7275
# FOR C2K PDE SUPPORT, set the following
# C2K_HOST=c2k.pde.com or IP
# C2K_PORT=1234
# Bitmask of slots that are available
# for write/install to, where 1s indicate writable,
# and the default value is 0 where no slots
# are writable. For example, AGPS_CERT_WRITABLE_MASK
# of b1000001010 makes 3 slots available
# and the remaining 7 slots unwritable.
#AGPS_CERT_WRITABLE_MASK=0
####################################
# LTE Positioning Profile Settings
####################################
# 0: Enable RRLP on LTE(Default)
# 1: Enable LPP_User_Plane on LTE
# 2: Enable LPP_Control_Plane
# 3: Enable both LPP_User_Plane and LPP_Control_Plane
LPP_PROFILE = 0
################################
# EXTRA SETTINGS
################################
# NMEA provider (1=Modem Processor, 0=Application Processor)
NMEA_PROVIDER=0
# Mark if it is a SGLTE target (1=SGLTE, 0=nonSGLTE)
SGLTE_TARGET=0
##################################################
# Select Positioning Protocol on A-GLONASS system
##################################################
# 0x1: RRC CPlane
# 0x2: RRLP UPlane
# 0x4: LLP Uplane
A_GLONASS_POS_PROTOCOL_SELECT = 0
看完这一些配置文件,又是一批概念,提供一个文章有空再来看看Location based services with
GPS, GLONASS, Galileo and OTDOA,主要是讲gps协议层。
接下来我们继续跟随SET_REQUEST的处理逻辑,最后通过 startNavigating(singleShot);
和 stopNavigating();
控制gps开启关闭,当开启之后,hal层的新数据上报到framework-native层,framework-native层继续上报到framework-java层,而这些回调接口会在哪里被注册了 ?
当framework-java层中的GnssLocationProvider类初始化时,通过static { class_init_native(); }
代码初始化framework-native层的com_android_server_location_GnssLocationProvider.cpp,初始化的时候保存了framework-java层的回调接口,以供合适的时候上报framework-native层数据给framework-native层。
frameworks/base/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) {
method_reportLocation = env->GetMethodID(clazz, "reportLocation",
"(ZLandroid/location/Location;)V");
method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V");
method_reportSvStatus = env->GetMethodID(clazz, "reportSvStatus", "()V");
method_reportAGpsStatus = env->GetMethodID(clazz, "reportAGpsStatus", "(II[B)V");
method_reportNmea = env->GetMethodID(clazz, "reportNmea", "(J)V");
method_setEngineCapabilities = env->GetMethodID(clazz, "setEngineCapabilities", "(I)V");
method_setGnssYearOfHardware = env->GetMethodID(clazz, "setGnssYearOfHardware", "(I)V");
method_xtraDownloadRequest = env->GetMethodID(clazz, "xtraDownloadRequest", "()V");
method_reportNiNotification = env->GetMethodID(clazz, "reportNiNotification",
"(IIIIILjava/lang/String;Ljava/lang/String;II)V");
method_requestRefLocation = env->GetMethodID(clazz, "requestRefLocation", "()V");
method_requestSetID = env->GetMethodID(clazz, "requestSetID", "(I)V");
method_requestUtcTime = env->GetMethodID(clazz, "requestUtcTime", "()V");
method_reportGeofenceTransition = env->GetMethodID(clazz, "reportGeofenceTransition",
"(ILandroid/location/Location;IJ)V");
method_reportGeofenceStatus = env->GetMethodID(clazz, "reportGeofenceStatus",
"(ILandroid/location/Location;)V");
method_reportGeofenceAddStatus = env->GetMethodID(clazz, "reportGeofenceAddStatus",
"(II)V");
method_reportGeofenceRemoveStatus = env->GetMethodID(clazz, "reportGeofenceRemoveStatus",
"(II)V");
method_reportGeofenceResumeStatus = env->GetMethodID(clazz, "reportGeofenceResumeStatus",
"(II)V");
method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
"(II)V");
method_reportMeasurementData = env->GetMethodID(
clazz,
"reportMeasurementData",
"(Landroid/location/GnssMeasurementsEvent;)V");
method_reportNavigationMessages = env->GetMethodID(
clazz,
"reportNavigationMessage",
"(Landroid/location/GnssNavigationMessage;)V");
method_reportLocationBatch = env->GetMethodID(
clazz,
"reportLocationBatch",
"([Landroid/location/Location;)V");
...
}
有21个回调接口,那么我们需要关注的是这21个回调接口的回调顺序也就是我们常说的生命周期。
再来看看LocationProviderProxy
@Override
public void setRequest(ProviderRequest request, WorkSource source) {
synchronized (mLock) {
mRequest = request;
mWorksource = source;
}
ILocationProvider service = getService();
if (service == null) return;
try {
service.setRequest(request, source);
} catch (RemoteException e) {
Log.w(TAG, e);
} catch (Exception e) {
// never let remote service crash system server
Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
在调用setRequest之前,我们需要知道LocationProviderProxy和网络端的连接有个很重要的任务mNewServiceWork。当客户端和网络端连接成功会通过LocationWorkerHandler(用来处理工作线程的任务)执行任务。
private Runnable mNewServiceWork = new Runnable() {
@Override
public void run() {
if (D) Log.d(TAG, "applying state to connected service");
boolean enabled;
ProviderProperties properties = null;
ProviderRequest request;
WorkSource source;
ILocationProvider service;
synchronized (mLock) {
enabled = mEnabled;
request = mRequest;
source = mWorksource;
service = getService();
}
if (service == null) return;
try {
// load properties from provider
properties = service.getProperties();
if (properties == null) {
Log.e(TAG, mServiceWatcher.getBestPackageName() +
" has invalid locatino provider properties");
}
// apply current state to new service
if (enabled) {
service.enable();
if (request != null) {
service.setRequest(request, source);
}
}
} catch (RemoteException e) {
Log.w(TAG, e);
} catch (Exception e) {
// never let remote service crash system server
Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
synchronized (mLock) {
mProperties = properties;
}
}
};
讲到这里的配置属性我们顺带了解一下ILocationProvider和LocationProvider。
客户端通过LocationManager的getProvider方法获得LocationProvider对象,LocationProvider有个成员变量ProviderProperties
是需要通过binder从服务端的getProviderProperties方法获取,所以存储provider信息的ProviderProperties才是核心。那么服务端的ProviderProperties又是怎么来的呢 ?来自于LocationProviderInterface的子类。
像gps的配置信息,是直接默认初始化,就像这样
private static final ProviderProperties PROPERTIES = new ProviderProperties(
true, true, false, false, true, true, true,
Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
可以看出gps的配置信息是
- 需要Internet网络(我们常说的互联网,数据传输采用ps)
- 需要有卫星
- 不需要cellular网络(我们常说的电话通讯网,数据传输采用cs)
- 不需要花费monetary
- 需要支持上报海拔
- 需要支持上报速度
- 需要支持上报方位
- 需要电量为POWER_HIGH级别
- 需要精度为ACCURACY_FINE级别
像network的配置信息,是来至ILocationProvider,那么ILocationProvider又是什么 ?在LocationProviderProxy初始化的时候会创建ServiceConnection,ServiceConnection为ServiceConnection接口的实现类,用来连接网络端的provider。Android团队为了给优化网络的定位数据,以及为了让网络定位的服务厂商提供服务,设计了LocationProviderBase类。该类保存了ILocationProvider接口实例化对象,用来响应客户端。
来看个源码感受一下吧。
public abstract class LocationProviderBase {
...
private final class Service extends ILocationProvider.Stub {
@Override
public void enable() {
onEnable();
}
@Override
public void disable() {
onDisable();
}
@Override
public void setRequest(ProviderRequest request, WorkSource ws) {
onSetRequest(new ProviderRequestUnbundled(request), ws);
}
@Override
public ProviderProperties getProperties() {
return mProperties;
}
@Override
public int getStatus(Bundle extras) {
return onGetStatus(extras);
}
@Override
public long getStatusUpdateTime() {
return onGetStatusUpdateTime();
}
@Override
public boolean sendExtraCommand(String command, Bundle extras) {
return onSendExtraCommand(command, extras);
}
@Override
public void dump(FileDescriptor fd, String[] args) {
PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
onDump(fd, pw, args);
pw.flush();
}
}
public final void reportLocation(Location location) {
try {
mLocationManager.reportLocation(location, false);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException", e);
} catch (Exception e) {
// never crash provider, might be running in a system process
Log.e(TAG, "Exception", e);
}
}
...
public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
...
public abstract int onGetStatus(Bundle extras);
public abstract long onGetStatusUpdateTime();
...
}
一般网络定位服务厂商需要重写LocationProviderBase类一些方法来处理来自客户端的请求,,提供provider的配置属性等等一下东西,还需要提供一个Service类用来连接ServiceWatcher。
Android团队为了优化定位数据,就是利用这个接口实现了FusedLocation应用。会对来自gps和network的定位数据进行比较,以便获取更加有用的数据。
该应用源码位于framework/base/packages下面
这里我们可以大致说一下Android团队如何做的。FusedLocationProvider重写了LocationProviderBase提供的方法,并且也提供了自己的配置属性。
ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
false, false, false, false, true, true, true, Criteria.POWER_LOW,
Criteria.ACCURACY_FINE);
可以看出服务端FusedLocation应用能够提供的配置信息是
- 不需要Internet网络(我们常说的互联网,数据传输采用ps)
- 不需要有卫星
- 不需要cellular网络(我们常说的电话通讯网,数据传输采用cs)
- 不需要花费monetary
- 需要支持上报海拔
- 需要支持上报速度
- 需要支持上报方位
- 需要电量为POWER_LOW级别
- 需要精度为ACCURACY_FINE级别
然后通过主线程Handler把优化的任务交给了FusionEngine类
/**
* Test whether one location (a) is better to use than another (b).
*/
private static boolean isBetterThan(Location locationA, Location locationB) {
if (locationA == null) {
return false;
}
if (locationB == null) {
return true;
}
// A provider is better if the reading is sufficiently newer. Heading
// underground can cause GPS to stop reporting fixes. In this case it's
// appropriate to revert to cell, even when its accuracy is less.
if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
return true;
}
// A provider is better if it has better accuracy. Assuming both readings
// are fresh (and by that accurate), choose the one with the smaller
// accuracy circle.
if (!locationA.hasAccuracy()) {
return false;
}
if (!locationB.hasAccuracy()) {
return true;
}
return locationA.getAccuracy() < locationB.getAccuracy();
}
这个就是优化的算法。
接着回到ILocationProvider的setRequest方法调用,这下我们就可以轻松的知道LocationProviderProxy通过binder将请求发送给网络端的服务,而网络端的ILocationProvider接口就是接受者。
参考资料
Android Open Source Project