Java运算符

一:与运算符(&)

运算规则:

0&0=0;0&1=0;1&0=0;1&1=1

即:两个同时为1,结果为1,否则为0

例如:3&5

十进制3转为二进制的3:0000 0011

十进制5转为二进制的5:0000 0101

————————结果:0000 0001 ->转为十进制:1

即:3&5 = 1

二:或运算(|)

运算规则:

0|0=0; 0|1=1; 1|0=1; 1|1=1;

即 :参加运算的两个对象,一个为1,其值为1。

例如:3|5 即 00000011 | 0000 0101 = 00000111,因此,3|5=7。 

三:异或运算符(^)

运算规则:0^0=0; 0^1=1; 1^0=1; 1^1=0;

即:参加运算的两个对象,如果两个位为“异”(值不同),则该位结果为1,否则为0。

例如:3^5 = 0000 0011 | 0000 0101 =0000 0110,因此,3^5 = 6

应用

Android源码中常见的一些flag的运算的理解

a&~b: 清除标志位b

a|b : 添加标志位b

a&b: 取出标志位b

a^b: 取出a与b的不同部分

例子: a = 101 b = 010

那么:a |= b a= 111 可以看到b的标志位全部被添加进去了

a &= b 那么 a = 111 & 010 = 010 可以看到取出了标志位b

ViewGroup很少会用到得东西

轻量级的移除和添加view,addView和removeView都是重量级的操作,会触发重绘,导致卡顿。

  • detachViewFromParent(View view, int index, LayoutParams params)

  • removeDetachedView(View view,boolean animate)

效果:

图片替换文本

实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
private fun moveToTop(target: View) {
var mViewGroup = conn
//先确定现在在哪个位置
val startIndex: Int = mViewGroup.indexOfChild(target)
//计算一共需要几次交换,就可到达最上面
val count: Int = mViewGroup.childCount - 1 - startIndex
for (i in 0 until count) {
//更新索引
val fromIndex: Int = mViewGroup.indexOfChild(target)
//目标是它的上层
val toIndex = fromIndex + 1
//获取需要交换位置的两个子View
val to: View = mViewGroup.getChildAt(toIndex)

//先把它们拿出来 这个方法是project的,所以我们需要自定义viewgroup然后重载这个方法变成public
mViewGroup.detachViewFromParent(toIndex)
mViewGroup.detachViewFromParent(fromIndex)

//再放回去,但是放回去的位置(索引)互换了
mViewGroup.attachViewToParent(to, fromIndex, to.layoutParams)
mViewGroup.attachViewToParent(target, toIndex, target.layoutParams)
}
//刷新
mViewGroup.invalidate()
}

java阻塞队列之ArrayBlockingQueue

java阻塞队列之ArrayBlockingQueue - 北漂程序员 - 博客园 (cnblogs.com)

take:取出一个元素,它会将线程挂起,直到被中断或者取出成功。

poll:取出一个元素,取不到返回null,取得到返回值。不会挂起线程。

put:判断元素是否null,然后判断当前队列中的元素数量和队列的长度,如果二者相等则阻塞当前线程;

offer:队列中的元素数量和队列长度相等,则直接返回false,否则执行enqueue方法,put方法会将线程挂起,直到被中断或插入成功。

比较:

img1

Pr学习

d

手动标记卡点:

听音乐,然后通过按M进行标记 按Shift+M:移动标记点

利用插件进行标记

beatedit

Recycleview滑动分析

通过使用as的Profiler监听cpu的运行状况,我们可以更容易的了解到Recycleview滑动的执行过程。由下图可以看到,recycle view的滑动一次调用了:

1
2
3
4
5
6
7
8
9
RecyclerView.onTouchEvent()
RecyclerView.scrollByInternal()
RecyclerView.scrollStep()
LinearLayoutManager.scrollVerticallyBy()
LinearLayoutManager.scrollBy()
OrientationHelper.offsetChildren()
LayoutManager.offsetChildrenVertical()
RecyclerView.offsetChildrenVertical()
View.offsetTopAndBottom()

img1

获取Recycleview滑了多少距离得一个好方式:

1
2
3
4
5
6
7
public int getScollYDistance() {
LinearLayoutManager layoutManager = (LinearLayoutManager) this.getLayoutManager();
int position = layoutManager.findFirstVisibleItemPosition();
View firstVisiableChildView = layoutManager.findViewByPosition(position);
int itemHeight = firstVisiableChildView.getHeight();
return (position) * itemHeight - firstVisiableChildView.getTop();
}
img1

scrollByInternal

