面试所知

性能优化面试:

首先我们要知道性能优化解决了什么问题,就是页面卡顿现象严重。

那么我们就应该先去检测页面的加载速度,前面有段时间我以为activity的oncreate到onresume的运行时间就是页面的加载时间,其实这是错误的,在源码中显示onresume生命周期方法中只表示页面可以进行交互了,而addview还在以后进行调用才去渲染布局。

然后我找到了其他的方法,iderHandler可以去监听ui线程中渲染消息被处理了后的回调,获取ui线程后再获取消息队列进行addIderHander进行监听。这样就能获取到activity启动后到渲染完页面的准确时间。超过60ms则定义成页面卡顿,这样我们就可以获取那些页面出现卡顿现象。

接下来就是真正开始进行优化了,从页面布局角度出发,布局层级结构应该扁平化设计,多使用relativeLayout来开发,多使用include标签提高布局复用,在xml布局中先把控件设置成gone,在网络请求数据后再进行显示,去掉不必要的背景,避免过度重绘的现象,避免出现scollview嵌套listview这样复杂现象。

从代码角度出发,代码量也不小的情况下,这里要把卡顿现象定位到问题代码上,利用method trace工具来获取方法的消耗时间来推断出那些代码出现了卡顿。把耗时操作的代码放在子线程进行,把一些耗时资源与无用资源进行合理释放,避免创建不必要的对象,使用lint来减少冗余代码提高执行效率,listview复用,利用线程池来管理线程创建销毁的开销。

从内存角度出发,在这说明下,内存方面对于页面卡顿现象来说影响不大,主要是gc操作会产生一点点的页面卡顿。那我们就应该减少gc操作,动态加大分配内存,在清单文件里声明该app加大内存分配,图片资源内存优化,默认8888改成565,尽可能缓存小尺寸的图片,使用lrucache进行缓存图片资源,利用dump 操作来获取内存情况,使用leakCanary,检测并解决内存泄漏问题,常见的内存泄漏问题,匿名内部类,内部类的默认持有外部引用的内存泄漏,比如handler.thread.runnable,单例模式下的context引用,单例模式的生命周期是与application一致,应该用context.getapplicatiancontext来使用,但是在显示popwindow.dialog时需要activity的context,所以也要避免在单例模式中显示弹窗。bitmap.cursor.广播接受者.在页面被销毁的情况下进行关闭与释放,避免过度使用static成员变量。

这些操作下来,就大体解决了性能优化问题。

线程池面试:

降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。                                                  ScheduledExecutorService扩展ExecutorService接口并增加了schedule方法。调用schedule方法可以在指定的延时后执行一个Runnable或者Callable任务。

corePoolSize:核心线程数量,当有新任务在execute()方法提交时 如果运行的线程少于 corePoolSize,则创建新线程来处理任务,即使线程池中的其他线程是空闲的;

如果线程池中的线程数量大于等于 corePoolSize 且小于 maximumPoolSize,则只有当workQueue满时才创建新的线程去处理任务;

如果设置的corePoolSize 和 maximumPoolSize相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程去从workQueue中取任务并处理;

如果运行的线程数量大于等于maximumPoolSize,这时如果workQueue已经满了,则通过handler所指定的策略来处理任务; maximumPoolSize:最大线程数量;workQueue:等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;SynchronousQueue,LinkedBlockingQueue,ArrayBlockingQueue。keepAliveTime:线程池维护线程所允许的空闲时间。                                                                                                                                                          Worker类设计如下:

1、继承了AQS类,可以方便的实现工作线程的中止操作;

2、实现了Runnable接口,可以将自身作为一个任务在工作线程中执行;

3、当前提交的任务firstTask作为参数传入Worker的构造方法;                                                                                                                                                                                runWorker方法是线程池的核心: 1、线程启动之后,通过unlock方法释放锁,设置AQS的state为0,表示运行中断;

2、获取第一个任务firstTask,执行任务的run方法,不过在执行任务之前,会进行加锁操作,任务执行完会释放锁;

3、在执行任务的前后,可以根据业务场景自定义beforeExecute和afterExecute方法;

4、firstTask执行完成之后,通过getTask方法从阻塞队列中获取等待的任务,如果队列中没有任务,getTask方法会被阻塞并挂起,不会占用cpu资源;通过ExecutorService.submit()方法提交的任务,可以获取任务执行完的返回值。

