android开发艺术探索
一、activity
activity的生命周期
典型情况
onCreate -> (onRestart ->)onStart(界面可见不可操作) -> onResume(可操作) -> running -> onPause -> onStop(不可见,当采用透明主题时不调用) -> onDestroy异常情况
1 系统配置发生变化(屏幕旋转)
可以配置android:configChanges=”orientation|screenSize”来避免重走生命周期,screenSize在api13之后屏幕旋转会导致activity重启。1 内存不足(后台activity被杀死)
如果一个进程中没有四大组件,该进程将很快会被系统杀死,所以一些耗时操作最好时放在service中进行。
activity的启动模式
launch model
1 standard
2 singleTop
3 singleTask
4 singleInstance 总是单独的任务栈
activity的flag
1 Intent.FLAG_ACTIVITY_CLEAR_TOP
2 Intent.FLAG_ACTIVITY_NEW_TASK
3 Intent.FLAG_ACTIVITY_SINGLE_TOP
4 Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS 用户不能通过历史栈回到此activity
可以通过配置android:taskAffinity=”xxx.xxx.xxx”来设置activity的任务栈,默认为包名。IntentFilter
1 action
intent中的action要有一个和filter规则中定义的匹配就ok。2 category
intent中的所有category都需要和filter规则中定义的匹配才ok3 data
和action一样。系统默认的data其uri的scheme为content、file。4 action、category和data如果都定义了,那么intent中也必须要将其三个都匹配才行。
二、IPC
IPC简介
inter process communication,就是进程间通信。一个app一般时对应一个进程,一个进程会又多个线程运行。
为CPU最小的调度单元,同一个进程会共享一段内存。
android 中多进程模式
可以通过设置android:process=”:xxx”来实现让activity运行在不同的进程中。
android IPC基础概念介绍
serializable
java中的序列化接口,通过io操作实现序列化和反序列化,方便网络传输。serialVersionUID用于parcelable
android中定义的序列化接口,通过writeToParcel和一系列read方法实现序列化和反序列化。parcelable主要是在
内存中实现序列化的,相比serializable更高效,但是如果通过网络传输相比serializable则更复杂。binder
binder有很多的解释,是android的一个类,是进程间通信的一种方式。在android开发中binder主要用在service中包括aidl和messenger,
而messenger的底层也是aidl,所以通过aidl可以很好的解释binder。使用binder进行进程间传输的对象都必须是实现parcelable接口的。
IPC方式
使用bundle
四大组件都支持bundle,但是有些情况bundle并不能满足所有数据都能放入bundle。使用文件共享
android基于linux支持文件的并发读写,有可能不安全。同样使用xml来存储键值对的SharedPreferences也是同样的理由。使用messenger
messenger通过传送message实现进程间通信,是一个轻量级的IPC方案。它是串行方式处理消息,不支持跨进程方法调用,aidl可以解决这个问题。
4.aidl
android interface definition language 是一种规范,遵循这哥规范实现进程间通信。支持基本数据类型、String、CharSequence、List(ArrayList)、
Map(HashMap)、Parcelable和AIDL。
服务端的aidl需要考虑权限的限制,不能让所有客户端随意的访问。
ContentProvider
ContentProvider底层也是binder。socket
略
binder链接池
aidl的流程是一个service,一个aidl和一个继承了aidl接口中stud的类,由service在onBind中返回此类。为了避免多个service创建可以采用binder连接池,
服务端只有一个service,通过queryBinder获取相应模块的binder,从而减少服务端的开销。
选择合适的IPC
略
三、View的事件体系
View的基础知识
什么是View;View的位置参数;MontionEvent、TouchSlop;VelocityTracker、GestureDetector和Scroller。
View的滑动
通过scrollTo和scrollBy(简单适合对view内容滑动)、使用动画(简单,适合没有交互的复杂滑动)和改变布局(复杂适合有交互)。
其中动画分视图动画和属性动画,视图动画需要设置fillAfter,否则会回到初始状态。android3.0(api11)之后支持属性动画。
弹性滑动
通过Scroller、动画和延时策略。
View的事件分发机制
事件分发机制类似上级有个问题,一级级下达下去,有最底层的人员处理解决掉。如果最底层的人员不能解决掉,那么就一层层往上抛。
事件是由activity -> window -> decor -> view -> 子view一级级往下分发,通过dispatchTouchEvent(MotionEvent ev),
onInterceptTouchEvent(MotionEvent ev)和onTouchEvent(MotionEvent ev)三个方法来实现。
View的滑动冲突
通过外层的onInterceptTouchEvent事件拦截和内部的onTouchEvent事件里调用requestDisallowInterceptTouchEvent拦截事件实现。
四、view的工作原理
初试ViewRoot和DecorView和WindowManager
1 | root.setView(view,params,parentView); |
ViewRoot是连接DecorView和WindowManager的纽带。
View的绘制流程是从ViewRoot的performTraversals开始的(performMeasure、performLayout和performDraw)。
理解MeasureSpec
View的尺寸测量通过调用onMeasure方法实现。父View的MeasureSpec和View自身的LayoutParams决定了View的测量结果。
View的工作流程
measure、layout和draw就是View的工作流程。由最顶层的view调用onMeasure、onLayout和onDraw方法开始执行往下执行
measure、layout和draw。
自定义View
不规则的图形继承自View,重写onDraw;继承ViewGroup派生特殊的Layout(比较接近View的底层);继承特定的View(比如TextView);继承特定的ViewGroup。
五、理解RemoteViews
RemoteViews的应用
用于跨进程界面更新,NotificationManager和AppWidgetManager(本质是一个BroadCastReceiver)在开发过程中都会用到RemoteViews。
RemoteViews的内部机制
RemoteViews只支持有限的View,不支持findViewById,使用一系列set方法来设置属性。通过binder在进程间传递,之后通过反射(ReflectionAction)调用View的方法实现界面更新。
RemoteViews的意义
在跨进程频繁更新界面,而界面又不复杂的的情况下RemoteViews是很好的AIDL替代品。
六、Android的Drawable
drawable表示的是一种可以在canvas上进行绘制的概念,可以是图片和颜色。
BitmapDrawable
1 android:antialias 抗锯齿
2 android:dither 抖动效果
3 android:tileMode 平铺模式
ShapeDrawable
LayerDrawable
StateListDrawable 状态Drawable集合
LevelListDrawable 等级Drawable集合
TransitionDrawable 淡入淡出效果
InsetDrawable 可以将其它Drawable嵌套在自己当中
ScaleDrawable 可以根据自己等级将Drawable缩放到一定比例
ClipDrawable 可以根据当前等级裁剪另一个Drawable
自定义Drawable很少用到,因为不能直接在XML中使用
七、Android动画深入分析
View动画
- 平移(TranslateAnimation)
- 旋转(RotateAnimation)
- 缩放(ScaleAnimation)
- 透明(AlphaAnimation)
帧动画
播放一组预先定义好的图片
属性动画
属性动画从android3.0(api 11)开始,老版本兼容可采用nineoldandroids库(其实内部是view动画实现)
常用的几个类为ObjectAnimation、ValueAnimation和AnimationSet
差值器(TimeInterpolator)和估值器(TypeEvaluator)
1 差值器根据时间流失的百分比来计算出当前属性值改变的百分比。
2 估值器根据属性改变的百分比计算改变后的属性值。
属性动画的原理,该对象必须提供属性的get和set方法,动画在looper中执行通过反射调用属性的get和set方法设置属性值。
使用动画的注意事项
1 帧动画注意OOM
2 内存泄漏,一般是属性动画中的无限循环动画
3 兼容性问题,属性动画android 3.0(api 11)才支持的
4 不要使用px,尽量都使用dp
5 动画元素的交互,view动画的事件是保留在原来的位置,属性动画的事件位置则随view改变和改变
6 硬件加速推荐使用,android4.0(api 14)默认开启
八、理解Window和WindowManager
在android中所有的视图都是通过Window来呈现的,包括Activity、Dialog和Toast。Window是一个抽象类,具体实现是PhoneWindow;
WindowManager用于管理Window的创建和销毁。
Window和WindowManager
WindowManager.LayoutParams 的flag属性
1 FLAG_NOT_FOCUSABLE(window不需要获取焦点)
2 FLAG_NOT_TOUCH_MODAL (window区域内的点击事件自己处理,区域外的交由底层window处理)
3 FLAG_SHOW_WHEN_CLOCKED(window可以显示在锁屏上)
WindowManager.LayoutParams 的type属性
1 应用级window(1-99)
2 子window(1000-1999)
3 系统级window(2000-2999)定义系统级别的window需要申请user-permission
WindowManager操控Window是通过添加、删除和更新Window里的view是显现。
Window的内部机制
每个Window对应一个View和ViewRootImpl,Window和View通过ViewRootImpl建立联系。ViewRootImpl由WindowManagerGlobal(WindowManager的实现类)
创建。最后Window中View的添加、删除和更新通过ViewRootImpl调用WindowManagerService实现,这3个操作都是IPC过程。
Window的创建
- Activity的创建
- Dialog的创建
- Toast的创建
九、四大组件的工作过程
四大组件的运行状态
- Activity
- Service
- BroadcastReceiver
- ContentProvider
Activity的工作过程
- 通过Binder机制将创建Activity的消息通知给ActivityManagerService;
- 根据activity的启动模式判断是否应该创建并将Activity放于栈顶;
- 判断Activity是否已存在,如果存在置为resume状态,如果不存在重新开启创建;
- 判断Activity的进程是否创建,没有开始创建,已创建开启Activity;
- 调用Activity.attach初始化,回调onCreate方法,调用ActivityThread.handleResumeActivity将Activity设置为resume状态。
Service的工作过程
Service启动
Service绑定
BroadcastReceiver的工作过程
- BroadcastReceiver注册
- BroadcastReceiver发送
- BroadcastReceiver接受
ContentProvider的工作过程
ContentProvider的onCrate先于Application的onCreate执行。
10、Android的消息机制
Android消息机制概述
创建Handler需要使用到当前线程的Looper来构建内部消息循环系统,如果当前线程没有Looper则会报错。(可以调用Looper.prepare创建Looper)
Handler创建完毕后,Looper和MessageQueue就可以和Handler系统工作了。通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理或则
通过Handler的send方法发送消息,post方法最终也是调用send方法。当Handler的send方法被调用时,它会调用MessageQueue的enqueueMessage方法将这个消息
添加到消息队列中,然后Looper发现有新的消息到来时,就会处理这个消息,最终消息中的Runnable或者Handler的handleMessage方法会被调用。因为Looper所在
的线程时Handler被创建的线程,这样一样业务逻辑就切换到创建Handler的线程中去执行了。
Android消息机制分析
ThreadLocal的工作原理
MessageQueue的工作原理
Looper的工作原理
Handler的工作原理
主线程的消息循环
11、Android的线程和线程池
主线程和子线程
android3.0(api 14)新特性
- 主线程中不能做网络请求
- 支持属性动画
- Fragment替代了ViewGroup
Android线程的形态
- AsyncTask
- HandlerThread
- IntentService
Android中的线程池
- FixedThreadPool 线程数量固定,空闲线程
- CacheThreadPool
- ScheduledThreadPool
- SingleThreadExecutor
12、Bitmap的加载和Cache
Bitmap的高效加载
android中通过BitmapFactory的四个方法加载图片:decodeFile、decodeSource、decodeStream、decodeByteArray。
通过BitmapFactory.options来加载所需的尺寸,通过inSampleSize采样实现。
注意采样需要2次decode,而decodeStream采用 FileInputStream 时会出现问题,因为 FileInputStream采用有序的文件流,
会导致第二次decode拿不到stream。解决办法是通过文件流获取文件描述,再通过BitmapFactory.decodeFileDescriptor加载缩放后的图片。
Android中的缓存策略
- LruCache(least recent use cache)
LruCache是android3.1提供的缓存类,兼容早起版本可以使用support-v4包。它采用LinkedHashMap以强引用的方式存储外界的缓存对象,提供put和get方法来添加和删除缓存。
当缓存满时LruCache会移除较早使用的缓存对象,然后添加新缓存。
强引用 Object o = new Object()
软引用 SoftReference
- DiskCache 磁盘缓存
ImageLoader的使用
13、综合技术
CrashHandler
略
Multidex来解决方法数越界
略
Android的动态加载技术
- 资源的访问
通过调用AssetManager的addAssetPath发放,我们可以将一个apk中的资源加载到Resource对象中,由于addAssetPath隐藏在api中,所以我们只能通过反射。
[addAssetPat(assetManager,dexPath)] - activity 生命周期的管理
通过提供生命周期接口,所有的插件Activity都实现此接口,通过代理的Activity调用插件Activity的生命周期方法。 - 插件ClassLoader管理
为了防止多个ClassLoader加载同一个类造成类型转换错误,在代码中,通过将不通插件的ClassLoader村春在HashMap中,来保证不同插件中的类互不干扰。
反编译
略
14、JNI和NDK编程
JNI的开发流程
- 在java中声明native方法
- 编译java原文件得到class文件,通过javah命令到处jni头文件
- 实现jni方法,可以通过C或者是C++
- 编译so库,可以通过gcc,然后运行使用
NDK的开发流程
- 下载和配置NDK
- 创建android项目,并声明所需的native方法
- 实现项目中的native方法(test.cpp,Android.mk,Application.mk)
- 通过ndk-build命令编译产生so文件
JNI的数据类型和类型签名
略
JNI调用java方法
先是通过类名找到类,再通过方法名找到方法id,最后就可以调用这个方法了。
15、Android的性能优化
Android性能优化
布局优化
include标签
1 可以重用布局,配合merge可以减少布局层级。
merge标签
1 可以减少布局层级。
ViewStub 按需加载
1 因为ViewStub高宽都是0,所以不会参与布局。通过调用setVisible和inflate后被layout指定的布局替换;它能提高页面初始布局效率,在网络出错的业务场景中比较好用。
线性布局优于相对布局
绘制优化
- onDraw方法不要创建新对象
- onDraw方法不要做耗时任务
内存泄漏优化
ListView和Bitmap优化
- 避免在getView中做耗时操作
- 根据滑动状态来控制任务的执行频率
- 1 使用inSampleSize缩放图片
- 2 android3.0以后可以使用inBitmap复用图片内存
- 3 使用图片缓存
线程优化
- 线程池可以重用线程避免创建和销毁的开销
- 避免抢占CPU导致堵塞