点击蓝色“架构文摘”关注我哟

加个“星标”,每天上午 09:25,干货推送!

最近在做一功能不大、业务也不复杂的小众App,以往做App是发现自己从来没有考虑过一些架构方面的问题,只是按照自己以往的习惯去写代码,忽略了App的设计。本次分享主要包含一些开发App的小经验和技巧,来一次App开发与设计的分享。

先和分享下一下实体类的设计与组织形式

实体类的组织

在做App开发的时候有很多的实体类,项目越复杂实体类就会越多,经过我的一番思考大致这可以将实体分为以下几大数:

  • 面向数据库的

  • 服务端返回的数据实体

  • 用于渲染View的实体(使用Databinding)

一般情况下实体类的操作会经过以下步骤:

  1. App请求服务器获取数据

  2. 将数据存入数据库(可选)

  3. 渲染页面展示数据

21

现在的实体的产生只用在请求服务器数据的时候才需要新建,后续的数据库、页面渲染其实是可以使用一套实体:

22

先不说这样做的行不行,首先三个地方使用同一实体就会引起字段歧义比如服务器数据有Id、本地数据也有Id,那两个id字段就有冲突了不得不改字段名。

另一种情况渲染和数据本身并不会一一对应,有时候后端数据给的是一个纯数字而前端页面显示的是字符串两个都对应不上,强行放在一起会起来更多的问题。

所为实体类的的正确组织形式应该是:相互隔离、互不干扰

23

数据实体的在渲染之前都需要准备好,比如在ViewModel中将int型的数据转换成文本型的数据然后再使用Databinding+页面渲染实体来渲染页面。

优雅的处理网络数据

现在Android开发使用的网络库大部分都是Okhttp + Retrofit,使用Retrofit网络交互变的非常简单一个Service接口就能搞定一切,美兹兹~~,现在大部分后端返回的数据都会是以下形式:

{    "code":0,    "data": {},    "msg": ""}

虽然不能涵盖所有,但还是可以非常赞的数据、消息、成功与否啥都有!对于前面主要是关注data字段,其他msgcode等都属于辅助字段。前端对应的实体对象应该是这样的(假代码):

public class ApiResponse<T> {    private int code;    private T data;    private String msg;}

对应的Service那就得定义成这样(使用了RxJava):

public intface UserService {    @GET("/xx/{id}")    Single<ApiResponse<UserInfo> getUserInfoById(@Path("id") Long userId);}

从接口中可以看出来,方法的返回值就包了几层,如果要拿data字段需要经过:ApiResponse -> UserInfo,而且在拿之前还要判断code字段:

...

if(ApiResponse.code == 0){    UserInfo info = ApiResponse.getData();}

...

为了消除这些冗余的代码可以使用CallAdapter来使Service方法返回的数据直接就是实体类:

public intface UserService {    @GET("/xx/{id}")    Single<UserInfo> getUserInfoById(@Path("id") Long userId);}

CallAdapter的代码就不贴了,可以自行查找。这样做带来的另外一个问题就是业务代码如何判断接口是否成功或失败,前端必需友好的把错误提示给用户而不是一直搞个Loading在那里瞎转~~。现阶段最方便的的错误传递方式是使用Java异常,前端可以定义业务异常网络异常

public class BizException extends RuntimeException {    ...}

CallAdapter中检查ApiResponse的返回值是否成功:

if(!ApiResponse != 0){    throw new BizExcepiton(ApiResponse);}

如果后端返回业务异常那前端就对应抛出一个BizExcepiton,如果是http错误如:404、400那可以抛出HttpException。除了BizExcepitonHttpException外还可使用特定的异常比如后端返回密码错误异常:

public class InvalidPasswordException extends BizException {    ...}

如需特殊处理,也可以满足要求。

健壮的数据层

