2018正版葡京赌侠诗那可能是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的onTouch伊夫(Eve)nt方法。
2.ViewGroup
想把自己分发给自己的onTouch伊芙(Eve)nt,必要拦截器onInterceptTouch伊夫(Eve)nt方法return
true 把事件拦截下来。
3.ViewGroup 的拦截器onInterceptTouch伊夫nt 默许是不阻碍的,所以return
super.onInterceptTouch伊夫nt()=return false;
4.View
没有拦截器,为了让View能够把事件分发给协调的onTouch伊夫(Eve)nt,View的dispatchTouch伊夫nt默许落成(super)就是把事件分发给协调的onTouch伊芙(Eve)nt。

ViewGroup和View 的dispatchTouch伊芙nt
是做事件分发,那么这一个事件或者分发出去的几个对象
注:——> 后边代表事件目标需求怎么办。
1、 自己花费,终结传递。——->return true
2、 给自己的onTouch伊夫nt处理——->
调用super.dispatchTouch伊芙nt()系统默许会去调用
onInterceptTouch伊夫nt,在onInterceptTouch伊芙(Eve)nt return
true就会去把事件分给自己的onTouch伊夫(Eve)nt处理。
3、 传给子View——>调用super.dispatchTouch伊芙nt()默许完毕会去调用
onInterceptTouch伊夫nt 在onInterceptTouch伊夫nt return
false,就会把事件传给子类。
4、
不传给子View,事件终止往下传递,事件开头回忆,从父View的onTouch伊芙(Eve)nt初始事件从下到上回归执行各样控件的onTouch伊芙nt——->return
false

注: 由于View没有子View所以不要求onInterceptTouch伊芙(Eve)nt
来控件是否把事件传递给子View如故拦截,所以View的风浪分发调用super.dispatchTouch伊夫nt()的时候默许把事件传给自己的onTouch伊夫(Eve)nt处理(相当于阻挡),相比ViewGroup的dispatchTouch伊芙(Eve)nt
事件分发,View的风浪分发唯有dispatchTouch伊芙(Eve)nt()和onTouch伊芙nt()不须要onInterceptTouch伊芙nt()加入。

到此事件分发计算收尾。假如想详细摸底事件分发机制的请看那篇博客:
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渲染的总体进程,如有问题欢迎指正。

相关文章