首页 存档 技术 查看内容

如何从零开始搭建架构实施Android项目 1、服务端概要设计 2 Android客户端

2018-3-30 13:00 |来自: 互联网 267 0

摘要: 从零开始搭建架构实施Android项目 我们先假设一个场景需求:刚有孩子的爸爸妈妈对用照片、视频记录宝宝成长有强烈的意愿,但苦于目前没有一款专门的手机APP做这件事。A公司洞察到市场需求,要求开发团队尽快完成Andr ...

从零开始搭建架构实施Android项目

我们先假设一个场景需求:刚有孩子的爸爸妈妈对用照片、视频记录宝宝成长有强烈的意愿,但苦于目前没有一款专门的手机APP做这件事。A公司洞察到市场需求,要求开发团队尽快完成Android客户端的开发。以下模拟团队和工作开展。


  • 团队情况:产品经理1人,Android开发2人,服务端开发2人,UI设计1人。

  • 开发周期:两个月。

  • 工作量:大约50个界面。

  • 隐含需求:考虑到用户群体有可能激增的情况,服务端需要有一定的并发能力

  • 前提:原型已设计完成。


1、服务端概要设计


1.1 系统架构

先给出服务端的架构图。



由于服务端开发有Java、PHP背景,为了快速完成开发任务,我们选择PHP作为服务端开发语言,顺便也把数据库定为MySQL。考虑后期扩展和数据库访问性能,拟引入Redis非关系型数据库。同时为了提高数据读的性能,在云服务器和数据库之间用上缓存,并为数据库主从备份、读写分离。服务器就不搭建在本地了,管理是一大问题。现在云服务器一大把,七牛、阿里云、腾讯云、百度云、金山云等等,技术成熟,而且价格还算公道。在此我们选择阿里云。为了应对可能面临的并发问题,云服务器要考虑负载均衡。项目中可能存在大量的需要上传和下载照片和视频,我们选择阿里云的开放存储服务,同时为了提升各个地区的下载体验,我们引入CDN。客户端通过API Service和服务端交换数据,图片和视频的下载直接通过CDN。


1.2 模块划分


根据需求和原型设计,可能的模块划分如下:


  • 注册登录模块

  • 用户模块

  • 小孩模块

  • 媒体(图片 视频)模块

  • 相册模块

  • ……


1.3 数据交换和API接口


服务端与客户端使用JSON交换数据,使用自定义JSON格式,约定返回code、message,实体封装在result中,支持单个实体、实体列表、多个实体列表,定义如下:

{  
  "code":500,  
  "message":"系统异常,请稍后重试",  
  "result":""  }
{  
  "code":200,  
  "message":"登录成功",  
  "result":{  
    "user":{  
      "userId":1,  
      "nickName":"Leo",  
      "email":"Leo@**.com",  
      "gender":0  
    }  
  }  
}
{  
  "code":200,  
  "message":"SUCCESS",  
  "result":{  
    "album":{  
       "kid":{  
         "kidId":1,  
         "nickName":"LEE",  
         "gender":1,  
         "birthday":"2014-3-6",  
         ......  
       },  
       "media":{  
         "mediaId":123,  
         "mediaType":1,  
         "createdTime":193743546746,  
         "mediaDescription":"这是小孩第一次出去春游",  
         ......  
       }  
     }   
  }  
}

主要API接口设计如下:

http://api.**.com/service/v1.0/user/login   
http://api.**.com/service/v1.0/user/third-login
http://api.**.com/service/v1.0/user/register
http://api.**.com/service/v1.0/user/logout
http://api.**.com/service/v1.0/user/info/update
http://api.**.com/service/v1.0/album/upload
http://api.**.com/service/v1.0/album/update
http://api.**.com/service/v1.0/album/delete
......

也许你看到了,API做了二级域名映射,同时为了服务端后期API版本的升级管理,在URL中加上了版本标识V1.0。命名方面我尽量做到restful的风格。对了,此处没有使用Https。为了解决数据传输的安全,我做了点特别的处理:对请求体和响应结果进行RSA加密(如果服务端返回的数据稍稍过大,这个RSA严重影响客户端解密,后来我换成了AES),所有请求为POST请求,所以API URL后面没有带参数,你也看不到任何请求相关的信息。


1.4 数据库设计


根据需求和原型设计,数据库的设计大概需要两周时间。其实一周基本搞定了,但为了考虑充分,留出一周时间来检验和调整。数据库E-R图略。


2 Android客户端


2.1 基本结构


Android本身就是MVC,所以我不打算引入MVP或MVVM。我的理念是职责分层,快速推出Android 1.0。主要的包结构如下:



工程的搭建和包的划分有各种各样的,适合自己的就行了。想讨论或想看别人怎么做的,点击这里:App工程结构搭建:几种常见Android代码架构分析


2.2 功能划分


注册登录,个人信息,我的小孩,相册管理,消息通知,系统设置等等。


2.3 引入的第三方技术


重复发明轮子是不可取的。有些模块根本没必要自己写。以下是引入的第三方库,以及优势说明。


2.3.1 网络请求库android-async-http


  • 在匿名回调中处理请求结果

  • 在UI线程外进行http请求

  • 文件断点上传

  • 智能重试

  • 默认gzip压缩

  • 支持解析成Json格式

  • 可将Cookies持久化到SharedPreferences


