code学习

2022Android开发;关于面试的一些经验;我有话说

作者:初壹十五a

作为一名互联网从业者三年多的时间确实挺长的,也学到了很多东西,但是感觉经常碰到瓶颈,就打算出去走走开拓一下眼界,很多面试过程还是挺快的,谈的也比较顺利。工作起来确实跟以前不一样。

下面就闲聊下Android面试的东西吧。Android面试其实技术面挺广的,大概分为以下以下个方面吧

2022Android开发;关于面试的一些经验;我有话说

腾讯Android开发笔记+2022Android十一位大厂面试真题+音视频60道面试题+Jetpack+Matrix+JVM

128道11位大厂面试真题(含答案)

获取方式:私信(面试)

一丶Java技术知识

这方面的内容的内容其实挺多的。比如

1.说一下JVM的堆栈

①概述

JAVA在程序运行时,在内存中划分5片空间进行数据的存储。分别是:

1:寄存器

2:本地方法区

3:方法区

4:栈

5:堆

可以把堆理解为一家餐厅,里面有200张桌子,也就是说最多容纳200桌客人就餐,来一批客人就为他们安排一些桌子,如果某天来的客人特别多,超过200桌了,哪就不能在接待超出的客人了。

当然,进来吃饭的客人不能是同时的,有的早有的晚,先吃好的客人,老板会安排给他们结账走人,然后空出来的桌子又能接待新的客人。

这里,堆就是餐桌,最大容量200桌就是堆内存的大小,老板就相当于GC(垃圾回收)给客人安排桌子就相当于Java创建对象的时候分配堆内存,结账就相当于GC回收对象占用的空间。

接着把栈比作一座废井,这口井多年不用已经没水了,主人现在把它作为存储酿酒的地方,存酒的时候就用绳子勾着酒坛子慢慢放下去,后面在存酒就一坛一坛的堆着放上去,取酒的时候就先取最上面的坛子。

②堆内存

什么是堆内存?

堆内存是Java内存中的一种,它的作用是用于存储Java中的对象和数组,当我们new一个对象或者创建一个数组的时候,就会在堆内存中开辟一段空间给它,用于存放。

堆内存的特点是什么?

  • 堆起始可以类似的看做是管道,或者是平时去排队买票的情况差不多,所以堆内存的特点就是:先进先出,后进后出,也就是你先排队好,你先买票
  • 堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢。

new对象在堆中如何分配

由Java虚拟机的自动垃圾回收器来管理

③栈内存

什么是栈内存?

栈内存是Java的另一种内存,主要是用来执行程序的,比如:基本类型的变量和对象的引用变量栈内存的特点

  • 栈内存就好像是一个矿泉水瓶,往里面放东西,那么先放入的沉入底部,所以它的特点是:先进后出,后进先出
  • 存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是,存在栈中的数据大小与生存必须是确定的,缺乏灵活性。

栈内存分配机制:

栈内存可以称为一级缓存,由垃圾回收器自动回收。

数据共享:

例子:

int a = 3;
  int b = 3;           

④栈和堆的区别

JVM是基于堆栈的虚拟机,JVM为新创建的线程都分配一个堆栈,也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

差异:

  • 堆内存用来存放有new创建的对象和数组
  • 栈内存用来存放方法或者局部变量等
  • 堆是先进先出,后进后出
  • 栈是先进后出,后进先出
  • 共享性不同(栈内存是线程私有的,堆内存是所有线程共有的)

2.单例常见的实现方式

①单例模式

单例模式(Singleton Pattern)是Java中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

②单例模式的要求

  • 构造方法必须私有化(确保只有自己能创建)
  • 以静态方法返回实例(外界不能通过new来获取到对象)
  • 确保对象实例只有一个(只对类进行一次实例化,以后都直接获取第一次实例化的对象)

③单例模式的实现

1、懒汉式(线程不安全)

描述:这种方式是最基本的实现方式,但是不支持多线程。因为没有加锁,在多线程不能正常工作

//懒汉式(线程不安全) 
public class Singleton { 
   private static Singleton instance; 
   private Singleton (){
} 
   public static Singleton getInstance() { 
       if (instance == null) { 
           instance = new Singleton(); 
       }
       return instance; 
   } 
}           

2、懒汉式(线程安全)

描述:能够在多线程下正常工作,但是,效率极低.

//懒汉式(线程安全) 
public class Singleton { 
    private static Singleton instance; 
    private Singleton (){
} 
    public static synchronized Singleton getInstance() { 
        if (instance == null) { 
            instance = new Singleton(); 
    }
    return instance; 
    }
 }           

3、双重校验锁(DCL,即double-checked locking)(线程安全)

描述:对懒汉式(线程安全)的优化,采用双锁的机制,安全且在多线程情况下能保持高性能

