Android网络定位源码分析[上]

前言

HI,欢迎来到《每周一博》。今天是九月第二周,我给大家分析一下Android网络定位源码。

App中基本都会用到定位服务,NLP即NetworkLocationProvider,是位置提供服务的一种,谷歌有提供自己的NLP,在国内厂商一般会集成百度或高德的NLP。

下面是我绘制的一张定位服务架构图,一共分为四层,每层都依赖下面一层完成其所需提供的服务:

1.应用层:是android.location包中包含的内容,主要通过LocationManager来进行方法调用;

2.框架层:这一层包含了系统服务的实现,LocationManager通过Binder机制来和LocationManagerService进行通讯,LocationManagerService会选择合适的provider来提供位置,其中LocationProviderProxy的一个实现就是NLP,可以理解为LocationProviderProxy和GeocoderProxy都是一个空壳,如果没有第三方实现他们,那么将不提供服务,如果使用了GpsLocationProvider则会去调用硬件来获取位置;

3.共享库层:GpsLocationProvider通过JNI来调用本层libgps.so中的C++代码;

4.Linux内核层:C++代码最终去调用GPS硬件来获取位置;

一. 为什么要分析源码

在做NLP的时候对于和系统交互这块一直都充满疑惑:

A. App调用请求是如何到了NLP的?

B. 不同的App同一时刻发起定位系统是如何处理的?

C. 为什么取消定位有的时候传off,有的时候传on,off是啥意思?

D. 为什么请求单次定位会回调2次onSetRequest方法?

E. NLP上抛的位置又是如何返给App的呢?

F. onSetRequest到底是该怎么用?

为了对NLP有一个更加深入的理解,我决定带着这些问题去看下安卓定位相关的源码,告别过去对系统行为的种种猜测。所以这篇文章不仅仅是源码解析,更是结合了日常开发NLP时候遇到的各种问题,各种测试现象,从源码里去找出答案。

二. LocationManager分析

App调用定位接口是通过LocationManager的API,那就先从LocationManager入手,我查看的是安卓8.0的源码,发现它的很多方法都是代理了service的一些方法,这个service的声明类型是ILocationManager,这个对象就是代理对象,很显然是AIDL的调用,具体实现类则是LocationManagerService,LocationManager和LocationManagerService就是通过Binder 机制来进行通讯的。

    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();
       }
    }

LocationManager提供的主要方法有:

1.getLastKnownLocation:获取上一次缓存的位置,这个方法不会发起定位请求,返回的是上一次的位置信息,但此前如果没有位置更新的话,返回的位置信息可能是错误的;

2.requestSingleUpdate:只请求一次定位,会发起位置监听,该方法要在主线程上执行,可以传入Listener或广播来接收位置;

3.requestLocationUpdates:持续请求定位,根据传入的时间间隔和位置差进行回调,该方法要在主线程上执行,可以传入Listener或广播来接收位置;

4.removeUpdates:移除定位请求,传入Listener;

5.addProximityAlert:添加一个地理围栏,这是一个圆形的围栏;

6.getProvider:获取Provider,可以指定条件,也可以根据名字来获取;

7.sendExtraCommand:给系统发送辅助指令;

这些方法的最终都是由service来实现的,发起定位时传入的Listener经过包装成AIDL接口传给了服务端,因为它们是需要跨进程来进行通讯的。

这里分析一下requestSingleUpdate方法,这个方法主要是传一个Listener,然后内部创建了一个LocationRequest,最小时间和最小距离都是0,还给singleShot设置为了true,并最终调用requestLocationUpdates方法,所以requestLocationUpdates才是核心,而所有定制的参数都封装成了LocationRequest。

    @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
    public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
        checkProvider(provider);
        checkListener(listener);

        LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                provider, 0, 0, true);
        requestLocationUpdates(request, listener, looper, null);
    }

