Android开发艺术探索读书笔记

前言 

Android开发艺术(这本书真的是艺术,太崇拜刚哥了,值得每一个做Android开发刷十遍的书)

1,Activity生命周期和启动模式
典型情况下的生命周期分析
onCreate() onStart() onResume() onPause() onStop() onDestroy() onRestart()


异常情况下的生命周期分析
屏幕旋转
onSaveInstanceState() onRestoreInstanceState()
View中也有这两个方法
View保存和回复View层次结构,Activity意外终止,Activity调用onSaveInstanceState()保存数据,然后Activity会委托Window去保存数据,接着Window会委托他上面的顶层容器去保存数据,一般来说很可能是DecorView.最后顶层容器再去一一通知它的子元素来保存数据
上层委托下层的委托机制Android运用很多,比如View的绘制,事件的分发
android:configChanges onConfigurationChange


2,Activity的启动模式
Activity启动模式 singleTop singleTask调用onNewInstance
standard singleTop singleTask singleInstance 
TaskAffinity 任务栈名称默认是应用包名可以指定,不能是包名,需要和singleTask启动模式或者allowTaskReparenting属性配对使用
Acticity Flags
IntentFilter匹配规则
action 至少有一个要匹配
category 所有的category都要匹配上 接收隐式调用android.intent.category.DEFUALT
date 至少有一个要匹配 uri默认值content file


总结
隐式启动Activity的时候应检测是否有Activity匹配隐式启动


IPC机制
1,IPC简介
Linux 命名管道,共享内存,信号量,Socket


2,Android多进程模式
开启多进程模式
android:process 
多进程模式的运行机制
shareUid 签名相同可以使两个应用共享一个内存
多进程造成的问题
静态成员,单例模式完全失效
线程同步机制完全失效
sharePrefernces可靠性下降
Application会创建多次


3,IPC基础概念介绍
Serializable
serializableId 静态成员不属于对象不参与序列化构成,transiem关键字标记的成员不参与序列化
Parcelable 进程间通信只能用这种方式序列化数据
Binder
aidl自动生成文件
Stub内部类服务端继承
DESCRIPTOR Binder唯一标识
asInterface() 服务端binder对象,换成客户端所需的aidl接口类型的对象,如果同进程返回服务端stub对象本身,如果不同进程返回的是系统封装后的Stub.proxy对象
asBinder() 返回客户端对象
onTranact() 此方法运行在服务端中Binder线程池中,客户端请求,系统底层封装后交由此方法处理 code可确定调用方法 reply中写返回值 return为false表示请求服务端失败


当客户端请求,当前线程会被挂起直至服务端返回数据,所以有耗时操作不能在ui线程中
服务端Binder是运行在Binder线程池中,所以Binder方法不管是否耗时都应采用同步的方式去实现


unlinkToDeath linkToDeath


4,Android中IPC方式 
使用Bundle
A进程启动B进程的服务计算数值后在启动要启动的目标组件
使用共享文件
使用Messager 串行
在进程中传递Message对象,使用Handle 一次处理一个请求不需要考虑同步的问题
使用AIDL 多进程要考虑同步
RemoteCallBackList接触跨进程监听
onServiceDisconnected() 连接断开重连远程服务
验证权限
使用ContentProvider curd
使用Socket


5,Binder链接池
6,选择适用的IPC方式


View事件体系
1,View基础知识
什么是View
View位置参数
MotionEvent 
Touchslop 手指在屏幕上滑动的最小距离
VelocityTracker 用于追中手指在滑动过程中的速度
GestureDetector 手势检测
Scroller 弹性滑动对象,需要和View的computeScroll配合使用
2,View滑动
使用scrollTo scrollBy mScrollX mScrollY 只能滑动View内部,不能滑动View本身
使用动画 如果不是属性动画点击事件还会停留在动画起始位置
使用延时策略
3,弹性滑动
Scroller
Handler postDelayed
Thread sleep
4,View事件分发机制
点击事件传递规则
dispathTouchEvent()
onIntercepetTouchEvent()
onTouchEvent()
onTouch()
onClick()
5,View滑动冲突
常见的滑动冲突场景
外部方向内部方向不一致
外部方向内部方向一致
上面两种嵌套
滑动冲突的处理规则
外部拦截法 重写onInterceptTouchEvent()
内部拦截发 requestDisallowInterceptTouchEvent()
滑动冲突的解决方式


