一、重构,夜未眠
1.重新规划Android项目结构
第一步,建立AndroidLib类库,将与业务无关的逻辑转移到AndroidLib。activity(无业务无关的Activity基类)、net(网络底层封装)、cache(缓存数据和图片的相关处理)、ui(自定义控件)、utils(与业务无关的公用方法,比如对SharedPreferences的封装)
第二步,将主项目中的类分门别类地进行划分,放置在各种包中。activity(将不同模块的Activity划分到不同的包下)、adapter(所有适配器放在一起)、entity(所有的实体都放在一起)、db(SQLite相关逻辑的封装)、engine(业务相关的类)、ui(自定义控件)、utils(公用方法)、interfaces(真正意义上的接口,命名以I作为开头)、listener(基于Listener的接口,命名以On作为开头)
2.为Activity定义新的生命周期
拆分成子方法
3.统一事件编程模型
点击方法用匿名内部类实现
4.实体化编程
JSON用GSON等第三方类解析,使用fastJSON时混淆文件要添加:--keepattributes Signature,--keepattributes Annotation
实体生成器:EntityGenerater
页面跳转中使用实体时不建议使用全局变量,可能被回收然后App崩溃。可把实体实现Serializable然后传送。
5.Adapter模板
所有Adapter都继承自BaseAdapter,从构造函数注入List<>这样的数据集合。
6.类型安全转换函数
对于一个Object类型的对象,我们对其直接使用字符串操作函数toString,当其为null时就会崩溃,所以实现一个判断是否为空的转换函数。
如果长度不够,那么招行substring函数时,就会崩溃。所以在使用时要判断start和end两个参数是否越界了。
二、Android网络底层框架设计
1.网络低层封装
不推荐使用AsyncTask来封装网络底层,因为可扩展性不高。
现在一般使用RxJava+Retrofit
实例:使用原生的ThreadPoolExecutor+Runnable+Handler
2.App数据缓存设计
减少调用次数,然后把获取的数据保存在App上。
3.MockService
当API没做好时,自己模拟相关的的接口实现
4.用户登录
登录成功后场景:点击登录按钮,进入登录页面LoginActivity,登录成功后,直接进入个人中心,这种情况一路执行startActivity()就能达到目的;在页面A,想跳转到页面B,并携带一些参数,却发现没有登录,于是先跳转到登录页,登录成功后,再跳转到B页面,同时仍然带着那些参数,用setResult实现;在页面A,执行某个操作,却发现没有登录,于是跳转到登录面,登录成功后再回到页面A,继续执行该操作,也是用setResult来完成回调。
自动登录,不推荐本地保存用户名和密码,使用Cookie机制,也叫Token。登录成功后,会从服务器获取到一个Cookie,取出来放在本地文件中即可。当用户注销时,要清空本地的Cookie。
Cookie过期统一处理:加一个onCookieExpired回调方法。
防止黑客刷库:登录接口增加第三个参数,比如验证码;同一IP短时间内频繁访问时要求输入验证码,用WebView实现。
5.HTTP头中的奥妙
请求、时间校准、开启gzip压缩
三、Android经典场景设计
1.App图片缓存设计
ImageLoader设计原理:在显示图片时,先在内存中查找;如果没有,就去本地查找;如果还没有,就开一个新线程去下载这张图片,下载成功会把图片同时缓存到内存和本地。对图片是软引用形式,所有内存中图片会在内存不足的时候被系统回收。
Freso原理:设计了一个Image Pipeline的要信,它负责先后检查内存、磁盘文件,如果都没有就去从网络下载图片。三层缓存:Bitmap缓存、内存缓存、磁盘缓存。
2.对网络流量进行优化
通信层面的优化:返回的数据存在于1KB时用gzip压缩;数据传递遵守JSON协议,推荐ProtoBuffer协议;减少网络访问次数;使用TCP长连接;建立取消网络请求机制,当一个页面没有请求完数据,跳转到另一个页面之前,把之前的网络请求都取消;增加重试机制。
图片策略优化:确保下载的每张图,都符合ImageView控件的大小;低流量模式,降低图片质量;极速模式,没有图片,只有文字。
3.城市列表的设计
4.App与HTML5的交互
用PhoneGap太重,把交互操作在底层封装,然后给开发人员使用。
对于经常需要改动的页面,做成H5页面,在App中以WebView的形式加载。H5开发周期短,但慢。可以做两套页面,Native一套,H5一套,在App中设置一个变量,判断该页面用哪个。变量从后台中获取。
5.消灭全局变量
在内存不足时,系统会回收全局变量,会导致APP崩溃。解决这个问题,使用序列化技术。
对社交和电商类App而言,页面繁多,没必要保存页面状态,直接让页面重新执行一遍onCreate方法,丢失的数据让用户重新操作。使用MVVM设计模式,可以把ViewModel序列化到本地,用以保存页面。
SharedPreferences中存放的变量数量应严格控制。
User是唯一例外的全局变量:序列化到本地,避免崩溃
四、Android命名规划和编码规范
1.Android命名规范
Activity、Adapter、Entity都为结尾,控件缩写,常量命名等
2.Android编码规范
分门另类存放各种类,Layout中使用的常量定义在Strings.xml中,Layout控件字体大小,定义在dimens.xml中,拆分onCreate,JSON解析用第三方库,页面传值用Intent,控件事件用匿名内部类,Activity中不要嵌套内部类,实体不要在不同模块间共享,节省内存,使用ArrayList而不是HashMap,图片处理用第三方,尽量使用ApplicationContext代替Context,数据类型转换一定要校验,使用常量代替枚举。
3.统一代码格式
团队中所有人使用同一种代码格式
五、Crash异常收集与统计
1.异常收集
设计一个CrashHandler类,继承自UncaughtExceptionHandler,处理未捕获的异常。基本关键方法就handleException,做三件事:发错误日志到服务器,给用户崩溃前的友好提示,把错误日志记录到SD卡。
2.异常收集与统计
要么记录到第三方平台,要么记录到自己的数据库中然后处理
六、Crash异常分析
1.Java语法相关的异常
空指针NullPointException、角标越界IndexOutOfBoundsException/StringIndexOutOfBoundsException/ArrayIndexOutOfBoundsException、试图调用一个空对象的方法、类型转换异常、数字转换错误,声明数组长度为-1、遍历集合同时删除其中元素、比较器使用不当、当除数为0、不能随便使用的asList、又有类找不到了(一):ClassNotFoundException、又有类找不到了(二):NoClassDefFoundError
2.Activity相关的异常
找不到Activity、不能实例化Activity、找不到Service、不能启动BroadcastReceiver、startActivityForResult不能回传、猴急的Fragment
3.序列化相关的异常
实体对象不支持序列化、序列化时未指定ClassLoader、反序列化时发现类找不到:被ProGuard混淆导致的崩溃、反序列化时发现类找不到:传入畸形数据、反序列化时出错
4.列表相关的异常
Adapter数据源变化但是没通知ListView、ListView滚动时点击刷新按钮后崩溃、AbsListView的obtainView返回空指针、Adapter数据源变化但是没调用notifyDataSetChanged
5.窗体相关的异常
窗口句柄泄露、View not attached to window manager、窗体在不恰当 的时候获取了焦点、token null is not for an application、permission denied for this window type、is your activity running、添加窗体失败、AlertDialog.resolveDialogTheme、The specified child already has a parent、子线程不能修改UI、不能在子线程操作AlertDialog和Toast
6.资源相关的异常
Resources$NotFoundException、StackOverFlowError、UnsatisfiedLinkError、InfiateException之FileNotFoundException、InfiateException之缺少构造器、InfiateException之style与android:textStyle的区别、TransactionTooLargeException
7.系统碎片化相关的异常
NoSuchMethodError、RemoteViews、pointerIndex out of range、SecurityException之一:Intent中图片太大、SecurityException之二:动态加载其他apk的activity、之三:No permission to modify thread、view的getDrawingCache()返回null、DeadObjectException、Android 2.1 不支持SSL、ViewFlipper引发的血案、ActivityNotFundException、Android 2.2 不支持xlargeScreens、Package manager has died、SpannableString与富文本字符串、Can not perform this action agter onSaveInstanceState、Service Intent must be explicit
8.SQLite相关的异常
No transaction is active、忘记关闭Cursor、数据库被锁定、试图再打开已经关闭的对象、文件加密了或无数据库、WebView中SQLLite缓存导致的崩溃、磁盘读写错误、android_metadate表不存在、android_metadata表中locale字段、数据库或磁盘满了
9.不明觉厉的异常
内存溢出、Verify Failed
10.其他情况的异常
TimeoutException、JSON解析异常、JSONArray在初始化时为空、第三方SDK抛出的Crash、两个不同类型的View有相同的id、LayoutInflater.from().inflate()使用不当导致的崩溃、ViewGroup中的玄机、Monkey点击过快导致的崩溃、图片宽高为0、不能重复添加组件