那么接下来看下LocationRequest的createFromDeprecatedProvider方法,这里把传来的最小时间频率,最小距离差值存下,设置了定位的精度类型,如果singleShot为true,会设置locationRequest.setNumUpdates(1),numUpdate这个变量的默认值是一个很大的数,Integer.MAX_VALUE = 0x7fffffff,而单次定位g该值就设为了1,这个点在分析service的代码时会用到。

 /** @hide */
    @SystemApi
    public static LocationRequest createFromDeprecatedProvider(String provider, long minTime,
            float minDistance, boolean singleShot) {
        if (minTime < 0) minTime = 0;
        if (minDistance < 0) minDistance = 0;

        int quality;
        if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
            quality = POWER_NONE;
        } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
            quality = ACCURACY_FINE;
        } else {
            quality = POWER_LOW;
        }

        LocationRequest request = new LocationRequest()
            .setProvider(provider)
            .setQuality(quality)
            .setInterval(minTime)
            .setFastestInterval(minTime)
            .setSmallestDisplacement(minDistance);
        if (singleShot) request.setNumUpdates(1);
        return request;
    }

三. LocationManagerService的初始化

获取LocationManager是调用Activity的getSystemService(Context.LOCATION_SERVICE)方法来获得,那么它的初始化是在哪里呢?我们知道Activity是Context,那么这个方法的最终实现就是在ContextImpl类里面,所以我们看下ContextImpl的getSystemService方法,它调用了SystemServiceRegistry的getSystemService方法。

    @Override
    public Object getSystemService(String name) {
        return SystemServiceRegistry.getSystemService(this, name);
    }

在SystemServiceRegistry类里,有LocationManager向ServiceManager注册的代码,这个类是6.0新加的,主要是用来缓存,注册,获取系统服务的,早期安卓版本直接在ComtextImpl里面实现了。

 registerService(Context.LOCATION_SERVICE, LocationManager.class,
                new CachedServiceFetcher<LocationManager>() {
            @override  
            public LocationManager createService(ContextImpl ctx) throws ServiceNotFoundException {
                IBinder b = ServiceManager.getServiceOrThrow(Context.LOCATION_SERVICE);
                return new LocationManager(ctx, ILocationManager.Stub.asInterface(b));
            }});

ServiceManager是安卓系统专门用来管理系统服务的,它负责注册并管理所有的系统服务。可以把ServiceManager当做一个容器,它里面存储了系统所有的服务,比如PackageManagerService,ActivityManagerService,AlarmManagerService等等,通过对应的Key就可以获得对应的服务。我们可以获取定位服务的实现类对象,然后再通过 ILocationManager.Stub.asInterface(b) 将其转换成服务的代理存放到 LocationManager中。

那 ServiceManager 中所管理的系统服务对象又是从哪里来的呢?在 Android 系统启动过程中,需要完成一系列的初始化动作。在Java层最终会调用到ZygoteInit类中,会调用startSystemServer 方法来启动系统服务。启动的方法是 fork一个新的进程,然后在其中加载SystemServer类。在SystemServer中执行了系统服务的创建和注册。以LocationManagerService为例,在SystemServer的startOtherService中有以下代码:

      if (!disableLocation) {
                traceBeginAndSlog("StartLocationManagerService");                
                try {
                    location = new LocationManagerService(context);
                    ServiceManager.addService(Context.LOCATION_SERVICE, location);
                } catch (Throwable e) {
                    reportWtf("starting Location Manager", e);
                }
                traceEnd();

                traceBeginAndSlog("StartCountryDetectorService");
                try {
                    countryDetector = new CountryDetectorService(context);
                    ServiceManager.addService(Context.COUNTRY_DETECTOR, countryDetector);
                } catch (Throwable e) {
                    reportWtf("starting Country Detector", e);
                }
                traceEnd();
            }

在这个类里面new出了LocationManagerService对象,并把它加入到ServiceManager容器里面,当然还有其他的服务也会被加入到ServiceManager里面,然后走它的systemRunning方法;

            traceBeginAndSlog("MakeLocationServiceReady");
            try {
                if (locationF != null) locationF.systemRunning();
            } catch (Throwable e) {
                reportWtf("Notifying Location Service running", e);
            }
            traceEnd();

