Android经典面试题分析(2017)

作者: 酱爆大头菜 | 来源:发表于2017-08-15 17:52 被阅读178次

    转载请注明出处:http://blog.csdn.net/w525721508/article/details/77198233


    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经典面试题分析(2017)

        本文链接:https://www.haomeiwen.com/subject/vcrvrxtx.html