scrollStep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// Recycleview
void scrollStep(int dx, int dy, @Nullable int[] consumed) {
// 设置mInterceptRequestLayoutDepth++
startInterceptRequestLayout();
// 设置mLayoutOrScrollCounter++
onEnterLayoutOrScroll();

TraceCompat.beginSection(TRACE_SCROLL_TAG);
fillRemainingScrollValues(mState);

int consumedX = 0;
int consumedY = 0;
if (dx != 0) {
// consumedX代表这次recycleview滚动具体消耗调的滚动距离
consumedX = mLayout.scrollHorizontallyBy(dx, mRecycler, mState);
}
if (dy != 0) {
// consumedY代表这次recycleview滚动具体消耗调的滚动距离
// 在自定义LayoutManager的时候scrollVerticallyBy这个方法一定是要我们重写的
// 这是实现页面滚动的关键。这个方法的返回值也就是我们真正消耗掉的滚动距离
// 在LinearlayoutManger里面,
consumedY = mLayout.scrollVerticallyBy(dy, mRecycler, mState);
}

TraceCompat.endSection();
repositionShadowingViews();

onExitLayoutOrScroll();
stopInterceptRequestLayout(false);

if (consumed != null) {
consumed[0] = consumedX;
consumed[1] = consumedY;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// LinearLayoutManager
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
}
int scrollBy(int delta, RecyclerView.Recycler recycler, RecyclerView.State state) {
if (getChildCount() == 0 || delta == 0) {
return 0;
}
ensureLayoutState();
mLayoutState.mRecycle = true;
final int layoutDirection = delta > 0 ? LayoutState.LAYOUT_END : LayoutState.LAYOUT_START;
final int absDelta = Math.abs(delta);
updateLayoutState(layoutDirection, absDelta, true, state);
// 这里执行了很关键的 f
final int consumed = mLayoutState.mScrollingOffset
+ fill(recycler, mLayoutState, state, false);
if (consumed < 0) {
// consumed小于0代表我们的recycleview并没有消耗这次滑动
return 0;
}
final int scrolled = absDelta > consumed ? layoutDirection * consumed : delta;
// 这里会回调到recycleview里面执行offsetChildrenVertical方法。遍历全部的view执行offsetTopAndBottom方法。实现他们的位移变化。
// 既然这里是做了位移变化,那上面的fill方法是在干嘛?
mOrientationHelper.offsetChildren(-scrolled);
mLayoutState.mLastScrollDelta = scrolled;
return scrolled;
}

Kotlin之Flow学习

官方:kotlinx.coroutines.flow - kotlinx-coroutines-core

流到底是什么?流是可以实现断断续续的收集数据。

冷流 :只有订阅者订阅时,才开始执行发射数据流的代码。并且冷流和订阅者只能是一对一的关系,当有多个不同的订阅者时,消息是重新完整发送的。也就是说对冷流而言,有多个订阅者的时候,他们各自的事件是独立的。

热流:无论有没有订阅者订阅,事件始终都会发生。当 热流有多个订阅者时,热流与订阅者们的关系是一对多的关系,可以与多个订阅者共享信息。

创建Flow:

1
var f = flow<Int> { emit(1) }

single() : 这个流只能返回一个结果

1
2
3
4
var result = f.map {"笑了"+ it + "下"}
.map {"小王"+ it}
.single()
// 打印结果:result = "小王笑了1下"

collect: 打印每次的emit结果:

1
2
3
4
5
6
var f = flow<Int> {emit(1)emit(2)}
f.map {"笑了"+ it + "下"}
.collect{result->
Log.i("zjc","${result}")
}
// 答应结果: 笑了1下 笑了2下

flowOn: 指定当前流在哪个线程执行,他不会影响下一个流的执行线程:

1
2
3
4
5
6
7
lifecycleScope.launch {
f.map {Log.i("zjc","${Thread.currentThread().name}")}// 子线程
.flowOn(Dispatchers.IO)
.map {Log.i("zjc","${Thread.currentThread().name}")}// 主线程
.flowOn(Dispatchers.Main).collect {
}
}

filter操作符 我们可以对结果集添加过滤条件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

fun loadData1() = flow {
println("进入加载数据的方法")
for (i in 1..3) {
delay(1000)
emit(i)
}
}

runBlocking {
loadData1().filter {
it > 1
}.collect {
println(it)
}
}
// 结果:2 3

buffer操作符,尽可能减少发送数据和处理事件的时间 Kotlin Flow 看这一篇 带你入门~_黄林晴-CSDN博客

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var startTime: Long = 0
var endTime: Long = 0


fun loadData1() = flow {
startTime = System.currentTimeMillis() / 1000
for (i in 1..3) {
delay(1000)
emit(i)
}
}


fun main() {
runBlocking {
loadData1().collect { value ->
delay(2000)
println("$value")
}
endTime = System.currentTimeMillis() / 1000
println("处理时间:${endTime - startTime}s")
}
}
/**
*1 2 3
*处理时间 9s
*/

现在改成buffer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun main() {
runBlocking {
loadData1().buffer().collect { value ->
delay(2000)
println("$value")
}
endTime = System.currentTimeMillis() / 1000
println("处理时间:${endTime - startTime}s")
}
}
/**
*1 2 3
*处理时间 8s
*/

通过协程的方式下载文件

首先构建Retrofit:

1
2
3
4
5
6
7
OkHttpClient client = new OkHttpClient.Builder()
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("www.baidu.com")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
1
2
3
4
5
6
interface DownLoadService {
// 下载文件 fileUrl:文件的网络路径
@Streaming
@GET
suspend fun downloadFileWithDynamicUrlAsync(@Url fileUrl: String?): Response<ResponseBody>
}

进行网络请求:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
var app_dir = AppCache.getContext().getExternalFilesDir("")?.absolutePath
var IMAGE_DOWNLOAD_DIR = "$IMAGE_BASE_DIR/download/" // 原图保存目录
// downloadPath 文件下载地址
fun downLoad(downloadPath:String){
// 首先需要判断保存图片的目录存不存在 不存在创建
var fileRoot = File(IMAGE_DOWNLOAD_DIR)
if (!fileRoot.exists()) {
fileRoot.mkdirs()
}
lifecycleScope.launch {
var responseBoy = retrofit.create(DownLoadService::class.java).downService.downloadFileWithDynamicUrlAsync(downloadPath)
/**到这里我们就已经可以获取到对应文件的所有数据了
val body = responseBoy.body() ?: throw RuntimeException("下载出错")
//文件总长度
val length = body.contentLength()
//文件minetype
val contentType = body.contentType()
val ios = body.byteStream()
*/
// 我们需要读取请求得到的字节数据 然后写入文件
dowload(
this@PictureDetailsActivity,
responseBoy
) {
setFileName = {
fileName //如果在下载文件前知道文件的类型的话 可以直接在这里写上 **.jpg 或者 **.txt 记得一定要写明他的类型
}
success {url->
ImageView.setImageURI(FileUtil.getUrl(url.path.toString())
}
error {
Toast.makeText(this@PictureDetailsActivity, "原图下载失败", Toast.LENGTH_SHORT).show()
}
}.startDowload()
}

}

写流读取工具:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package com.chat.module_chat.util

import android.content.ContentResolver
import okhttp3.ResponseBody
import retrofit2.Response

/**
* @Author Yu
* @Date 2021/6/1 8:59
* @Description TODO
*/
import android.content.Context
import android.net.Uri
import android.os.Environment
import android.util.Log
import android.webkit.MimeTypeMap
import com.lin.baselib.util.TUIKitConstants
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.withContext
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import okhttp3.MediaType

/**
* Created by jingzz on 2020/7/9.
*/
typealias DOWLOAD_ERROR = (Throwable) -> Unit
typealias DOWLOAD_PROCESS = (downloadedSize: Long, length: Long, process: Float) -> Unit
typealias DOWLOAD_SUCCESS = (uri: Uri) -> Unit

public suspend fun dowload(
context: Context,
response: Response<ResponseBody>,
block: DowloadBuild.() -> Unit
): DowloadBuild {
val build = DowloadBuild(response, context)
build.block()
return build
}

public class DowloadBuild(val response: Response<ResponseBody>, var mContext: Context) {
private var error: DOWLOAD_ERROR = {} //错误贺词
private var process: DOWLOAD_PROCESS = { downloadedSize, filsLength, process -> } //进度
private var success: DOWLOAD_SUCCESS = {} //下载完成
private val context: Context = mContext //全局context
var setUri: () -> Uri? = { null } //设置下载的uri
var setFileName: () -> String? = { null } //设置文件名

fun process(process: DOWLOAD_PROCESS) {
this.process = process
}

fun error(error: DOWLOAD_ERROR) {
this.error = error
}

fun success(success: DOWLOAD_SUCCESS) {
this.success = success
}

suspend fun startDowload() {
withContext(Dispatchers.Main) {
//使用流获取下载进度
flow.flowOn(Dispatchers.IO)
.collect {
when (it) {
is DowloadStatus.DowloadErron -> error(it.t)
is DowloadStatus.DowloadProcess -> process(
it.currentLength,
it.length,
it.process
)
is DowloadStatus.DowloadSuccess -> success(it.uri)
}
}
}
}

val flow = flow<DowloadStatus> {
try {
val body = response.body() ?: throw RuntimeException("下载出错")
//文件总长度
val length = body.contentLength()
//文件minetype
val contentType = body.contentType()
val ios = body.byteStream()
var uri: Uri? = null
var file: File? = null
val ops = kotlin.run {
setUri()?.let {
//url转OutPutStream
uri = it
context.contentResolver.openOutputStream(it)
} ?: kotlin.run {
val fileName = setFileName() ?: kotlin.run {
//如果连文件名都不给,那就自己生成文件名
"${System.currentTimeMillis()}.${
contentType!!.subtype
}"
}
file =
File("${TUIKitConstants.IMAGE_DOWNLOAD_DIR}$fileName")
FileOutputStream(file)
}
}
//下载的长度
var currentLength: Int = 0
//写入文件
val bufferSize = 1024 * 8
val buffer = ByteArray(bufferSize)
val bufferedInputStream = BufferedInputStream(ios, bufferSize)
var readLength: Int = 0
while (bufferedInputStream.read(buffer, 0, bufferSize)
.also { readLength = it } != -1
) {
ops.write(buffer, 0, readLength)
currentLength += readLength
emit(
DowloadStatus.DowloadProcess(
currentLength.toLong(),
length,
currentLength.toFloat() / length.toFloat()
)
)
}
bufferedInputStream.close()
ops.close()
ios.close()
if (uri != null) {
emit(DowloadStatus.DowloadSuccess(uri!!))
} else if (file != null) {
emit(DowloadStatus.DowloadSuccess(Uri.fromFile(file)))
}

} catch (e: Exception) {
emit(DowloadStatus.DowloadErron(e))
}
}


}

sealed class DowloadStatus {
class DowloadProcess(val currentLength: Long, val length: Long, val process: Float) :
DowloadStatus()

class DowloadErron(val t: Throwable) : DowloadStatus()
class DowloadSuccess(val uri: Uri) : DowloadStatus()
}

这里使用了kotlin里面的flow 方法:

使用方法就是 创建一个流,然后在需要返回结果的使用掉用 emit() 就行。

Android消息机制

同步屏障

博客: Handler同步屏障机制的原理和使用场景_u013728021的博客-CSDN博客

Android中同步屏障的应用及简析 - 简书 (jianshu.com)

什么是同步屏障:

         Handler的消息分为两种:同步消息和异步消息。

1
2
3
4
5
6
7
8
9
10
11
12
//Message.java 获取这个消息属于哪种
public boolean isAsynchronous() {
return (flags & FLAG_ASYNCHRONOUS) != 0;
}
//Message.java 设置这个消息类型
public void setAsynchronous(boolean async) {
if (async) {
flags |= FLAG_ASYNCHRONOUS;
} else {
flags &= ~FLAG_ASYNCHRONOUS;
}
}

这两种消息在常规使用中毫无不同之处。但是当使用同步屏障后就不同了。

设置使用同步屏障后,只会获取和分发Looper里面的异步消息,如果没有异步消息,就会进入阻塞。

当设置了同步屏障之后,next函数将会忽略所有的同步消息,返回异步消息。换句话说就是,设置了同步屏障之后,Handler只会处理异步消息。再换句话说,同步屏障为Handler消息机制增加了一种简单的优先级机制,异步消息的优先级要高于同步消息。

如何使用

  1. 通过MessageQueue的postSyncBarrier方法在链表头部插入一个target = null 的Message
  2. 发送一个异步消息
    1. 1 发送异步消息的方式:Message.setAsynchronous(true)

设置同步屏障

通过MessageQueue里的postSyncBarrier函数设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private int postSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();
msg.markInUse();
msg.when = when;
msg.arg1 = token;

Message prev = null;
Message p = mMessages;
if (when != 0) {
while (p != null && p.when <= when) {
prev = p;
p = p.next;
}
}
// 下面这操作就是将target=null的message插到头部
if (prev != null) { // invariant: p == prev.next
msg.next = p;
prev.next = msg;
} else {
msg.next = p;
mMessages = msg; // 将当前msg设置给mMessages
}
return token;
}
}