那么接下来看LocationManagerService这个类,在systemRunning方法里,做了一系列初始化的操作,其中一个重要的方法就是loadProvidersLocked(),它就是来加载provider的;位置服务的提供者是LocationProvider,它包含3种:GPS_PROVIDER,NETWORK_PROVIDER,PASSIVE_PROVIDER,BaiduNLP就是NETWORK_PROVIDER的一种。大概看下这个类的成员变量,就知道这个类的很多工作就是来管理这些provider的,那么来看下loadProvidersLocked()这个方法;

        PassiveProvider passiveProvider = new PassiveProvider(this);
        addProviderLocked(passiveProvider);
        mEnabledProviders.add(passiveProvider.getName());
        mPassiveProvider = passiveProvider;

        if (GnssLocationProvider.isSupported()) {
            // Create a gps location provider
            GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
                    mLocationHandler.getLooper());
            mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
            mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
            mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
            mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
            addProviderLocked(gnssProvider);
            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
            mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
            mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
            mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
        }

首先创建了一个PassiveProvider,并把它加到可用的provider里面,也就是PassiveProvider始终可用,然后根据GPS是否可用,增加一个GpsLocationProvider,这个代码在不同的系统版本上还是有许多差别的,原来是GpsLocationProvider,现在改成了GnssLocationProvider,另外PassiveProvider的创建顺序也发生了改变。

        Resources resources = mContext.getResources();
        ArrayList<String> providerPackageNames = new ArrayList<>();
        String[] pkgs = resources.getStringArray(
                com.android.internal.R.array.config_locationProviderPackageNames);
        if (D) Log.d(TAG, "certificates for location providers pulled from: " +
                Arrays.toString(pkgs));
        if (pkgs != null) providerPackageNames.addAll(Arrays.asList(pkgs));

        ensureFallbackFusedProviderPresentLocked(providerPackageNames);

        // bind to network provider
        LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                mContext,
                LocationManager.NETWORK_PROVIDER,
                NETWORK_LOCATION_SERVICE_ACTION,
                com.android.internal.R.bool.config_enableNetworkLocationOverlay,
                com.android.internal.R.string.config_networkLocationProviderPackageName,
                com.android.internal.R.array.config_locationProviderPackageNames,
                mLocationHandler);
        if (networkProvider != null) {
            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
            mProxyProviders.add(networkProvider);
            addProviderLocked(networkProvider);
        } else {
            Slog.w(TAG,  "no network location provider found");
        }

接下来就是加载NetworkLocationProvider了, LocationProviderProxy是对NetworkLocationProvider的代理,而第三方NLP才是NetworkLocationProvider的具体实现,这里会根据XML文件中配置的布尔值,包名和字符串数组去绑定指定action的服务,如果bind成功就把它加入到可用provider中。那么实现方必然要创建一个Service来实现LocationProviderProxy中使用的AIDL对象的接口,这个类名没有具体要求,但是Service必须要对指定的action进行绑定并返回binder对象才能被唤醒。所以有的时候会遇到第三方NLP没有被厂商bind上,后续就无法通过第三方NLP来获取位置。

这个action是"com.android.location.service.v3.NetworkLocationProvider",这个action在不同系统上可能会不同,所以需要适配v2,v3,否则可能会出现无法绑定的情况。

    public static LocationProviderProxy createAndBind(
            Context context, String name, String action,
            int overlaySwitchResId, int defaultServicePackageNameResId,
            int initialPackageNamesResId, Handler handler) {
        LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
                overlaySwitchResId, defaultServicePackageNameResId, initialPackageNamesResId,
                handler);
        if (proxy.bind()) {
            return proxy;
        } else {
            return null;
        }
    }

    private LocationProviderProxy(Context context, String name, String action,
            int overlaySwitchResId, int defaultServicePackageNameResId,
            int initialPackageNamesResId, Handler handler) {
        mContext = context;
        mName = name;
        mServiceWatcher = new ServiceWatcher(mContext, TAG + "-" + name, action, overlaySwitchResId,
                defaultServicePackageNameResId, initialPackageNamesResId,
                mNewServiceWork, handler);
    }

那么接下来继续看LocationProviderProxy的createAndBind方法,在这里创建了一个ServiceWatcher对象,然后执行了它的start方法。ServiceWatcher是用来连接和监视应用程序实现LocationProvider服务的,成功binder到服务后,会对该服务进行监控,包的卸载,加载、安装都会引起rebinder动作,它实现了ServiceConnection,在构造函数里,把xml中的配置项都传了过来,包括一个boolean值overlay(覆盖),一个字符串数组,一个默认的服务字符串,如果开启覆盖即overlay=true,则使用字符串数组中指定包名的provider,如果不覆盖,则使用包名字符串中的provider来提供服务。

