Android基础总结(2017.11.01)

转载请注明出处


synchronized函数和synchronized代码块的区别


    1. 首先synchronized函数和synchronized代码快的作用范围有区别,synchronized函数一般锁定的是当前类对象,synchronized代码块锁定作用域可以选择是本对象,也可以是字符串等等.
    1. 当前类对象锁没有释放的时候,本类的所有synchronized(this)同步代码块都阻塞。如果有并发请求synchronized函数,同一时间只能有一个请求执行 .
    1. 但是当前类对象锁没有释放的时候,其他请求可以访问本类中不带synchronized(this)的代码块,也可以访问非同一把锁的代码块例如synchronized(Str)等.
    1. 由于作用范围有区别,一般作用范围越小执行效率越高,平时开发中一般选择作用范围较小的synchronized.

如何判断一个对象是可以被回收的


    1. 之前java虚拟机使用引用计数器的算法,当引用计数器为0时代表该对象没有引用了然后被清理。但是这个方式很难解决循环引用问题,所以目前停止使用了。
    1. 目前用到的是可达性分析算法来确定一个对象是不是可以被回收。
    1. 原理是:通过一个叫GC Roots的对象当作根对象,然后开始向下搜索,搜索的路径叫做引用链,当对象到GC Roots没有任何引用链相连的时候,则证明此对象是不可用的.
    1. 不可用对象并不是马上就执行回收方法,执行清理方法之前至少要经历两次标记过程.
  • ①如果对象在进行可达性分析后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。(即意味着直接回收).
  • ②如果这个对象被判定为有必要执行finalize()方法,那么这个对象将会放置在一个叫做F-Queue的队列之中,并在稍后由一个由虚拟机自动建立的、低优先级的Finalizer线程去执行它。这里所谓的“执行”是指虚拟机会触发这个方法,但并不承诺会等待它运行结束,这样做的原因是,如果一个对象在finalize()方法中执行缓慢,或者发生了死循环(更极端的情况),将很可能会导致F-Queue队列中其他对象永久处于等待,甚至导致整个内存回收系统崩溃.
    1. finalize()方法是对象回收前的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中不被回收,只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。
    1. 任何一个对象的finalize()方法都只会被系统自动调用一次,如果对象面临下一次回收,它的finalize()方法不会被再次执行,因此第二段代码的自救行动失败了。因为finalize()方法已经被虚拟机调用过,虚拟机都视为“没有必要执行”。(即意味着直接回收).

写一个函数,输入一个数如38,拆分 3 + 8 = 11,1 + 1 = 2,最后2无法拆分就返回


    public  int  getNum(int num) {
        while (num >= 10) {
            num = num / 10 + num % 10;
        }
        return num;
    }

多个进程同时调用一个ContentProvider的query获取数据,ContentPrvoider是如何反应的呢?


  • 分析:
    我们知道Activity这样的组件,它生命周期的回调函数是在UI线程中执行的,ContentProvider的onCreate()方法也是在UI线程中运行的,回答这个问题前,我们首先要搞清楚ContentProvider的Query(),insert(),delete(),updata()这几个方法是否也是在UI线程中运行。
  • 发现问题:
    如果以上几个方法是在UI线程中运行的,那么多个线程并发去调用就很有可能出现ANR;如果不是在UI线程运行的,那它是在一个工作线程中运行的还是在多个线程中运行的呢?即ContentProvider是否支持并发操作呢?
  • 分析问题:
    ContentResolver与ContentProvider类隐藏了实现细节,但是ContentProvider所提供的Query(),insert(),delete(),updata()这几个方法都是在ContentProvider进行的线程池中运行的,而不是在进程的主线程中运行,以为这些方法有可能被多个地方调用,所以它们是线程安全的。
    ContentProvider实现进程通信是依赖于Binder机制的,所以以上问题会回归到Binder线程处理问题,并不是每一个ContentProvider都会有一个线程池,而是一个进程共用一个线程池,共用的线程池就是Binder线程池。
  • 标准答案:
    一个content provider可以接受来自另外一个进程的数据请求。尽管ContentResolver与ContentProvider类隐藏了实现细节,但是ContentProvider所提供的query(),insert(),delete(),update()都是在ContentProvider进程的线程池中被调用执行的,而不是进程的主线程中。这个线程池是有Binder创建和维护的,其实使用的就是每个应用进程中的Binder线程池。