方法分析:这个函数创建了一个Message对象然后插入到了消息列表头部的位置,这个消息的最大特征就是他的target为null。我们普通发的消息,在底层都设置了target的值。

1
2
3
4
5
6
//Handler.java
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
//...
return queue.enqueueMessage(msg, uptimeMillis);
}

总结:

同步屏障就是需要通过MessageQueue里的postSyncBarrier方法添加进入一个target字段为空的Message。

同步屏障的应用

View的创建: 在ViewRootImpl的requestLayout开启绘制机制的时候:

1
2
3
4
5
6
7
8
9
10
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();//检查是否在主线程
mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
//重要函数
scheduleTraversals();
}
}

Android框架为了更快的响应ui刷新事件在ViewRootImpl.scheduleTraversals中使用了同步屏障。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//设置同步障碍,确保mTraversalRunnable优先被执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//内部通过Handler发送了一个异步消息
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}

mTraversalRunnable 调用了 performTraversals 执行measure、layout、draw

为了让mTraversalRunnable尽快被执行,在发消息之前调用MessageQueue.postSyncBarrier设置了同步屏障。

Androi触摸事件分发流程

事件传递是从里向外触发,也就是说是父视图先拿到事件然后依次传到子view。

ViewGroup的dispatchTouchEvent(Event e),收到一个事件,首先判断这个view是不是可见然后它有没有被遮挡。如果可见并且没有遮挡就就可以进行事件的分发。在事件分发里面有一个很重要的参数mFirstTouchTarget,这个参数只有当我们的事件找到了那个可以消费他的那个view的时候,这个mFirstTouchTarget才会被附上值。

