《Android秘籍.第三卷》
目录
MotionEvent是什么?包含几种事件?什么条件下会产生?
onTouch()、onTouchEvent()和onClick()关系?
invalidate()和postInvalidate()的区别?
View动画为何不能真正改变View的位置?而属性动画为何可以?
mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和dpi存在怎样的关系?
《数据存储篇》
Android中提供哪些数据持久存储的方法。
Java中的I/O流(输入输出流)。
- Java中的流分为两种,一种是字节流,另一种是字符流,分别由四个抽象类来表示(每种流包括输入和输出两种所以一共四个):所有输入流类都是抽象类InputStream(字节输入流)或抽象类Reader(字符输入流)的子类;而所有输出流都是抽象类OutputStream(字节输出流)或抽象类Writer(字符输出流)的子类。
SharePreferences适用情形?使用中需要注意什么?
- 保存登录用户名密码等情形,应注意多进程并发读的时候数据可能不准确。
- 需要注意:getSharedPreferences(“User”, Context.MODE_PRIVATE)方法中第二个参数需要了解Android的四种枚举方式下面是详细的解释:
- 私有模式
- Context.MODE_PRIVATE 的值是 0;
- ①只能被创建这个文件的当前应用访问
- ②若文件不存在会创建文件;若创建的文件已存在则会覆盖掉原来的文件
- 追加模式
- Context.MODE_APPEND 的值是 32768;
- ①只能被创建这个文件的当前应用访问
- ②若文件不存在会创建文件;若文件存在则在文件的末尾进行追加内容
- 可读模式
- Context.MODE_WORLD_READABLE的值是1;
- ①创建出来的文件可以被其他应用所读取
- 可写模式
- Context.MODE_WORLD_WRITEABLE的值是2
- ①允许其他应用对其进行写入。
了解SQLite中的事务处理吗?是如何做的?
- 事务(Transactioin)是一个对数据库执行工作单元。事务(Transction)是以逻辑顺序完成的工作单位或序列,可以是由用户手动操作完成,也可以是由某种数据库程序自动完成。
- 事务控制
- ·BEGIN TRANSACTION:开始事务处理。
- ·COMMIT:保存更改,或者可以使用END TRANSACTION命令;
- ·ROLLBACK:回滚所做的更改。
- 事务控制命令只与DML命令INSERT、UPDATE和DELETE一起使用。它们不能在创建表或删除表时使用,因为这些操作在数据库中是自动提交的
- 使用SQLiteDatabase的beginTransaction()方法可以开启一个事务,程序执行到endTransaction() 方法时会检查事务的标志是否为成功,如果程序执行到endTransaction()之前调用了setTransactionSuccessful() 方法设置事务的标志为成功则提交事务,如果没有调用setTransactionSuccessful() 方法则回滚事务。多用于大量数据操作时,能明显减少耗时。
使用SQLite做批量操作有什么好的方法吗?
即使用事务处理进行优化,默认SQLite的数据库插入操作,如果没有采用事务的话,它每次写入提交,就会触发一次事务操作,而这样几千条的数据,就会触发几千个事务的操作,这就是时间耗费的根源
如果现在要删除SQLite中表的一个字段如何做?
SQLite目前还不支持drop column,所以必须想出另外一种方法来进行表字段的删除。
如下sql语句会复制一个和record表一样表结构的temp表出来,但是我们想要的是去除某一个字段(例如去除record表中的name字段,就不要复制它就好了),所以sql语句如下:
create table temp as select recordId, customer, place, time from record where 1 = 1;
这样复制出来的表就会缺少“name”字段,然后我们删除旧表并修改新表名即可。
使用SQLite时会有哪些优化操作?
- 编译SQL语句(重用SQLiteStatement)
- 显示使用事务(降低批量数据库更新文件打开关闭次数)
- 简历索引(快速找到表中对应记录)
- 查询数据优化
- ContentValues的容量调整
- 及时关闭Cursor
- 耗时异步化
《View篇》
MotionEvent是什么?包含几种事件?什么条件下会产生?
- MotionEvent是事件传递中一个重要的概念(事件类型)。
- MotionEvent.ACTION_DOWN:当屏幕检测到第一个触点按下之后就会触发到这个事件。
- MotionEvent.ACTION_MOVE:当触点在屏幕上移动时触发,触点在屏幕上停留也是会触发的,主要是由于它的灵敏度很高,而我们的手指又不可能完全静止(即使我们感觉不到移动,但其实我们的手指也在不停地抖动)。
- MotionEvent.ACTION_POINTER_DOWN:当屏幕上已经有触点处于按下的状态的时候,再有新的触点被按下时触发。
- MotionEvent.ACTION_POINTER_UP:当屏幕上有多个点被按住,松开其中一个点时触发(即非最后一个点被放开时)触发。
- MotionEvent.ACTION_UP:当触点松开时被触发。
- MotionEvent.ACTION_OUTSIDE: 表示用户触碰超出了正常的UI边界.
- MotionEvent.ACTION_SCROLL:android3.1引入,非触摸滚动,主要是由鼠标、滚轮、轨迹球触发。
- MotionEvent.ACTION_CANCEL:不是由用户直接触发,由系统在需要的时候触发,例如当父view通过使函数onInterceptTouchEvent()返回true,从子view拿回处理事件的控制权时,就会给子view发一个ACTION_CANCEL事件,子view就再也不会收到后续事件了
scrollTo()和scrollBy()的区别?
- scrollBy()也调用了scrollTo()方法,实现了基于当前位置的滑动,而scrollTo()是基于所传参数的绝对滑动
Scroller中最重要的两个方法是什么?主要目的是?
- Scroller方法一共可以分三步:最重要是后两个方法
- 创建一个Scroller对象,一般在View的构造器中创建:
- 重写View的computeScroll()方法
- 调用startScroll()方法,startX和startY为开始滚动的坐标点,dx和dy为对应的偏移量:
- startScroll()设置好了一堆初始值,之后调用了invalidate()让View重新绘制,这里又有一个很重要的点,在draw()中会调用computeScroll()这个方法!
谈一谈View的事件分发机制?
- Android事件分发机制主要由“事件分发”—>“事件拦截”—>“事件响应”这三步来进行逻辑控制的
ViewGroup:
- 事件开始>dispatchTouchEvent问自己是否需要拦截此事件?
- true:拦截事件,直接交给自己的onTouchEvent方法去处理事件,事件终止
- false:不拦截,交给子控件去处理.
View:
- 传递到view控件:View下没有子控件
- true:当前view处理该事件,事件终止
- false:不处理当前事件,向上回传(传到上层的onTouchEvent)
- 如果最高层的onTouchEvent收到了子控件回传的事件,返回true则处理该事件,返回false,则不处理事件,事件丢失
- 事件分发:public boolean dispatchTouchEvent(MotionEvent ev):当监听到有触发事件时,首先由Activity进行捕获,然后事件就进入事件分发的流程。Activity本身没有事件拦截,从而将事件传递给最外层的View的dispatchTouchEvent(MotionEvent ev)方法,该方法将对事件进行分发。return true : View消费所有事件。return false :停止分发,交由上层控件的onTouchEvent方法进行消费,如果本层控件是Activity,那么事件将被系统消费、处理。super.dispatchTouchEvent(ev): 将事件交由本层的事件拦截onInterceptTouchEvent方法处理。
- 事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev):return true: 对事件拦截,交由本层的onTouchEvent进行处理,return false: 不拦截,分发到子View,由子View的dispatchTouchEvent方法处理。super.onInterceptTouchEvent(ev):默认表示事件拦截,交由本层的onTouchEvent进行处理。
- 事件响应:public boolean onTouchEvent(MotionEvent ev):return true: 表示onTouchEvent处理完事件后消费了此次事件。return false: 不响应事件,不断的传递给上层的onTouchEvent方法处理,直到某个View的onTouchEvent返回true,则认为该事件被消费。如果到最顶层View还是返回false,那么该事件不消费,将交由Activity的onTouchEvent进行处理。return: super.onTouchEvent,不响应事件,结果与return返回false一样。
如何解决View的滑动冲突?
外部拦截:可以在父布局中将onInterceptTouchEvent的返回值设为true进行拦截
内部拦截:类似反射的方式进行拦截,稍显复杂。需要配合requestDisallowInterceptTouchEvent进行拦截
谈一谈View的工作原理?
View的工作原理不知道该从何说起,可以简单讲下View的三个过程,几个常见回调方法,以及两个重要概念。
三个过程:
- measure:测量View的宽和高
- layout:确定View在父控件中的放置位置
- draw:负责将View绘制在屏幕上。
常见回调方法:
- 构造方法
- onAttachToWindow:在包含View的Activity启动时调用
- onDetachFromWindow:在包含View的Activity退出或者View被remove时回调
- onVisibilityChanged:当View的可见状态发生改变时调用
两个重要概念:
- ViewRoot:连接WindowManager(外界访问Window的入口)和DecorView(顶级View)的纽带,View的三大流程均是通过ViewRoot来完成的。
- DecorView:顶级View
View的工作流程
View的工作流程主要是指measure、layout、draw这三大流程,即测量、布局、绘制,其中measure确定View的测量宽/高,layout确定View的最终宽/高和四个顶点的位置,而draw则将View绘制到屏幕上。
measure过程
measure过程要分情况来看,如果是一个原始的View,那么通过measure方法就完成了其测量过程,如果是一个ViewGroup,除了完成自己的测量过程外,还会遍历去调用所有子元素的measure方法,各个子元素再递归去执行这个流程。
Layout过程
Layout的作用是ViewGroup用来确定子元素的位置,当ViewGroup的位置被确定后,它在onLayout中会遍历所有的子元素并调用其layout方法,在layout方法中onLayout方法又会被调用。
draw过程
draw过程就比较简单了,它的作用是将View绘制到屏幕上面,View的绘制过程循序以下几步:
- 绘制View的背景
- 如果有必要的话,保存这个canvas画布,为该层边缘的fading效果作准备
- 绘制View的内容
- 绘制子View
- 绘制View边缘的渐变褪色效果,类似于阴影效果
- 绘制View的装饰物
MeasureSpec(测量规格)是什么?有什么作用?
- MeasureSpec封装了父布局传递给子布局的布局要求,每个MeasureSpec代表了一组宽度和高度的要求 MeasureSpec由size和mode组成。
-
它有三种模式:
- MeasureSpec.UNSPECIFIED(未指定(未指定尺寸)),父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小;
- MeasureSpec.EXACTLY(完全(精确尺寸)),父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小;
- .MeasureSpecAT_MOST(至多(最大尺寸)),子元素至多达到指定大小的值。
- MeasureSpecs使用了二进制去减少对象的分配。
自定义View/ViewGroup需要注意什么?
- 让View支持wrap_content
- 如果有必要 支持padding
- 尽量不要在view中使用handler 因为view提供了post方法
- view中有线程或者动画 要及时停止
- 有嵌套滑动效果时 注意处理滑动冲突
onTouch()、onTouchEvent()和onClick()关系?
- 首先了解下三者的顺序为:onTouch—–>onTouchEvent—>onclick
- onTouch方法优先级比onTouchEvent高,会先触发。假如onTouch方法返回false会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。
- onclick事件的实现等等都基于onTouchEvent,假如onTouch返回true,这些事件将不会被触发。
SurfaceView和View的区别?
- view在UI线程去更新自己;而SurfaceView则在一个子线程中去更新自己
- surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面
- 在UI的主线程中更新动画,时间一旦太长就会出现问题
- surfaceView 在新的线程中更新画面所以不会阻塞你的UI主线程,但是涉及到线程同步,需要surfaceView中 thread处理,一般就需要有一个event queue的设计来保存touch event
invalidate()和postInvalidate()的区别?
- invalidate()用于在主线程中更新UI,postInvalidate()用与在子线程中更新UI,其内部也是使用了handler。
《Bitmap篇》
加载图片的时候需要注意什么?
- 尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,
- 因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存。
- 因此,改用先通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source,
- decodeStream最大的秘密在于其直接调用JNI>>nativeDecodeAsset()来完成decode,
- 无需再使用java层的createBitmap,从而节省了java层的空间。
LRU算法的原理?
- LRU全称是Least Recently Used,即最近最久未使用的意思。
- LRU算法的设计原则是:如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。也就是说,当限定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。也是利用了程序的局部性原理。
Android中缓存更新策略?
- 目前常用的一种缓存算法是Least Recently Used,简称:LRU,LRU是近期最少使用算法,它的核心机制是当缓存控件满时,会优先淘汰那些近期最少使用的缓存对象。采用LRU算法的缓存有:LruCache以及DiskLruCache,LruCache用于实现内存缓存,DiskLruCache用于实现存储设备缓存,因此通过这二者的结合使用,就可以很方便地实现一个高效的ImageLoader。
《Animation篇》
Android中有哪几种类型的动画?
- Drawable Animation也就是所谓的帧动画,Frame动画。指通过指定每一帧的图片和播放时间,有序的进行播放而形成动画效果。
- View Animation视图动画(Tween动画,补间动画)。指通过指定View的初始状态、变化时间、方式,通过一系列的算法去进行图形变换,从而形成动画效果,主要有Alpha、Scale、Translate、Rotate四种效果。注意:只是在视图层实现了动画效果,并没有真正改变View的属性。
- Property Animation属性动画,通过不断的改变View的属性,不断的重绘而形成动画效果。相比于视图动画,View的属性是真正改变了。注意:Android 3.0(API 11)以上才支持。
帧动画在使用时需要注意什么?
帧动画使用比较简单,但比较容易引起OOM(或者内存占用过大),因此在使用帧动画时要尽量避免使用过多尺寸过大的图片。
View动画和属性动画的区别?
View动画不能真正改变View的属性,而属性动画可以。
View动画为何不能真正改变View的位置?而属性动画为何可以?
属性动画改变的是对象的属性,比如真正的X,Y坐标属性,而不仅仅是view。
属性动画插值器和估值器的作用?
时间插值器(TimeInterpolator)的作用是根据时间流逝的百分比计算出动画进度的百分比。
类型估值器(TypeEvaluator)的作用是根据属性值改变的百分比计算出改变后的属性值。
《Drawable等资源篇》
了解哪些Drawable?适用场景?
- ColorDrawable 颜色可绘制类实现简单的单颜色的绘制。
- BitmapDrawable 位图可绘制类位图可绘制类
- ClipDrawable 裁剪可绘制类
- AnimationDrawable 帧动画可绘制类
- InsetDrawable缩进可绘制类
- ScaleDrawable 缩放可绘制类
- RotateDrawable 旋转可绘制类
- ShapeDrawable 形状可绘制类。
- PaintDrawable 圆角矩形可绘制类。
- LayerDrawable 图层可绘制类。
- TransitionDrawable 淡入淡出可绘制类
- LevelListDrawable 等级显示可绘制类。
- StateListDrawable 不同状态下显示可绘制类
- NinePatchDrawable .9格式的可绘制类
- GradientDrawable 渐变的可绘制类。
- PictureDrawable 图像可绘制类。
mipmap系列中xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi和dpi存在怎样的关系?
xxxhdpi、xxhdpi、xhdpi、hdpi、mdpi对应不同的dpi(像素密度)范围。
dp、dpi、px的区别?
- 屏幕分辨率:手机在横向、纵向上的像素点数总和,单位:px(pixel),1px=1像素点
- 屏幕像素密度:每英寸的像素点数,单位:dpi(dots per ich)
- 密度无关像素:density-independent pixel,叫dp或dip,与终端上的实际物理像素点无关,单位:dp,可以保证在不同屏幕像素密度的设备上显示相同的效果(在Android中,规定以160dpi(即屏幕分辨率为320x480)为基准:1dp=1px,假如同样都是画一条长度是屏幕一半的线,如果使用px作为计量单位,那么在480x800分辨率手机上设置应为240px;在320x480的手机上应设置为160px,二者设置就不同了;如果使用dp为单位,在这两种分辨率下,160dp都显示为屏幕一半的长度)
res目录和assets目录的区别?
-
res会在R.java生成索引ID,在打包的时候判断资源有没有用到,没用到的时候不会被打包进apk中(res/raw文件夹除外),而assets不会。
-
res用getResource()访问,assets用AssetsManager访问。
-
res/raw与assets里的文件在打包的时候都不会被系统二进制编译,都被原封不动打包进APK,通常用来存放游戏资源、脚本、字体文件等。但res/raw不可以创建子文件夹,而assets可以。
-
res/xml会被编译成二进制文件。res/anim存放动画资源。