2.3.2云巴推送


专注于为需要实时数据交换的产品提供完美解决方案

基于发布者/订阅者(publisher/subscriber)模式,集成简单

对比了百度云推送、腾讯信鸽推送,云巴效果更好

原极光推送CTO创办的


2.3.3 xUtils(只使用其中的DbUtils和ViewUtils)


  • Android中的ORM框架,一行代码就可以进行增删改查

  • 支持事务,默认关闭

  • 可通过注解自定义表名、列名、外键、唯一性约束、NOT NULL约束、CHECK约束等(需要混淆的时候请注解表名和列名)

  • Android中的IOC框架,完全注解方式就可以进行UI,资源和事件绑定

  • 新的事件绑定方式,使用混淆工具混淆后仍可正常工作


2.3.4 友盟统计


  • 国内专业的移动应用统计分析平台

  • 统计和分析流量来源、内容使用、用户属性和行为数据

  • Crash log跟踪


2.3.5 云通讯验证码


  • 解决方案成熟,众多公司使用


2.3.6 高德地图定位


  • GPS 基站 wifi的混合定位方式

  • 接入简单


2.3.7 异步图片加载库Android-Universal-Image-Loader


  • 多线程下载图片,图片可以来源于网络,文件系统,项目文件夹assets中以及drawable中等

  • 支持随意的配置ImageLoader,例如线程池,图片下载器,内存缓存策略,硬盘缓存策略,图片显示选项以及其他的一些配置

  • 支持图片的内存缓存,文件系统缓存或者SD卡缓存

  • 支持图片下载过程的禁用词语

  • 根据控件(ImageView)的大小对Bitmap进行裁剪,减少Bitmap占用过多的内存

  • 较好的控制图片的加载过程,例如暂停图片加载,重新开始加载图片,一般使用在ListView,GridView中,滑动过程中暂停加载图片,停止滑动的时候去加载图片

  • 提供在较慢的网络下对图片进行加载


2.3.8 阿里云OSS Android客户端SDK


  • 提供文件(图片、视频等等)上传

  • 大文件分块上传

  • 删除操作(不推荐在客户端使用)


2.3.9 组件内通讯EventBus


  • 基于发布者/订阅者(publisher/subscriber)模式

  • 简化了应用程序内各组件间、组件与后台线程间的通信


2.3.10 Android本地数据库加密库SQLCipher


  • 基于SQLite扩展的开源数据库,在SQLite的基础之上增加了数据加密功能SQLCipher对Android SDK中所有与数据库相关的API都制作了一份镜像,使得开发者可以像操作普遍的数据库文件一样来操作SQLCipher


2.4 基础组件封装


2.4.1 基础回调接口

public inte**ce DataCallback {  

  void onSuccess(Object result);  

  void onFailure(Object result);  

}.

2.4.2 网络访问


先看一下登录的序列图:


HttpManager类负责调用AsyncHttpWrapper中的post方法,和对服务端返回的数据解密、JSON转对象、回调上层;AsyncHttpWrapper则负责请求体的封装加密和其它的校验参数封装。看一下HttpManager类的post方法:

public void post(Context context, String url, RequestParams params, final String modelName,  
           final DataCallback callback) {  
    AsyncHttpWrapper.post(context, url, params, new AsyncHttpResponseHandler() {  
      @Override  
      public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {  
        try {  
          if (modelName != null) {  
            handleResponse(responseBody, callback, modelName);  
          } else {  
            String response = new String(responseBody);  
            // 解密  
            response = AES128.getInstance().decrypt(AppUtil.decodeReplace(response));  

            // JSON转对象  
            BaseMessage message = AppUtil.getMessage(response);  
            if (callback != null) {  
              // 如果自定义code是200  
              if (Coder.CODE_200.equals(message.getCode())) {  
                callback.onSuccess(message.getMessage());  
              } else {  
                callback.onFailure(new ServerError(message.getCode()));  
              }  
            }  
          }  
        } catch (JSONException e) {  
          LogUtil.e(e);  
          callback.onFailure("服务端返回的数据不能解析成JSON");  
        } catch (Exception e) {  
          LogUtil.e(e);  
          callback.onFailure(e);  
        }  
      }  

      @Override  
      public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {  
        if (callback != null) {  
          callback.onFailure(error);  
          if (responseBody != null) {  
            String s = new String(responseBody);  
            LogUtil.e(s);  
          }  
        }  
      }  
    });  
  }

AsyncHttpWrapper中的post方法

public static void post(Context context, String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {  
    // 设置请求头部信息  
    generateHeader(context);  

    // 加密请求参数  
    String encryParams = AES128.getInstance().encrypt(params.toString());  

    RequestParams requestParams = new RequestParams();  
    requestParams.put("param", AppUtil.encodeReplace(encryParams));  

    client.post(context, url, requestParams, responseHandler);  
  }
private static AsyncHttpClient client = new AsyncHttpClient();

2.4.3 Adapter封装


为了加快开发速度,重用代码,Adapter的使用有技巧。每次在getView中查找控件id、利用ViewHolder、赋值,最后返回convertView,看着都是差不多的代码。是时候脱离这个苦海了。先看怎么解决共用的ViewHolder问题。

public static
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

相关分类

返回顶部