1
2
3
//可见并且没有遮挡就就可以进行事件的分发    
if (onFilterTouchEventForSecurity(ev)) {
}

ViewGroup的dispatchTouchevent这个方法返回false就代表这个事件这个viewGroup和他的所有子view都没有一个有能力进行消费的。

当事件可以进行分发的时候,判断是不是点击事件,是的话就重置所有状态,代表这是一轮新的事件循环:

1
2
3
4
5
if (actionMasked == MotionEvent.ACTION_DOWN) {
// 将 mFirstTouchTarget 设置为了 null
cancelAndClearTouchTargets(ev);
resetTouchState();
}

当是down事件或者mFirstTouchTarget不为null的时候,会调用viewGroup的onInterceptTouchEvent方法,判断自己这个viewGroup要不要拦截这个事件,如果拦截的话,那么这个viewGroup的所有子view都不会收到任何事件,然后被拦截的事件会交给自己这个viewGroup去处理。

1
2
3
4
5
6
 // intercepted 这是有没有拦截的标志,如果为true 代表拦截。那么里面的循环就不会执行,导致的结果就是里面的所有子view都不会收到任何事件
if (!canceled && !intercepted) {
final View[] children = mChildren;
for (int i = childrenCount - 1; i >= 0; i--) {
}
}

现在问题来了,那系统怎么知道你这个事件有没有被消费呢?

