【读书笔记】《Android开发艺术探索》

这本书对初学者来说还是比较有深度的,知识覆盖面也比较广
虽然可能讲解的重点不一定都是你想看到的,但是对于安卓开发进阶来说是一本不错的总体概览书籍,能够get到一些实用的技能点

前后用了一个星期的时间来看,总体来说比较有参考价值的,阅读起来也没有太大难度

评价:三星半


链接、图片、笔记文本

下面是印象笔记公开链接、XMind思维导图图片与文字

EverNote链接

图片:

Android开发艺术.png

文字:

Android 开发艺术探索
1 1 Activity生命周期和启动模式
1.1 生命周期
1.1.1 典型周期
1.1.1.1 onStart
1.1.1.1.1 Activity已经可见,但是没有出现在前台
1.1.1.2 onResume
1.1.1.2.1 Activity可见,出现在前台并且开始活动
1.1.1.3 onPause
1.1.1.3.1 可以做一些存储数据、停止动画等工作,但是不能太耗时
1.1.1.4 onStop
1.1.1.4.1 可以做一些重量级工作,但也不能太耗时
1.1.1.4.2 如果新Activity采用了透明主题,那么当前Activity不会回调onStop
1.1.1.5 从Activity A切换到Activity B
1.1.1.5.1 A先执行onPause,B再执行onCreate-onStart-onResume,最后才是A执行onStop
1.1.2 异常周期
1.1.2.1 被系统回收
1.1.2.2 当Activity并不是由我主动点击back键而丧失焦点时,onSaveInstanceState方法就一定会调用
1.1.2.2.1 google工程师们对onSaveInstanceState如此设计就是让其完成对一些临时的、非永久数据存储并进行恢复
1.1.2.2.2 Android的View本身自己就实现了onSaveInstanceState方法,这些控件自己就具有保存临时数据和恢复临时数据的能力
1.1.2.2.2.1 值得一提的是,只有当你给这个wiget在xml中指定id时,它才具有保存数据并且恢复的能力,并且不同的wiget还不能共用这个id,否则会出现数据覆盖的情况。
1.1.2.2.3 onRestoreInstanceState一旦被调用,其参数Bundle savedInstanceState一定是有值的
1.1.2.3 资源不足导致低优先级的Activity被杀死
1.1.2.4 Configuration改变导致的销毁重建
1.1.2.4.1 通过configChanges配置
1.2 启动模式
1.2.1 Activity的LaunchMode
1.2.1.1 standard
1.2.1.1.1 这种模式的Activity默认会进入启动它的Activity所属的任务栈中,但诸如ApplicationContext等Context并没有所谓的任务栈,需要在启动时添加FLAG_ACTIVITY_NEW_TASK,这样启动时就会为它创建一个新的任务栈,此时其实是以singleTask模式启动的
1.2.1.2 singleTop
1.2.1.3 singleTask
1.2.1.3.1 taskAffinity属性,配合singleTask使用,指定Activity运行的任务栈名,是一个字符串,Activity默认的栈名是Application的包名
1.2.1.3.2 singleTask模式的Activity切换到栈顶会导致在它之上的栈内的Activity出栈
1.2.1.4 singleInstance
1.2.2 Activity的Flags
1.2.2.1 FLAG_ACTIVITY_NEW_TASK
1.2.2.2 FLAG_ACTIVITY_SINGLE_TOP
1.2.2.3 FLAG_ACTIVITY_CLEAR_TOP
1.2.2.3.1 带此标记位启动Activity,启动它时,在同一栈中所有位于它上面的Activity都要出栈
1.2.2.3.2 一般会和singleTask启动模式一起出现,如果Activity已经创建,则调用onNewIntent
1.2.2.4 FLAG_ACTIVITY_EXCLUDE_FROME_RECENTS
1.2.2.4.1 相当于xml中声明 android:exludeFromRecents="true"
1.2.2.4.2 Activity不会出现在历史Activity列表中
1.2.3 IntentFilter匹配规则
1.2.3.1 为了匹配过滤列表,需要同时匹配过滤列表中的action、category、data信息,否则匹配失败。只有一个Intent同时匹配action、category、data才算完全匹配,只有完全匹配才能启动目标Activity。
1.2.3.2 一个Activity中可以有多个intent-filter,一个Intent只要能匹配任何一组intent-filter即可成功启动对应的Activity
1.2.3.3 具体细则
1.2.3.3.1 action匹配规则
1.2.3.3.1.1 字符必须完全相同,一个过滤规则中可以有多个action,只要Intent的action能够与其中任意一个相同即可匹配成功
1.2.3.3.1.2 必须含有action项,可以有多个,只要其中一项能在intent-filter中找到匹配项就行
1.2.3.3.2 category匹配规则
1.2.3.3.2.1 可以没有,如果有,则必须全部被包含在intent-filter中
1.2.3.3.2.2 startActivity时默认含有android.intent.category.DEFAULT这个category
1.2.3.3.3 data匹配规则
1.2.3.3.3.1 与action类似
1.2.3.3.3.2 由两部分组成 mimeType和URI
1.2.3.3.3.2.1 URI
1.2.3.3.3.2.1.1 <scheme>://<host>:<port>/[<path>][<pathPrefix>][<pathPattern>]
1.2.3.3.3.3 如果为intent指定完整的data,必须调用setDataAndType,不能先setData再setType,因为这两个方法彼此会清楚对方的值
2 2. IPC机制
2.1 简介
2.1.1 缩写: Inter-Process Communication 进程间通信或者跨进程通信
2.1.2 进程与线程
2.1.2.1 线程是CPU调度的最小单元,线程是一种有限的系统资源
2.1.2.2 进程一般指一个执行单元,在PC或移动设备上指一个程序或者一个应用。
2.1.2.3 一个进程中可以只有一个线程,即主线程;在Android里的主线程也叫UI线程,由此引出ANR,线程
2.1.3 不是Android独有,Linux上可以通过命名管道、共享内存、信号量等来进行进程间通信。
2.1.4 Android中最有特色的进程间通信方式就是Binder,也支持socket
2.2 Android中的多进程模式
2.2.1 使用多进程的方法:在AndroidMenifest中指定android:process属性
2.2.1.1 以“:”开头的进程属于当前进程的私有进程,否则为全局进程,其他应用通过ShareUID方式可以和它运行在同一进程中
2.2.2 数据共享的问题
2.2.3 引发的问题
2.2.3.1 静态成员和单例模式完全失败
2.2.3.1.1 运行在不同进程中的四大组件,想通过内存来共享数据都会失败
2.2.3.2 线程同步机制完全失效
2.2.3.3 SharedPreference可靠性下降
2.2.3.4 Application会多次创建
2.3 基础概念
2.3.1 Serializable接口
2.3.1.1 是Java的一个序列化接口,只需在类中声明serialVersionUID标识即可自动实现默认序列化过程
2.3.1.1.1 标识用以辅助序列化与反序列化过程,反序列化时会检查和当前类是否一致(可能类被修改了,如添加了成员)
2.3.1.2 Serializable使用简单,但是含有大量IO操作,开销大
2.3.2 Parcelable
2.3.2.1 只要实现这个接口,一个类的对象就可以实现序列化,并且可以通过Intent和Binder传递
2.3.2.2 系统已经为我们提供了许多实现了Parcelable接口的类,如Intent、Bundle、Bitmap,同时List和Map也可以序列化
2.3.2.3 Parcelable稍显麻烦,但是效率很高
2.3.3 Binder
2.3.3.1 Android特有,可以理解为虚拟的物理设备
2.3.3.2 应用层来说,是客户端和服务端进行通信的媒介
2.3.3.3 主要用在Service中,包括AIDL和Messenger
2.3.3.4 手动实现Binder的步骤
2.3.3.4.1 声明一个AIDL接口,只需继承IInterface(只有一个asBinder方法)
2.3.3.4.2 实现Stub类和Stub类中的Proxy代理类
2.3.3.5 linkToDeath与unlinkToDeath
2.4 Android中的IPC
2.4.1 方式
2.4.1.1 Intent中添加extras
2.4.1.2 Binder跨进程通信
2.4.1.3 ContentProvider支持跨进程
2.4.1.4 网络通信,如socket
2.4.1.5 文件共享
2.4.1.5.1 并发问题
2.4.1.5.2 SharedPreference虽然本质也也是文件,但系统对它的读写有一定的缓存策略,所以不在进程间通信时使用
2.4.2 使用Messenger
2.4.2.1 一种轻量级的IPC方案,底层实现是AIDL
2.4.2.2 使用方法简单,它对AIDL做了封装
2.4.2.3 由于一次处理一个请求,因此在服务端我们不用考虑线程同步的问题(服务端不存在并发执行的情况)
2.4.2.4 实现步骤
2.4.2.4.1 服务端进程
2.4.2.4.2 客户端进程
2.4.3 AIDL
2.4.3.1 服务端
2.4.3.1.1 创建Service
2.4.3.1.2 创建AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明
2.4.3.1.3 在Service中实现这个接口
2.4.3.2 客户端
2.4.3.2.1 绑定服务端Service
2.4.3.2.2 将服务端返回的Binder对象转换成AIDL接口所属的类型
2.4.3.2.3 调用AIDL中的方法
2.4.3.3 AIDL接口的创建
2.4.3.3.1 支持的数据类型
2.4.3.3.1.1 基本数据类型
2.4.3.3.1.2 String CharSequence
2.4.3.3.1.3 ArrayList
2.4.3.3.1.4 HashMap
2.4.3.3.1.5 Parcelable
2.4.3.3.1.6 AIDL
2.4.4 ContentProvider
2.4.4.1 继承ContentProvider,实现六个方法:onCreate、query、update、insert、delete和getType
2.4.4.1.1 onCreate用来创建
2.4.4.1.1.1 在主线程中,不能进行耗时操作
2.4.4.1.2 getType用以返回MIME类型,如果返回null或者 /表示不关注
2.4.4.2 底层看来来像一个SQLite数据库,但其实对底层数据储存没有任何要求,可以使用普通文件,可以采用内存中的一个对象来进行数据存储
2.4.4.3 注册自定义的ContentProvider
2.4.4.3.1 android:authorities是ContentProvider的唯一表示,通过这个属性外部应用就可以访问我们的ContentProvider了,所以必须保证authorities唯一(可以加上包名前缀)
2.4.5 Sockete
2.4.6 Binder连接池
2.4.6.1 可以极大的提高AIDL的开发效率,并且可以避免大量的Service创建
3 3. View事件体系
3.1 基础
3.1.1 位置参数
3.1.1.1 left、right、top、bottom
3.1.1.2 3.0之后添加了x、y、translationX、translationY
3.1.2 MotionEvent与TouchSlop
3.1.3 VelocityTracker、GestureDetector、Scroller
3.1.3.1 获取速度前必须先计算速度
3.1.3.2 这里的速度是指一段时间内手指所滑过的像素
3.1.3.3 不需要使用时,需要调用clear方法来重置并回收内存
3.2 View的滑动
3.2.1 scrollTo、scrollBy
3.2.1.1 只能滑动View的内容,并不能滑动View本身
3.2.2 使用动画
3.2.2.1 将fillAfter属性设置为true可以保持动画结束后的view效果(但是view的四个顶点坐标并没有发生变化)
3.2.3 改变布局参数
3.2.4 比较: scrollTo/ScrollBy操作简单,适合对View内容的滑动 动画:操作简单,主要适用于没有交互的View和实现复杂的动画效果 改变布局参数:操作相对复杂,适用于有交互的View
3.2.5 eg:
3.2.5.1 具有跟手滑动效果的View:只需要重写onTouchEvent方法并处理ACTION_MOVE事件,根据两次滑动之间的距离就可以实现它的滑动。为了实现全屏滑动,需要采用动画的方式来实现(原书P134)
3.3 弹性滑动
3.3.1 使用Scroller
3.3.1.1 配合View的computeScroll方法完成,对View没有丝毫的引用,内部也不含有计时器
3.3.2 通过动画,一帧一帧地更新,设置动画的updateListener实现
3.3.3 使用延时策略
3.3.3.1 使用Handler或View的postDelay方法
3.4 View的事件分发机制
3.4.1 传递规则
3.4.1.1 dispatchTouchEvent
3.4.1.1.1 用来进行事件分发
3.4.1.2 onInterceptTouchEvent
3.4.1.2.1 用来判断是否拦截某个事件
3.4.1.3 onTouchEvent
3.4.1.3.1 dispatchTouchEvent中调用,处理点击事件,返回结果表示是否消耗当前事件
3.4.1.4 关系伪代码
3.4.1.4.1 public boolean dispatchTouchEvent(MotionEvent ev){ boolean consume = false; if(onInterceptTouchEvent(ev)){ consume = onTouchEvent(ev); }else{ consume = child.dispatchTouchEvent(ev); } return consume; }
3.4.1.5 结论
3.4.1.5.1 同一个事件序列内的事件不能分别由两个View同时处理(不包含特殊处理)
3.4.1.5.2 某个View一旦决定拦截,那么这一事件序列都只能由它来处理,并且它的onInterceptTouchEvent不会再被调用
3.4.1.5.3 如果View不消耗除ACTION_DOWN以外的其他事件,那么这个点击事件会小时,父元素的onTouchEvent并不会被调用,最终这些消耗的点击事件会传递给Activity处理
3.4.1.5.4 ViewGroup默认不拦截任何事件
3.4.1.5.5 View没有onInterceptTouchEvent方法
3.4.1.5.6 View的onTouchEvent默认都会消耗事件(返回true),除非是不可点击的(clickable和longClickable同时为false)
3.4.1.5.7 View的enable属性不影响onTouchEvent默认返回值。哪怕一个View是disable状态,只要它的clickable或者longclickable有一个为true,那么它的onTouchEvent就返回true
4 4. View工作原理
4.1 ViewRoot、DecorView
4.1.1 DecorView作为顶级View,一般会包含一个竖直方向的LinearLayout
4.1.1.1 一个是titlebar,一个是content,这也是setContentView所设置的对象
4.1.2 View层的事件都先经过DecorView,然后才传递给我们的View
4.2 MeasureSpec
4.2.1 一个32位的int值,高2位为SpecMode,低30位代表SpecSize
4.3 View工作流程
4.3.1 主要指measure、layout、draw
4.3.1.1 如何在Activity中获取view的宽高?
4.3.1.1.1 Activity#onWindowFocusChanged
4.3.1.1.2 view.post(runnable)
4.3.1.1.3 ViewTreeObserver
4.3.1.1.4 view.measure(int windthMeasureSpec, int heightMeasureSpec)
4.3.1.2 ViewGroup会逐个measure子view
4.3.2 layout过程
4.3.2.1 通过setFrame去设置子元素的四个顶点位置
4.3.3 draw过程
4.3.3.1 View绘制过程是通过dispatchDraw来实现的,dispatchDraw会遍历调用所有字元素的draw
4.4 自定义View
4.4.1 分类
4.4.1.1 onDraw
4.4.1.2 继承ViewGroup
4.4.1.3 继承特定View
4.4.1.4 继承特定ViewGroup
4.4.2 须知
4.4.2.1 让View支持wrap_content
4.4.2.2 如有必要,让其支持padding
4.4.2.2.1 在onDraw中取padding值,加入绘制参数计算
4.4.2.3 尽量不用Handler
4.4.2.4 View中如果有线程或动画,需要及时停止,View#onDetachFromWindow
4.4.2.5 View带有滑动嵌套时,处理好滑动冲突
4.4.3 xml属性扩展
4.4.3.1 1. values目录下创建xml
4.4.3.1.1 eg: <declare-styleable name="CircleView"> <attr name="circle_color" format="color"> <.declare-syleable>
4.4.3.2 2. 在View的构造方法中解析自定义属性的值并处理
4.4.3.2.1 eg: TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleView); mColor = a.getColor(R.styleable.CircleView_circle_color, Color.RED); a.recycle();
4.4.3.3 3. 在布局文件中使用自定义属性
4.4.3.3.1 s
5 5. RemoteViews
5.1 应用
5.1.1 通知栏
5.1.1.1 通过RemoteViews提供的方法来更新视图
5.1.2 桌面小部件
5.1.2.1 定义小部件界面
5.1.2.2 定义配置信息
5.1.2.3 定义实现类
5.1.2.4 在androidManifest中声明小部件
5.1.3 PendingIntent
5.1.3.1 内部的Intent相同,并且requestCode也相同即为相同;其中intent相同只需要ComponentName和intent-filter相同
5.2 内部机制
5.2.1 系统并没有通过binder去支持所有的view操作,仅支持了少量的view(TextView,ImageView等),这是出于效率考虑
5.2.2 提供了一个Action概念,Action实现了Parcelable接口,代表一个View操作;将这些actions放到ArrayList中,统一通过Binder进行通信,提高IPC效率
6 6. Drawable
6.1 分类
6.1.1 BitmapDrawable
6.1.1.1 android:antialias 抗锯齿
6.1.1.2 android:dither 抖动效果,开启这个选项可以让高质量的图片在低质量的屏幕上还能保持较好的显示效果
6.1.1.3 android:filter 是否开启过滤效果。当图片尺寸被拉伸或者压缩时,开启过滤效果可以保持较好的显示效果
6.1.1.4 android:tileMode 平铺模式。有 disable|clamp|repeat|mirror几个选项,开启平铺模式之后gravity属性会被忽略
6.1.2 ShapeDrawable
6.1.2.1 可以通过纯色构造,也可以是具有渐变效果的图形
6.1.2.2 android:shape —— rectangle/oval/line/ring
6.1.2.2.1 line 和ring两个选项必须通过stroke标签来指定线的宽度和颜色等信息
6.1.2.3 <corners>表示shape 的四个角度,只使用于矩形shape,这里的角度是指圆角程度,用px表示
6.1.2.4 <gradient> 与solid标签是相互排斥的,其中solid表示纯色填充,而gradient则表示渐变效果
6.1.3 LayerDrawable
6.1.3.1 对应的标签是 <layer-list>,它表示一种层次化的Drawable集合,通过将不同的Drawable放置在不同的层上面从而达到一种叠加后的效果
6.1.4 StateListDrawable
6.1.4.1 对应的标签是<selector>,也是表示Drawable集合,每个Drawable都对应着View的一种状态
6.1.5 LevelListDrawable
6.1.5.1 <level-list>,集合中的每个Drawable都有一个等级的概念(level 0~10000)
6.1.5.2 如果被用来作为ImageView的前景Drawable,那么还可以通过ImageView的setImageLevel方法来切换Drawable
6.1.6 TransitionDrawable
6.1.6.1 <transition>,用于实现两个Drawable之间的淡入淡出效果
6.1.6.2 可以用作View的背景,也可以在ImageView中直接作为Drawable来使用
6.1.7 InsertDrawable
6.1.7.1 <insert>,可以将其他Drawable内嵌到自身当中,当一个View希望自己的背景比自己的实际区域小时可以采用
6.1.8 ScaleDrawable
6.1.8.1 <scale>,
6.1.9 ClipDrawable
6.1.9.1 <clip>,根据自己当前level来裁剪另一个Drawable
6.2 自定义Drawable
6.2.1 通常我们很少自定义Drawable,因为自定义的Drawable无法再xml中使用
7 7. 动画
7.1 View动画
7.1.1 四个子类:TranslateAnimation、ScaleAnimation、RotateAnimation、AlphaAnimation
7.1.1.1 可以通过代码设置,也可用XML
7.1.1.2 既可以是单个动画,也可以由一系列动画组成
7.1.2 自定义View动画
7.1.2.1 方式:继承Animation类,重写initialize和applyTransformation方法,在initialize中做一些初始化工作,在applyTransformation中进行相应的矩阵变换即可,很多时候需要采用Camera来简化矩阵变换过程
7.1.3 帧动画
7.1.3.1 AnimationDrawable 顺序播放一组预先定义好的图片,类似于电影播放
7.1.3.2 帧动画使用比较简单,但是比较容易引起OOM,在使用时应尽量避免使用过多尺寸较大的图片
7.2 View动画的特殊使用场景
7.2.1 LayoutAnimation
7.2.1.1 作用于ViewGroup,为ViewGroup指定一个动画,这样当它的子元素出场时都会具有这种动画效果
7.2.1.2 步骤
7.2.1.2.1 定义LayoutAnimation
7.2.1.2.2 为子元素指定具体入场动画
7.2.1.2.3 为ViewGroup指定android:layoutAnimation属性
7.2.1.2.3.1 对ListView来说,这样Listview的item就具有出场动画了
7.2.1.2.3.2 除了xml指定,还可以通过LayoutAnimationController来实现
7.2.2 Activity切换效果
7.3 属性动画
7.3.1 简介: ValueAnimation、ObjectAnimation、AnimatorSet
7.3.2 使用属性动画
7.3.2.1 属性动画从API 11 才有,可以使用nineoldandroids来兼容老版
7.3.2.2 可以利用代码、XML形式实现 一般会采用代码形式
7.3.3 理解插值器和估值器
7.3.4 属性动画的监听器
7.3.4.1 主要接口:AnimatorUpdateListener和AnimatorListener
7.3.4.2 AnimatorUpdateListener比较特殊,它会对整个动画过程进行监听,动画每播放一帧,onAnimationUpdate就会被调用一次
7.3.5 对任意属性做动画
7.3.5.1 我们对object的属性abc做动画,如果想让动画生效,要同时满足两个条件:
7.3.5.1.1 object必须提供setAbc方法,如果动画的时候没有传递初始值,那么还要提供getAbc方法
7.3.5.1.2 object的setAbc对属性abc所做的改变必须能够通过某种方法反映出来,比如带来UI的改变之类
7.3.5.2 如果无法满足条件
7.3.5.2.1 1. 给你的对象加上get和set方法,如果有权限的话
7.3.5.2.2 2. 用一个类来包装原始对象,间接为其提供get和set方法
7.3.5.2.3 3. 采用ValueAnimator,监听动画过程,自己实现属性的改变
7.3.6 工作原理
7.4 使用动画的注意事项
7.4.1 OOM
7.4.2 memory leak
7.4.2.1 无限循环动画需要在Activity退出时及时停止,否则将导致Activity无法释放从而造成内存泄露
7.4.2.2 兼容性问题
7.4.2.3 View动画的问题
7.4.2.3.1 View动画是对View的影像做动画,不不是真正改变View的状态,有时会出现动画完成后View无法隐藏的现象,即setVisiblity(View.GONE)失效了,这时只要调用View.clearAnimation()清除View动画即可解决此问题
7.4.2.4 不要使用px
7.4.2.5 动画元素交互
7.4.2.5.1 新位置将无法触发单击事件
7.4.2.6 硬件加速
8 8. Window和WindowManager
8.1 简介
8.1.1 Flags参数、Type参数
8.1.2 windowManger提供三个常用方法:添加view、更新View、删除View
8.2 Window的内部机制
8.2.1 Window添加
8.2.1.1 使用到了桥接模式,内部使用WindowManagerGlobal统一委托实现
8.2.1.2 添加过程其实是一次IPC调用,binder模式
8.2.2 Window删除
8.2.3 Window 更新
8.3 Window的创建过程
8.3.1 Activity的Window创建
8.3.1.1 Window对象的创建是通过PolicyManager的makeNewWindow实现的
8.3.1.1.1 Window的具体实现类是PhoneWindow
8.3.1.2 步骤
8.3.1.2.1 如果没有DecorView,创建它
8.3.1.2.2 将View添加到DecorView的mContentParent中
8.3.1.2.3 回调Activity的onContentChanged方法通知Activity视图已经发生改变
8.3.2 Dialog的Window创建
8.3.2.1 创建Window
8.3.2.2 初始化DecorView并将Dialog视图添加到DecorView中
8.3.2.3 将DecorView添加到Window中并显示
8.3.2.3.1 Dialog的context必须使用Activity的,不能使用Application的context;一定要使用的话可以设定Window类型,声明alert权限(见书本P310)
8.3.3 Toast的Window创建
9 9. 四大组件工作过程
9.1 运行状态
9.1.1 除BroadcastReceiver外,其他三个都要在Manifest中注册
9.2 Activity的工作过程
9.2.1 performLauncherActivity最终完成Activity对象的创建和启动过程,并且ActivityThread通过handleResumeActivity方法来调用被启动的Activity
9.2.1.1 1. 从ActivityClientRecord中获取待启动的Activity组件信息
9.2.1.2 2. 通过instrumentation的newActivity方法使用类加载器创建Activity对象
9.2.1.3 3. 通过LoadedApk的makeApplication方法来尝试创建Application对象
9.2.1.4 4. 创建ContextImpl对象并通过Activity的attach方法来完成一些重要数据的初始化
9.2.1.5 5. 调用Activity的onCreate方法
9.3 Service工作过程
9.3.1 分类
9.3.1.1 启动状态,用于执行后台计算任务
9.3.1.2 绑定状态,用户其它组件和Service交互
9.3.2 启动过程
9.3.2.1 利用ActivityManagerService
9.3.3 绑定过程
9.3.3.1 当多次绑定同一个Service时,onBind方法只会执行一次,除非Service被终止
9.4 BroadcastReceiver工作过程
9.4.1 注册
9.4.2 发送接受
9.5 ContentProvider
9.5.1 onCreate要先于Application的onCreate执行,这在四大组件中是比较少见的现象
9.5.2 步骤
9.5.2.1 1. 创建ContextImpl和Instrumentation
9.5.2.2 2. 创建Application
9.5.2.3 3. 启动当前进程的ContentProvider并调用其onCreate方法
9.5.2.4 4. 调用Application的onCreate方法
10 10. Android消息机制
10.1 概述
10.1.1 MessageQueue内部是单链表
10.1.2 Handler创建时会采用当前线程的Looper来构建内部消息循环系统
10.2 消息机制分析
10.2.1 ThreadLocal
10.2.1.1 是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储后只有在指定线程中可以获取到数据
10.2.1.2 若数据以线程为作用域且不同线程具有不同的数据副本时,可以考虑采用ThreadLocal
10.2.1.3 另一个使用场景是复杂逻辑下的对象传递
10.2.1.4 实现原理
10.2.1.4.1 set方法: public void set(T value){ Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if(values == null) values = initializeValues(currentThread); values.put(this, value); }
10.2.2 MessageQueue
10.2.2.1 插入和读取,数据结构为单链表
10.2.2.2 读取操作中的next实质上是无限循环
10.2.3 Looper
10.2.3.1 退出:quit、quitSately quit——直接退出Looper; quitSafely——把消息队列中的已有消息处理完毕后才安全退出
10.2.3.2 在子线程中,如果手动为其创建了Looper,那么在所有事情完成后应该调用quit方法来终止Looper,否则这个子线程就会一直处于等待状态
10.2.4 Handler工作原理
10.3 主线程的消息循环
10.3.1 Android的主线程就是ActivityThread
11 11. Android线程和线程池
11.1 概述
11.1.1 AsyncTask底部用到了线程池和Handler
11.1.2 IntentService和HandlerThread则是直接使用了线程
11.1.3 IntentService内部采用HandlerThread来执行任务,当任务执行完毕后IntentService会自动退出;IntentService作用很想一个后台线程,但是IntentService是一种服务,它不容易被系统杀死从而可以尽量保证任务的执行,直接使用后台线程的话,则很容易被系统杀死;
11.1.4 HandlerThread是一种具有消息循环的线程
11.2 主线程和子线程
11.2.1 主:进程所拥有的线程
11.2.2 子:执行耗时任务
11.3 线程形态
11.3.1 AsyncTask
11.3.1.1 封装了Thread和Handler,但不适合特别耗时的任务
11.3.1.2 泛型参数:Params、Progress、Result
11.3.1.3 核心方法
11.3.1.3.1 onPreExecute()
11.3.1.3.2 doInBackground(Params... params)
11.3.1.3.3 onProgressUpdate(Progress...values)
11.3.1.3.4 onPostExecute(Result result)
11.3.1.4 限制
11.3.1.4.1 必须在主线程中加载
11.3.1.4.2 对象必须在主线程中创建
11.3.1.4.3 execute必行在UI线程调用
11.3.1.4.4 不直接调用核心方法
11.3.1.4.5 兵行、串行的来回改版
11.3.1.5 工作原理
11.3.1.5.1 串行线程池
11.3.1.5.2 Handler切换线程,要求Handler来自主线程
11.3.2 HandlerThread
11.3.2.1 run中通过Looper.prepare()来创建消息队列,并通过Looper.loop()来开启消息循环
11.3.2.2 由于其run方法是一个无限循环,因此当明确不需要再使用HandlerThread时可以通过quit或者quitSafely来终止线程执行
11.3.3 IntentService
11.3.3.1 抽象类,必须建立它的子类才能使用
11.3.3.2 可以执行后台耗时任务,执行完成后自动停止
11.4 线程池
11.4.1 ThreadPoolExecutor
11.4.1.1 参数
11.4.1.1.1 corePoolSize
11.4.1.1.2 maximumPoolSize
11.4.1.1.3 keepAliveTime
11.4.1.1.4 unit
11.4.1.1.5 workQueue
11.4.1.1.6 threadFactory
11.4.1.1.7 RejectedExecutionHandler handler
11.4.2 分类
11.4.2.1 FixedThreadPool
11.4.2.2 CachedThreadPool
11.4.2.3 ScheduledThreadPool
11.4.2.4 SingleThreadExecutor
12 12. Bitmap的加载和Cache
12.1 高效加载
12.1.1 使用BitmapFactory.Options来加载所需尺寸的图片
12.1.1.1 使用inSampleSize设置采样率
12.1.1.2 如何获取采样率
12.1.1.2.1 1. 将inJustDecodeBounds 参数设为true并加载图片
12.1.1.2.2 2. 取出图片原始宽高信息,对应于outWidth和outHeight
12.1.1.2.3 3. 根据采样率规则并结合目标View所需大小计算出采样率inSampleSize
12.1.1.2.4 4. 将inJustDecodeBounds参数设为false,然后重新加载图片
12.2 缓存策略
12.2.1 常用LRU算法:Least Recently Used
12.2.1.1 LruCache实现内存缓存,DiskLruCache充当存储设备缓存
12.2.2 LruCache
12.2.2.1 使用support-v4包中的LruCache兼容性更好
12.2.2.2 是线程安全的
12.2.2.3 引用的几种类别
12.2.2.3.1 强引用
12.2.2.3.2 软引用
12.2.2.3.2.1 内存不足时会被gc
12.2.2.3.3 弱引用
12.2.3 DiskLruCache
12.2.3.1 创建
12.2.3.2 添加
12.2.3.3 查找
12.2.4 ImageLoader的实现
13 13. 综合技术
13.1 使用CrashHandler来获取应用信息
13.1.1 UncaughtExceptionHandler处理crash——记录、上报、用户提醒
13.1.2 使用方法
13.1.2.1 在Application初始化时为线程设置CrashHandler
13.2 multiDex解决方法数越界
13.2.1 1. 在build.gradle中添加multidex配置
13.2.2 2. 在代码中支持multidex功能
13.2.2.1 一、 在manifest中指定Application 为MultiDexApplication
13.2.2.2 二、 让应用继承自MultiDexApplication
13.2.2.3 三、 重写 application中的attachBaseContext方法(因为这个方法在onCreate之前执行)
13.2.3 3. 指定主dex文件所要包含的类——可以通过-- main-dex-list指定
13.2.3.1 在build.gradle文件中添加afterEvaluate区域(见书P460)
13.3 动态加载技术
13.4 反编译
14 14. JNI和NDK
15 15. Android性能优化
15.1 性能优化方法
15.1.1 布局优化
15.1.1.1 include标签
15.1.1.2 merge标签
15.1.1.3 ViewStub
15.1.1.3.1 使用时才加载
15.1.1.3.2 暂时不支持merge
15.1.2 绘制优化
15.1.2.1 onDraw要避免执行大量操作
15.1.3 内存泄露优化
15.1.3.1 静态变量导致
15.1.3.2 单例导致
15.1.3.3 属性动画导致
15.1.4 响应速度优化和ANR日志分析
15.1.4.1 进程ANR会在/data/anr目录下创建traces.txt记录
15.1.5 ListView和Bitmap优化
15.1.5.1 避免getView中耗时
15.1.5.2 根据滑动状态来控制任务执行频率
15.1.5.3 BitmapFactory.Options进行采样
15.1.6 线程优化
15.1.6.1 线程池
15.1.7 建议
15.1.7.1 避免过多对象
15.1.7.2 不要过多使用enum
15.1.7.3 const使用static final修饰
15.1.7.4 使用Android特有的数据结构,如SparseArray和Pair,它们都具有更好的性能
15.1.7.5 适当使用软引用和弱引用
15.1.7.6 采用内存缓存和磁盘缓存
15.1.7.7 尽量采用静态内部类,可以避免潜在的由于内部类而导致的内存泄露

推荐阅读更多精彩内容