Android 程序框架设计
这篇文章主要内容来自于之前我讲的一个PPT文档,现在将其整理如下。欢迎指正。以下的内容都是来自于我自身的经验,欢迎大家多提自己的建议。
1、一些概念
模式的定义:
每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。通过这种方式,你可以无数次地使用那些已有的解决方案,无需在重复相同的工作。
什么是设计模式?
设计模式是在某种特别的情况下,针对某种问题的某种典型、通用的解决方法。
我们是需要适当了解并学习一些设计模式,在程序开发过程中,总是会涉及到一些框架设计,模块设计之类的东西,如果能很好理解并运行设计模式,你所设计的模块或框架将会要稳定得多,因为这些设计模式它们都是通用的解决方案,是经过实践经验了的。
比如说,在程序里,可能会有通知模块,A模块的数据发生变化,B模块需要得到通知,对于这样的需要,你可能会想到用"广播","消息"或者"回调"的方式来解决,的确,刚才我所说的那三种也能解决,但是,这三种都是存在一些缺点,比如说广播,用Intent来传输数据很困难,对于"消息",无法很好的跟踪,对于"回调",有可能你A与B模块根本不可相互访问。此时,如果你会用观察者模式的问题,这种问题可以很轻松解决。
当然,这里是需要具体问题具体分析的,我主要的意思就是说,要适当利用模式,我们不能为了用模式而去用模式,我们是要用模式来解决我们实际的问题。
概念完整性
关于概念完整性,在《人月神话》一书在有大量的阐述,这里,我把我的理解写出来,与大家分享。
1)概念完整性是系统设计中最重要的考虑因素。当你的系统规模越大,这一点体现得越明显。
2)为了获取概念的完整性,设计必须由一个人或者具有共识的小型团队来完成。这一点很好理解,关于设计,可以让所有的人参与,但是决定权在少数人手里,如果大家都想参与设计,这是根本没有办法保证系统设计是统一完整的。
3)要获得概念上的完整性,就必须有人控制这些概念,类似于贵族的专制统治。这里,对于团队中的项目经理或架构师必须对项目有绝对的权威,不然,这个项目里面的就无法统一号令。
4)概念完整性表现有:
- 开发过程中,需求、设计、编码的一致性
- 整个程序具有统一的风格,比如对话框样式,按钮风格,色调等UI元素
- 整个程序具体统一的结构,比如不同模块访问网络,它们的调用方式一致,例如异步访问都用回调方式通知结果,相同的功能应该提取成共通模块。
- 开发人员能很好的执行需求人员和设计人员的意图。
- 有完整的文档,需求文档,设计文档,测试文档,处理流程的文档等。
如何保持概念完整性
- 在制度上给予保证,产品的负责人必须建立技术上的绝对权威
- 技术负责人员(SE,SL)必须严格执行项目的需求,设计,必须深入到编码细节
- 在不同阶段,保持与所有人员的持续沟通,鼓励开发人员提意见。
- 让开发人员参与设计,但不决定设计
- 通过持续的反馈和沟通来实现模块重用
2、设计之前应该做什么
2.1 共通类的设计
2.1.1 Widget设计
- TextView
- EditText
- Button
- Title bar
- Tool bar
- ...
为什么要提供这些共通控件?
- 统一字体大小,如App字体不随系统字体变化而变化
- 统一UI式样,如Button, EditText具有相同的背景等
- 复用代码
2.1.2 Adapter Items
- 根据式样,提取需要在AdapterView中显示的Item
- 简单的复合布局
- 自绘制,从而提高滑动性能
- ListView中放Gallery时,提高上下滑动性能
- 尽量优化绘制
数据驱动
- Adapter Items提供核心的方法
- setData(Object data)
- getData(); - Adapter#getView实现更加简单
- 实现简单
- 不会因为UI变化而变化
下面代码示例了Adapter#getView()方法的实现,它返回BookView,BookView提供方法来接收数据,至于BookView的显示,则根据设置的数据来显示,这就是数据驱动UI。
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (null == convertView) {
- convertView = new BookView(getContext());
- convertView.setLayoutParameter(new AbsListView.LayoutParameter(150, 150));
- }
- Book book = m_bookList.get(position);
- BookView bookView = (BookView)convertView;
- bookView.setBook(book);
- return convertView;
- }
2.1.3 Dialog
- 扩展于Dialog类
- 提供Dialog关闭的事件
- Dialog的高度随内容的变化而变化
- 可以设置按钮的文字,可见性,字体等方法
- 设置按钮点击事件的listener
- 要考虑对话框的三个属性:Title, Content area, Action buttons
2.1.4 Utility
- Log
- DateFormat
- Bitmap
- Notification
- Shared Preference
- Environment
- Device
- ...
2.2 Task管理
线程只是一种机制,保证我们要完成的任务不运行在UI线程(也就是说不阻塞UI),完成的任务才是我们关注的核心,因此,我们可以通过设计,把线程封装,让使用者根本感觉不到是线程,他只用关心他要做的事情就行了。
这里,我们可以设计一种"异步链式调用"的框架,把线程进行了封装。使用都只需要这样用:
- new TaskManager()
- .next(task1)
- .next(task2)
- .next(task3).
- .execute();
这里,task1, task2, task3是顺序执行的,举个例子:我们要访问网络,取得一个图片,使用这个TaskManager我们需要3个task,
task1:显示一个ProgressDialog。
task2:访问网络,创建bitmap。
task3:关闭对话框,显示bitmap。
这一点,可以参考CoreLib工程中的task.TaskManager类。
关于TaskManager,有以下几点需要注意:
- 封装了线程
- 让调用者只关注自己的业务处理
- 保证顺序链式地执行某一个任务
- 上一个任务的输出,作为下一个任务的输入
- 能暂停、恢复任何一个任务
2.3 缓存设计
- 把内存占用量大的对象存放在缓存中,如bitmap
- 利用了LruCache类来实现
- 利用了AsyncTask类来加载bitmap
- 不用再手动释放bitmap内存,该操作有风险
- 不用再关心AbsListView的scroll状态
关于缓存的更多详细细节,请参考[ 请参考CoreLib工程中的cache包 ]。
这样做,有什么好处, 不用再手动释放bitmap内在,该操作有风险,因为该bitmap是否有View引用,如果当一个View在试图绘制一个已经回收的bitmap,这里会抛出异常。
2.4 线程管理
无消息循环的线程:
- new Thread(null, new Runnable() {
- public void run() {
- // Do you works.
- }
- }, "Thread_name_xxx").start();
什么情况下使用这种线程:
- 做完一件事情就结束,这件事发生频率不高,比如从SD card中读取图片数据
- 不需要复用线程
在使用线程,最好给线程加上名字,这样利用高度与跟踪。
有消息循环的线程:
这样的线程拥有消息循环,当消息队列中没有消息时,这个线程会被挂起。我们要做一件事情时,只需要给它发送一个消息就行了。
这种情况通常是为了复用线程,不用频繁创建线程,比如音乐播放器程序,专门启动一个有消息循环的线程来获得音乐的专辑图片。
我们通常还要创建一个与这个线程的消息循环(Looper)相关联的Handler,由它来处理消息,注意,这做的事情是运行在后台线程的。
3,程序框架如何设计
Android程序的结构
- UI层
- 数据展示与管理
- 用户交互
- 绘制
- Adapter
- 业务逻辑层
- 持久化数据(内存中,相当于全局数据)
- 数据加式(数据层的数据有时候需要进行加工成UI层需要的数据)
- 数据变化的通知机制
- 数据层
- 数据访问(DB,文件,网络等)
- 缓存(图片,文件等)
- 配置文件(shared perference)
下面,我试着画了一个Android程序的结构,如果有不好的地方,欢迎指正。