解答: 我们知道ViewGroup也是继承自view的。在view的源码里面,我们也能找到dispatchTouchEvent方法。在这个方法里面,正是我们点击按钮触发点击事件的调用地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// view.java
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;
final int actionMasked = event.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
stopNestedScroll();
}
// 判断这个view是不是可见并且没有遮挡
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
// 这里可以看到 当我们给view设置了mOnTouchListener,那么result就等于true,代表这个事件,我们的View必定会去消费。
// 如果设置了mOnTouchListener 那我们的setOnClickListener事件就不会响应了。原因是onTouchEvent事件不会被触发了
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}

if (!result && onTouchEvent(event)) {
result = true;
}
}
if (actionMasked == MotionEvent.ACTION_UP ||
actionMasked == MotionEvent.ACTION_CANCEL ||
(actionMasked == MotionEvent.ACTION_DOWN && !result)) {
stopNestedScroll();
}
return result;
}

通过上面代码可以看到。在view的dispatchTouchEvent方法里,只要这个方法返回了true,那么就代表此次事件找到了消费者。找到了那个真正会干掉它的人。

这里有一点很重要的知识点需要注意:事件有没有被消费都是在我们执行onTouchEvent或者mTouchListener的时候的返回值来决定的,如果这两个方法,我们没有返回true,那么就导致这个事件虽然传递到了我们真正点击的那个视图上面了,我们只能拿到down事件

现在来看看是怎么把事件分发到子view的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
for (int i = childrenCount - 1; i >= 0; i--) {
// 找到view的index
final int childIndex = getAndVerifyPreorderedIndex(
childrenCount, i, customOrder);
// 找到view
final View child = getAndVerifyPreorderedView(
preorderedList, children, childIndex);
//isTransformedTouchPointInView()用来判断点击的坐标是不是在这个view里面
if (!child.canReceivePointerEvents()
|| !isTransformedTouchPointInView(x, y, child, null)) {
continue;
}
// 坐标在这个子view里面
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}

resetCancelNextUpFlag(child);
// dispatchTransformedTouchEvent 判断这个view是否成功消费了事件
// 如果成功消费了事件,newTouchTarget 会被附上值 mFirstTouchTarget 会被附上值
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 进来了就代表我们找到了那个正真消费事件的人了
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
// 给newTouchTarget 附上值
// 给mFirstTouchTarget 附上值
newTouchTarget = addTouchTarget(child, idBitsToAssign);
// 标志着事件被消费
alreadyDispatchedToNewTouchTarget = true;
break;
}

// The accessibility focus didn't handle the event, so clear
// the flag and do a normal dispatch to all children.
ev.setTargetAccessibilityFocus(false);
}

// =null 代表事件没有被消费,需要viewgroup自己去消费
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
// Dispatch to touch targets, excluding the new touch target if we already
// dispatched to it. Cancel touch targets if necessary.
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
if (cancelChild) {
if (predecessor == null) {
mFirstTouchTarget = next;
} else {
predecessor.next = next;
}
target.recycle();
target = next;
continue;
}
}
predecessor = target;
target = next;
}
}

dispatchTransformedTouchEvent方法是用来判断这个事件有没有被成功消费,如果成功消费了,那么newTouchTarget 会被附上值 mFirstTouchTarget 会被附上值。这样就不会再走自己viewgroup的onTouchEvent事件,否则就执行自己的onTouchevent,判断需不需要消费这个事件。

这里着重讲下isTransformedTouchPointInView方法,他是怎么判断这个在屏幕按下的这一点是不是在子View内部的:

首先获取手指在父控件的相对坐标传给point数组。

然后执行transformPointToViewLocal 方法 将相对父控件的坐标转换成相对子view的坐标,中i但就是这个转换方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* x y 是手指按在父控件上的时候通过event.getX() event.getY()获取的值。
* getX获得的值 是手指相对于自己的坐标
*
*/
protected boolean isTransformedTouchPointInView(float x, float y, View child,
PointF outLocalPoint) {
final float[] point = getTempPoint();
point[0] = x;
point[1] = y;
transformPointToViewLocal(point, child);
final boolean isInView = child.pointInView(point[0], point[1]);
if (isInView && outLocalPoint != null) {
outLocalPoint.set(point[0], point[1]);
}
return isInView;
}
public void transformPointToViewLocal(float[] point, View child) {
// 这么一减,就得出了对应在子view里面的坐标
point[0] += mScrollX - child.mLeft;
point[1] += mScrollY - child.mTop;

//hasIdentityMatrix() 鉴定这个view所所对应的矩阵有没有应用过比如setTranslation setRotation setScale 这些方法。如果有返回false 没有返回true
if (!child.hasIdentityMatrix()) {
child.getInverseMatrix().mapPoints(point);
}
}
public boolean pointInView(float localX, float localY, float slop) {
return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
localY < ((mBottom - mTop) + slop);
}
img1

现在分析transformPointToViewLocal的计算原理:

img1

