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

本文深入探讨了在Android应用中获取位置信息的方法,包括使用LocationManager服务,设定地理位置标准,选择最佳Provider,以及权限审查流程。同时,文章详细解释了getBestProvider函数的工作原理和getProviders函数的权限检查过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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<String> 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<String> getProviders(Criteria criteria, boolean enabledOnly) {
        int allowedResolutionLevel = getCallerAllowedResolutionLevel();
        ArrayList<String> out;
        int uid = Binder.getCallingUid();;
        long identity = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                out = new ArrayList<String>(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;
        }
    }

分析以下这张图:
这里写图片描述
图中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()进行权限检查后回去应用申请的权限,如果应用细粒度和粗粒度的权限都申请了,那么返回细粒度的分辨率即可。
对权限就分析到这吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值