View工作原理
ViewRootImpl会调用performTraversals()
ViewGroup中会调用 
performMeasure() 
measure()
onMeasure() 
performLayout()
layout()
onLayout()
performDraw()
draw()
onDraw()


View中会调用
measure
layout
draw






Measure
理解MeasureSpec
SpecMode SpecSize
子View的MeasureSpec需要父View的MeasureSepc和子View的LayoutParams共同决定


View Measure
setMeasuredDimension() 设置测量的大小 getDefaultSize()获取尺寸
ViewGroup中的Measure 空实现onMeasure要在自己具体实现
measureChildWithMargins() 调用getChildMeasureSpec() 通过父View的MeasureSpec和子View的LayoutParams来测量子View的MeasureSpec
measureChildren() 遍历子View中的measure()方法


Activity创建完毕就获取View宽高
回调View onWindowsFocuesChanged
View post(runnable) 
ViewTreeObserver
view.measure()比较复杂


Layout
View Layout
setFrame() 设置四个顶点的位置,然后调用onLayout,子View四个顶点确定了,父view的四个顶点就确定了
View测量宽高getMeasureWidth()和最终宽高getWidth()的区别
测量宽高形成于measure阶段,最终宽高形成于layout阶段


Draw
绘制背景 background.draw
绘制自己 onDraw
绘制child dispatchDraw
绘制装饰 onDrawScrollBars


自定义View
1,继承自View或者以实现的View
2,继承自ViewGroup或者以实现的ViewGroup




总结
1,在自定义View的时候要自行处理MeasureSpec.AT_MOST下wrap_content的情况
2,如果有必要支持padding
3,尽量不要在View中使用Handler
4,View中有线程,动画需要及时停止  View不可见  View退出Activity onDetachFromWindow()
5,View有嵌套滑动的时候处理滑动冲突




RemoteViews
通知栏,桌面小部件


动画的深入分析
View动画(平移,缩放,旋转,透明度)
Animation
TranslateAnimation 平移
ScaleAnimation 缩放
RotateAnimation 旋转
AlphaAnimation 透明度
AnimationSet 多个动画的集合
动画的监听 setAnimationListener()
自定义动画 继承Animation重写initialize 初始化和applyTransFormation 改变矩阵


帧动画
AnimationDrawable 容易引起OOM


属性动画
Api 11新加入的功能
可以对任意的对象做动画,在一定时间间隔内完成对象从一个属性值到另一个属性值的改变
ValueAnimator 不作用任何对象,直接使用他是没有任何效果的,我们监听其动画过程,在动画过程中修改我们的对象属性值
ObjectAnimator
AnimatorSet
属性动画要求该属性有set,get方法,set方法对属性改变必须能够通过某种方式反应出来
解决办法
给对象提供set,get方法
使用一个类包装原始对象,间接为其提供set,get方法
采用ValueAnimator监听动画过程,自己实现属性的改变




属性动画的监听
AnimatorUpdateListener 监听每一帧的动画
AnimatorListener


插值器,估值器
TimeInterpolator插值器。根据时间流逝的百分比来计算出当前属性值改变的百分比
TypeEvaluator估值器。根据当前属性改变的百分比计算改变后的属性值


View动画的特殊使用场合
xml中使用LayoutAnimation作用于ViewGroup为它指定一个动画,这样他的子元素就具有这种动画了
代码中使用LayoutAnimationController


Activity切换效果
overridePendingTransition()这个方法必须在startActivity或者finish之后调用才能生效