能执行dispatchTransformedTouchEvent方法的对象一定是一个viewgroup,他的执行需要传一个 child:View 的参数,这个参数如果为null就代表,执行super.dispatchTouchEvent(event);也就是自己父类的dispatchTouchEvent(自己的父类也就是view)。自己父类也就是view的dispatchTouchEvent方法干的事情上面讲了,用来设置这个事件有没有被消费。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
final int oldAction = event.getAction();
if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
event.setAction(MotionEvent.ACTION_CANCEL);
if (child == null) {
// 执行自己父类(view)的dispatchTouchEvent 也相当于去执行onTouchEvent方法
handled = super.dispatchTouchEvent(event);
} else {
// child不等于null代表去执行 自己孩子的dispatchEvent方法
handled = child.dispatchTouchEvent(event);
}
event.setAction(oldAction);
return handled;
}

// Calculate the number of pointers to deliver.
final int oldPointerIdBits = event.getPointerIdBits();
final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

// If for some reason we ended up in an inconsistent state where it looks like we
// might produce a motion event with no pointers in it, then drop the event.
if (newPointerIdBits == 0) {
return false;
}

// If the number of pointers is the same and we don't need to perform any fancy
// irreversible transformations, then we can reuse the motion event for this
// dispatch as long as we are careful to revert any changes we make.
// Otherwise we need to make a copy.
final MotionEvent transformedEvent;
if (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
event.offsetLocation(offsetX, offsetY);

handled = child.dispatchTouchEvent(event);

event.offsetLocation(-offsetX, -offsetY);
}
return handled;
}
transformedEvent = MotionEvent.obtain(event);
} else {
transformedEvent = event.split(newPointerIdBits);
}

// Perform any necessary transformations and dispatch.
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}

handled = child.dispatchTouchEvent(transformedEvent);
}

// Done.
transformedEvent.recycle();
return handled;
}

以上整个事件分发讲完。

总结:

事件最先由viewGroup进行分发,进入dispatchTouchEvent方法后,他会先掉用onInterceptTouchEvent方法判断事件是否拦截,如果拦截,也就是返回true,那么这个事件就交给自己这个viewgroup处理,执行他自己的onTouchEvent方法。如果不拦截返回false,那么进入for循环,遍历自己的全部子view,调用dispatchTransformedTouchEvent方法,依次调用子view的onTouchEvent方法,如果down事件还是没有被消费,然后就又回到自己这个viewgroup里面,执行子的onTouchEvent方法。

Activity的创建流程

在TransactionExecutor顺序调用了Activity全部生命周期:参考博客:(21条消息) Android | 基于Android9.0的startActivity流程分析(3):新Activity的onStart流程、onResume流程_明朗晨光的专栏-CSDN博客

先来看execute(ClientTransaction transaction)方法:

1
2
3
4
5
public void execute(ClientTransaction transaction) {
executeCallbacks(transaction);

executeLifecycleState(transaction);
}

在看看execteLifecycleState方法:

1
2
3
4
5
6
7
8
/** Transition to the final state if requested by the transaction. */
private void executeLifecycleState(ClientTransaction transaction) {
// Cycle to the state right before the final requested state.
cycleToPath(r, lifecycleItem.getTargetState(), true /* excludeLastState */, transaction);
// Execute the final transition with proper parameters.
lifecycleItem.execute(mTransactionHandler, token, mPendingActions);
lifecycleItem.postExecute(mTransactionHandler, token, mPendingActions);
}

在看cycleToPath方法:

1
2
3
4
5
6
7
8
9
10
11
12
private void cycleToPath(ActivityClientRecord r, int finish, boolean excludeLastState,
ClientTransaction transaction) {
final int start = r.getLifecycleState();
if (DEBUG_RESOLVER) {
Slog.d(TAG, tId(transaction) + "Cycle activity: "
+ getShortActivityName(r.token, mTransactionHandler)
+ " from: " + getStateName(start) + " to: " + getStateName(finish)
+ " excludeLastState: " + excludeLastState);
}
final IntArray path = mHelper.getLifecyclePath(start, finish, excludeLastState);
performLifecycleSequence(r, path, transaction);
}

再看performLifecycleSequence方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
ClientTransaction transaction) {
final int size = path.size();
for (int i = 0, state; i < size; i++) {
switch (state) {
case ON_CREATE:
mTransactionHandler.handleLaunchActivity(r, mPendingActions,
null /* customIntent */);
break;
case ON_START:
mTransactionHandler.handleStartActivity(r, mPendingActions);
break;
case ON_RESUME:
mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
false /* userLeaving */, 0 /* configChanges */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
mTransactionHandler.handleStopActivity(r.token, false /* show */,
0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
"LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
0 /* configChanges */, false /* getNonConfigInstance */,
"performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r.token, false /* start */);
break;
default:
throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
}
}
}

可以看到 依次完成了 Activity的全部生命周期的调用。mTransactionHandler的对象是ClientTransactionHandler,这个类是一个抽象类,他的唯一实现类是ActivityThread。当调用到ActivityThread的handleLaunchActivity的时候,在handlerLaunchActivity里面调用了performLaunchActivity创建了Activity对象,并完成了Activity的onCreate()。

Activity对象的创建是在ActivityThread里面的performLaunchActivity方法:

1
2
3
4
5
6
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent){
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
return activity
}

首先在performLaunchActivity里面完成了Activity对象的创建。

在看这串代码会发现一个很好的东西Instrumentation,Activity对象的实例话是通过它进行的,Activity的create方法也是最先由它调用的。