现在很多应用都开发使用MVVM开发模式数据层都使用Repository来表示,面向数据驱动的开发模式,页面变化都需要随着数据变更而更新,数据发生变化然后页面再做出响应。Repository的拆分要细一点,不建议简单的弄个UserRepository包含登陆、注册、更新密码等等操作,设计Repository的一些想法:

  1. 面向接口编程

  2. 保持单一原则

  3. 功能边界要清晰(如:登陆、注册可以分开)

  4. 业务逻辑尽可能的少(复杂的业务考虑Presenter)

一个判断是否是好的设计的办法可以这样:一个登陆页面从Activive/Fragment到ViewModel再到Repository,有没有多余的代码。比如上面说的UserRepository包含登陆、注册但是在一个登陆页面就不需要有注册功能,从登陆页面上来看注册的代码就是多余的(有些App登陆/注册在一个页面的~~)。

一个包含登陆、注册的UserRepository简单图:

另外一点是尽量将repository使用到的一些东西集中管理,可引入一个基础的repository:

public class SimpleRepository {

    protected final  <T> T getService(Class<T> clz){        return Services.getService(clz);    }}

做为SimpleRepository的子类,就不需要考虑从哪里获取service的问题。

简洁的UI层

UI层面可以分为ViewModel和View(Activity/Fragment), View的职责应当只有二点:

  1. 展示业务数据

  2. 收集业务数据

例如一些数据的组织、判断都不应该出现在View中比如:

 if (Strings.isNullOrEmpty(phone)) {       ...        return; }

 if (Strings.isNullOrEmpty(pwd)) {        ...        return;  }

像上面这类的代码都不应该出现在View中,而在放置在ViewModel里面,View只收集用户数据传递给ViewModel由它来进行数据校验。再比如像这样的if/else代码也应该放置在ViewModel中:

 int age = 10; String desc = ""; if(age < 18){    desc = "青年"; }else if(age < 29){    desc = "中年"; }

如果数据的显示和数据的收集过多,建议使用Databinding来进行双向绑定数据。再搭配LiveData使View作为观察者实时监听数据变化:

registerViewModel.getRegistryResult().observe(this, new SimpleObserver<RegistryInfo>(this));

一旦数据发生变化LiveData就会通知Observer更新,通过DataBinding更新各个页面数据。

再说ViewModel应该只包含一些简单的判断、检查、打通数据的代码,如果业务过于复杂可以考虑加Presetner,如果真的超级复杂那可以反思下这个复杂的逻辑应不应该放在前端,能不能放在后端呢?

推荐阅读:

  • 面试再被问到 ConcurrentHashMap,把这篇文章甩给他!

  • 万变不离其宗,高并发秒杀系统的设计思考!

  • 技术总监的反思录,我是如何失去团队掌控的?

  • 假如有人今天把支付宝的存储服务器炸了,支付宝里的钱是不是就没了。。。

  • 再也不用担心被虐啦,高频率JVM面试题,都在这里!

  • 饿了么千万级交易系统的重构设计思路

  • 支付系统高可用架构设计实战,可用性高达99.999!

  • 大型互联网公司分布式ID方案总结

-END-

架构文摘

ArchDigest

架构知识丨大型网站丨大数据丨机器学习

如有收获,点个在看,诚挚感谢