总结
1,oom问题 帧动画图片数量较多的时候会出现
2,内存泄漏,Activity退出时及时停止动画
3,兼容性问题 动画在3.0一下的兼容性问题
4,View动画的问题 只是做影响动画并不是真正的改变View的状态,需要在执行动画后调用View.setVisibility(View.GONE)和view.clearAnimation()
5,不要使用px
6,动画元素的交互
7,硬件加速


Window和WindowManager
所有的View都是附加在Window上来呈现的
params = new WindowManger.Layparams()
params.flags 
FLAG_NOT_FOUCESABLE window不需要获取焦点
FLAG_NOT_TOUCH_MODAL window以外的单机事件传递给底层的window,当前window区域的单击事件自己处理
FLAG_SHOW_WHEN_LOCKED window显示在锁屏界面上
params.type
三种类型,
应用window,Activity 层级1-99
子window,dialog 层级1000-1999
系统window 需要声明权限,比如toast 系统状态栏 2000-2999


window是分层的,层级大的会覆盖在层级小的window上面
window内部,window是一个抽象的概念
每一个window都对应着一个view和一个viewRootImpl,window和view通过ViewRootImpl建立联系


WindowManager提供三个方法 
添加View 创建window添加view
更新View 跟新window中的view
删除View 删除window中的view


windowManager他的真正实现的windowManagerImpl然后交个WindowManagerGlobal来处理 WindowManagerGlobal通过工厂的形式提供实例:桥接模式
ViewRootImpl更新界面,会调用setView内部会通过requestLayout在调用scheduleTraversals
通过Binder,WindowManagerService添加window


Window创建过程
Activity Window创建过程
Activity的attach方法里会创建Activity所属的window对象PolicyManager.makeNewWindow()整个对象就是PhoneWindow对象,并实现回调Window CallBack接口,onAttachedToWindow onDetachedFromWindow dispatchTouchEvent
Activity显示过程
setContent()里面是getWindow().setContent(),返回的是PhoneWindow对象,会检测mContentParent是不是Null,如果是null,new一个DecotorView赋值给
mContentRoot和mContentParent
将View添加到mContentParent
回调Activity的onContentChange方法通知Activity视图更改了
ActivityThread中调用handleResumeActivity会创建ViewRootImpl然后Binder通信WindowMangerService显示View


Dialog的window创建过程
在构造方法中,创建window的过程跟Activity的一样的
PolicyManager.makeNewWindow()
setContentView时调用mWindow.setContentView
在dialog的show方法的时候,通过WindowManager将DecorView添加到Window
应用token只有Activity有所以dialog的context需要用Activity的


Toast的创建window 使用Handler无法在没有Looper的线程中弹出
基于window实现,并且具有定时取消功能采用了hanlder,内部有两个IPC过程,第一个Toast访问NotificationManagerService 第二类NotificationManagerService回调Toast里的TN接口


四大组件的工作过程
Activity
启动Activity在应用进程中使用到的类有
Activity
ActivityThread 处理进程间通信后更新Activity的任务
ApplicationThread ActivityThread 内部类负责进程间通信的


SystemService进程中使用到的类有
ActivityManagerService
ActicityStackSupervisor
ActivityStack




Activity.startActivity()
Activity.startActivityForResult()
Instrumentation.execStartActivity() checkStartActivityResult()检测Activity是不是能正常启动
ActivityManagerNative().getDefault().startActvity()  这里进行了Binder()通信,通知ActivityMangerService startActivity


ActivityManagerService,startActivity()
ActivitySatckSupervisor.startActivityMayWait()
ActivityStackSupervisor.startActivityLocked()
ActiivtyStackSupervisor.startActivityUnchekedLocked()
ActivityStack.resumeTopActivitiesLocked()
ActivityStack.resumeTopActivityInnerLocked()
ActivityStackSupervisor.startSpecificActivityLocked()
ActivitySatckSupervispr.realStartActivityLocked()
IApplicationThread.scheduleLaunchActivity() 进程间通信,通知应用进程