1
2
3
4
5
6
7
8
9
10
11
12
Activity对象的实例化
public Activity newActivity(ClassLoader cl, String className,
Intent intent)
return activity;
}
Activity的create调用
public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}

接着调用了activity的attach方法:

1
2
3
4
5
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);

在attach里面做了很多重要的事情:

  • 在performLaunchActity的时候并没给我们的Activity父类的mBase:Context赋值,赋值的地方就是在attach的时候。
  • 创建PhoneWindow对象。
  • 指定ui线程为当前Activity所在的线程
  • 给PhoneWindow设置WindowManager 也就是WindowManagerImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context); //给父类的mBase:Context赋值
mWindow = new PhoneWindow(this, window, activityConfigCallback);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
// 当软件盘的状态被指定了,则将软键盘状态设置为给定的
mWindow.setSoftInputMode(info.softInputMode);
}
mUiThread = Thread.currentThread();//指定ui线程为当前线程
// 给每个Activity的PhoneWindow创建WindowManagerImpl对象。
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
}

为什么要通过服务的方式获取WindManagerService,服务创建的WindowManagerService一样吗?

Activity通过执行attach方法,创建了Activity的Window对象,指定了ui线程,给window对象设置了WindowManagerIml。 一个Activity有一个window变量 一个mWindowManager变量 然后Winodw里面又保存了WindowManager对象。WindowManager里面由保存了window对象。

在performLaunchActivity()里面接着调用了callActivityOnCreate方法,去完成Activity的调用。

1
2
3
4
5
6
7
// 判断当前activity是不是数据持久化 
// 在Manifest里面申明 android:persistableMode="persistAcrossReboots"
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
  • 调用prePerformCreate(Activity)的时候往消息队列里面添加了一个启动activity的消息。

  • 然后activity调用performCreate去创建Activity

    这里没搞懂,为啥要往消息队列里面添加一个消息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 public void callActivityOnCreate(Activity activity, Bundle icicle) {
prePerformCreate(activity);
activity.performCreate(icicle);
postPerformCreate(activity);
}
// 启动Acitivity的时候往MessageQueue里面添加了一个消息
private void prePerformCreate(Activity activity) {
if (mWaitingActivities != null) {
synchronized (mSync) {
final int N = mWaitingActivities.size();
for (int i=0; i<N; i++) {
final ActivityWaiter aw = mWaitingActivities.get(i);
final Intent intent = aw.intent;
if (intent.filterEquals(activity.getIntent())) {
aw.activity = activity;
// 往消息队列里面添加一个消息
mMessageQueue.addIdleHandler(new ActivityGoing(aw));
}
}
}
}
}

现在来看Activity的performCreate方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
// 遍历调用全部有注册监听activity创建的监听
dispatchActivityPreCreated(icicle);
mCanEnterPictureInPicture = true;
restoreHasCurrentPermissionRequest(icicle);
if (persistentState != null) {
onCreate(icicle, persistentState);
} else {
onCreate(icicle);
}
writeEventLog(LOG_AM_ON_CREATE_CALLED, "performCreate");
mActivityTransitionState.readState(icicle);

mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
com.android.internal.R.styleable.Window_windowNoDisplay, false);
mFragments.dispatchActivityCreated(); // 这里也去调用了Fragment的创建流程
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
// 遍历调用全部注册有监听activty的postcreate监听
dispatchActivityPostCreated(icicle);
}

通过看源码,我们知道了另外一种用来全局监听activity状态的方式:就是在Application里面注册监听:

Application里面有个一公开接口 ActivityLifecycleCallbacks,我们可以站在全局的角度上观察所有activut的情况,使用方式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MainApplication : BaseApplication() {
override fun onCreate() {
registerActivityLifecycleCallbacks(object :ActivityLifecycleCallbacks{
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}

override fun onActivityStarted(activity: Activity) {}

override fun onActivityResumed(activity: Activity) {}

override fun onActivityPaused(activity: Activity) {}

override fun onActivityStopped(activity: Activity) {}

override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}

override fun onActivityDestroyed(activity: Activity) {}
})
}
}
至此走完了Activity生命周期的所有方法。

ActivityTaskManagerService.java

1
2
3
// 实现了转场动画
public void overridePendingTransition(IBinder token, String packageName,
int enterAnim, int exitAnim)

一个Activity的创建首先在Activity的attach里面创建了PhoneWindow对象。

在调用到Activity的onCreate方法时候,将attach里面创建的PhoneWindow对象传递给了AppCompatDelegate里面的mWindow对象。

1
2
3
4
5
6
// AppCompatDelegate
@Override
public void onCreate(Bundle savedInstanceState) {
ensureWindow();
mCreated = true;
}

接着我们在onCreate里面调用setContentView(int layoutId)方法。

​ 执行逻辑是:在setContentView里面给我们的PhoneWindow创建了DecorView对象,然后拿到PhoneWindow里面的content父控件,然后将自己的view通过add的方式添加到这个content的view里面。

1
2
3
4
5
6
7
8
9
10
11
12
13
// AppCompatActivity
public void setContentView(@LayoutRes int layoutResID) {
getDelegate().setContentView(layoutResID);
}
// AppCompatDelegateImpl
@Override
public void setContentView(View v) {
ensureSubDecor(); //创建我们DecorView mSubDecor
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}

