关于地址选择器的思考与实践

前言

我们知道,地址选择器是一个通用组件,网上的开源项目也有很多。那么为什么还会有这篇文章呢?因为我在调研过程中发现,虽然都是地址选择器,但是实现的方式却各不相同。以下是调研地址选择器的一些总结和思考实践。希望对大家有帮助,大家有什么更好的想法,也要告知我哦~

地址选择器实现方式介绍

  • 本地存放area.db文件
    大多数App都是此种实现

    • 优点:启动快,不受网络影响
    • 缺点:
      • 不能实时更新,数据更新依赖发布新版本。(或则自己实现一套更新机制,文件从后端下载,实现较为复杂)
      • 各端(服务器、前端、移动端)需要维护一份相同的地址信息表,维护成本高。
      • 地址信息表本地保存,增大安装包体积
  • 一次性从服务端拉取所有地址信息
    在App启动时候,一次性拉取所有地址信息。

    • 优点:数据可配置、请求少
    • 缺点:
      • 每次启动都请求服务器,大部分是无用请求,浪费了服务器资源。
      • 请求数据量大
    • 优化方案:更换请求时机,点击选择地址的时候才去一次性请求所有数据。但是还是不能避免请求数据量过大问题。
  • 实时获取省、市、区信息,选中上一级才去获取对应的下一级数据
    如:京东

    • 优点:数据可配置,请求数据量小
    • 缺点:请求较多,需要处理的异常case较多
    • 思考优化方案:每次唤起地址选择器时,缓存获取的地址信息历史数据。显示地址信息的时候,只有本地缓存没有当前数据,才向服务端发送请求。(其实这种场景很少出现,但是再小的苍蝇也是肉,能优化就优化了吧……)

因为用户基本是在有网络的情况下才会使用,所以选择联网获取地址信息也是合理的。又考虑到网络请求的大小,服务端性能影响,安装包大小以及地址信息可配置性等因素,实时获取地址信息是一个不错的方式。下面介绍地址选择器实现一些关键点。

实时获取信息的地址选择器设计

先来看效果,如下图所示。唤起地址选择器会有一次省份的网络请求,之后每一级数据都是实时去获取。在当前地址选择器唤起状态,获取之后就将历史数据保存在本地,下一次就不再发送网络请求了。


地址选择器效果图
地址选择器效果图

数据结构设计

当前的地址信息按照省、市、区/县、街道四级划分,后一级总是和前一级相关联。

  • Province
public class Province {
    public long id;
    public String name;
}
  • City
public class City  {
    public long id;
    public long province_id;
    public String name;
}
  • County
public class County {
    public long id;
    public long city_id;
    public String name;
}
  • Street
public class Street {
    public long id;
    public long county_id;
    public String name;
}

回调接口设计

主要提供了4个回调方法:

public interface OnAddressSelectedListener {
    // 获取地址完成回调
    void onAddressSelected(Province province, City city, County county, Street street);
    // 选取省份完成回调
    void onProvinceSelected(Province province);
    // 选取城市完成回调
    void onCitySelected(City city);
    // 选取区/县完成回调
    void onCountySelected(County county);
}

这里着重说一下onAddressSelected回调方法,其是在地址选择完成的时候调用。那么是如何判断地址选择已经完成呢。这个在AddressSelector中有如下一个机制。
每次一个级别选择完成后,会获取下一个级别的数据(网络请求或者缓存获取)进行显示。显示的时候有这么一个逻辑,当前级别有数据,则正常显示;若没有,则说明地址选择已经完成,此时调用onAddressSelected方法。

缓存设计

我们这里默认服务端每个地址的id都是唯一的。缓存机制比较简单,可以看下如下的流程图。

缓存机制流程图
缓存机制流程图

  1. 建立三个缓存map,分别缓存省-市市-区区-街道
/** 缓存数据:省-市 */
private ArrayMap<Long, List<City>> province2city = new ArrayMap<>();
/** 缓存数据:市-区 */
private ArrayMap<Long, List<County>> city2county = new ArrayMap<>();
/** 缓存数据:区-街道 */
private ArrayMap<Long, List<Street>> county2street = new ArrayMap<>();
  1. 选择省、市、区某一级时,先查看是否有缓存数据,若有则使用缓存数据;若没有,则向服务端发送网络请求。
// 有缓存则直接使用缓存,否则去重新请求
if(province2city.containsKey(province.id)){
    setCities(province2city.get(province.id));
} else {
    progressBar.setVisibility(View.VISIBLE);
    listener.onProvinceSelected(province);
}

3、每次获取的历史数据,存储在相应的缓存map中。

province2city.put(provinceId, cities);

控件使用

  1. gradle导入控件
compile 'com.zr.addressselector:library:1.0.1'
  1. Activity实现OnAddressSelectedListener接口
public class MainActivity extends AppCompatActivity implements OnAddressSelectedListener
  1. 展现地址选择器
dialog = new BottomSelectorDialog(MainActivity.this);
dialog.setOnAddressSelectedListener(MainActivity.this);
dialog.show();
  1. 根据网络返回,设置数据
dialog.getSelector().setProvinces(Collections.singletonList(province));
  1. 控件提供的几个设置方法
/**
     * 设置回调接口
     * @param listener
     */
    public void setOnAddressSelectedListener(OnAddressSelectedListener listener) {
        this.listener = listener;
    }

    /**
     * 设置省列表
     * @param provinces 省份列表
     */
    public void setProvinces(List<Province> provinces){
        handler.sendMessage(Message.obtain(handler, WHAT_PROVINCES_PROVIDED, provinces));
    }

    /**
     * 设置市列表
     * @param cities 城市列表
     */
    public void setCities(List<City> cities){
        handler.sendMessage(Message.obtain(handler, WHAT_CITIES_PROVIDED, cities));
    }

    /**
     * 设置区列表
     * @param countries 区/县列表
     */
    public void setCountries(List<County> countries){
        handler.sendMessage(Message.obtain(handler, WHAT_COUNTIES_PROVIDED, countries));
    }

    /**
     * 设置街道列表
     * @param streets 街道列表
     */
    public void setStreets(List<Street> streets){
        handler.sendMessage(Message.obtain(handler, WHAT_STREETS_PROVIDED, streets));
    }

写在最后

虽然提供了gradle compile导入的方式,但是整个控件的源码其实是很简单的。如果有定制需求,可以到这里下载源码。

项目源码下载:ZRAddressSelector

特别感谢

JDAddressSelector,本组件设计也是受此开源项目启发,非常感谢作者开源~

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

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,497评论 18 139
  • 国家电网公司企业标准(Q/GDW)- 面向对象的用电信息数据交换协议 - 报批稿:20170802 前言: 排版 ...
    庭说阅读 10,800评论 6 13
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,500评论 18 399
  • API定义规范 本规范设计基于如下使用场景: 请求频率不是非常高:如果产品的使用周期内请求频率非常高,建议使用双通...
    有涯逐无涯阅读 2,509评论 0 6
  • 写在90这天 今天终于用邮箱注册了账号,然后报名了,其实心里有点慌,有时候觉得自己复习了这么久完全没进步,很疲惫,...
    cheon阅读 155评论 0 0