//双重校验锁 
public class Singleton { 
    private volatile static Singleton instance; 
    private Singleton() { 
    }
    public static Singleton getInstance() { 
        if (instance == null) { 
            synchronized (Singleton.class) { 
               if (instance == null) { 
                   instance = new Singleton(); 
               } 
            }
        }
        return instance; 
    } 
}           

4、饿汉式(线程安全)

描述:这种方式比较常用,但容易产生垃圾对象

//饿汉式 
public class Singleton { 
    private static Singleton instance = new Singleton(); 
    private Singleton() { 
    }
    public static Singleton getInstance() { 
        return instance; 
    } 
}           

5、静态内部类(线程安全)

描述:这种方式达到跟双重校验锁一样的效果,这种方式只适用于静态域的情况,双重校验锁可在实例域需要延迟初始化时使用.

//静态内部类 
public class Singleton { 
  private static class SingletonHolder { 
     private static final Singleton INSTANCE = new Singleton(); 
  }

  private Singleton() { 
  
  }
  public static final Singleton getInstance() { 
      return SingletonHolder.INSTANCE; 
  } 
}           

6、枚举(线程安全)

描述:这种方式还没有被广泛采用,但是这种实现是单例模式的最佳方法。更简洁、自动支持序列化机制、绝对防止多次实例化.

//枚举 
public enum Singleton { 
    INSTANCE; 

    public void whateverMethod() { 
    } 
}           

④总结

一般情况下,不建议使用第1种和第2种懒汉方式,建议使用第4种饿汉方式。只有在明确实现lazyloading时,才会使用第5种静态内部类方式。如果涉及到反序列化创建对象时,可以使用第6种枚举方式。如果有其他需求,可以考虑使用第3种双重校验锁方式。

垃圾回收机制,

类加载机制,

泛型,

常用的集合类,

IO,线程等等

这里面就有好多问题可以问了,

比如:

  • 弱引用与软引用,
  • HashMap的各个版本差异,
  • 什么是线程安全,
  • 如何实现线程安全,
  • 常用的线程池种类,为什么要这么设计,
  • DCL为什么要加两层判断。
  • ReentrantLock如何实现公平锁的,
  • 线程池里面非核心线程什么时候会被回收,
  • Type接口与泛型联系等等。

二丶Android技术知识

前面的Java知识都是为了给Android开发的铺垫,中高级就是对源码的了解程度,涉及到Android四大组件,Fragment,事件分发,自定义View。

比如:

自定义view主要重写那个方法?

答案是:onDraw()

  • 动画原理
  • 常用的开源框架Retrofit,OKHttp,Glide等等

如果是中高级岗位的话,还要了解源码、原理。

比如说:

  • handler机制,
  • AsyncTask原理,
  • Activity窗口层级,
  • binder机制,
  • Activity的启动流程,
  • Intent传输数据的限制,
  • ANR原理,
  • 性能问题,
  • RxJava 线程池的使用注意问题,
  • OKHTTP拦截器,
  • Activity的启动流程,打包等等,

这都是实际开发会遇到的问题。

三丶网络知识

这部分应该属于基础了,开发应该都要知道,TCP与UDP,HTTP与HTTPS,HTTP2解决了什么还存在的问题,Https如何实现安全传输的,TCP如何保证可靠的,HTTPS握手过程等等,建议大家多看看开源库OKhttp,Retrofit的源码,有没有什么网络优化的点,比如DNS之类的。

四丶Android性能优化

这部分的内容实在太多了,根本讲不完,建议大家还是根据实际项目来谈,平时多想想自己的项目有没有什么优化的地方。

2022Android开发;关于面试的一些经验;我有话说

布局优化,

内存优化,

网络优化,

apk瘦身

UI优化

启动优化,崩溃优化,卡顿优化等等,

这部分应该是最考验开发者技术能力的点了,如果有相关的实践经验,哪更好。

卡顿优化和布局优化

卡顿优化

1.分析工具

  1. Systrace是Android提供的一款工具,需要运行在Python环境下。

Systrace主要是分析掉帧的情况的。帧:android手机一般都是60帧,所以1帧的时间=1000毫秒/60=16.6毫秒。也就是android手机16.6毫秒刷新一次,超过这个数就是掉帧了

会有三色球,绿=正常,黄=一点不正常,红=掉帧严重。 少几个没啥事,大面积的出现红、黄,就需要研究为啥掉帧了。

2022Android开发;关于面试的一些经验;我有话说

可以看上面有一条CPU的横轴,绿色=正在执行,蓝色=等待,可以运行。紫色=休眠。白色=休眠阻塞。如果是出现了紫色就说明IO等耗时操作导致掉帧。如果紫+蓝比较多,说明cpu拿不到时间片,cpu很忙。

2.CPU Profile

这个就能看那个方法运行多少时间等,所以可以直接用android studio自带的分析。