4,一些基本原则
下面列出一些通常的原则,我们应当在开发过程中遵循,欢迎补充与指正。
4.1 提供initialize()方法
在Activity.onCreate()或者View的构造方法中调用,在以后看代码时,人们通常首先会去找initialize()这样的方法。
4.2 封装点击事件
把View的点击事件,提成方法,这样在listener处只是一个方法调用者,一般的事件封装为:onXXXClick(View v)。
4.3 设计一个BaseActivity类
让所有的Activity都继承自BaseActivity类,这样,我们可以做很多有用的事情
- 定义共通属性
- 显示共通对话框(Progress dialog)
- 取得top activity
- 可以手动管理启动的activity
4.4 设计Application类
- 存全局数据,比top activity, application context。
4.5 异常处理
- 报告功能是处理异常的精髓
- 在finally块中执行清理操作
- 不要用try-catch-finally来判断业务逻辑
- 考虑设计自定义的异常类
4.6 标注的使用
- 重写的方法一定要加@Override
- 不使用的方法,不要删除,可以标记为@Deprecated,这个做法在维护型的项目中特别有用。
4.7 注册与反注册
- 局部广播
- 各种listener
- Service等
4.8 封装Bitmap操作
我们应当把Bitmap操作封装起来,比如从文件加载,保存,网络下载,动态计算sample size等。有了封装后,我们可以对其集中优化。
4.9 绘制处理
一定要注意绘制方面的东西,不要在onDraw()/onTouchEvent()中创建新对象。
Android 程序框架设计的更多相关文章
- [转]Android程序框架设计
这篇文章主要内容来自于之前我讲的一个PPT文档,现在将其整理如下.欢迎指正.以下的内容都是来自于我自身的经验,欢迎大家多提自己的建议. 1.一些概念 模式的定义: 每个模式都描述了一个在我们的环境中不 ...
- Android通用框架设计与完整电商APP开发系列文章
作者|傅猿猿 责编|Javen205 有福利 有福利 有福利 鸣谢 感谢@傅猿猿 邀请写此系列文章 Android通用框架设计与完整电商APP开发 课程介绍 [导学视频] [课程详细介绍] 以下是部分 ...
- Android 开发快速导引:Android程序框架【草】
概述 学习一项新技术之前要先了解这个技术的整体框架,这里先简单说一下 Android 的程序结构. Android App 有四个顶层的类:Activity.Service.ContentProvid ...
- 阶段4-独挡一面\项目-基于视频压缩的实时监控系统\Sprint1-基于Epoll架构的采集端程序框架设计\第2课-基于Epoll的采集端程序框架设计
回顾之前的整个程序架构 把epoll机制应用到这个架构上去 下面主要去分析我们的系统中有没有需要等待的事件,先看看采集子系统 在采集子系统当中,摄像头有数据,摄像头采集到图像数据可以作为一个等待事件. ...
- 01 Taro_Mall 开源多端小程序框架设计
项目介绍 Taro_Mall是一款多端开源在线商城应用程序,后台是基于litemall基础上进行开发,前端采用Taro框架编写,现已全部完成小程序和h5移动端,后续会对APP,淘宝,头条,百度小程序进 ...
- Android核心分析之二十Android应用程序框架之无边界设计意图
Android应用程序框架1 无边界设计理念 Android的应用框架的外特性空间的描述在SDK文档(http://androidappdocs.appspot.com/guide/topics/fu ...
- Android应用程序框架之无边界设计意图
Android的应用框架的外特性空间的描述在SDK文档有十分清楚的描述,Android应用的基本概念,组件生命周期等等有详细的描述.在外特性空间中,Android提供了Activity,Service ...
- Android学习笔记一之第一个Android程序
/** *Title:总结昨天下午至今天上午的学习成果 *Author:zsg *Date:2017-8-13 / 一.了解Android 1.Android架构 Android大致可分为四层架构:L ...
- Android程序设计基础 • 【第1章 Android程序入门】
全部章节 >>>> 本章目录 1.1 Android 平台介绍 1.1.1 Android 简介 1.1.2 Android 平台的发展 1.1.3 Android 平台架 ...
随机推荐
- Python中如何写控制台进度条的整理
本文实例讲述了Python显示进度条的方法,是Python程序设计中非常实用的技巧.分享给大家供大家参考.具体方法如下: 首先,进度条和一般的print区别在哪里呢? 答案就是print会输出一个\n ...
- Redis操作Set工具类封装,Java Redis Set命令封装
Redis操作Set工具类封装,Java Redis Set命令封装 >>>>>>>>>>>>>>>>& ...
- onitemcommand 的作用以及onitemdatabound的具体作用
Repeater控件.DataList控件的属性:OnItemCommand 当在ItemTemplate 中所宣告的Button 或LinkButton 控件触发事件时,如果该控件CommandNa ...
- jBPM5 vs Actitivi
http://www.blogways.net/blog/2013/07/16/activiti-jbpm-compare.html jBPM是目前市场上主流开源工作引擎之一,在创建者Tom Baey ...
- 关于ibatis进行物理游标分页
http://www.iteye.com/topic/136712 详细demo:参照http://www.kusoft.net 我的数据库是采用mssql2000 采用分页必定数据量比较大: 按照i ...
- HOOK函数(二)——全局HOOK
如果钩子函数定义于当前进程相关的线程中,则钩子函数只能处理当前进程的线程的消息,如果要想处理当前正在运行的所有进程的鼠标消息和键盘消息,那么安装钩子函数的代码必须实现在动态链接库中.所以如果想让安装的 ...
- Java 十六进制转十进制
public static int hexToDecimal(String hex) { int decimalValue = 0; for (int i = 0; i < hex.length ...
- Entity Framework 使用sql语句分页(查询单表)
1.查询单表 var pageSize = 2;//条数 var pageIndex = 2;//索引 var sql = @" SELECT D.* FROM ( SELECT ROW_N ...
- C#基础整理
元旦整理书架发现一本小册子——<C#精髓>中国出版社2001年出版的,粗略翻了下关于C#的知识点挺全的虽然内容谈得很浅也有很多过时的内容(话说这本书是我在旧书店花5块钱淘的)我保留原有章节 ...
- 用crontab、crond在嵌入式系统中添加定时任务
在嵌入式系统中,定时任务通过crond和cronttab两个系统命令来联合执行. 其中crond是定时任务的守护进程,系统开始时是没有开启的.crontab主要作用是管理用户的crontab file ...