106    public ServiceWatcher(Context context, String logTag, String action,
107            int overlaySwitchResId, int defaultServicePackageNameResId,
108            int initialPackageNamesResId, Runnable newServiceWork,
109            Handler handler) {
110        mContext = context;
111        mTag = logTag;
112        mAction = action;
113        mPm = mContext.getPackageManager();
114        mNewServiceWork = newServiceWork;
115        mHandler = handler;
116        Resources resources = context.getResources();
117
118        // Whether to enable service overlay.
119        boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
120        ArrayList<String> initialPackageNames = new ArrayList<String>();
121        if (enableOverlay) {
122            // A list of package names used to create the signatures.
123            String[] pkgs = resources.getStringArray(initialPackageNamesResId);
124            if (pkgs != null) initialPackageNames.addAll(Arrays.asList(pkgs));
125            mServicePackageName = null;
126            if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
127        } else {
128            // The default package name that is searched for service implementation when overlay is
129            // disabled.
130            String servicePackageName = resources.getString(defaultServicePackageNameResId);
131            if (servicePackageName != null) initialPackageNames.add(servicePackageName);
132            mServicePackageName = servicePackageName;
133            if (D) Log.d(mTag, "Overlay disabled, default package=" + servicePackageName);
134        }
135        mSignatureSets = getSignatureSets(context, initialPackageNames);
136    }

在ServiceWatcher的start方法里,执行了bindBestPackageLocked这个关键的方法,在这个方法里,先去给intent设置之前提到的Action,然后根据传来的包名去查询service,如果前面使用了字符串数组,那么包名就是空的,接着对遍历出来的service做签名效验。

