Android申请地理位置时的流程及源代码分析

1.首先看一下在android应用中是如何获取Location的

      LocationManager locationManager;
       String serviceName = Context.LOCATION_SERVICE;
       locationManager = (LocationManager) this.getSystemService(serviceName);    // 查找到服务信息

       Criteria criteria = new Criteria();  //定义标准
       criteria.setAccuracy(Criteria.ACCURACY_FINE); // 高精度
       criteria.setAltitudeRequired(false);
       criteria.setBearingRequired(false);
       criteria.setCostAllowed(true);
       criteria.setPowerRequirement(Criteria.POWER_LOW); // 低功耗

       String provider = locationManager.getBestProvider(criteria, true); // 获取最好的provider,就是在此时检测权限的
       Location location = locationManager.getLastKnownLocation(provider); // 通过GPS获取位置
       updateToNewLocation(location);
       // 设置监听器,自动更新的最小时间为间隔N秒(1秒为1*1000,这样写主要为了方便)或最小位移变化超过N米
       locationManager.requestLocationUpdates(provider, 100 * 1000, 500,
               locationListener);

首先通过getSystemService找到LocationManager服务,然后设定需要的地理位置的一些标准。然后通过LocationManager中的getBestProvider函数调用LocationManagerService.java中的getBestProvider函数来找到最好的Provider,其中Provider有四种:
1)NETWORK_PROVIDER:通过信号塔和wifi接入点来定位
2)GPS_PROVIDER:需要ACCESS_FINE_LOCATION权限,是通过卫星定位
3)PASSIVE_PROVIDER:需要ACCESS_FINE_LOCATION权限
4)FUSED_PROVIDER:融合所有的位置信息提供最佳的地理位置

2.下面来看一下getBestProvider函数

    public String getBestProvider(Criteria criteria, boolean enabledOnly) {
        String result = null;

        List<String> providers = getProviders(criteria, enabledOnly);
        if (!providers.isEmpty()) {
            result = pickBest(providers);
            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
            return result;
        }    
        providers = getProviders(null, enabledOnly);
        if (!providers.isEmpty()) {
            result = pickBest(providers);
            if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
            return result;
        }    

        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
        return null;
    }    

首先通过根据标准Criteria通过getProviders()方法查找所有的符合的providers,如果providers不为空,那么通过pickBest函数,在providers列表中选择一个最好的provider,pickBest函数如下:

    private String pickBest(List providers) {
        if (providers.contains(LocationManager.GPS_PROVIDER)) {
            return LocationManager.GPS_PROVIDER;
        } else if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
            return LocationManager.NETWORK_PROVIDER;
        } else {
            return providers.get(0);
        }    
    }    

意思就是如果providers列表中存在GPS则首先选择GPS,如果没有GPS,则首先选择NET_PROVIDER,如果两个都么有,那么就选择providers中的第一个。(接上一段)如果providers列表为空,那么则通过getProviders(null, enabledOnly)来获取无标准的providers,接着做上面同样的事情。如果此时providers列表还是为空,那么返回null。

3.getProviders函数

    public List getProviders(Criteria criteria, boolean enabledOnly) {
        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
        ArrayList out;
        int uid = Binder.getCallingUid();;
        long identity = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                out = new ArrayList(mProviders.size());
                for (LocationProviderInterface provider : mProviders) {
                    String name = provider.getName();
                    if (LocationManager.FUSED_PROVIDER.equals(name)) {
                        continue;
                    }
                    if (allowedResolutionLevel >= getMinimumResolutionLevelForProviderUse(name)) {
                        if (enabledOnly && !isAllowedByUserSettingsLocked(name, uid)) {
                            continue;
                        }
                        if (criteria != null && !LocationProvider.propertiesMeetCriteria(
                                name, provider.getProperties(), criteria)) {
                            continue;
                        }
                        out.add(name);
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(identity);
        }

        if (D) Log.d(TAG, "getProviders()=" + out);
        return out;
    }

该函数首先通过getCallerAllowedResolutionLevel()函数来获取应用允许的位置信息分辨率(即权限审查)。getCallerAllowedResolutionLevel()函数如下:

    private int getCallerAllowedResolutionLevel() {
        return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
    }

该函数简单的调用了getAllowedResolutionLevel(),函数如下:

    private int getAllowedResolutionLevel(int pid, int uid) {
        if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
                pid, uid) == PackageManager.PERMISSION_GRANTED) {
            return RESOLUTION_LEVEL_FINE;
        } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
                pid, uid) == PackageManager.PERMISSION_GRANTED) {
            return RESOLUTION_LEVEL_COARSE;
        } else {
            return RESOLUTION_LEVEL_NONE;
        }
    }

分析以下这张图:
Android申请地理位置时的流程及源代码分析_第1张图片
图中ContextImpl类和ContextWrapper类中checkPermission函数如下:
ContextImpl类中的checkPermission函数如下:经分析可知最终调用的还是ActivityManagerService类中的checkPermission()。

    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        try {
            return ActivityManagerNative.getDefault().checkPermission(
                    permission, pid, uid);
        } catch (RemoteException e) {
            return PackageManager.PERMISSION_DENIED;
        }
    }

ContextWrapper类中checkPermission函数如下:其中mBase是Context的实例,其实调用的是ContextImpl类中的checkPermission函数。

    public int checkPermission(String permission, int pid, int uid) {
        return mBase.checkPermission(permission, pid, uid);
    }

所以,最终调用checkPermission函数为PackageManagerService.java中的checkPermission函数。getAllowedResolutionLevel()进行权限检查后回去应用申请的权限,如果应用细粒度和粗粒度的权限都申请了,那么返回细粒度的分辨率即可。
对权限就分析到这吧

你可能感兴趣的:(android源代码)