Android

作者: android小菜鸡一枚 | 来源:发表于2022-10-28 19:11 被阅读0次

    自我介绍
    项目经历,重点介绍工作经历和内容

    Java

    多线程和并发编程

    a.线程和进程
    线程:调度CPU资源的最先单位

    b.线程状态
    新建-》运行-》阻塞-》等待-》终止
    New-》Runnable-》Blocked-》Waiting-》Timed_Waiting-》Terminated

    c.wait和sleep

    d.synchronized

    e.volatile

    f.死锁
    两个线程互相等待的现象

    线程池

    线程复用,减少线程创建,销毁的开销,提升性能

    • 参数
      corePooledSize 核心线程数
      maximumPoolSize 最大线程数
      keepAliveTime 超时时间
      unit 超时时间单位
      workQueue 阻塞队列
      threadFactory 线程工厂
      handler拒绝策略

    • 工作原理
      1.如果当前运行线程数小于corePoolSize,立即执行任务
      2.如果当前运行线程数大于或等于corePoolSize,放入队列
      3.队列满了且线程数小于maximumPoolSize,创建非核心线程立即执行
      4.如果当前线程数大于maximumPoolSize,线程池会启动拒绝饱和策略
      5.当一个线程完成任务,它会从队列中取下一个任务来执行

    java基础面试题

    • 4种引用类型
      强引用StrongReference: 在程序内存不足OOM时会被回收
      软引用SoftReference: 在内存不足时,JVM会回收早先创建的对象
      弱引用WeakReference: JVM垃圾回收器发现就会回收
      虚引用PhantomReference: 对象销毁前的一些操作,如资源释放

    • 深浅拷贝区别

    • 接口和抽象区别

    • 集合List,Set和Map

    android基础面试题

    • 启动模式和哪些场景会用到

    • 横竖屏切换的声明周期
      onconfigchanges配置了orientation或keyboardhidden,
      onpause,onstop,onrestart,onstart,onresume,onconfigurationchanged的

    • service启动方式

    • activity和fragment如何通信

    • android和h5通信
      android调用js: webview.loadurl()
      js调用android: addJavascriptInterface()

    • service如何增加权限

    • 自定义View,两行左对齐,一行中间对齐

    • 子线程切换主线程刷新UI
      new Handler(Looper.getMainLooper()).post()
      Activity.runOnUiThread()
      View.post()

    • 屏幕适配
      屏幕尺寸:单位英寸
      屏幕像素密度:每英寸像素点素,单位dpi
      密度无关像素:dp
      独立比例像素:sp
      使用dp和sp指定尺寸;布局优先使用RelativeLayout;使用wrap_content,match_parent,权重 ;设置组件最小宽高minWidth,minHeight和行数lines,不破坏整体布局;定义dimens,不同的屏幕尺寸定义不同的dimens;
      根据屏幕的适配加载不同的布局,配置限定符(size限定符,最小宽度限定符layout-sw600dp)

    进程间通信binder
    线程间通信handler

    原理:
    子线程Hanlder#sendMessage(),将消息发送到UI线程的MessageQueue消息队列中,Looper#loop()不断轮询消息,最后Handler#dispatchMessage()
    1.一个线程有几个handler,looper,如何区分不同handler发送的消息(荣耀负一屏面试题)

    2.handler让子线程和子线程通信
    Looper#prepare()
    Looper#loop()

    3.handler导致的内存泄漏如何分析解决?
    分析:
    当使用内部类创建handler,handler会隐式持有外部类对象,通常是一个activity引用

    • handler#postdelayed方法,在delay到达之前,messagequeue-message-handler-activity,导致activity无法释放
    • 网络请求过程中关闭了activity
      线程持有handler,handler持有activity,导致activity无法回收

    解决:handler#removeCallbacks(),将handler声明为静态类+activity弱应用

    4.Looper#loop()死循环为什么没有阻塞主线程
    android是基于事件驱动模型的
    循环内部会调用nativePoolOnce的native方法,这是一个C/C++方法,利用了Linux中的管道机制epoll,在没有消息的时候会调用epoll_wait进行等待,会释放CPU, 应用会处在休眠状态

    自定义View

    自定义下载进度按钮
    绘制背景圆形矩形,绘制进度,绘制文字,加入动画ValueAnimator,执行View.invalidate方法

    View事件分发和滑动冲突

    1.事件的传递顺序:
    Activity->PhoneWindow->DecorView->ViewGroup->View
    对于一个根ViewGroup,点击事件产生后,首先会传递给它,这时它的dispatchTouchEvent方法被调用,如果这个ViewGroup#onInterceptTouchEvent方法返回true就表示拦截当前事件,接着事件就交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用,如果返回false就表示它不拦截当前事件,这时事件就会传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复直至事件最终被处理

    2.滑动冲突
    外部拦截法:
    ViewGroup#onInterceptTouchEvent方法,在ACTION_MOVE中根据需要进行拦截返回true,ACTION_DOWN和ACTION_UP需要返回false
    内部拦截法:
    父ViewGroup#onInterceptTouchEvent方法,不拦截ACTION_DOWN返回false,其他返回true; 子View#dispatchTouchView方法,在ACTION_MOVE如果父View需要处理事件,调用parent#requestDisallowInterceptTouchEvent(false),让父容器拦截事件

    性能优化

    1.卡顿优化
    android系统每隔16ms发送vsync垂直同步信号,对UI进行渲染,
    在一个vsync到来前,新的帧并没有生成完成,只能显示上一帧的画面,这时就出现了卡顿现象,系统运算资源比较紧张;

    原因:
    1.层级和过渡绘制
    2.内存引起:
    内存抖动,gc频繁对内存进行回收,暂停其它线程,除了gc的线程, ui线程也被暂停,ui线程要求16ms进行一次更新,影响ui渲染
    3.线程引起

    定位:
    systrace
    查看UI线程状态
    紫色:有大面积生产对象的代码(事件和draw)
    灰色:有锁问题
    蓝色:系统资源不足
    橙色:有IO问题
    blockcanary

    2.崩溃优化
    anr分析?如果没有app堆栈信息如何分析?(荣耀负一屏面试)
    结合trace.txt和mainlog主日志文件
    1.anr时间点 2.主线程状态 3.问题类型
    堆栈信息,内存信息和CPU负载

    主线程处于BLOCK,WAITING,TIME_WAITING状态,基本就是函数阻塞导致的anr,
    若主线程无异常,则排查下CPU负载,是否有其他应用抢占了CPU资源导致的ANR
    如果CPU和堆栈都很正常,考虑是否内存紧张,系统日志里搜索下am_meminfo,onTrimMemory

    案例:

    • 主线程状态是Runnable,观察堆栈是我们自己的代码中
    • 主线程状态是Blocked, 有waiting to lock held by xxx(其他线程),就是其他线程持有了锁,并长时间未释放,主线程等待这把锁发生超时了
    • 主线程状态正常,查看CPU负载,各个进程占用CPU的详细情况,是否CPU被抢占
    • 记录anr发生的时间点,去系统日志中搜索am_meminfo,onTrimMemory level:80 pid:XXX, 查看是否内存紧张,如果是内存紧张,会导致其他多个应用anr
    • 系统服务超时,搜索BindProxy

    3.启动优化
    4.内存优化
    OOM
    内存抖动
    内存泄漏
    内存泄漏分析?如何分析一份内存快照?
    首先Android Profiler抓取内存快照,capture heap dump点击record,选择arrange by class生成hprof文件,查看到内存泄漏的是哪个activity;在platform-tools下hprof-conv.exe转换hprof文件,打开MAT,点击Histogram柱状图,搜索内存泄漏的activity, 右键排除虚软弱应用,结合堆栈和代码,可以分析activity被哪些对象引用了

    Android Profiler->Memory Allocations-》Allocation Call Stack
    Heap Dump->xxx.hprof

    5.存储优化
    6.UI渲染优化
    7.图片加载优化
    8.Apk瘦身

    MVP,MVC,MVVM
    http和https
    动画

    帧动画
    补间动画(淡入淡出,位移,缩放,旋转)
    属性动画ObjectAnimator继承自ValueAnimator
    TypeEvaluator决定了动画如何从初始值过渡到结束值
    TimeInterpolator决定了动画从初始值过渡到结束值的节奏

    1. android中有哪些动画?
    2. 属性动画和view动画区别?(荣耀商城面试)
    android中设计模式
    jetpack
    kotlin
    变量

    属性在声明的同时需要初始化,kotlin的变量没有默认值

    • 空安全设计:
      var name: String? = "Mike"
      view?.setBackgroundColor(Color.RED)
    • 延迟初始化 lateinit
    • 类型推断
      在声明的时候就赋值,那不写变量类型
    • val 和 var
      val只读变量,它只能赋值一次,不能修改
      var可读可写变量
    函数

    以 fun 关键字开头
    返回值写在了函数和参数后面
    没有返回值,kotlin里返回Unit

    对象
    • : 继承

    • 构造方法
      constructor()
      class A constructor() : B {}
      class A : B() {}

    • open
      继承:kotlin类默认是final的
      open class A : B() {}

    • override
      override fun onCreate() {}
      final override fun onCreate() {} 关闭override遗传性
      实例化对象:没有new关键字
      类型判断:is关键字;强转调用:as关键字(activity as? NewActivity)?.action()

    • init代码块

    • object关键字
      object class A{}单例类
      既有 class 关键字的功能,又实现了单例,用 object 修饰的对象中的变量和函数都是静态的

    • 匿名类
      val listener = object:ViewPager.SimpleOnPageChangeListener() {}

    • companion object

    • top level顶层声明
      这样写的属性和函数,不属于任何 class,而是直接属于 package

    • 常量
      const

    • 数组
      val strs: Array<String> = arrayOf("a", "b", "c")
      kotlin数组不支持协变,就是子类数组对象不能赋值给父类的数组变量
      Kotlin 中要用专门的基本类型数组类 (IntArray FloatArray LongArray) 才可以免于装箱

    • 集合
      List, Set, Map
      kotlin集合是支持协变的,就是可以把子类的 List 赋值给父类的 List 变量
      创建方法:listOf(),setOf(),mapOf()

    • 可变集合
      mutableListOf(),mutableSetOf(),mutableMapOf()
      不可变的可以通过 toMutable*() 系函数转换成可变的集合

    • Sequence序列

    • 可见性修饰符
      public
      internal 对module内可见
      protected private+子类可见

    • 主构造器和次构造器
      class User constructor(var name: String){} -主构造器
      constructor(name:String,id: Int): this(name) -次构造器

    • 命名参数
      sayHi(name = "wo", age = 21, isStudent = false, isFat = true, isTall = false)

    • 嵌套函数

    • 字符串模板

    • 原生字符串
      用法就是使用一对 """ 将字符串括起来
      trimMargin() 函数去除每行前面的空格

    • 数组和集合的操作符
      forEach 遍历每一个元素
      filter 对每一个元素进行过滤操作
      map 遍历每一个元素并执行给定表达式
      flatmap 遍历每一个元素,并为每一个元素创建新的集合,最后合并到一个集合中

    • Range
      0..1000 -[0,1000]
      0 until 1000 -[0,1000)
      for (i in range step 2)

    • 条件控制
      if/else:
      val max = if (a > b) a else b
      when:

    when (x) {
        1 -> { println("1") }
        2 -> { println("2") }
        else -> { println("else") }
    }
    
    val value: Int = when (x) {
        1 -> { x + 1 }
        2 -> { x * 2 }
        else -> { x + 5 }
    }
    
    Kotlin 中多种情况执行同一份代码时,可以将多个分支条件放在一起,用 , 符号隔开
    when (x) {
        1, 2 -> print("x == 1 or x == 2")
        else -> print("else")
    }
    
    使用 in 检测是否在一个区间或者集合中
    when (x) {
        in 1..10 -> print("x 在区间 1..10 中")
        in listOf(1,2) -> print("x 在集合中")
        // not in
        !in 10..20 -> print("x 不在区间 10..20 中")
        else -> print("不在任何区间上")
    }
    
    使用 is 进行特定类型的检测
    val isString = when(x) {
        is String -> true
        else -> false
    }
    
    省略 when 后面的参数,每一个分支条件都可以是一个布尔表达式
    when {
        str1.contains("a") -> print("字符串 str1 包含 a")
        str2.length == 3 -> print("字符串 str2 的长度为 3")
    }
    

    for:

    val array = intArrayOf(1, 2, 3, 4)
    for (item in array) {
        ...
    }
    
    • ?:
    如果左侧表达式 str?.length 结果为空,则返回右侧的值 -1
    val str: String? = "Hello"
    val length: Int = str?.length ?: -1
    
    • == 和 ===
      ==: 基本数据类型以及 String 等类型进行内容比较,相当于 Java 中的 equals
      ===:内存地址进行比较,相当于 Java 中的 ==
    泛型

    <? extends> 协变 out 只能读取不能修改,只用来输出,不用来输入,你只能读我不能写我
    <? super> 逆变 in 只能修改不能读取,只用来输入,不用来输出,你只能写我不能读我

    • *号
      等价于out Any,java中的? extends Object
    • where关键字
    class Monster<T> where T : Animal, T : Food
    
    • reified 关键字
    协程Coroutines

    协程是轻量级的线程
    用同步的方式写异步的代码
    闭包:当函数的最后一个参数是 lambda 表达式时,可以将 lambda 写在括号外

    协程三种写法:

    1.runBlocking顶层函数, 线程阻塞的
    2.GlobalScope.launch
    3.
    val coroutineScope = CoroutineScope(context)
    coroutineScope.launch {
        getImage(imageId)
    }
    
    //withContext切换到指定线程
    //supend关键字
    suspend fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
        ...
    }
    
    4.async函数
    返回的 Coroutine 实现了 Deferred 接口
    

    启动一个协程可以使用 launch 或者 async 函数,协程其实就是这两个函数中闭包的代码块
    挂起:launch ,async 或者其他函数创建的协程,在执行到某一个 suspend 函数的时候,这个协程会被「suspend」,也就是被挂起,所谓的被挂起就是切了线程,挂起就是线程调度操作

    相关文章

      网友评论

          本文标题:Android

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