ApplicationThread.scheduleLaunchActivity() 发送Handler
ActivityThread.handleLaunchActivity()
ActivityThread.performLaunchActivity() 使用类加载器加载创建Activity对象,创建Application对象,如果有一个那么就不创建,创建ContentImpl对象
他是Context的具体实现,并通过attach方法来完成一些重要的数据初始化(比如phoneWindow)同时与Activity建立关联,调用onCreate方法


Service的启动过程
从ContentWrapper的startService开始
mBase.startService(),mBase的类型就是ContextImpl是Context的实现类
ContextImpl.startSericeCommon()
ActivityManagerNative.getDefault().startService() 进程间通信
 
ActivityManagerService.startService()
ActiveServices.startServiceLocked() 辅助ActivityManagerService进行Service管理的,包括Service的启动,绑定,停止
ActiveServices.startServiceInnerLocked() 
ActiveServices.bringUpServiceLocked()
ActiveServices.realStartServiceLocked() 
IApplacationThread.scheduleCreateService() 进程间通信,通知应用进程


ApplicationThread.scheduleCreateService() 使用Handler
ActivityThread.handleCreateService() 通过类加载器创建service,创建Application对象,Application只能有一个,创建ContextImpl对象,并通过
Service的attach方法建立联系,调用Service.onCreate()


从ContentWrapper的bindService开始
mBase.bindService()
ContextImpl.bindService()
ContextImpl.bindServiceCommon() 
LoadedApk.getServiceDispatcher() 1,将客户端的ServiceConnection对象转化为ServiceDispatcher.InnerConnection对象 ServiceDispatcher起着链接
ServiceConnection和InnerConnectionde作用当Service和客户端建立链接后,系统会通过InnnerConnection来调用ServiceConnection中的onServiceConnected()
ActivitManagerNative.getDefault().bindService() 进程通信


ActivityManagerService.bindService()
ActiveServices.bindServiceLocked()
ActiveServices.bringUpServiceLocked()
ActiveServices.realStartServiceLocked()
ActiveServices.requestServiceBindingLocked()
IApplicaitonThread.scheduleBindService() 进程间通信


ApplicationThread.scheduleBindService() 使用Handler
ActivityThread.handleBindService() 调用onBind方法
ActivityManagerNative.getDefault().publishedService() 进程间通信


ActivityManagerService.publishService()
ActiveServices.publishServiceLocked()


BroadcastReceiver广播注册,广播发送与接收
ContextWrapper.registerReceiver()
mBase.registerReceiver()
ContextImpl.registerReceiverInternall()
LoadedApk.getReceiverDispather() 内部保存了BroadcastReceiver和InnerReceiver
ActivityManagerNative.getDefault().registerReceiver() 进程间通信


ActivityManagerService.registerReceiver()


广播发送
ContextWapper.sendBroadcast()
ContextImpl.sendBroadcast()
ActivityManagerNative.getDefault().broadcastIntent() 进程间通信


ActivityManagerService.broadcastIntent()
ActivityManagerService.broadcastIntentLocked() 根据intent-filter查出匹配的广播,添加到BroadcastQueue中,然后发送给接收者


ContentProvider
ContentProvider在启动时,ContentProvider会同时启动被发布到ActivityManagerService,这个时候ContentProvider的onCreate要先于Application的onCreate执行
应用启动会调用ActivityThread的main方法,会创建主线程,在ActivityThread的attch方法中会远程调用ActivityManagerService的attachApplication并将ApplicationTread对象提供给ActivityManagerService


Android的消息机制
Android ui控件是不安全的,多线程并发访问可能会导致Ui控件处于不可预期的状态
为什么不加上锁机制
1,访问逻辑会变复杂
2,锁机制会降低Ui访问的效率


Handler MessageQueue Looper ThreadLocal
ThreadLocal 线程内部存储,不同的线程需要有不同的数据副本就需要用这个


MessageQueue 插入,读取
Looper工作原理 不停地从MessageQueue中取查看是否有消息
Handler的工作需要Looper,创建Looper调用Looper.prepare()创建一个Looper对象
prepareMainLooper主要是给主线程创建Looper使用
Looper可以退出,提供quit 直接退出和quitSafely 设定一个退出标记,然后把消息队列中的已有消息都除了完毕后才安全退出
Looper.loop() 系统消息会起作用循环 有消息直接交给Handle.dispatchMessage()