一般是在top down里一直点,耗时较多的,然后点到自己熟悉的地方,挨个分析。 这是一个漫长的耗时的过程,可能找半天只找到几个地方能优化,然后每个几毫秒,加起来也没有直观的变快,但是性能优化就是这样的一个过程,积少成多。 如:我经过查找就发现adapter的 notifyDataSetChanged因为不小心,有些地方多次调用了。 甚至还有没有在线程进行io操作。

2022Android开发;关于面试的一些经验;我有话说

布局优化

经过上面的一顿操作,发现占时间大块的少不了setContentView。说明布局渲染视图还是挺费时的。

1.减少层级

自定义Viewmeasure、layout、draw这三个过程,都需要对整个视图树自顶向下的遍历,而且很多情况都会多次触发整个遍历过程(Linearlayout 的 weight等),所以如果层级太深,就会让整个绘制过程变慢,从而造成启动速度慢、卡顿等问题。 而onDraw在频繁刷新时可能多次触发,因此 onDraw更不能做耗时操作,同时需要注意内存抖动。对于布局性能的检测,依然可以使用systrace与traceview按 照绘制流程检查绘制耗时函数。

工具Layout Inspector DecorView开始。content往下才是自己写的布局。

2022Android开发;关于面试的一些经验;我有话说

重复的布局使用include。 一个布局+到另一个上,如果加上以后,有两一样的ViewGroup,可以把被加的顶层控件的ViewGroup换成merge ViewStub:失败提示框等。需要展示的时候才创建,放在那不占位置。

2.过度渲染

一块区域内(一个像素),如果被绘制了好几次,就是过度渲染。 过度绘制不可避免,但是应该尽量的减少。 手机->开发者模式->GPU 过度绘制 打开以后能看到不同颜色的块,越红就说明过度绘制的越严重。对于严重的地方得减少过度绘制。

1.移除布局中不需要的背景。

2.降低透明度。

3.使视图层次结构扁平化。

3.布局加载优化

异步加载布局,视情况而用。

五丶其他

2022Android开发;关于面试的一些经验;我有话说
  • kotlin
  • 常用设计模式
  • MVC,MVVM等等。

MVC,MVP,MVVM

①MVC

2022Android开发;关于面试的一些经验;我有话说

在 Android 中,三者的关系如下:

2022Android开发;关于面试的一些经验;我有话说

由于在 Android 中xml布局的功能性太弱,所以Activity承担了绝大部分的工作,所以在 Android 中mvc更像:

2022Android开发;关于面试的一些经验;我有话说

总结:

  • 具有一定的分层,model解耦,controller和 view 并没有解耦
  • controller 和 view 在 Android 中无法做到彻底分离,Controller 变得臃肿不堪
  • 易于理解、开发速度快、可维护性高.

②MVP

2022Android开发;关于面试的一些经验;我有话说

通过引入接口BaseView,让相应的视图组件如Activity,Fragment去实现BaseView,把业务逻辑放在presenter层中,弱化Model只有跟view相关的操作都由View层去完成。

总结:

  • 彻底解决了 MVC 中 View 和 Controller 傻傻分不清楚的问题
  • 但是随着业务逻辑的增加,一个页面可能会非常复杂,UI 的改变是非常多,会有非常多的 case,这样就会造成 View 的接口会很庞大
  • 更容易单元测试

③MVVM

2022Android开发;关于面试的一些经验;我有话说

在MVP中View和Presenter要相互持有,方便调用对方,而在MVP中View和ViewModel通过Binding进行关联,他们之前的关联处理通过DataBinding完成。

总结:

  • 很好地解决了 MVC 和 MVP 的问题
  • 视图状态较多,ViewModel的构建和维护的成本都会比较高
  • 但是由于数据和视图的双向绑定,导致出现问题时不太好定位来源

六丶面试之外

有人说知道这些有啥用,平时又用不到,就面试的时候。这~,我也没法反驳啊。因为这些都属于知识,学到了就是你的知识,下一次你在实际开发中用了,这就是技术。如果没有前面的知识积累,没有这个知识面,是想不出来的解决方案的,所以要多学习,也不是为了面试,而是为了将来的技术应用。举些例子吧。

比如项目中要读取本地一张大图片要注意什么?读取大图片IO时间长,CPU占用少,如果经常性的去读取会浪费时间,并且多次读取会占用更多的内存,所以要做什么,做缓存,缓存解决了多次IO时间长的问题,并且内存只有一份图片文件,但是一张大图片的缓存对于一些低配的机器来说本来内存就吃紧,一直存着,也不是办法,那么内存不够用的时候应该释放掉的,那么这个时候缓存要用强引用,弱引用还是软引用呢?那就留给大家去思考了。

面试和工作经验分享就到这了,上面写的说不定会在面试中碰到哦~~

腾讯Android开发笔记+2022Android十一位大厂面试真题+音视频60道面试题+Jetpack+Matrix+JVM

128道11位大厂面试真题(含答案)

获取方式:私信(面试)

继续阅读