201    private boolean bindBestPackageLocked(String justCheckThisPackage, boolean forceRebind) {
202        Intent intent = new Intent(mAction);
203        if (justCheckThisPackage != null) {
204            intent.setPackage(justCheckThisPackage);
205        }
206        final List<ResolveInfo> rInfos = mPm.queryIntentServicesAsUser(intent,
207                PackageManager.GET_META_DATA | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
208                mCurrentUserId);
209        int bestVersion = Integer.MIN_VALUE;
210        ComponentName bestComponent = null;
211        boolean bestIsMultiuser = false;
212        if (rInfos != null) {
213            for (ResolveInfo rInfo : rInfos) {
214                final ComponentName component = rInfo.serviceInfo.getComponentName();
215                final String packageName = component.getPackageName();
216
217                // check signature
218                try {
219                    PackageInfo pInfo;
220                    pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES
221                            | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
222                    if (!isSignatureMatch(pInfo.signatures)) {
223                        Log.w(mTag, packageName + " resolves service " + mAction
224                                + ", but has wrong signature, ignoring");
225                        continue;
226                    }
227                } catch (NameNotFoundException e) {
228                    Log.wtf(mTag, e);
229                    continue;
230                }

这里需要注意的是必须要在NLP的service里配置metadata属性,给service_version配置value。因为这段代码会去读这个字段,没有就赋值为Integer.MIN_VALUE,也就是-2147483648,而version的初始值也是-2147483648,所以version > bestVersion条件通不过,那bestComponent就是null,所以无法绑定。

// check metadata
233                int version = Integer.MIN_VALUE;
234                boolean isMultiuser = false;
235                if (rInfo.serviceInfo.metaData != null) {
236                    version = rInfo.serviceInfo.metaData.getInt(
237                            EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
238                    isMultiuser = rInfo.serviceInfo.metaData.getBoolean(EXTRA_SERVICE_IS_MULTIUSER);
239                }
240
241                if (version > bestVersion) {
242                    bestVersion = version;
243                    bestComponent = component;
244                    bestIsMultiuser = isMultiuser;
245                }

找到bestComponent后,就会调用bindToPackageLocked方法,在这里又调用了bindServiceAsUser方法,去绑定第三方NLP里的Service,随后就会回调自己的onServiceConnected方法,因为它本身是个ServiceConnection,在回调方法里会执行mNewServiceWork,它是由LocationProviderProxy提供的一个Runnable对象,在这个方法里执行的是;

93    /**
94     * Work to apply current state to a newly connected provider.
95     * Remember we can switch the service that implements a providers
96     * at run-time, so need to apply current state.
97     */
98    private Runnable mNewServiceWork = new Runnable() {
99        @Override
100        public void run() {
101            if (D) Log.d(TAG, "applying state to connected service");
102
103            boolean enabled;
104            ProviderProperties properties = null;
105            ProviderRequest request;
106            WorkSource source;
107            ILocationProvider service;
108            synchronized (mLock) {
109                enabled = mEnabled;
110                request = mRequest;
111                source = mWorksource;
112                service = getService();
113            }
114
115            if (service == null) return;
116
117            try {
118                // load properties from provider
119                properties = service.getProperties();
120                if (properties == null) {
121                    Log.e(TAG, mServiceWatcher.getBestPackageName() +
122                            " has invalid locatino provider properties");
123                }
124
125                // apply current state to new service
126                if (enabled) {
127                    service.enable();
128                    if (request != null) {
129                        service.setRequest(request, source);
130                    }
131                }
132            } catch (RemoteException e) {
133                Log.w(TAG, e);
134            } catch (Exception e) {
135                // never let remote service crash system server
136                Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
137            }
138
139            synchronized (mLock) {
140                mProperties = properties;
141            }
142        }
143    };

获取属性信息,把这些属性统一封装在类型为ProviderProperties的对象中,并回调enable方法,如果客户端有请求,则回调setRequest方法,这里要注意的是这些回调方法的对象是ILocationProvider,而不是NLP提供商。把NLP添加到可用provider之后,又添加了融合定位的provider和GeocoderProvider,GeocoderProvider和NLP的代理过程类似,至此LocationManagerService的初始化流程就算是结束了,还是比较复杂的,我们可以看到目前的这个过程和NLP提供商还没有任何关联。

656        // bind to geocoder provider
657        mGeocodeProvider = GeocoderProxy.createAndBind(mContext,
658                com.android.internal.R.bool.config_enableGeocoderOverlay,
659                com.android.internal.R.string.config_geocoderProviderPackageName,
660                com.android.internal.R.array.config_locationProviderPackageNames,
661                mLocationHandler);
662        if (mGeocodeProvider == null) {
663            Slog.e(TAG,  "no geocoder provider found");
664        }
665
666        // bind to fused hardware provider if supported
667        // in devices without support, requesting an instance of FlpHardwareProvider will raise an
668        // exception, so make sure we only do that when supported
669        FlpHardwareProvider flpHardwareProvider;
670        if (FlpHardwareProvider.isSupported()) {
671            flpHardwareProvider = FlpHardwareProvider.getInstance(mContext);
672            FusedProxy fusedProxy = FusedProxy.createAndBind(
673                    mContext,
674                    mLocationHandler,
675                    flpHardwareProvider.getLocationHardware(),
676                    com.android.internal.R.bool.config_enableHardwareFlpOverlay,
677                    com.android.internal.R.string.config_hardwareFlpPackageName,
678                    com.android.internal.R.array.config_locationProviderPackageNames);
679            if (fusedProxy == null) {
680                Slog.d(TAG, "Unable to bind FusedProxy.");
681            }
682        } else {
683            flpHardwareProvider = null;
684            Slog.d(TAG, "FLP HAL not supported");
685        }
686
687        // bind to geofence provider
688        GeofenceProxy provider = GeofenceProxy.createAndBind(
689                mContext,com.android.internal.R.bool.config_enableGeofenceOverlay,
690                com.android.internal.R.string.config_geofenceProviderPackageName,
691                com.android.internal.R.array.config_locationProviderPackageNames,
692                mLocationHandler,
693                mGpsGeofenceProxy,
694                flpHardwareProvider != null ? flpHardwareProvider.getGeofenceHardware() : null);
695        if (provider == null) {
696            Slog.d(TAG,  "Unable to bind FLP Geofence proxy.");
697        }
698

四. 双重Client-Server模型

那么LocationProviderProxy又是怎么和第三方NLP关联在一起的呢?

在回答这个问题前,我们可以从宏观上看下App是如何从NLP提供商得到位置的。App向OS发请求,OS里接收到请求后向NLP提供商发请求,NLP提供商把位置返给系统,系统再返给App。从Binder机制来看,App-OS是一组Client-Server模型,OS-BaiduNLP是一组Client-Server模型,App通过Binder请求OS的服务,然后OS通过Binder请求NLP提供商的服务,前者通过ILocationManager.aidl,后者通ILocationProvider.aidl,所以OS既是客户端,也是服务端,看你看的视角是哪个。这也是Binder机制一个优秀的点,我们以为系统是服务方,其实有时候它也是个客户端。

那么接下来我来分析下系统和NLP提供商交互的过程,系统有个类已经实现了ILocationProvider.aidl的接口,那就是LocationProviderBase,所以我们只需要继承LocationProviderBase并实现抽象接口就可以了,这里先看下LocationProviderBase里相关的方法;

78    private final class Service extends ILocationProvider.Stub {
79        @Override
80        public void enable() {
81            onEnable();
82        }
83        @Override
84        public void disable() {
85            onDisable();
86        }
87        @Override
88        public void setRequest(ProviderRequest request, WorkSource ws) {
89            onSetRequest(new ProviderRequestUnbundled(request), ws);
90        }
91        @Override
92        public ProviderProperties getProperties() {
93            return mProperties;
94        }
95        @Override
96        public int getStatus(Bundle extras) {
97            return onGetStatus(extras);
98        }
99        @Override
100        public long getStatusUpdateTime() {
101            return onGetStatusUpdateTime();
102        }
103        @Override
104        public boolean sendExtraCommand(String command, Bundle extras) {
105            return onSendExtraCommand(command, extras);
106        }
107        @Override
108        public void dump(FileDescriptor fd, String[] args) {
109            PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
110            onDump(fd, pw, args);
111            pw.flush();
112        }
113    }

是不是瞬间觉得熟悉了许多,比如onEnable,onDisable,onSetRequest。需要注意的是这个类系统是有的,但是android.jar里面没有,所以我们出APK的时候需要编译依赖,而不能打包进去,可以provided一个jar包,我们在制作jar包的时候不仅要把LocationProviderBase放进去,还要把相关联的类也放进去,既然系统选择了外包的方式来实现NLP,那么关联的类一定不会无限关联下去。

另外还需要注意的是早期ILocationProvider.aidl的实现类是com.android.location.provider.LocationProvider,也就是BaiduNetworkLocationProvider所继承的类,BaiduNetworkLocationProvider1继承的是LocationProviderBase,所以BaiduNetworkLocationProvider是兼容旧版本用的,这点从Action上也可以看出来,现在基本上已经不会被调用了。

五. LocationManagerService相关类梳理

那么至此我们就打通了从App到系统再到NLP提供商的路径,这里对和LocationManagerService相关的类做一个简单的梳理:

1.ILocationManager.aidl:LocationManagerService在客户端的代理;

2.ILocationProvider.aidl:NLP提供商如BaiduNLP在系统层的代理;

3.LocationManager:客户端调用位置服务的类;

4.LocationManagerService:真正实现LocationManager中方法的类;

5.LocationProvider:位置服务提供者;

6.LocationProviderInterface:位置服务提供者的抽象接口,它的实现类有PassiveProvider,GpsLocationProvider,LocationProviderProxy等;

7.LocationProviderProxy:它是NLP提供商的代理类,通过ILocationProvider.aidl来远程调用NLP提供商的服务;

8.ServiceWatcher:它是LocationProviderProxy用到的一个类,通过配置xml文件可以读取到指定的包名,然后去绑定对应的服务,并监听包的变更事件;

9.LocationProviderBase:它是NLP提供商需要继承并实现的抽象类;

10.GeocoderProxy:是Geocoder的代理类,实现方式和NetworkLocationProvider类似;

11.LocationRequest:客户端请求定位时传的参数会被封装成这个类传给服务端;

它们之间的简单关系图可以这样表示;

由于文章长度有限制,分为上下两篇,继续阅读请跳转到
Android网络定位源码分析[下]

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容