数据库框架面试:

描述对应要创建表格的对象的注解 描述对应要创建表格的主键ID的注解 描述对应要创建表格的对象的列字段注解 

创建表操作:获取要创建表的对象,通过反射来获取表名,如果表存在数据库或者表名为空,则不进行操作,否则进行建表操作, 通过反射来拿到表的所有要创建的字段名,利用一个HashMap进行缓存字段名与字段类型,这里如果不存在字段名就不需要创建了,将所有的字段拼接成对应的SQL语句,执行对应的创建表操作。

增删改查的操作:获取bean的字段数组,先进行暴力反射,反正字段是私有字段,先判断字段上是否有Column注解,如果有,表示此字段是和列对应,其注解对应的值就是表示的列名,然后通过field.get(m)获取对象其字段对应的值,然后条件到ContentValues对象中,values.put(key, value);key是注解Column对应的值,value是通过反射获取对象的值。获取bean中的Column注解,然后获取其值(类名),通过columnvalue=cursor.getColumnIndex(columnName)获取此列在游标中的值,然后通过field.set(bean,columnvalue),设置给传进来的bean对象字段值。

LRUcache面试:

LruCache算法就是当缓存空间满了的时候,将最近最少使用的数据从缓存空间中删除以增加可用的缓存空间来缓存新内容,缓存图片看的是占用的内存的大小,每张图片的占用内存也是不一样的,一次不能这样算。因此要重写sizeOf方法,手动将这里改为本次缓存的图片的大小。 LruCache通过强引用来缓存一定数量的值. 每当一个值被访问的时候,这个值就会移动到缓存队列的头部. 如果插入数据时发现缓存不够了,就会将队列中最近访问次数最少的数据删掉.可以设置缓存大小,一般设置成4M。 this.map = new LinkedHashMap(0, 0.75f, true); 初始化LinkedHashMap。第一个参数是初始容量,第二个参数是填装因子,或叫加载因子,第三个参数是排序模式,true表示在访问的时候进行排序,否则只在插入的时候才排序。get和put都可能会执行trimToSize方法中在缓存队列中先找到最近最少使用的元素,调用LinkedHashMap的eldest()方法返回最不经常使用的方法。然后删掉这个元素,并减少已使用的缓存空间。LinkedHashMap集合内部本来就有个排序功能,当第三个参数是true的时候,数据在被访问的时候就会排序,这个排序的结果就是把最近访问的数据放到集合的最后面。LinkedHashMap内部是使用双向循环链表来存储数据的。也就是每一个元素都持有他上一个元素的地址和下一个元素的地址。当集合的get方法被调用时,会调用这个方法。 如果accessOrder为true,就把这个元素放在集合的最末端。eldest方法获取到的就是距离链表顶部最近的元素。

kotlin面试:

Kotlin 具有现代静态编程语言的很多特点,如类型推断、多范式支持、可空性表达、扩展函数、null安全检测,属性访问,Kotlin的缺点 编译速度,额外运行的大小,代码初始可读性。能够扩展一个类的新功能而无需继承该类或使用像装饰者这样的任何类型的设计模式。 这通过叫做 扩展 的特殊声明完成。Kotlin 支持 扩展函数 和 扩展属性。声明一个扩展函数,我们需要用一个 接收者类型 也就是被扩展的类型来作为他的前缀。由于扩展没有实际的将成员插入类中,因此对扩展属性来说幕后字段是无效的。这就是为什么扩展属性不能有初始化器。他们的行为只能由显式提供的 getters/setters 定义。Kotlin 的类型系统旨在从我们的代码中消除 NullPointerException。NPE 的唯一可能的原因可能是:显式调用 throw NullPointerException();使用了下文描述的 !! 操作符;有些数据在初始化时不一致,例如当:传递一个在构造函数中出现的未初始化的 this 并用于其他地方(“泄漏 this”);超类的构造函数调用一个开放成员,该成员在派生中类的实现使用了未初始化的状态;Java 互操作:企图访问平台类型的 null 引用的成员;用于具有错误可空性的 Java 互操作的泛型类型,例如一段 Java 代码可能会向 Kotlin 的 MutableList中加入 null,这意味着应该使用 MutableList 来处理它;                                   

官方文档:https://hltj.gitbooks.io/kotlin-reference-chinese/content/txt/classes.html

你可能感兴趣的:(面试所知)