IntentFilter
IntentFilter
为了匹配过滤列表,需要同时匹配action
、data
、category
,否则匹配失败,acting
、data
、category
可以有多个,只有一个intent
同时匹配action
、category
与data
类别才算完全匹配。data
匹配规则:scheme
:URI模式,比如http、file、content
,如果URI没有指定scheme
那么整个URI其他参数无效。
Host
:URI主机名,比如www.google.com
,如果host
未指定,整个URI无效Port
:端口号,比如80 只有当指定scheme与host
,端口号才有意义。
Android 多进程模式
Android
多进程模式下会造成以下几方面问题:
- 单例与静态成员完全失效
- 线程同步机制失效
sharedprefrences
的可靠性降低Application
多次创建,当一个组件跑在一个新的进程中,系统由于要创建新的进程同时分配独立的虚拟机,这个过程就是启动一个新的应用过程,重新启动一遍,自然会创建新的Application
。
进程通信
Binder进程通信
生成的Binder
类
DESCRIPETOR
:Binder
唯一标识,一般用当前binder
类名asInterface
将服务端binder
对象转换成客户端所需的AIDL
接口
- 如果客户端与服务端运行在同一个进程,那么返回的就是服务端
Stub
对象本身,否则返回的是系统封装后的stub.proxy
对象
asBinder
用于返回当前binder
对象onTransact
此方法运行在binder
线程池中,当客户端发起跨进程请求时,远程请求经由系统底层封装后交由此方法进行处理,服务端通过code
确定客户端请求目标是什么,接着从data
中取出目标所需的参数,当目标方法执行完毕后,就向reply
中写入返回值。
- 如果
onTransact
方法返回false,那么客户端请求失败。
Binder
提供两个配对方法,linkDeath和unlinkDeath
,通知linkDeath
来设置死亡代理,当binder
死亡时,重新发起连接从而恢复连接。
Android IPC方式
- 使用
Bundle
传递数据 - 使用文件共享。需要避免并发写,通过使用同步限制多个线程写操作,适用于对数据同步要求不高的进程通信,需要妥善处理并发写的问题
- 使用
Messenger
是一个轻量级的IPC方案,它的底层实现了AIDL
。支持一对多串行通信。支持实时通信。只能传输Bundle
支持的数据类型。不支持RPC。 - 使用
AIDL
,处理大量请求:
- 服务端首先创建一个
service
监听客户端请求,然后创建一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在service
中实现接口即可 - 首先需要绑定服务端
service
,绑定成功后,将服务端返回的binder
对象转换成AIDL接口所属类型就可以调用AIDL方法了 - 对象不能跨进程进行传输,需要实现
Parcelable
接口,通过RemoteCallbackList
,它是系统专门提供删除跨进程listener
接口的 - 客户端调用远程服务时,被调用的方法运行在服务端
binder
线程池中,同时客户端线程会被挂起,如果服务端方法比较耗时,就会造成客户端阻塞,如果客户端运行在UI线程,就会出现ANR。放在非UI线程即可 - 在
ADIL
中使用权限验证功能:- 在
onBind
中进行验证,验证不通过直接返回null - 在
onTransact
方法中进行验证,验证失败直接返回false
- 在
- 使用
ContentProvide
,是用来不同应用之间可以数据共享。底层一样是binder
onCreate()
代表ContenProvider
创建,一般做初始化操作,getType
返回Uri请求所对应的MIME
类型,如图片、视频等contentProvider
通过Uri来区分外界访问的数据集合update、insert、delete
方法会引起数据源改变,需要通过contentProvider
的notifyChange
通知外界数据发生改变,要观察contentProvider
外界数据源已经发生改变,可以通过registerContentProvider
注册观察者,与unregisterContentProvider
解除观察者query、insert、delete、update
四大方法存在多线程并发访问,需要进行线程同步,存在多个SQLiteDatabase
需要进行同步因为对象之间无法进行线程同步,一个不需要线程同步,由于其内存对数据库操作有同步处理
- 使用
Socket
实现进程间通信。不能在主线程中访问网络。可以通过网络传输字节流,支持一对多并发实时通信。不支持直接的RPC
RPC 是什么
- RPC 即
Remote Procedure Call
(远程过程调 用) 是一种计算机通讯协议,它为我们定义了计算机 C 中的程序如何调用另外一台计算机 S 的程序,让程序员不需要操心底层网络协议,使得开发包括网络分布式多程序在内的应用程序更加容易。** - RPC 是典型的
Client/Server
模式,由客户端对服务器发出若干请求,服务器收到后根据客户端提供的参数进行操作,然后将执行结果返回给客户端。** - RPC 位于 OSI 模型中的会话层:
- OSI模型由低到高分别是:物理层,数据链路层,网络层,传输层,会话层,表示层,应用层。
View的事件体系
View的点击
TouchSlop
:是系统所能识别出的被认为最小滑动距离,手指在屏幕上滑动,如果小于这个常量,就不认为进行滑动操作VelocityTracker
:用于追踪手指在滑动过程中的速度GestureDecteor
:手势检测,onDown
手指轻轻触摸屏幕瞬间完成onShowPress
手指轻轻触摸屏幕,尚未松开或拖动onSingleTabUp
手指触摸屏幕后松开,这是单击行为onDoubleTap
:双击,由两次连续的单击组成,不能与onSingleTabConfirmed
共存onSingleTabConfirmed
严格的单击行为,如果触发onSingleTabConfirmed
,那么后面不能再跟一个单击行为
View的滑动
Scroller
:实现view的弹性滑动,实现过度效果。工作原理是:通过computeScroll
让view
不断进行重绘,根据重绘的时间间隔,得出view的当前滑动位置,根据位置通过scrollTo
完成滑动,多次小幅度滑动就组成了弹性滑动了。- 使用
scrollBy/scrollTo
实现view的滑动,只能将view的内容移动,不能将view的本身进行移动。
滑动对比:
scrollBy/scrollTo
操作简单,适合view内容的滑动- 动画 操作简单,主要适用于没有交互的view和实现复杂的动画效果
- 改变布局参数 操作复杂,适用于有交互的view
View的滑动冲突
- 场景1:外部滑动与内部滑动方向不一致
- viewpage与listview的嵌套,因为viewpage内部中处理了这种滑动冲突,所以无需考虑。如果外面是scrollview就需要考虑了
- 场景2:外部滑动与内部滑动方向一致
- 场景3:上面两种滑动的嵌套
解决滑动冲突方法
- 外部拦截:重写父容器
onInterceptTouchEvent
,在内部做相应的拦截,首先ACTION_DOWN
事件必须返回false,否则后续的ACTION_MOVE与ACTION_UP
事件会直接交由父容器处理,无法传递给子元素。如果父容器ACTION_UP
返回true,那么子元素的onclick
事件无法触发。 - 内部拦截:父容器不拦截事件,所有事件都交由子元素进行处理。
- 调用
requestDisallowInterceptTouchEvent方法
,当子元素调用parent.requestDisallowInterceptTouchEvent
并设置为false,父容器才能拦截所需的事件,否则为true交由子控件处理。
View的工作原理
ViewRoot
对应于ViewRootImpl
类,它是连接windowmanager和DecorView
的纽带,View的三大流程均是通过ViewRoot
来完成的,在ActivityThread
中,当activity
创建完毕后,会将DecorView
添加到window
中,同时创建viewrootImpl
对象,并将viewrootImpl
与DecorView
关联。View的绘制流程从viewRoot
的performTraversals
方法开始,经过三个过程将view绘制出来
onMeasure
View的measure
measure
过程决定了view
的宽、高,measure
完成后可以通过getMeasureWidth
来获取view的测量后的宽与高,在几乎所有情况都是这样,特殊情况,Layout决定view的四个顶点,可以通过getTop,getBottom
来获取view的四个顶点位置,通过getWidth
获取view的最终宽高,只有draw
方法完成后,view的内容才会显示在屏幕上
由源码可知,DecorView
其实就是一个FrameLayout,view层事件都先经过DecoreView,然后在传递给view.
MeasureSpec
是view的内部类,他封装了view的规格尺寸,包括view的宽高信息,代表一个32位int值,高2位代表测量模式,低30位代表测量大小。
测量模式有三种:
UNSPECIFIED
:父容器对view没有任何限制,view要多大有多大EXACTIY
:父容器已经检测出view所需的精确大小,对应match_parent
与具体数值AT_MOST
:父容器指定了一个可用大小的specsize,view
大小不能大于这个值,对应wrap_content
.
对于view其measurespec
由父容器的measurespec
与自身的LayoutParams
共同决定的。measurespec
一旦确定,就可以确定view的宽高。
如果父容器的measurespec
为wrap_content
,子元素的layoutparams
为wrap_content
与match_parent
显示效果一样,需要在layoutparams
为wrap_content
指定默认的宽与高即可.
ViewGroup的measure
ViewGroup
的measure过程:
- 对于
viewgroup
来说,除了完成自己的measure
,还需遍历所有子元素的measure
,和view不同,viewgroup是一个抽象类,没有重写view的onMeasure
方法,提供了measureChildren
方法。
在某些极端得情况下,在onMeasure
方法中拿到的宽与高可能不准确,在onLayout
中获取宽与高才是最终的宽与高。
无法获取view的宽高解决方法
在activity启动时,获取view的宽高,在activity的生命周期中无法准确获取宽高,无法保证view测量完毕,获取宽高只能是0.
- 可以在
onWindowFocusChanged
方法中获取,表示view已经初始化,onWindowFocusChanged
会被调用多次,在activity窗口得到与失去焦点时都会被调用,继续执行,暂停执行也会,频繁进行onPause与onResume
也会频繁调用。 - 通过
post
将一个runnable
投递到消息队列尾部 view.post(new Runnable(){ @Overribe public void run(){ int width = view.getMeasureWidth(); } }) - 使用
viewTreeObserver
众多回调可以获取宽高。使用onGlobalLayoutListener
接口,当view树状态发生改变或者view内部可见性发生改变,它回调。 - 手动对view进行
measure
得到view的宽高,比较复杂.
Draw过程
- 绘制背景
- 绘制自己
- 绘制childern
- 绘制装饰
自定义view须知
- 让view支持
wrap_content
,必须对wrap_content
做特殊处理,否则使用wrap_content
就相当于使用match_parent
- 让view支持
padding
,直接继承自viewgroup控件需要在onMeasure与onLayout
中考虑padding
与margin
对其造成的影响,不然导致padding
与子元素的margin
失效 - 避免在view中使用
handler
,使用post
替代 - view中有线程与动画需要及时停止,在
onDetachFromWindow
中,不及时处理,可能会造成内存泄漏 - view带有嵌套,需要处理好滑动冲突。
**直接继承view或viewgroup的控件,padding默认是不会生效的,需要自行处理。
Android View 的requestLayout、invalidate与postInvalidate
requestLayout
view的requestLayout()绘制方式:
从源码注释可以看出,如果当前View在请求布局的时候,View树正在进行布局流程的话,该请求会延迟到布局流程完成后或者绘制流程完成且下一次布局发现的时候再执行。
子View调用requestLayout
方法,会标记当前View及父容器,同时逐层向上提交,直到ViewRootImpl
处理该事件,ViewRootImpl
会调用三大流程,从measure开始,对于每一个含有标记位的view及其子View都会进行测量、布局、绘制。
invalidate
该方法的调用会引起View树的重绘,常用于内部调用(比如 setVisiblity()
)或者需要刷新界面的时候,需要在主线程(即UI线程)中调用该方法。那么我们来分析一下它的实现
当子View调用了invalidate方法后,会为该View添加一个标记位,同时不断向父容器请求刷新,父容器通过计算得出自身需要重绘的区域,直到传递到ViewRootImpl
中,最终触发performTraversals
方法,进行开始View树重绘流程(只绘制需要重绘的视图)。
postInvalidate
这个方法与invalidate方法的作用是一样的,都是使View树重绘,但两者的使用条件不同,postInvalidate
是在非UI线程中调用,invalidate
则是在UI线程中调用
一张图反映不同
总结
一般来说,如果View确定自身不再适合当前区域,比如说它的LayoutParams
发生了改变,需要父布局对其进行重新测量、布局、绘制这三个流程,往往使用requestLayout。而invalidate则是刷新当前View,使当前View进行重绘,不会进行测量、布局流程,因此如果View只需要重绘而不需要测量,布局的时候,使用invalidate
方法往往比requestLayout
方法更高效
理解RemoteViews
它表示view的结构,可以在其他进程显示,提供一组跨进程更新界面。在Android中主要用来:通知栏与桌面小部件
它无法直接访问里面的view,必须通过所提供的方法来更新view,比如textview的setTextView
方法。两个参数要设置的ID与提供的文本。
remoteviews
使用了AppWidgetProvider
类实现桌面小部件,本质是一个广播。
AppWidgetProvider 方法
onEnable
:当窗口小部件第一次添加到桌面时,调用这个方法,多次添加只会调用一次onUpdate
:当小部件添加或每次更新都会调用方法,设置updatePeriodMillis
指定更新周期onDelete
:每删除一次都会调用onDisabled
:最后一个该类型的桌面小部件被删除调用onReceive
:广播内置方法,分发具体事件
PendingIntent概述
表示一种待定,等待,即将发生的意思。而Intent是立刻发生
**flags
常见类型
FLAG_ONE_SHOT
:只能使用一次,它会自动cancel,后续有,那么send会调用失败FLAG_NO_CREATE
:当前描述的pendingintent
不会主动创建,如果当前的pendingintent
之前不存在,那么getActivity
方法直接返回falseFLAG_CANCEL_CURRENT
:如果当前的pendingintent
已经存在,那么他们都会被cancel,系统会创建一个新的pendingintent
FLAG_UPDATE_CURRENT
:如果pendingintent
已经存在,那么都会被更新。intent的Extras
会被替换最新
如果manager.notify(1,notification)
第一个参数是常量,那么就会弹一个通知,后续通知会把前面完全替换掉,如果每次都不同,多次调用notify就会弹出多个通知.
系统没有通过Binder
直接支持view的跨进程访问,而是提供了一个action概念,action代表一个view的操作,实现了Parcelable
,首先将view的操作封装到这个action中,然后将对象跨进程传输传到远程中,接着在远程进程中执行action操作。应用每调用一次set方法,remoteViews
就会添加一个对应的action,它的apply进行view的更新操作。remoteviews
的apply方法内部会遍历所有action对象,并调用他们的apply方法,进行view的更新操作。
AppWidgetProvider的updateAppwidget内部通过apply与reapply
加载更新界面
apply
:加载布局并更新界面,而reapply
只会更新界面。初始化会调用apply,后续调用reapply更新界面
remoteviews
中的setOnclickPendingIntent
只能给普通的view设置单击事件,不能给listview与stackview
设置单击事件。要给它们设置单击事件,必须将setPendingIntentTemplate与setOnclickFillInIntent
组合使用才行
Drawable
它表示一种图像的概念,在开发中,被当做view的背景使用
一张图片所形成的的drawable,它的内部宽高就是图片的宽高,但一个颜色形成的drawable没有宽高,drawable内部宽高不等同于它的大小,drawable实际区域大小可以通过他的getBounds
方法获取,一般与view的尺寸相同。它没有大小概念,当它被当做view的背景时,会被拉伸至view的同等大小。
BitmapDrawable
BitmapDrawable
:表示一张图片,通过xml方式描述它。
android:src
图片资源idandroid:antialias
抗锯齿android:dither
抖动效果 开启这个选项,让高质量图片在低质量的屏幕上还能保持较好的显示效果android:filther
开启过滤 当图片拉伸时,也能保持很好的显示效果android:mipMap
图像相关的处理技术 纹理映射 默认设置为falseandroid:tileMode
平铺模式disable
表示关闭平铺模式repeat
重复显示mirror
镜面显示clamp
图片四周元素会扩展到周围区域
ShapeDrawable
ShapeDrawable
通过颜色来构造图形,可以纯白,也可以渐变
android:shape
:表示图片形状rectangle oval(椭圆) ring 圆环
其中line
与ring
要通过stroke
标签指定颜色与宽度,否则无法显示corners
表示角度gradient
表示渐变,与solid
冲突solid
纯色填充stroke shape
的描边padding
表示不是shape
的空白,而是包含它的view的空白size
标签设置的宽高就是shapedrawable
的固有宽高size
大小android:width
整型 宽度android:height
整型 高度
LayerDrawable
LayerDrawable
表示一种层次化的drawable xml
标签<layer-list>
,将不同的drawable
放置在不同的层上面达到一种叠加的效果
StateListDrawable
StateListDrawable
:对应selector
标签,表示Drawable集合,每个drawable对应一个状态。
android:constantSize
:表示StateLIstDrawable
固定大小是否不随着状态改变而改变,fasle表示改变**android:variablePadding:
表示它的padding
是否随着状态改变而改变,true表示改变**
view的常见状态
android:state_pressed
表示按下android:state_focused
表示获取焦点android:state_selected
表示用户选择了view
系统会根据view的当前状态从selector
中选择对应的item,每个item对应一种drawable,从上往下查找,直至查找第一条匹配的item,将默认的item放在最后,不带任何状态。
LevelListDrawable
LevelListDrawable
:表示drawable集合,集合中每个drawable都有一个等级,最小等级0 最大等级10000 如果被当做Imageview
,可以调用setImageLevel
来切换。 等级范围 由maxLevel与minlevel
TransitionDrawable
TransitionDrawable
:对应transition
标签,实现两个drawable之间淡入淡出效果
- 通过
startTransition
与reverseTransition
实现淡入淡出已经逆过程
InsertDrawable
InsertDrawable
:对应insert
标签,将其他drawable内嵌到自己中,当一个view希望自己的背景比自己实际区域小时,可以用这个样式。LayerDrawable
也可以实现。
ScaleDrawable
ScaleDrawable
:对应标签<scale>
,根据等级来指定缩放比例
- 等级为0表示不可见,如果等级为10000,那么就没有缩放效果。
- 如果
scaledrawable
等级越大,那么内部drawable就看起来越大 - 如果
scaledrawable xml
定义的缩放比例越大,那么内部drawable就看起来越小
ClipDrawable
ClipDrawable
:对应<clip>
标签,根据当前等级来裁剪另一个drawable
clipOrientation
表示裁剪方向,水平与竖直,gravity
需要与clipOrientation
一起才能有作用。等级为0表示裁剪全部区域,等级为10000表示不裁剪。
Drawable 类中的几个重要的方法
Drawable 类有四个抽象方法子类必须实现:
代码语言:javascript复制public abstract void draw(Canvas canvas);
public abstract void setAlpha(@IntRange(from=0,to=255) int alpha);
public abstract void setColorFilter(ColorFilter colorFilter);
public abstract @PixelFormat.Opacity int getOpacity();
- draw
:在 setBounds
方法设置的区域的 Canvas 中进行Drawable 的绘制,要绘制状态效果的话,可以由 setAlpha,setColorFilter
等方法控制;
setAlpha
:给 Drawable 指定一个 alpha 值,在 0 - 255 之间;setColorFilter
:设置滤镜效果,有时我们会在 Drawable 内部定义一个 Paint 对象,所以该方法的实现可以为mPaint.setColorFilter(colorFilter)
;getOpacity
:返回 Drawable 的透明度,取值为PixelFormat.UNKNOWN,PixelFormat.TRANSLUCENT,PixelFormat.TRANSPARENT,PixelFormat.OPAQUE
中的一个; public void setBounds(int left, int top, int right, int bottom); public void setBounds(@NonNull Rect bounds); protected void onBoundsChange(Rect bounds)setBounds
设置绘制区域矩形边界,draw 方法调用时会用到其设置的值,不设置默认边界均为 0,所以自定义 Drawable 时要重写该方法onBoundsChange setBounds
方法中新旧 bounds 发生变化时回调,默认为空方法; public int getIntrinsicWidth(); public int getIntrinsicHeight();- 获取 Drawable 的内部宽高,包含 padding,一张图片形成的 Drawable 内部宽高就是图片的宽高,不同的 Drawable 子类是有不同的实现的,而一个颜色所形成的 Drawable 就没有内部宽高的概念,在用作 View 的 background 时自动拉伸为 View 大小。 public int getMinimumWidth() public int getMinimumHeight()
- 返回 Drawable 建议的最小宽高,View 用作背景时要大于该最小宽高,默认的返回为内部宽高或 0;
动画深入浅析
android:interpolator
:插值器,会影响动画速度。android:shareInterpolator
:集合中的动画是否和集合共享一个插值器android:fillAfter
:表示动画结束后,是否停留在结束为止,true表示停留
旋转动画放在位移动画之前,否则位移动画无法执行,组合动画执行顺序最好按照,缩放、位移、旋转与透明。
自定义view的方法并在需要的时候参考矩阵的变换细节,就可以写出特定的自定义view动画
帧动画使用简单,但较容易引起OOM,所以尽量避免使用过多尺寸较大的图片。
view的特殊使用场景:
LayoutAnimation
:作用于viewgroup,为viewgroup指定一个动画
属性:
android:delay
设置动画时间延迟android:animationOrder
:动画顺序:normal ,reverse
表示排在后面先开始,逆向,random
随机动画**
android:animation
具体的入场动画
Activity切换效果
overridePendingTransition(int enterAnim,int exitAnim)
enterAnim
被打开,所需动画资源exitAnim activity
被暂停,动画资源
属性动画要求动画作用的对象提供get和set方法,以动画效果多次调用set方法,每次传递set值不一样,随着时间推移,传递的值越接近于最终值。
- object必须提供set方法,如果没有传递值,还需提供get方法,让系统取abc属性初始值(这条件不满足,直接崩溃)
- set对abc的改变必须通过某种方式反映出来,比如UI(这条件不满足,动画无效果,不会崩溃)
针对上述问题:
- 给对象增加get与set方法,系统sdk无权限,不可行
- 用类包装原始对象,间接提供get与set
- 采用valueAnimator,监听动画过程,自己实现属性改变
使用动画注意事项
- OOM,避免使用帧动画,图片过多就会出现
- 内存泄漏,属性动画无限循坏时,需要在activity退出时及时停止
- 兼容性问题,3.0以下有兼容问题
- view动画问题,view动画是对view的影像做动画,不是真正改变view状态,有时会出现无法隐藏,调用
view.clearAnimation
清除动画 - 不使用px,否则在不同的设备出现不同的效果
- 动画元素交互,3.0系统之前,新位置无法触发单击事件,3.0之后,单击触发为移动后的位置,但是view的动画仍在原位置
- 开启硬件加速,提高动画流畅性
<activity android:name=".view.activity.LeadActivity" android:hardwareAccelerated="true" android:configChanges="keyboardHidden|orientation|screenSize" android:theme="@style/MyTheme"> </activity>
View.isHardwareAccelerated()
如果返回的是true,表示使用了硬件加速Canvas.isHardwareAccelerated()
,如果返回true表示这个图层开启了硬件加速