高质量App的架构设计与思考!相关推荐

  1. 谈一谈App的架构设计

    如何在软件开发的道路上更进一步? 我们可能已经在研发的这条道路上持续了5年,甚至更久的时间,如何才能拉开和大众的距离,让自己的工作能力提升一步?架构设计应该是其中一个方向,大到app整个的设计,小到每 ...

  2. Android App的架构设计:从VM、MVC、MVP到MVVM

    随着Android应用开发规模的扩大,客户端业务逻辑也越来越复杂,已然不是简单的数据展示了.如同后端开发遇到瓶颈时采用的组件拆分思想,客户端也需要进行架构设计,拆分视图和数据,解除模块之间的耦合,提高 ...

  3. 微服务_SpringCloud微服务架构实战:高并发微服务架构设计

    高并发微服务架构设计 作为一个 IT 从业人员,我们经常会碰到类似于下面的一些问题: 单个项目巨大而沉重,难以维护. 系统稳定性得不到更有效的保证. 怎样才能持续地提升系统的性能. 怎样才能快速地响应 ...

  4. 互联网大厂高并发抢购系统架构设计

    背景 大家好,这篇文章给大家介绍一个非常经典的去大厂面试经常被问的一个问题,就是瞬时 高并发抢购问题,通常来说,大厂开发的系统经常会遇到一些类似电商秒杀抢购.景点门票高并发抢购.特殊商品(比如口罩)高 ...

  5. LiveVideoStackCon讲师热身分享 ( 十一 ) —— 短视频APP的架构设计

    LiveVideoStackCon 2018音视频技术大会是每年的多媒体技术人的盛宴,为了让参会者与大会讲师更多互动交流,我们推出了LiveVideoStackCon讲师热身分享第一季,在每周四晚19 ...

  6. 6个不为人知的高质量APP推荐:知乎3万人点赞,2万人收藏!

    一谈到高质量APP想必大部分人都提到:微信,淘宝,今日头条等等,但是今天为大家分享的这6个不为人知的高质量APP推荐,可能大部分人都从来没有听说过,但其功能绝对令人咂舌!知乎里面已经有3万多人点赞,2 ...

  7. 高并发大型互联网站架构设计

    每年进入3-4月所有的高等院校开始了一年一度的毕业生答辩准备阶段,现如今毕业论文或者毕业设计也更加的贴近了互联发展的趋势,很多学校开始做最热话题云计算openstack架构的实现以及云计算环境搭建,先 ...

  8. android+iso+app,Android、iOS中7个超不错的高质量App

    Android.iOS中7个超不错的高质量App 2020-02-10 21:58:41 48点赞 457收藏 20评论 1.笔趣阁-综合阅读极速版(Android.iOS) 笔趣阁大家一定不陌生,我 ...

  9. Android App整体架构设计

    避免代码臃肿混乱,最根本的是需要代码功底以及对于程序的整体把控和设计能力.除此之外,对于Android App,个人抛砖引玉,提点自己的思路.如果只是轻量级的App或者Web App,在App内做点简 ...

最新文章

  1. Javascript cookie使用详解
  2. jQuery插件 -- Cookie插件
  3. hdu 3022 Sum of Digits
  4. Matlab 卷积函数 ——conv2
  5. 部署项目的问题(一)—— vue工程打包上线样式错乱问题
  6. php不发送referer,php – 注意:未定义的索引:HTTP_REFERER
  7. 什么是CouchDB?
  8. 《Spring揭秘》知识点总结
  9. 计算线性回归、指数回归公式
  10. 杂记十四:DataFactory使用教程
  11. NC18979 毒瘤xor
  12. MOOS-ivp 实验九 分布式旅行商问题(1)
  13. vsCode使vue中的代码高亮
  14. 静态内部类、静态变量的加载次数-理解静态内部类实现线程安全的单例模式懒加载
  15. SpringCloudAlibaba之gateway网关
  16. 黑客攻防与网络安全-N-0
  17. aspnet+sqlserver同学录校友录网站系统
  18. Web1.0时期进入Web3.0时代,即将跨入Web4.0时代
  19. 2.4G蓝牙双模方案xii5168 超高性价比
  20. 华为计算机存储周期,华为OceanStor存储系统日常维护建议(方法及周期)

热门文章

  1. 触发器新增、修改、删除
  2. 白嫖 ABP Commercial 团队版许可证,免费用户也能享受氪金待遇!
  3. 修改自动填充的input背景色
  4. 让狗狗短时间独自在车上也放心 Tesla智能连网功能推出狗狗模式
  5. springcloudspringboot+vue+elementui+flowable+自定义表单+VUE流程设计器
  6. 某火炮武器系统电气控制软件测试实践
  7. absl教程(三):The Abseil Flags Library
  8. 0.目标检测基础知识
  9. 能源大数据应用的现状及前景
  10. Matlab/simulink 二次调频,同步机二次调频,风电二次调频,储能二次调频,水电二次调频。