1、介绍一下Handler
- 答:Handler机制是Android系统中用于实现线程之间通信的一种机制。它包含了一个MessageQueue对象和一个Looper对象,用来处理MessageQueue中的消息。
- 当我们需要在一个线程中更新UI或执行一些耗时操作时,我们可以使用Handler机制。我们可以在主线程中创建一个Handler对象,并将其关联到主线程的Looper对象上。然后,在其他线程中,我们可以通过Handler.post()方法或Handler.sendMessage()方法向主线程发送消息,这些消息会被加入到主线程的MessageQueue中。主线程通过Looper.loop()方法不断地从MessageQueue中获取消息,并根据消息类型执行相应的操作。
- 通过这种方式,我们可以在其他线程中发送消息,然后在主线程中处理这些消息,从而实现线程之间的通信。这种机制也被广泛应用于Android应用程序中的异步任务、定时器等场景。
2、描述Android应用进程的启动流程
- 1、点击启动一个App,Launcher进程采用Binder IPC向system_server进程(ActivityManagerService)发起startActivity请求;
- 2、system_server进程(ActivityManagerService)接收到请求后,向zygote进程发送创建进程的请求;Zygote进程fork出新的子进程,即App进程;
- 3、App进程通过Binder IPC向system_server进程发起绑定Application请求;
- 4、system_server进程在收到请求后,进行一系列准备工作后,再通过binder IPC向App进程发送scheduleLaunchActivity请求;
- 5、App进程的binder线程(ApplicationThread)在收到请求后,通过handler向主线程发送LAUNCH_ACTIVITY消息;
- 6、主线程在收到Message后,通过反射机制创建目标Activity,并回调Activity.onCreate()等方法。
到此,App便正式启动,开始进入Activity生命周期,执行完onCreate/onStart/onResume方法,UI渲染结束后便可以看到App的主界面。
其流程图如下所示:
![](https://img.haomeiwen.com/i11268516/fda61cb116e5a650.png)
3、HTTP 与 HTTPS 的区别
- 1、安全性:HTTP是不安全的协议,因为它的数据传输是明文的,容易被黑客窃取和篡改。而HTTPS是安全的协议,因为它使用SSL/TLS协议对数据进行加密和认证,保证数据传输的安全性。
- 2、加密:HTTP不提供加密机制,而HTTPS使用SSL/TLS协议对数据进行加密,保证数据传输的机密性和完整性。
- 3、端口:HTTP使用80端口,而HTTPS使用443端口。
- 4、证书:HTTPS需要使用数字证书来验证服务器的身份,确保数据传输的安全性。而HTTP不需要证书。(HTTPS 协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。)
- 5、性能:HTTPS比HTTP慢,因为它需要进行加密和解密操作,增加了数据传输的时间和成本。
4、TCP连接的三次握手
SYN是TCP连接的第一个包,非常小的一种数据包。
- 第一次握手
第一次握手就是客户端给服务器端发送一个报文;
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
- 第二次握手
第二次握手就是服务器收到报文之后,会应答一个报文给客户端;
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
- 第三次握手
第三次握手就是客户端收到报文后再给服务器发送一个报文,建立连接;
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
TCP连接的四次挥手:
- 第一次挥手:客户端打算关闭连接,此时会发送一个 FIN 报文,之后客户端进入 FIN_WAIT_1 状态。
- 第二次挥手:服务端收到该报文后,就向客户端发送 ACK 应答报文,接着服务端进入 CLOSED_WAIT 状态。
- 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
- 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 + 1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态。
- 服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
5、OSI模型和TCP/IP模型
![](https://img.haomeiwen.com/i26777047/efd2752e11fd6278.png)
6、apply、also、let 、run、with扩展函数总结一下:
函数名 | 使用方法 | 返回值 | 内部持有对象 | 作用 |
---|---|---|---|---|
apply | info.apply{this} | 本身 | this | 1、定义一个变量在特定的作用域;2、统一判空处理; |
also | info.also{it} | 本身 | it | 1、定义一个变量在特定的作用域;2、统一判空处理; |
let | info.let{it} | 最后一行 | it | 1、定义一个变量在特定的作用域;2、统一判空处理; |
run | info.run{this} | 最后一行 | this | 1、定义一个变量在特定的作用域;2、统一判空处理; |
with | with(info) {this} | 最后一行 | this | 1、直接调用一个对象的方法或者属性,可以省略对象名 |
7、讲述你做过的一个自定义View
详细地址:https://www.jianshu.com/p/19799a344e07
8、讲一下Kotlin协程
Kotlin协程是一种轻量级的并发编程框架,它基于协作式多任务处理模型,可以在单线程中实现异步、非阻塞的并发操作。
Kotlin协程的工作原理如下:
- 协程是
一种轻量级的线程
,它可以在一个线程中执行多个任务,而不需要创建多个线程。- 协程的执行是
基于挂起和恢复的机制,当一个协程遇到阻塞操作时,它会挂起当前任务,让出执行权给其他协程,等待阻塞操作完成后再恢复执行
。- Kotlin协程提供了
一套协程上下文和调度器的机制
,可以控制协程的执行环境和执行顺序。协程的创建和启动是通过协程构建器来实现的
,常用的协程构建器有launch、async和runBlocking
等。- Kotlin协程还提供了一些常用的协程操作符,如
delay、withContext和await等
,可以方便地实现异步、非阻塞的并发操作。
总之,Kotlin协程是一种高效、灵活、易用的并发编程框架,可以帮助开发者更好地处理异步、非阻塞的并发场景。
9、Kotlin的协程是如何切换线程的?
Kotlin的协程通过使用挂起函数来实现异步操作,而不需要使用回调函数或者线程池。
协程可以在不同的线程之间切换,以便在不阻塞主线程的情况下执行长时间运行的操作。协程的线程切换是通过调度器来实现的。
调度器是一个负责协程调度的组件,它可以将协程分配到不同的线程上执行。
Kotlin提供了几种不同的调度器,包括:
Dispatchers.Default:
使用共享的线程池来执行协程,适用于CPU密集型任务。Dispatchers.IO:
使用专门的线程池来执行协程,适用于IO密集型任务。Dispatchers.Main:
在Android平台上使用主线程来执行协程,适用于UI操作。
协程可以使用withContext函数来切换线程。withContext函数接受一个调度器作为参数,它会将当前协程挂起,并将其分配到指定的线程上执行。
例如:
//MainScope中默认的是Dispatchers.Main,是主线程
MainScope().launch {
loadData()
}
//挂起函数,不阻塞主线程
suspend fun loadData() {
val data = withContext(Dispatchers.IO) {
//在IO线程上执行长时间运行的操作
fetchDataFromNetwork()
//---当前线程是否主线程1:false 线程名字: DefaultDispatcher-worker-1
Log.d("lyy", "---当前线程是否主线程1:${isMainThread()} 线程名字: ${getThreadName()}")
}
//在主线程上更新UI
updateUI(data)
//---当前线程是否主线程2:true 线程名字: main
Log.d("lyy", "---当前线程是否主线程2:${isMainThread()} 线程名字: ${getThreadName()}")
}
/**
* 判断当前是否是主线程
*/
fun isMainThread(): Boolean {
if (Looper.myLooper() == Looper.getMainLooper()) {
// 当前线程是主线程
return true
}
// 当前线程不是主线程
return false
}
/**
* 获取线程的名字
*/
fun getThreadName(): String {
return Thread.currentThread().getName();
}
在上面的例子中,loadData函数会在IO线程上执行fetchDataFromNetwork函数,然后在主线程上更新UI。这样可以避免在主线程上执行长时间运行的操作,从而保持UI的响应性。
10、 Map的区别
类型 | 底层实现 | 是否线程安全 | 扩容方式 | 是否允许添加 null 元素 | 功能 |
---|---|---|---|---|---|
HashMap | jdk1.7采用数组+链表;jdk1.8 采用数组+红黑树 | 线程不安全 | HashMap默认的初始化大小为16。之后每次扩充,容量变为原来的2倍 | 允许key添加一个null元素 | 查询快,增删快,无序集合 |
LinkedHashMap | 是继承 HashMap 的,底层是数组(哈希表)+链表/红黑树,并自己维持了一个双向链表 ,按照插入顺序进行访问,实现了LRU算法 |
线程不安全 | LinkedHashMap 的扩容方式跟 HashMap 相同,当元素个数达到阈值时,会将数组容量扩大2倍 | 允许key添加一个null元素 | 查询快,增删快,有序集合 |
CocurrentHashMap | jdk1.7:Segments数组+ReentrantHashMap(作为互斥锁来控制并发访问 )+链表,采用分段锁保证安全性;jdk1.8:Striped locking(算法来实现互斥锁控制,避免了使用synchronized关键字对整个HashMap进行加锁的效率问题,提高了并发度和性能表现 ) + CAS算法 +红黑树 |
线程安全 | 当链表中元素个数超过默认设定(8个),当数组的大小还未超过64的时候,此时进行数组的扩容,如果超过则将链表转化成红黑树 | 不允许null键和值 | 查询快,增删快,无序集合(ConcurrentHashMap是线程安全的数组,是HashTable的替代品,同为线程安全,其性能要比HashTable更好) |
HashTable | 底层通过数组+链表的方式实现 | 线程安全(通过synchronized关键字实现线程安全) | 创建时如果不指定容量初始值,Hashtable默认的初始大小为11,之后每次扩容,容量变为原来的2n+1 | 允许null值作为key和value | 查询快,增删快,无序集合 |
TreeMap | 基于红黑树实现 | 线程不安全 | 新底层数组的长度是原长度的2倍 | key 不可以存入null | 查询快,增删快,有序集合 |
注意:Android 中常用的 map 集合:
类型 | 底层实现 | 是否线程安全 | 扩容方式 | 是否允许添加 null 元素 | 功能 |
---|---|---|---|---|---|
ArrayMap | ArrayMap 的底层实现基于数组和红黑树,适用于小型数据集 | 线程不安全 | 每次扩容时的新数组大小是原有数组大小的2倍 | 可以添加null元素 | 查询快,增删快,有序集合(相比HashMap内存空间占用更少) |
SparseArray | 它的底层实现主要依赖于两个数据结构:一个数组和一个 Map | 线程不安全 | 每次扩容时的新数组大小是原有数组大小的2倍 | 允许存储null元素 | 查询快,增删快,有序集合(Key 是 int 是代替 HashMap) |
11、简单介绍一下mvc、mvp和mvvm
MVC、MVP和MVVM都是一种软件架构模式,用于将应用程序的用户界面、数据和业务逻辑分离开来,以便更好地管理和维护应用程序。
MVC(Model-View-Controller
)是最早的一种模式,它将应用程序分为三个部分:模型(Model)、视图(View)和控制器(Controller)
。模型表示应用程序的数据和业务逻辑,视图表示用户界面,控制器负责协调模型和视图之间的交互。
-MVP(Model-View-Presenter)是MVC的变体
,它将控制器替换为Presenter,Presenter负责协调模型和视图之间的交互。MVP的优点是更好的可测试性和可维护性。
-MVVM(Model-View-ViewModel)是一种新的模式
,它将视图和模型之间的交互通过ViewModel来实现。ViewModel是一个中介者,它将模型的数据转换为视图可以使用的数据,并将视图的操作转换为模型可以理解的操作
。MVVM的优点是更好的可扩展性和可维护性。
网友评论