Android设计ContentProvider的目的是什么?


    1. 隐藏数据的实现方式,对外提供统一的数据访问接口;
    1. 更好的数据访问权限管理。ContentProvider可以对开发的数据进行权限设置,不同的URI可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider的具体操作。
    1. ContentProvider封装了跨进程共享的逻辑,我们只需要Uri即可访问数据。由系统来管理ContentProvider的创建、生命周期及访问的线程分配,简化我们在应用间共享数据(进程间通信)的方式。我们只管通过ContentResolver访问ContentProvider所提示的数据接口,而不需要担心它所在进程是启动还是未启动。

运行在主线程的ContentProvider为什么不会影响主线程的UI操作?


    1. ContentProvider的onCreate()是运行在UI线程的,而query(),insert(),delete(),update()是运行在线程池中的工作线程的,所以调用这向个方法并不会阻塞ContentProvider所在进程的主线程,但可能会阻塞调用者所在的进程的UI线程!
    1. 所以,调用ContentProvider的操作仍然要放在子线程中去做。虽然直接的CRUD的操作是在工作线程的,但系统会让你的调用线程等待这个异步的操作完成,你才可以继续线程之前的工作。

请详细叙述Android事件分发机制:


这道题是很多家面试公司会问到的一道经典面试题,但又经常被面试者忽略。

看了很多博客也看了很多代码,大部分都是长篇大论,不利于阅读固总结如下:

主线传递只有三步:Activity->ViewGroup->View

Activity和View只有两个方法控制事件传递:dispatchTouchEvent(),onTouchEvent ();
ViewGroup有三个方法控制传递:dispatchTouchEvent(),onInterceptTouchEvent(),onTouchEvent ();

接下来用一张图给大家叙述下具体是怎么一步一步分发的。


总结:
1.对于 dispatchTouchEvent,onTouchEvent,return true是终结事件传递。return false 是回溯到父View的onTouchEvent方法。
2.ViewGroup 想把自己分发给自己的onTouchEvent,需要拦截器onInterceptTouchEvent方法return true 把事件拦截下来。
3.ViewGroup 的拦截器onInterceptTouchEvent 默认是不拦截的,所以return super.onInterceptTouchEvent()=return false;
4.View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent。

ViewGroup和View 的dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标
注:------> 后面代表事件目标需要怎么做。
1、 自己消费,终结传递。------->return true
2、 给自己的onTouchEvent处理-------> 调用super.dispatchTouchEvent()系统默认会去调用 onInterceptTouchEvent,在onInterceptTouchEvent return true就会去把事件分给自己的onTouchEvent处理。
3、 传给子View------>调用super.dispatchTouchEvent()默认实现会去调用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就会把事件传给子类。
4、 不传给子View,事件终止往下传递,事件开始回溯,从父View的onTouchEvent开始事件从下到上回归执行每个控件的onTouchEvent------->return false
注: 由于View没有子View所以不需要onInterceptTouchEvent 来控件是否把事件传递给子View还是拦截,所以View的事件分发调用super.dispatchTouchEvent()的时候默认把事件传给自己的onTouchEvent处理(相当于拦截),对比ViewGroup的dispatchTouchEvent 事件分发,View的事件分发只有dispatchTouchEvent()和onTouchEvent()不需要onInterceptTouchEvent()参与。

到此事件分发总结完毕。如果想详细了解事件分发机制的请看这篇博客:
http://blog.csdn.net/w525721508/article/details/78227154


View的渲染过程,或者叫View的绘制流程


这道题也是比较老的一道题了,但是无论BAT还是小创业公司中出现的频率相当高
接下来就总结性的叙述一遍View绘制流程,避免长篇大论,接下来的描述一切从简
希望各位读者耐心看完,相信你会有很大的收获!
View绘图流程是在ViewRoot.java类的performTraversals()函数中展开的
绘制部分一共需要三步:

measure() -> layout() -> draw();

1. 判读是否重新计算视图大小(measure)

这里写图片描述

原理:从顶层父View像子View递归调用view.measure(),measure方法中回调onMeasure()
MeasureSpec是View的测量内部类,测量规格为int型,值由高2位规格模式specMode和低30位的具体尺寸specSize组成。

specMode有三种值