Handler主要工作就是消息的发送和接收
post send来发送消息
dispatchMessage
1.先查看Message中是否设置了callBack
2,Handle中是否设置了callBack
3,调用Handler.handleMessage()


callBack作用:可以创建一个Handler实例但并不需要重写handleMessage


主线程消息循环模型
Android主线程就是ActivityThread,主线程入口为main
Looper.prepareMainLooper()来创建主线程的Looper以及MessageQueue,通过Looper.loop()来开启主线程消息循环


Android线程和线程池
AysncTask和IntentService和HandlerThread
AysncTask封装了线程池和Handler,方便开发者在子线程中更新ui
HandlerThread是具有消息循环的线层,在他的内部是可以使用Handler
IntentService是一个服务,内部采用HandlerThread来执行任务,任务执行完了会自动关闭


AsyncTask异任务类,不适合特别耗时的后台任务
1,doPreExecute()主线程中执行
2,doInBackgroud()在线程池中执行 结果会返回给onPostExecute()
3,onProgressUpdate 在主线程中执行,当后台任务执行进度发生改变时此方法调用
4,onPostExecute() 主线程中执行,在异步任务执行后,此方法会被调用
5,onCancelled() 当异步任务被取消时调用onPostExecute()不会调用


AsyncTask调教限制
1,AsyncTask类必须在主线程中加载,第一次访问AsyncTask必须发生在主线程
2,AsyncTask对象必须在主线程中创建
3,execute方法必须在ui线程中调用
4,不要在程序中直接调用onPreExecute() onPostExecute doInBackground onProgressUpdate方法
5,一个AsyncTask对象只能执行一次
6,Android 1.6之前,AsyncTask串行执行任务,之后并行执行任务,Android 3.0 串行,并行都可以


原理
execute 
executeOnExecutor
onPreExectue 最先执行
调动线程池中的执行方法 postResult发送Handler 执行finish
onCancelled onPostExecute


HandlerThread继承Thread 他是一种可以使用Handler的Thread
InnerService继承自Service是一个抽象类 适合执行高优先级后台任务 封装了HandlerThread和Handler
onHandleIntent 在线程中执行


Android线程池
优点
1,重用线程中的线程,避免因为线程的创建和销毁带来性能的开销
2,能有效控制线程最大的并发数,避免线程之间因互相抢占资源而导致的阻塞现象
3,能够对线程进行简单的管理


ThreadPoolExecutor是线程池真正的实现
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory)


corePoolSize 线程池核心线程数,默认情况下,核心线程会在线程池中一直存活
maximumPoolSize 线程池能容纳最大线程数
keepAliveTime 非核心线程闲时超时时长
unit 时间单位
workQueue 线程池中任务队列
threadFactory 线程工程 


FixedThreadPool 线程数量固定的线程池
CahchedThreadPool 非核心线程,线程数不定 设置超时机制60s
ScheduleThreadPool 核心线程数固定,非核心线程数没限制,非核心线程闲置时会立即回收
SingleThreadExecutor 只有一个核心线程 所有任务都按顺序执行


BintMap加载和Cache
LruCache 内存缓存 
DiskLruCache 存储缓存
lru 最少使用算法,当缓存快满时,会淘汰近期最少使用的缓存目标


图片加载
Bitmap 加载Bitmap BitmapFactory类提供了四类方法 decodeFile decodeResource decodeStream decodeByteArray
高效的加载Bitmap 采用BitmapFactory.Options类加载所需尺寸图片,主要是用到了他的inSampleSize参数采样率,为1图片原始大小,为2宽高均为原图片的1/2,像素为原图的1/4
如何获取采样率
1,BitmapFactory.Option的inJustDecodeBounds参数设置为true
2,从BitmapFactory.Option中取出图片的原始宽高信息,他们对应于outWidth和outHeight参数
3,根据采样的规则并结合View的所需大小计算出采样率inSampleSize
4,将BitmapFactory.Option的inJustDecodeBound参数设置为false 然后重新加载图片


