花了一天的时间,把这本书看了一遍,首先书中的项目结构演示还是基于eclipse的有一些过时,但也很具有参考意义。该书中设计技术代码方便的并不是很多,还是处于一个比较宏观的角度去介绍一个项目从无到有的方方面面,很适合工作经验不足,或者公司开发流程不规范的同学去了解一个项目比较专业的开发流程,还有很多实际开发过程中项目重构,和技术的选用的推荐方法。
下面就是对我而言感觉比较有用的知识点记录。
第一部分 高效App架构设计与重构
一、将项目种与业务逻辑无关的抽取出来单独创建一个AndroidLib类库,类库只要分为如下几部分。
- activity 包中存放的是与业务无关的 Activity 基类。AndroidLib 下的基类 BaseActivity 封装的是业务无关的公用逻辑,主项目中的AppBaseActivity 基类封装的是业务相关的公用逻辑。
- net 包里面存放的是网络底层封装。
- cache 包里面存放的是缓存数据和图片的相关处理。
- ui 包中存放的是自定义控件。
- utils 包中存放的是各种与业务无关的公用方法,比如对 SharedPreferences 的封装。
将无关的公共逻辑和各种工具类与业务逻辑分离出来,能大大方便日后项目的维护。
二、统一事件编程模型
这小节的意思就是讲在团队开发中的点击事件最好要统一成一种。
比如说这种方式用起来就会扰乱面向对象的编程风格。
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.sign_in_button:
Intent intent = new Intent(LoginActivity.this,PersonCenterActivity.class);
startActivity(intent);
}
}
作者更加推崇下面的这种方式
有以下两个优点:
- 直接在 btnLogin 这个按钮对象上增加点击事件,是面向对象的写法。
- 将 onClick 方面的实现,封装成一个 gotoLoginActivity 方法,如下所示:
// 登录事件
btnLogin = (Button)f indViewById(R.id.sign_in_button);
btnLogin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gotoLoginActivity();
}
});
最好在团队中要统一事件模型的方式。
三、使用fastJson问题注意
如果使用fastJson出现如下问题
- 加了符号 Annotation 的实体属性,一使用就崩溃。
- 当有泛型属性时,一使用就崩溃。
需要在混淆文件添加以下代码:
-keepattributes Signature // 避免混淆泛型
-keepattributes Annotation // 不混淆注解
四、类型安全转换函数
在某些情况下后台会返回一些脏数据(为null,角标参数不正确),会造成程序的崩溃。这种情况下作者建议创建一个函数类型转换的工具类。在使用后台返回的逻辑之前,先判断数据是否正常。这样可以防止程序直接崩溃。
第二部分 网络底层框架设计
一、AsyncTask的缺点,那就是不能灵活控制其内部的线程池。
线程池里面的每个线程存放的都是 MobileAPI 的调用请求,而 AsyncTask 中又没有暴露
出取消这些请求的方法,也就是我们熟知的 CancelRequest 方法,所以,一旦从 A 页面跳转
到 B 页面,那么在 A 页面发起的 MobileAPI 请求,如果还没有返回,并不会被取消。
二、自动登录
所谓自动登录,就是登录成功后,重启 App 后用户仍然是登录状态。
最直接的方法是,登录成功后,本地保存用户名和密码。重启 App 后,检查本地是否有
保存用户名和密码,如果有,则将用户名和密码传入到登录接口,模拟用户登录的行为。
本地保存用户密码,这种的敏感信息容易被人窃取。要么是在本地文件中看到这些信
息,要么是侦听 App 的网络请求,获取到请求的数据。所以本地保存密码时,一定要进行加密。对称加密是不可靠的,因为很难确保App 的源代码不外泄,所以别有用心的人还是可以根据源码中的对称加密算法,反向把密码推算出来。只有不对称加密才是安全的。
但是上面这种方法在需要用户需要用户输入验证码的情形下就不是很好了,于是就要了使用Cookie机制来解决自动登录。
三、开启gzip压缩
接下来要介绍的内容和 gzip 有关。 HTTP 协议上的 gzip 编码是一种用来改进 Web 应用
程序性能的技术。大流量的 Web 站点常常使用 gzip 压缩技术来减少传输量的大小,减少传
输量大小有两个明显的好处,一是可以减少存储空间,二是通过网络传输时,可以减少传输
的时间
第三部分 列表数据的增量更新机制
通过分析比较本地数据和远程数据的异同,以 cityId 作为唯一标识,只在 1.0 中出现的 cityId 是要删除的数据,只在 2.0 中出现的 cityId 是要增加的数据,二者的交集则是 cityId 相同的数据,这又分为两种情况,所有字段都相同的数据是不变的数据; cityId 相同但某个字段不相同,则是修改的数据。
于是,我们可以重新定义城市列表的 JSON 格式,在每笔增量数据中增加一个字段 type,用来区别是增( c)、删( d)、改( u)中的哪种情况。
客户端在收到上述格式 JSON 数据后,会根据 type 值来处理存放在本地的数据。因为不是全量更新,所以处理起来很快。
第四部分 Crash异常分析
一、Fragment not attached to Activity
解决办法:在使用fragment相应的方法之前,增加isAdded()方法判断,该方法只有在fragment添加到所属的Activity后才会返回true。
二、解决Dialog在dismiss的时候Activity已经不存在的完美方法(View not attached to window manager)
创建一个ProgressDialog的子类,复写dismiss方法,在ProgressDialog.diss()执行之前判断Activity是否存在。
三、子线程不能修改UI
有一种情况,直接开一个子线程,并在子线程中修改textView的显示的文字,然而系统却没有报异常。不是说好的不能再子线程中修改UI吗,然而实际上Android官方说的是:”不建议在子线程中更新UI,会因此产生不可预知的错误。“