美文网首页资料 | 汇总androidiOS Developer
LocationManager定位、地址搜索相关痛点解决(Bas

LocationManager定位、地址搜索相关痛点解决(Bas

作者: dj_rose | 来源:发表于2017-02-24 16:41 被阅读387次
    地图选址

    项目中对定位、地址搜索等功能采用了LocationManager单例模式,关于单例的优点在此就不赘述了,这里主要想分享一下单例模式下LocationManager的两个痛点。

    首先简单贴一下单例的实现:

    static LocationManger *locationInstance;
    +(LocationManger*)getInstanceWithDelegate:(id)delegate
    {
        @synchronized(self) {
            if (!locationInstance) {
                locationInstance = [[LocationManger alloc] init];
                locationInstance.geocoder = [[CLGeocoder alloc] init];
            }
            if (delegate) {
                locationInstance.delegate = delegate;
            }
        }
        return locationInstance;
    }
    

    当然,更推荐这样的写法:

    static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
          //TO DO
        });
    

    接下来,开始分析痛点:

    痛点一

    项目中某页面需要同时用到定位当前地址+对某坐标进行逆地理编码验证两个功能,高德回调函数难以区分。
    当前地址的逻辑大致如下(非完整代码):

    //发起定位
    [self.lManager startUpdatingLocation];
    
    //定位回调里拿到坐标->发起逆地理编码
    -(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
    {
        ...
        //构造AMapReGeocodeSearchRequest对象,location为必选项,radius为可选项
        AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init];
        regeoRequest.location = [AMapGeoPoint locationWithLatitude:lItem.coordinate.latitude longitude:lItem.coordinate.longitude];
        regeoRequest.radius = 10000;
        regeoRequest.requireExtension = YES;
         [self.lManager stopUpdatingLocation];
         //发起逆地理编码
        [_search AMapReGoecodeSearch:regeoRequest];
    }
    
    //逆地理编码的回调函数->数据处理并回调相应VC
    - (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
    {
          //TO DO
    }
    

    总结下来定位当前地址的流程:
    发起定位->定位回调里拿到坐标->发起逆地理编码-> 逆地理编码回调函数->数据处理并回调相应VC

    而对某坐标进行逆地理编码验证则也是通过:
    发起逆地理编码-> 逆地理编码回调函数->数据处理并回调相应VC

    细心的同学已经发现了:两个不同的功能都会走同一个高德回调函数。这样就有可能造成区分不清对应关系的问题。

    • 项目初期是在LocationManger里用一个全局的BOOL值去区分是来自哪种功能(不高频率切换使用两种功能的话,基本能掩盖这个问题)。
    • 后来QA同事中度暴力测试发现了这个问题,临上线改变方案:发起对某坐标逆地理编码验证功能时全局保存此坐标,并在逆地理回调函数里用此全局坐标作区分。(相当于用坐标代替BOOL值去作区分,准确度大增)。
    • 维持了大概两个版本,QA同事重度暴力测试又发现了这个问题,尽管出现概率不高,但还是在leader的指导下,采用了最后的解决方案。如下:
      在发起定位/对某坐标进行逆地理编码验证时分别给request动态绑定一个bSearchAddress属性
    //构造AMapReGeocodeSearchRequest对象,location为必选项,radius为可选项
    AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init];
    [regeoRequest setValue:@"0" forKey:ReGeocodeSearchRequestKey];//标记为定位模式,回调里用于区分
    
    AMapReGeocodeSearchRequest *regeoRequest = [[AMapReGeocodeSearchRequest alloc] init]; 
    [regeoRequest setValue:@"1" forKey:ReGeocodeSearchRequestKey];//标记为地址验证模式,回调里用于区分
    

    这样就能在逆地理回调里进行区分处理:

    //实现逆地理编码的回调函数
    - (void)onReGeocodeSearchDone:(AMapReGeocodeSearchRequest *)request response:(AMapReGeocodeSearchResponse *)response
    {
        if(response.regeocode != nil)
        {
            NSString *bSearchAddress = [request valueForKey:ReGeocodeSearchRequestKey];
            //通过AMapReGeocodeSearchResponse对象处理搜索结果
            if ([bSearchAddress isEqualToString:@"1"]) {
                //地址验证模式
            }else {
                //定位模式
            }
        }
    }
    
    这里需要提一下,给对象动态绑定属性需要用到RunTime+Category:
    #import "AMapReGeocodeSearchRequest+bSearchAddress.h"
    #import <objc/runtime.h>
    
    @implementation AMapReGeocodeSearchRequest (bSearchAddress)
    @dynamic bSearchAddress;
    static char str_bSearchAddress;
    
    - (void)setBSearchAddress:(NSString *)bSearchAddress
    {
        [self willChangeValueForKey:@"bSearchAddress"];
        objc_setAssociatedObject(self, &str_bSearchAddress, bSearchAddress, OBJC_ASSOCIATION_RETAIN);
        [self didChangeValueForKey:@"bSearchAddress"];
    }
    
    - (NSString *)bSearchAddress
    {
        return objc_getAssociatedObject(self, &str_bSearchAddress);
    }
    @end
    

    痛点二

    同一页面有两处需要用到同一个地址搜索的功能,如何区分的问题。
    在刚开始实现这个需求的时候,我还是很天真的在ViewController中用BOOL值去区分这两处的搜索,以期能在LocationManger回调给页面的方法里作区分。这个问题的解决方案应该更丰富一点,我最后是这么解决的:

    • 在第一处直接调用LocationManger中封装好的地址搜索方法:
    - (void)startAMapKeyWordsSearchRequest:(AMapPOIKeywordsSearchRequest *)request;//高德地址搜索
    
    • 对于第二处的处理,这里需要定义一个requestAddressObject,在requestAddressObject中封装LocationManger中对应的高德地址搜索方法,成为LocationManger的代理并实现地址搜索的回调函数。
    #import <Foundation/Foundation.h>
    #import "LocationManger.h"
    
    @protocol RequestAddressObjectDelegate <NSObject>
    - (void)requestKeywordsPOISuccess:(AMapPOISearchResponse *)response;
    - (void)requestKeywordsPOIfailed:(NSString *)sError;
    @end
    
    @interface requestAddressObject : NSObject
    <
    LocationManagerDelegate
    >
    @property (weak, nonatomic) id<RequestAddressObjectDelegate> delegate;
    @property (weak, nonatomic) LocationManger *locationManager;
    @property (strong, nonatomic) AMapPOIKeywordsSearchRequest *requestKeywordsPlaceList;
    
    - (void)requestPOIKeywordSearch:(AMapPOIKeywordsSearchRequest *)request;
    - (void)requestPOIWithKeyword:(NSString *)keyword city:(NSString *)city cityLimit:(BOOL)cityLimit;
    
    @end
    
    #import "requestAddressObject.h"
    @implementation requestAddressObject
    - (LocationManger *)locationManager{
        if (!_locationManager) {
            _locationManager = [LocationManger getInstanceWithDelegate:self];
        }else {
            if (_locationManager.delegate != self) {
                _locationManager.delegate = self;
            }
        }
        return _locationManager;
    }
    - (void)requestPOIKeywordSearch:(AMapPOIKeywordsSearchRequest *)request
    {
        [self.locationManager startAMapKeyWordsSearchRequest:request];
    }
    - (void)lmGetPOISearchDelegate:(AMapPOISearchResponse *)response
    {
        if (self.delegate && [self.delegate respondsToSelector:@selector(requestKeywordsPOISuccess:)]) {
            [self.delegate requestKeywordsPOISuccess:response];
        }
    }
    - (void)lmGetLocationFaild:(NSString *)sError
    {
        if (self.delegate && [self.delegate respondsToSelector:@selector(requestKeywordsPOIfailed:)]) {
            [self.delegate requestKeywordsPOIfailed:sError];
        }
    }
    @end
    
    • 即用requestAddressObject去发起第二处的地址搜索请求,在requestAddressObject中实现LocationManger的回调函数并用requestAddressObjectDelegate定义好的方法回调到VC中进行最后的数据处理和页面展示。这样就达到了区分同一页面两处用到同一地址搜索方法的效果。
    //ViewController中的第二处的处理
    
    //懒加载
    - (requestAddressObject *)reqAddressObj
    {
        if (!_reqAddressObj) {
            _reqAddressObj = [[requestAddressObject alloc] init];
            _reqAddressObj.delegate = self;
        }
        return _reqAddressObj;
    }
    
    //第二处地址搜索的调用
    [self.reqAddressObj requestPOIKeywordSearch:_requestKeywordsPlaceList];
    
    //LocationManager的回调方法经由RequestAddressObject中转了一次之后回到VC的回调方法
    #pragma mark - RequestAddressObjectDelegate
    - (void)requestKeywordsPOISuccess:(AMapPOISearchResponse *)response
    {
        //第二处地址搜索成功的回调
    }
    
    - (void)requestKeywordsPOIfailed:(NSString *)sError
    {
        //第二处地址搜索失败的回调
    }
    
    
    输入选址
    小结:两个小痛点都是如何对数据进行严格的区分问题。第一个是通过对request进行动态绑定属性来进行区分;第二个是通过用不同的请求方式(VC直接发起请求、间接通过object发起请求)来回避数据区分的问题。两个问题刚好是从不同的角度去解决问题,一个正面交锋;一个迂回战术。方案本无优劣,具体问题具体分析才是王道!

    相关文章

      网友评论

        本文标题:LocationManager定位、地址搜索相关痛点解决(Bas

        本文链接:https://www.haomeiwen.com/subject/kmxwwttx.html