MeasureSpec.UPSPECIFIED : 父容器对于子容器没有任何限制,子容器想要多大就多大
MeasureSpec.EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。
MeasureSpec.AT_MOST:子容器可以是声明大小内的任意大小

  • View的measure方法是final,不可以重载,只能重载inMeasure完成自己的测量逻辑

  • 顶层的DecorView的MeasureSpec是由ViewRootImpl中的getRootMeasureSpec方法确定(LayoutParams宽高参数均为MATCH_PARENT,specMode是EXACTLY,specSize为物理屏幕大小)。

  • ViewGroup类提供了measureChild,measureChild和measureChildWithMargins方法,简化了父子View的尺寸计算。

  • 只要是ViewGroup的子类就必须要求LayoutParams继承子MarginLayoutParams,否则无法使用layout_margin参数。

  • View的布局大小由父View和子View共同决定。

  • 使用View的getMeasuredWidth()和getMeasuredHeight()方法来获取View测量的宽高,必须保证这两个方法在onMeasure流程之后被调用才能返回有效值。

2. 是否重新分配视图的位置(layout)

这里写图片描述

原理: layout也是从顶层父View向子View的递归调用View.layout方法的过程,父View根据上一步measure子View得到的布局大小和布局参数,将子View放在合适的位置上。

  • View.layout方法可以被重载,ViewGroup.layout为final不可以被重载,ViewGroup.onLayout为abstract的子类必须重载实现自己的位置逻辑

  • measure结束后得到的是每个View经测量后的measuredWidth和measuredHeight,Layout操作完以后得到的是每个View进行位置分配后的mLeft,mTop、mRight、mBottom,这些值都是相对父View

  • 凡是layout_XXX的布局属性都是针对父级View的,如果View没有父级容器则layout_XXX属性是没有任何意义的

  • 使用View 的getWidth()和getHright()方法获取View测量的宽高必须保证这两个方法在在onLayout流程之后。

3. 是否重新绘制(draw)

这里写图片描述

原理: draw过程也是在ViewRootImpl的performTraversals()内部调运的,其调用顺序在measure()和layout()之后,这里的mView对于Actiity来说就是PhoneWindow.DecorView,ViewRootImpl中的代码会创建一个Canvas对象,然后调用View的draw()方法来执行具体的绘制工。所以又回归到了ViewGroup与View的树状递归draw过程

  • 如果该View是一个ViewGroup,则需要递归绘制其所包含的所有子View。

  • View默认不绘制任何内容,真正的绘制都在自己的子类中实现

  • View的绘制是借助onDraw()方法传入的Canvas类来进行的

  • 区分View 动画和ViewGroup动画,前者是View自身的动画可以通过setAnimation添加,后者可以通过xml布局的layoutAnimation属性添加

  • 在获取画布剪切区(每个View的draw中传入的Canvas)时会自动处理掉padding,子View获取Canvas不用关注这些逻辑,只关心如何绘制即可

  • 默认情况下子View的ViewGroup.drawChild绘制顺序和子View被添加的顺序一致,但是你也可以重载ViewGroup.getChildDrawingOrder()以提供不同的顺序

4. invalidate()

原理: invalidate方法请求重绘View树(也就是draw方法),如果View大小没有发生变化就不会调用layout过程,并且只绘制那些“需要重绘的”View,也就是哪个View(View只绘制该View,ViewGroup绘制整个ViewGroup)请求invalidate系列方法,就绘制该View。

  • 直接调用invalidate方法.请求重新draw,但只会绘制调用者本身。

  • 触发setSelection方法。请求重新draw,但只会绘制调用者本身。

  • 触发setVisibility方法。 当View可视状态在INVISIBLE转换VISIBLE时会间接调用invalidate方法,继而绘制该View。当View的可视状态在INVISIBLE\VISIBLE 转换为GONE状态时会间接调用requestLayout和invalidate方法,同时由于View树大小发生了变化,所以会请求measure过程以及draw过程,同样只绘制需要“重新绘制”的视图。

  • 触发setEnabled方法。请求重新draw,但不会重新绘制任何View包括该调用者本身。

  • 触发requestFocus方法。请求View树的draw过程,只绘制“需要重绘”的View。

例: 当我们写一个Activity时,我们一定会通过setContentView方法将我们要展示的界面传入该方法,该方法会讲我们界面通过addView追加到id为content的一个FrameLayout(ViewGroup)中,然后addView方法中通过调运invalidate(true)去通知触发ViewRootImpl类的performTraversals()方法,至此递归绘制我们自定义的所有布局。

5.requestLayout()

原理: View的requestLayout时其实质就是层层向上传递,直到ViewRootImpl为止,然后触发ViewRootImpl的requestLayout方法
requestLayout()方法会调用measure过程和layout过程,不会调用draw过程,也不会重新绘制任何View包括该调用者本身。

以上为View渲染的整体过程,如有问题欢迎指正。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容