android面试题记录2

  • ThreadLocal实现原理
  • EventBus原理
  • EventBus流程
  • EventBus内ThreadMode类型分为4种
  • HTTP 请求报文结构
  • HTTP 相应报文结构
  • cookie是用来干嘛的
  • android Intent 传递数据最大限制
  • synchronize 关键字用法
  • 还知道哪些同步的方式?
  • Activity启动模式
  • View的绘制流程,测量传参的形式是什么
  • 内部类有什么作用?
  • final关键字的作用
  • 单例模式为什么要两次判空
  • Android源码中使用的设计模式
  • APP启动优化
  • 分析内存泄露的一些方法

ThreadLocal实现原理

每个Thread的对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。
在该类中,最重要的方法就是两个:set()和get()方法。当调用ThreadLocal的get()方法的时候,会先找到当前线程的ThreadLocalMap,然后再找到对应的值。set()方法也是一样。)系统是如何监听ANR的(他说是androidframework层有一个单独的进程),后来问到如何定位和排查我不小心说了blockCanary,他有追问了一下源码和原理我回答的也不是很好(应用发生卡顿,一定是在dispatchMessage中执行了耗时操作。我们通过给主线程的Looper设置一个Printer,打点统计dispatchMessage方法执行的时间,如果超出阀值,表示发生卡顿,则dump出各种信息,提供开发者分析性能瓶颈。

EventBus原理

image

EventBus是一个事件发布/订阅总线。让组件之间的通信更加简单。

  • 针对在事件的发送者和订阅者之间进行解耦。
  • 非常好的运用在Activitys、Fragments和后台线程。
  • 避开了联系紧密易出错的依赖关系和容易出错生命周期。

EventBus 使用观察者模式,整体结构基于发布者/订阅者。订阅者首先注册,注册以后,当发布者发布数据的时候平台就会吧这些数据发送给已经注册了的订阅者(根据数据区分订阅者),订阅者收到以后自行进行处理。

EventBus流程

  1. 注册流程---register函数中会先根据订阅者类名去subscriberMethodFinder中查找当前订阅者所有事件响应函数,然后循环每一个事件响应函数,依次执行下面的 subscribe 函数。
第一,通过subscriptionsByEventType得到该事件类型的所有订阅者信息队列,根据优先级把当前订阅者信息插入到订阅者队列subscriptionsByEventType中。
第二,在typesBySubscriber中得到当前订阅者的所有事件队列,将此事件保存到队列typesBySubscriber中,用于后续取消订阅。
第三,检查这个事件是否是 Sticky 事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。
  1. 发布流程---post方法会首先得到当前线程的 post 信息PostingThreadState,其中包含事件队列,将当前事件添加到其事件队列中,然后循环调用 postSingleEvent 函数发布队列中的每个事件。
postSingleEvent 方法会先去eventTypesCache得到该事件对应类型的的父类及接口类型,没有缓存则查找并插入缓存。循环得到的每个类型和接口,调用 postSingleEventForEventType 方法发布每个事件到每个订阅者。
postSingleEventForEventType 方法在subscriptionsByEventType查找该事件订阅者订阅者队列,调用 postToSubscription 函数向每个订阅者发布事件。

EventBus内ThreadMode类型分为4种

  • PostThread,默认的ThreadMore, 表示post操作的线程 直接调用给订阅者的事件响应接口,无论该线程是否为主线程。(如果post线程为主线程的时候,响应接口不能有耗时操作,不然可能会卡主线程。)
  • MainThread,在主线程执行订阅者的事件响应接口。如果post就是主线程就直接调用,如果post不是主线程就通过Handler发送消息在主线程中处理。(响应接口不能有耗时操作,不然可能会卡主线程。)
  • BackgroundThread,和MainThread正好相反,在后台线程中执行订阅者的事件响应接口。如果post不在主线程就直接调用,如果post在主线程就启动后台线程去处理。后台线程是唯一的,所以当事件超过一个时候,就放到队列中依次执行。(如果一个事件耗时太多,则会影响其他事件,其他事件会排队等待)
  • Async,不管post是不是主线程,都使用一个空闲线程执行订阅者的事件响应接口。(和BackgroundThread不一样的是Async线程之间是独立的,一个事件有耗时也不会影响其他的事件执行。)

HTTP 请求报文结构

http请求报文由4部分组成:请求行,请求头部,空行,请求数据。

  1. 请求行,请求行主要目的是为了区分请求报文还是响应报文,以及记录相应的URL以及协议版本。请求行分为三部分组成:方法、请求资源的URL、HTTP的版本。HTTP请求的方法主要有:GET、POST、PUT、DELETE、OPTIONS、HEAD、TRANCE、CONNECT等。
 GET /URL HTTP/1.1
  1. 请求头,是用来通知服务器有关客户端请求的一些信息。请求头是由key/value 键值对构成,每行一对。(Accept、Host、User-Agent等等都是请求头中关键字)。
 Host: www.baidu.com
 User-Agent: curl/7.54.0
 Accept: */*
 ....
  1. 空行,请求头的结尾。
  2. 请求数据,GET方法没有请求数据。为POST时放置的是需要提交的数据。

HTTP 相应报文结构

HTTP响应报文也由三部分组成:响应行、响应头、响应体。

  1. 响应行,一般是由协议版本,状态码极其表述组成。
HTTP/1.1 302 Found
  1. 响应头,用于描述服务器的基本信息和数据的描述信息,客户端根据这些描述信息处理服务器回传的数据。
< Date: Fri, 15 Mar 2019 10:03:18 GMT
< Server: Apache
< Location: http://www.baidu.com/search/error.html
< Cache-Control: max-age=86400
< Expires: Sat, 16 Mar 2019 10:03:18 GMT
< Content-Length: 222
< Connection: Keep-Alive
< Content-Type: text/html; charset=iso-8859-1
  1. 响应体,服务器返回的数据。


302 Found

Found

The document has moved here.

cookie是用来干嘛的

一个由网页服务器放在您硬盘上的非常小的文本文件.信息的片断以‘名/值’对(name-value pairs)的形式储存。 它本质上就像您的身份证明一样,可以方便网站“记住”用户的偏好,方便用户使用网站的功能,但是有些网站利用cookie精准投放广告。并且不能像代码那样被执行或被用来散布病毒。它只能被您使用并且只能由提供的服务器读取。

android Intent 传递数据最大限制

Intent传递数据最大为1兆

synchronize 关键字用法

synchronize是Java中的关键字,是一种同步锁。主要修饰以下几种对象。

  • 修饰代码块,被修饰的代码块称为同步语句块,其作用的范围是{}括起来的代码,作用的对象是调用这个代码块的对象。
  • 修饰一个方法,被修饰的方法称为同步方法,其作用范围是整个方法,作用的对象是调用这个方法的对象。
  • 修饰一个静态方法,其作用的范围是整个class对象,对类的所有实例对象起作用。
  • 修饰一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。

还知道哪些同步的方式?

LOCK

Activity启动模式

  • Standard模式,Standard是activity默认的启动模式,使用此实例每次启动Activity,无论任务栈中是否已经有这个Activity实例,系统都会新建一个Activity。
  • SingleTop模式,SingleTop和Standard差不多,区别是如果启动的Activity已经在栈顶,则不会新建一个实例。如果不在栈顶的时候才会新建一个实例。 如果Activity在栈顶的时候重新启动的时候是不会调用onCreat()函数的,但是会调用onNewIntent()函数。可以在onNewIntent()函数做相应处理。
  • SingleTask模式,在此模式下Activity在同一个Task任务栈内只有一个实例。如果要启动的Activity在栈顶则和SingleTop模式一样,直接启动。如果不在栈顶,则把实例移到栈顶,并把它上面的实例出栈。(如果指定了不同的taskAffinity则会启动一个新的任务栈)
  • SingleInstance模式,此模式也是单例,但是和SingleTask不同,SingleTask是任务栈内单例,SingleInstance是整个系统内只有一个实例,启动SingleInstance模式的Activity时,系统则会创建一个新的任务栈,并且这个任务栈只有这一个Activity。(举例:A为普通默认,B为SingleInstance,C为普通模式。A启动B,B启动C, C点击返回,则会先销毁C,然后销毁A,最后销毁B。因为A和C是在同一个任务栈,所以销毁C以后系统发现任务栈内还有A所以也销毁,然后才去销毁B)。

View的绘制流程,测量传参的形式是什么

整个 View 树的绘图流程在ViewRoot.java类的performTraversals()函数展开,该函数所做 的工作可简单概况为是否需要重新计算视图大小(measure)、是否需要重新安置视图的位置(layout)、以及是否需要重绘(draw)。
用户主动调用 request,只会出发 measure 和 layout 过程,而不会执行 draw 过程。
View的绘制主要是三个方法,onMouse()onLayout()、 onDraw();
onMouse()传参会传一个32位的int型数字,高两位代表测量模式(SpecMode),低30位代表具体数值(SpecSize)。
SpecMode的取值可为以下三种:

  • EXACTLY: 对子View提出了一个确切的建议尺寸(SpecSize);
  • AT_MOST: 子View的大小不得超过SpecSize;
  • UNSPECIFIED:对子View的尺寸不作限制,通常用于系统内部。

View 绘制流程函数调用链

image

图片来源( https://plus.google.com/+ArpitMathur/posts/cT1EuBbxEgN)

内部类有什么作用?

  • 内部类可以很好的实现隐藏,一般的非内部类,是不允许有 private 与protected权限的,但内部类可以。
  • 内部类拥有外围类的所有元素的访问权限 (private修饰也能访问)
  • 可是实现多重继承 (让多个内部类分别继承多个其他类,使外部类可以同时获取多个其他类的属性)
  • 可以避免修改接口而实现同一个类中两种同名方法的调用。(外部类继承,让内部类实现接口)

final关键字的作用

  • 被final修饰的类不可以被继承
  • 被final修饰的方法不可以被重写
  • 被final修饰的变量不可以被改变
  • 被final修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的。

单例模式为什么要两次判空

双重校验锁模式

public static Singleton getInstance(){
    if(instance == null){
        synchronized(Singleton.class){
            if(instance == null){
                instance = new Singleton();
            }
        }
    }
    return instance;
}

在极低的几率下,通过if (instance == NULL)的线程才会有进入锁定临界区的可能性,这种几率还是比较低的,不会阻塞太多的线程,但为了防止一个线程进入临界区创建实例,另外的线程也进去临界区创建实例,又加上了一道防御if (instance == NULL),这样就确保不会重复创建了。

Android源码中使用的设计模式

  • 单例模式
  • 建造者模式,用于链式调用。
  • 原型模式,
  • 装饰者模式-(动态地给一个对象添加一些额外的职责。就增加功能来说,装饰者模式比生成子类更灵活)

APP启动优化

  • adb shell am start -W 首屏Activity。 启动时间统计
  • 设置一个闪屏图片背景的主题,让用户感觉点开以后就看到闪屏,给用户一种快的感觉,但是真实效率并没有提升。(这种治标不治本,只是通过交互给用户感觉的快)
  • 通过业务逻辑优化,比如延迟一些业务的初始化,通过一部线程处理一部分业务的初始化。
  • 尽量减少首屏activit密集的网络请求。可以适当缓存一些数据,下次打开应用的时候使用。

分析内存泄露的一些方法

  • 把Java应用程序使用的heap dump下来,可以使用jdk里面带的jmap工具。
jmap -dump:format=b,file=heap.bin   
format=b的含义是,dump出来的文件时二进制格式。
file-heap.bin的含义是,dump出来的文件名是heap.bin。
就是JVM的进程号。
(在linux下)先执行ps aux | grep java,找到JVM的pid;然后再执行jmap -dump:format=b,file=heap.bin ,得到heap dump文件。
  • 使用android 里面带的DDMS内的heap分析工具,找出内存占用超出预期(一般是因为数量太多)的嫌疑对象。
  • 内存分析工具 MAT(Memory Analyzer Tool)。

你可能感兴趣的:(android面试题记录2)