关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »从零开始搭建架构实施Android项目

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

    2016-01-12 00:00:00 出处:Keegan小钢
    分享

    我们先假设一个场景需求:刚有孩子的爸爸妈妈对用照片、视频记录宝宝成长有强烈的意愿,但苦于目前没有一款专门的手机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@xxx.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.xxx.com/service/v1.0/user/login
    
    http://api.xxx.com/service/v1.0/user/third-login
    
    
    http://api.xxx.com/service/v1.0/user/register
    
    
    http://api.xxx.com/service/v1.0/user/logout
    
    
    http://api.xxx.com/service/v1.0/user/info/update
    
    
    http://api.xxx.com/service/v1.0/album/upload
    
    
    http://api.xxx.com/service/v1.0/album/update
    
    
    http://api.xxx.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 interface 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 <T extends View> T get(View view, int id) {
            SparseArrayCompat<View> viewHolder = (SparseArrayCompat<View>) view.getTag();
            if (viewHolder == null) {
                viewHolder = new SparseArrayCompat<>();
                view.setTag(viewHolder);
            }
            View childView = viewHolder.get(id);
            if (childView == null) {
                childView = view.findViewById(id);
                viewHolder.put(id, childView);
            }
            return (T) childView;
        }

    ViewHolder的作用,就是通过convertView.setTag与convertView进行绑定。当convertView复用时,直接从与之对应的ViewHolder(getTag)中拿到convertView布局中的控件,省去了findViewById的时间。上面的代码就是这样的原理。

    然后就是CommonAdapter了。

    public abstract class CommonAdapter<T> extends BaseAdapter {
    
        protected LayoutInflater inflater;
        protected Context context;
        protected List<T> datas;
        protected final int itemLayoutId;
    
        public CommonAdapter(Context context, List<T> datas, int itemLayoutId) {
            this.context = context;
            this.inflater = LayoutInflater.from(context);
            this.datas = datas;
            this.itemLayoutId = itemLayoutId;
        }
    
        @Override
        public int getCount() {
            return datas != null   datas.size() : 0;
        }
    
        @Override
        public T getItem(int position) {
            return datas.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final CommonViewHolder viewHolder = getViewHolder(position, convertView, parent);
            convert(viewHolder, getItem(position), position);
            return viewHolder.getConvertView();
    
        }
    
        public abstract void convert(CommonViewHolder viewHolder, T item, int position);
    }

    好了,不贴代码了。看不明白了请点击这里:Android 快速开发系列 打造万能的ListView GridView 适配器

    2.4.5 其它Utils封装

    如AES128加密类、BitmapUtils、SecurePreferences、StringUtil、ToastUtil、IOUtil等等。

    2.5 接口测试

    为了保证数据交换、加解密正常,首先对某一个接口进行测试,以验证API Service能正常跑通。比如可以先对登录进行模拟测试,看是否成功,同时包括异常的测试,服务端是不是处理了边界异常,返回给客户端的都是封装过的异常信息,而不是抛一个敏感信息给客户端。提前进行接口测试有助于我们的基础组件运行没问题,方便后期其它模块的快速集成。

    2.6 快速开发

    基础组件封装好后,除了少量的从网络获取数据逻辑和本地数据库的增删改查,客户端基本上就是界面的布局工作了。界面开发基本看熟练程度和自定义View的重用。

    好了,基本就这些。两个Android开发人员两个月内完成肯定是可以的,前提是至少有一个熟手。后面再谈谈MVP,毕竟这个客户端设计没法进行单元测试,假如业务逻辑越来越复杂,Activity的职责会越来越重,问题多多,不利于后期维护。

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    抖音安全与信任开放日:揭秘推荐算法,告别单一标签依赖

    ultraedit编辑器打开文件时,总是提示是否转换为DOS格式,如何关闭?

    Cornell大神Kleinberg的经典教材《算法设计》是最好入门的算法教材

    从 Microsoft 下载中心安装 Windows 7 SP1 和 Windows Server 2008 R2 SP1 之前要执行的步骤

    Llama 2基于UCloud UK8S的创新应用

    火山引擎DataTester:如何使用A/B测试优化全域营销效果

    腾讯云、移动云继阿里云降价后宣布大幅度降价

    字节跳动数据平台论文被ICDE2023国际顶会收录,将通过火山引擎开放相关成果

    这个话题被围观超10000次,火山引擎VeDI如此解答

    误删库怎么办?火山引擎DataLeap“3招”守护数据安全

    IT头条

    平替CUDA!摩尔线程发布MUSA 4性能分析工具

    00:43

    三起案件揭开侵犯个人信息犯罪的黑灰产业链

    13:59

    百度三年开放2.1万实习岗,全力培育AI领域未来领袖

    00:36

    工信部:一季度,电信业务总量同比增长7.7%,业务收入累计完成4469亿元

    23:42

    Gartner:2024年全球半导体营收6559亿美元,AI助力英伟达首登榜首

    18:04

    技术热点

    iOS 8 中如何集成 Touch ID 功能

    windows7系统中鼠标滑轮键(中键)的快捷应用

    MySQL数据库的23个特别注意的安全事项

    Kruskal 最小生成树算法

    Ubuntu 14.10上安装新的字体图文教程

    Ubuntu14更新后无法进入系统卡在光标界面解怎么办?

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。