笔记提纲
app研发录第一章.png1.1,重新规划 Android 项目结构
第一步:建立 AndroidLib 类库,将与业务无关的逻辑转移到 AndroidLib。
- 1, activity 包中存放的是与业务无关的 Activity 基类。Activity 基类要分两层。
AndroidLib 下的基类 BaseActivity 封装的是业务无关的公用逻辑,主项目中的 AppBaseActivity 基类封装的是业务相关的公用逻辑。 - 2, net 包里面存放的是网络底层封装。这里封装的是 AsyncTask。
- 3, cache 包里面存放的是缓存数据和图片的相关处理。
- 4, ui 包中存放的是自定义控件。
- 5, utils 包中存放的是各种与业务无关的公用方法,比如对 SharedPreferences 的封装。
第二步:将主项目中的类分门别类地进行划分,放置在各种包中。
各个包的介绍如下:
- 1,activity:我们按照模块继续拆分,将不同模块的 Activity 划分到不同的包下。
- 2,adapter:所有适配器都放在一起。
- 3,entity:将所有的实体都放在一起。
- 4,db:SQLLite 相关逻辑的封装。
- 5,engine:将业务相关的类都放在一起。
- 6,ui:将自定义控件都放在这个包中。
- 7,utils:将所有的公用方法都放在这里。
- 8,interfaces:真正意义上的接口,命名以 I 作为开头。
- 9,listener:基于 Listener 的接口,命名以 On 作为开头。
这些划分主要是为了以下两个目的:
1)每个文件只有一个单独的类,不要有嵌套类,比如在 Activity 中嵌套 Adapter、Entity。
2)将 Activity 按照模块拆分归类后,可以迅速定位具体的一个页面。此外,将开发人员按照模块划分后,每个开发人员都只负责自己的那个包,开发边界线很清晰。
1.2 为 Activity 定义新的生命周期
把onCreate()方法拆分为几个方法:
- initVariables()
- initViews()
- loadData()
1,initVariables :初始化变量,包括 Intent 带的数据和 Activity 内的变量。
2,initViews:加载 layout 布局文件,初始化控件,为控件挂上事件方法。
3,loadData:调用 MobileAPI 获取数据。
这三个方法都是抽象的,所有子类都必须实现这三个方法,这样避免了onCreate方法过于臃肿,做的东西过多,也是单一职责的体现,一个类或方法,只做一件事情。
1.3 统一事件编程模型
给按钮点击事件增加方法,只要在一个团队内部达成了协议,决定使用某种事件编程方式,所有开发人员就要按照同样的方式编写代码。
1.4 实体化编程
1.4.1 在网络请求中使用实体
要知道 JSONObject 和 JSONArray 都是不支持序列化的,所以只好将这种对象封装到一个全局变量中,在跳转前设置,在跳转后取出,这是一种很糟糕的写法。
有以下两个问题:
1)根据 key 值取 value,我们可以认为这是一个字典。同样的功能实现,字典比实体更晦涩难懂,容易产生 bug。
2)每次都要手动从 JSONObject 或者 JSONArray中取值,很烦琐。
建议把网络请求返回的 JSON 数据,通过fastJSON 或者 GSON 解析成javaBean实体。
1.4.2 实体生成器
作者自己写了一个程序,根据json字符串自动产生javabean
在android studio中,可以使用GsonFormat插件将json数据转化为实体bean
1.4.3 在页面跳转中使用实体
很多人在页面跳转传递参数时,设置一个全局变量,在来源页设置全局变量,在目标页接收全局变量。
这是一个很不好的做法,App一旦被切换到后台,当手机内存不足的时候,就会回收这些全局变量,从而当App再次切换回到前台时,再继续使用全局变量,
就会因为他们为空而崩溃。
如果必须使用全局变量,就一定要把它们序列化到本地。这样即使全局变量为空,也能从本地文件中恢复。
建议使用 Intent 在页面间来传递数据实体bean,不过这个实体bean一般需要实现Serializable接口,以支持序列化。
1.5 Adapter 模板
统一规范,要求所有的 Adapter 都继承自 BaseAdapter,从构造函数注入 List< 自定义实体 >这样的数据集合,从而完成 ListView 的填充工作。
如果不对 Adapter 的写法进行规范,开发人员还是会根据自己的习惯,写出来各种各样的 Adapter,比如:
� � 1,很多开发人员都喜欢将 Adapter 内嵌在 Activity 中,一般会使用 SimpleAdapter。
� � 2,由于没有使用实体,所以一般会把一个字典作为构造函数的参数注入到 Adapter 中。
建议:这些写法都会看起来代码不统一,不便于其他人的修改。 Adapter 只有一种编码风格,这样发现了问题也很容易排查。
1.6 类型安全转换函数
类型转换不正确导致的崩溃占了很大的比例。发现主要集中在两个地方:Object 类型的对象、substring 函数。
1)对于一个 Object 类型的对象,我们对其直接使用字符串操作函数 toString,当其为null 时就会崩溃。
比如,我们经常会写出下面这样的程序:
int result = Integer.valueOf(obj.toString());
一旦 obj 这个对象为空,那么上面这行代码会直接崩溃。
这里的 obj,一般是从 JSON 数据中取出来的,对于 MobileAPI 返回的 JSON 数据,我们无法保证其永远不为空。
比较好的做法是,我们需要编写一个类型安全转换函数 convertToInt,实现如下,其核心思想就是,如果转换失败,就返回默认值:
public f inal static int convertToInt(Object value, int defaultValue) {
if (value == null || "".equals(value.toString().trim())) {
return defaultValue;
}
try {
return Integer.valueOf(value.toString());
} catch (Exception e) {
try {
return Double.valueOf(value.toString()).intValue();
} catch (Exception e1) {
return defaultValue;
}
}
}
2)如果长度不够,那么执行 substring 函数时,就会崩溃。
Java 的 substring 函数有 2 个参数:start 和 end。
对于第一个参数 start,我们的程序大多是设为 0,所以一般不会有问题。但是要设置为大于 0 的值时,就要仔细思量了,比如:
String cityName = "T";
String f irstLetter = cityName.substring(1, 2);
这样的代码必然崩溃,所以每次在使用 substring 函数的时候,都要判断 start 和 end 两个参数是否越界了。应该这样写:
String cityName = "T";
String f irstLetter = "";
if (cityName.length() > 1) {
firstLetter = cityName.substring(1, 2);
}
以上两类问题的根源,都来自 MobileAPI 返回的数据,由此而引出另一个很严肃的问
题,对于从 MobileAPI 返回的数据,可信度到底有多高呢?
首先,不能让 App 直接崩溃,应该在解析 JSON 数据的外面包一层 try…catch…语句,
将截获到的异常在 catch 中进行处理,比如说,发送错误日志给服务器
总结:
1,AndroidLib 这个业务无关的类库,我们将在接下来的章节中封装更多的公用逻辑
2,实体化编程将极大提升代码可读性
3,为 Activity 定义新的生命周期,也是提升代码可读性的一个手段。
网友评论