缓存策略
当程序第一次从网络加载图片后就将其缓存的设备上,这样下次使用这张图片就不用再去网络上获取了
LruCacha 线程安全的 Android3.1提供的缓存类,通过suprot-v4兼容包可以兼容到早起Android版本
内部采用了LinkedHashMap以强引用的方式存储外界缓存对象 
有方法获取,添加,当存储满了的时候会移除较早使用的缓存对象


DiskLruCache用于实现存储设备缓存,磁盘缓存
创建,缓存查找,添加
存储用的是Editor
查找使用Snapshot通过流获取FileDescriptor,然后进行缩放


列表滑动流畅性
不要再getView中做耗时操作
待列表停顿后在加载图图片 onScrollStateChanged
如果在卡顿硬件加速




综合技术
CrashHandler获取应用Cash日志实现UncaughtExceptionHandler
Thread.setDefaultUncaughtExceptionhandler()


使用multidex解决方法数越界
启动速度会降低


动态加载技术(插件化,热修复)
需要解决的问题
1,资源访问
Context实现类ContextImpl 
getAssets()
getResouces()


AssetManager.addAssetPath() 通过反射创建Resources对象


2,Acivity生命周期
反射方式
反射代码写起来比较复杂
反射有一定的性能开销
接口方式
将Activity的生命周期提取出来作为一个接口,通过代理Activity去调用插件Activity的生命周期方法


3,ClassLoader管理
不同插件ClassLoader存储在一个HashMap中


JNI和NDK编程
nkd好处
1,提高代码安全性
2,可以方便的使用目前C/C++开源库
3,便于平台移植
4,提高程序在某些特定情形下执行的效率


JNI开发流程
1,Java中声明native方法,static代码块中导入so库 System.loadLibrary("xxx")
2,编译java源文件得到class文件,然后通过javah命令导出JNI头文件
3,实现JNI方法
4,编译so库并在Java中调用


NDK开发流程
1,下载配置NDK
2,Java中声明所需native方法
3,实现Android项目中声明的native方法
4,切换到jni目录的父目录,然后通过ndk-build命令产生so库


Android性能优化
布局优化
1,减少层级,LinearLayout和FrameLayout效率要比RelativeLayout效率高,如果是复杂需要嵌套那么应该使用RelativeLayout
2,采用include标签 布局重用,merge标签 配合include标签降低布局层级,ViewStub标签 按需加载 setVisibility inflate


绘制优化
1,View onDraw方法中避免执行大量的操作
不要创建局部对象,短时间创建大量对象造成gc,影响效率
不要做耗时的任务


内存泄漏优化
1,在开发过程当中避免写出有内存泄漏的代码
2,通过MAT找出潜在的内存泄漏继而解决
场景1,静态变量导致的内存泄漏,静态变量持有Context
场景2,单例模式导致的内存泄漏 单例模式注册监听要解注册
场景3,属性动画导致的内存泄漏 Activity在onDestroy时要调用animator.cancel()
场景4,Handler()使用的时候使用弱引用,或者静态内部类,内部类默认持有外部类引用


响应速度优化和ANR日志分析
避免在主线程中做耗时操作,耗时操作放到子线程中
应用发生ANR会在data/anr目录下创建一个traces.txt,通过分析分析这个文件定位ANR


ListView和Bitmao优化
1,采用Viewholder避免在getView中执行耗时操作
2,根据列表滑动状态来控制任务执行频率


线程优化
1,线程池


避免创建过多的对象
不要过多的使用枚举
常量请使用static final来修饰
使用一些Android特有的数据结构 SpareArray Pair
使用软引用和弱引用
采用内存缓存和磁盘缓存
尽量采用静态内部类


MAT

































































































































































































































猜你喜欢

转载自blog.csdn.net/lovelyprogrammer/article/details/80231387