Android

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

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 = 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 中的 ==
泛型

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

  • *号
    等价于out Any,java中的? extends Object
  • where关键字
class Monster 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)