现在来看下这个DecorView是怎么创建的:

1
2
3
4
5
6
7
8
9
10
11
// AppCompatDelegateImpl
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
// 创建DecorView
mSubDecor = createSubDecor();
}
}
private ViewGroup createSubDecor() {


}

上面走完了Activity的onCreate方法,现在看下onStart执行流程。

在ActivityThread的handleStartActivity(ActivityClientRecord r,PendingTransactionActions pendingActions):

1
2
3
4
5
6
7
8
9
10
11
12
//  ActivityThread
public void handleStartActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions) {
final Activity activity = r.activity;
// Start
activity.performStart("handleStartActivity");
r.setState(ON_START);
}
// Activity
final void performStart(String reason) {
mInstrumentation.callActivityOnStart(this); //执行了Activity的start方法
}

上面走完了onStart方法。也没干什么


现在来看下onResume的执行流程:

在onResume里面干了最重要的事就是创建ViewRootImpl对象。

首先通过Activity获取对应的WindowManager。Activity的WindowManger对象是在Activity的attach放里面创建的。可以看上面的文章写了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// ActivityThread
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
// 这里执行了Activity的onResume 方法
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
// 获取在attach的时候创建的WindowManager对象
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
l.softInputMode |= forwardBit;
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// 第一次执行onResume的时候这里获取的是null
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 这里很关键,去创建了我们的ViewRootImpl
// 也将我们的DecorView添加到Window里面。
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
......省略代码
}

再来看看刚才添加Decorview到Window的操作:

1
2
3
4
5
6
7
8
9
10
if (!a.mWindowAdded) {
a.mWindowAdded = true;
wm.addView(decor, l);
}
// WindowManagerImpl
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

可以看到,WindowManagerImpl将添加view的操作交给了WindowManagerGlobal去做。

现在来看下WindowManagerGlobal是如何添加的:

1
2
3
4
//首先看下WindowManagerGlobal是怎么创建的:
// 可以看到他是透过懒加载的方式,创建了一个单例对象。
// 也就是说我们每个Activity的创建走的都是同一个对象。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
ViewRootImpl root;
View panelParentView = null;
// 创建了我们的ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
// 开始一个view从测量到布局的整个流程。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}

Android 应用框架中为了更快的响应UI刷新事件在 ViewRootImpl.scheduleTraversals 中使用了同步屏障。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
requestLayout(); //可以看到ViewRootImpl主动调用了requestlayout
}
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();// 按计划进行分发
}
}

final ViewRootHandler mHandler = new ViewRootHandler();
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
// 通过postSyncBarrier()设置同步屏障 目的是为了让绘制流程更快的被执行
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//handler发送消息,有了同步屏障mTraversalRunnable就会被优先执行。
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}

可以看到这里使用了消息机制里面的 同步屏障 机制。目的是为了让绘制流程尽快被执行。

设置同步屏障之后,next函数里面会忽略所有的同步消息,返回异步消息。换句话说就是Handler消息机制增加了一种简单的预先级制度,异步消息的优先级高于同步消息。

同步屏障就是将新的这个message插入到了表头的位置。

文章:Android中同步屏障的应用及简析 - 简书 (jianshu.com)

博客:《Android消息机制》

在mTraversalRunnable里面执行整个view的测量布局绘制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// ViewRootImpl
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal(); // 执行了全部的测量绘制布局过程
}
}

void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
// 移除同步锁
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals(); // 执行了全部的测量绘制布局过程
}
}

private void performTraversals() {
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
performLayout(lp, mWidth, mHeight);
performDraw();
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}

计算的时候先进入到 View的measure方法:

1
2
// 将大小和方式融合在一起
makeMeasureSpec(int size,int mode)
1
2
3
4
5
6
7
8
9
// View.java
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
onMeasure(widthMeasureSpec, heightMeasureSpec);
}
// 给我们的view设置指定大小
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

当时viewGroup的时候,都是重载了onMeasure方法。因为我们还需要计算viewgroup里面的子view的大小,才能确定我们的viewgroup的大小是多少。

这里我们来看下LinearLayout的onMeasure方法:

1
2
3
4
5
6
7
8
9
// LinearLayout
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (mOrientation == VERTICAL) {
measureVertical(widthMeasureSpec, heightMeasureSpec);
} else {
measureHorizontal(widthMeasureSpec, heightMeasureSpec);
}
}

总结: 一个view的测量过程都是首先通过view的measure执行下去,然后不同的view通过重写onMeasure方法实现对他们所有子view的测量。

上面就是从ViewRootImpl触发整个页面的执行测量的过程。


ViewRootImpl:performLayout()

1
2
3
4
5
6
7
8
9
// ViewRootImpl  
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
// View
public void layout(int l, int t, int r, int b) {
onLayout(changed, l, t, r, b);
}

通过查看源代码,发现可以使用 addOnLayoutChangeListener()方法,监听某个view布局的改变。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
viewpager.addOnLayoutChangeListener(object :View.OnLayoutChangeListener{
override fun onLayoutChange(
v: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int
) {
// 知道了开始位置 和 结束位置 不就可以做动画了嘛
}
})

至此 layout分析完了


performDraw