从输入cnblogs.com到新浪首页完全呈现发生了如何

物理传输

地点这么多层其实都是在为不同的目标对要传输的数据开展打包处理,而物理层则是通过各类传导介质(双绞线,电磁波,光纤等)以信号的花样将方面各层封装好的数码物理传送过去。

从这之后一个 http
请求漂洋过海终于抵达了服务器,接下去就是从物理层到应用层向上传递,将包装的数目一层层剥开,服务器在应用层得到最原始的乞请信息后飞快处理完,然后就起来向客户端发送响应音信。这一次是以服务器为起源,客户端为终端再走两回五层协议栈。

服务器的响应消息跋山跋涉终于到达了浏览器,接下去就是页面渲染(更现实可参照浏览器内部工作原理)。

页面的渲染工作关键由浏览器的渲染引擎来完成(这里以Chrome为例)。

示例一,默认处境下的轩然大波传递流程

始建3个类,一个Activity、一个继续自LinearLayout的View,一个连续自Button的View,不分厚薄写他们的dispatchTouch伊芙(Eve)nt()、onIntercepteTouch伊夫nt()、onTouch伊夫nt(),五个类及布局文件的代码如下:

  • EventDispatchActivity

/**
 * 事件分发机制测试Activity
 * Created by liuwei on 18/1/5.
 */
public class EventDispatchActivity extends AppCompatActivity {

    private final static String TAG = "Activity";//EventDispatchActivity.class.getSimpleName();

    private EventDispatchTestView edtv_test;
    private EventDispatchLinearLayout edll_test;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_dispatch);
        edtv_test = ViewUtils.find(this, R.id.edtv_test);
        edll_test = ViewUtils.find(this, R.id.edll_test);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        // 被调用时输出log,event.getAction表示事件的类型,0:ACTION_DOWN,1:ACTION_UP,2:ACTION_MOVE。

        Log.i(TAG, "dispatchTouchEvent: " + event.getAction() + " | 分发事件");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + true);
        return super.onTouchEvent(event);
    }
}
  • EventDispatchLinearLayout

/**
 * 事件分发机制测试 ViewGroup
 * Created by liuwei on 18/1/5.
 */
public class EventDispatchLinearLayout extends LinearLayout {

    private final static String TAG = "——Layout";//EventDispatchLinearLayout.class.getSimpleName();


    public EventDispatchLinearLayout(Context context) {
        super(context);
    }

    public EventDispatchLinearLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(TAG, "dispatchTouchEvent: " + event.getAction() + " | 分发事件");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        Log.i(TAG, "onInterceptTouchEvent: " + event.getAction() + " | 是否拦截:" + false);
        return super.onInterceptTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + false);
        return super.onTouchEvent(event);
    }
}
  • EventDispatchTestView

/**
 * 事件分发机制测试 View
 * Created by liuwei on 18/1/5.
 */
public class EventDispatchTestView extends Button {

    private final static String TAG = "————View";//EventDistpatchTestView.class.getSimpleName();

    public EventDispatchTestView(Context context) {
        super(context);
    }

    public EventDispatchTestView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.i(TAG, "dispatchTouchEvent: " + event.getAction() + " | 分发事件");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + true);
        return super.onTouchEvent(event);
    }
}
  • 布局文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="cn.codingblock.view.event_dispatch.EventDispatchActivity">

    <cn.codingblock.view.event_dispatch.EventDispatchLinearLayout
        android:id="@+id/edll_test"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#cccccc">

        <cn.codingblock.view.event_dispatch.EventDispatchTestView
            android:id="@+id/edtv_test"
            android:layout_width="300dp"
            android:layout_height="300dp"
            android:layout_margin="10dp"
            android:background="#000000"/>

    </cn.codingblock.view.event_dispatch.EventDispatchLinearLayout>

</LinearLayout>

运转代码,点击伊芙(Eve)ntDispatchTestView(藏肉色区域),log输出如下(log中的数字代表事件的档次,0:ACTION_DOWN,1:ACTION_UP,2:ACTION_MOVE):

01-05 16:58:05.574 23295-23295/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-05 16:58:05.574 23295-23295/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:true

01-05 16:58:05.611 23295-23295/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 2 | 是否拦截:false
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/————View: dispatchTouchEvent: 2 | 分发事件
01-05 16:58:05.611 23295-23295/cn.codingblock.view I/————View: onTouchEvent: 2 | 是否消耗事件:true

01-05 16:58:05.619 23295-23295/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 16:58:05.619 23295-23295/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-05 16:58:05.619 23295-23295/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 1 | 是否拦截:false
01-05 16:58:05.620 23295-23295/cn.codingblock.view I/————View: dispatchTouchEvent: 1 | 分发事件
01-05 16:58:05.620 23295-23295/cn.codingblock.view I/————View: onTouchEvent: 1 | 是否消耗事件:true

由log可以看来ViewGroup的onInterceptTouch伊芙nt方法默认是不阻碍事件的,View的onTouch伊夫nt方法默认消耗事件。事件流的ACTION_DOWN类型Motion
伊芙(Eve)nt率先到达View的onTouch伊夫nt方法中,此时onTouch伊夫nt方法重返true,表示要处理事件,所以事件流的存续部分仍然通过log中的流程到达了View的onTouch伊夫nt方法中。

从输入 cnblogs.com 到知乎首页完全显示这些过程可以大概分成 网络通信
页面渲染 五个步骤。

示例三、在示例二的基本功上让ViewGroup消耗事件

修改伊夫ntDispatchLinearLayout的onTouch伊夫(Eve)nt(),让其重返true。

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + true);
    return true;//super.onTouchEvent(event);
}

测试log如下:

01-05 18:34:53.409 21169-21169/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-05 18:34:53.409 21169-21169/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-05 18:34:53.410 21169-21169/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:false
01-05 18:34:53.410 21169-21169/cn.codingblock.view I/——Layout: onTouchEvent: 0 | 是否消耗事件:true

01-05 18:34:53.420 21169-21169/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 18:34:53.420 21169-21169/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-05 18:34:53.420 21169-21169/cn.codingblock.view I/——Layout: onTouchEvent: 2 | 是否消耗事件:true

01-05 18:34:53.470 21169-21169/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 18:34:53.470 21169-21169/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-05 18:34:53.470 21169-21169/cn.codingblock.view I/——Layout: onTouchEvent: 1 | 是否消耗事件:true

此种情状下,事件流的ACTION_DOWN先到达View的onTouch伊芙nt,发现它不消耗事件,继而再次来到上级的ViewGroup的onTouch伊夫nt中,发现它要消耗事件,事件流的继续部分就不在传递给View,也不在调用ViewGroup的onInterceptTouch伊芙(Eve)nt方法,因为早已了解View不处理事件,所以没必要再经过onInterceptTouch伊夫(Eve)nt方法来判定了。

绘制(paint)

制图阶段,遍历render树并调用渲染对象的paint方法将它们的始末呈现在屏幕上。和布局一样,绘制也可以是全局的(绘制完整的树)或增量的。在增量的绘图过程中,一些渲染对象以不影响整棵树的措施改变,改变的渲染对象使其在屏幕上的矩形区域失效(invalidate),这将招致操作系统将其看做dirty区域,并暴发一个paint事件,操作系统很抢眼的处理这些进程,并将六个区域统一为一个。

浏览器总是试着以细小的动作响应一个扭转,所以一个因素颜色的成形将只造成该因素的重绘,元素地点的变化将招致元素的布局和重绘,添加一个dom节点,也会促成这一个因素的布局和重绘。一些最紧要的更动,比如扩充html元素的字号,将会导致缓存失效,从而引起一切render树的布局和重绘。

等到绘制完毕,页面就全盘地显现在大家眼前了。

看似再简单可是的操作,背后匡助的技术链已经复杂到不行想像。上边只是起始的轮廓,其中的每一步深挖进去都是一门大学问。不过我们前端理解一下就行了,没必要较这些劲,不然就舍本逐最终。

以为不错就点个推荐呢:)

Motion伊夫(Eve)nt事件传递过程

当手指引击屏幕爆发一个Touch事件后,事件遵照Activity->Window->View的逐一依次传递。

率先会传送给Activity的dispatchTouch伊夫nt(),在此办法内部会将由Window处理,接着事件会传送给根View,根View接收到事件后就会按照事件分发机制去处理事件。

根View在此间就是一个ViewGroup,它在承受到事件后会调用dispatchTouch伊夫nt(),在此情势内部会由此onInterceptTouch伊芙nt()方法判断是否拦截事件,假诺onInterceptTouch伊夫(Eve)nt()重返true就象征它要阻拦事件,事件将传递给当下ViewGroup的onTouch伊夫(Eve)nt()。尽管onInterceptTouch伊芙(Eve)nt()放回false就表示它不阻碍事件,事件将传给其下属的View,调用下级View的dispatchTouch伊芙(Eve)nt()。

根View的部属View可能又是一个ViewGroup,假诺这样的话其传递流程同根View一样。无论根View的部下View是不是ViewGroup,如果不阻碍事件,最终事件会传递到一个纯View的控件上。

当一个View(纯View控件)接收到事件后,也会调用其dispatchTouch伊芙(Eve)nt(),然后在此方法内部会调用当前View的onTouch伊芙nt(),如若onTouch伊夫nt()再次来到true则象征要拍卖此事件。假如回到false表示不消耗事件,其顶头上司View的onTouch伊芙nt()将被调用,则事件流的持续部分不再传递到眼前View,在一个风波流中也不会再调用当前View的dispatchTouch伊夫(Eve)nt()。

接下去通过切实示例来查阅事件传递的流水线:

发送 http 请求

HTTP也是应用层协议。HTTP(HyperText Transport
Protocol)定义了一个基于请求/响应格局的、无状态的、应用层的商事,用于从万维网服务器传输超文本到本地浏览器。绝大多数的Web开发,都是构建在HTTP协议之上的Web应用。客户端社团并发送
http 请求报文,包含 method、url、host、cookie
等新闻,下面是造访和讯首页时 http 请求报文的样板:

GET https://www.cnblogs.com/ HTTP/1.1
Host: www.cnblogs.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: __gads=ID=b62b1e22b7de2e02:T=1493954370:S=ALNI_MYRebVRavER2PJmwdeFwpl33ACNoQ;
If-Modified-Since: Mon, 27 Nov 2017 12:21:04 GMT

请求头里的各个字段都有独家的功力,具体意思可查看 http 协议相关作品。

示例二、在示例一的基本功上,让View的onTouch伊夫(Eve)nt不消耗事件时的传递流程

接下去让地点的伊芙ntDispatchTestView的onTouch伊夫nt再次来到false:

@Override
public boolean onTouchEvent(MotionEvent event) {
    Log.i(TAG, "onTouchEvent: " + event.getAction() + " | 是否消耗事件:" + false);
    return false;//super.onTouchEvent(event);
}

测试log如下:

01-05 18:18:52.545 10771-10771/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 18:18:52.545 10771-10771/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:false
01-05 18:18:52.546 10771-10771/cn.codingblock.view I/——Layout: onTouchEvent: 0 | 是否消耗事件:false
01-05 18:18:52.547 10771-10771/cn.codingblock.view I/Activity: onTouchEvent: 0 | 是否消耗事件:true

01-05 18:18:52.629 10771-10771/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 18:18:52.629 10771-10771/cn.codingblock.view I/Activity: onTouchEvent: 2 | 是否消耗事件:true

01-05 18:18:52.630 10771-10771/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 18:18:52.630 10771-10771/cn.codingblock.view I/Activity: onTouchEvent: 1 | 是否消耗事件:true

当View的onTouch伊芙(Eve)nt不消耗事件时,事件会交到ViewGroup的onTouch伊芙nt方法处理,而从log可以观望ViewGroup的onTouch伊芙(Eve)nt默认也不消耗事件,所以事件由提交Activity的onTouch伊夫(Eve)nt方法处理,最终事件流的接轨部分不再传递给ViewGroup和View,而是一贯传送给Activity的onTouch伊芙nt处理。

页面渲染主流程

下面是渲染引擎在拿到内容后的中坚流程:

解析html构建dom树 -> 解析css构建render树 -> 布局render树 ->
绘制render树

渲染引擎首先开头解析html,并将标签转化为dom树中的dom节点。接着,它解析外部css文件及style标签中的样式信息,这些样式信息以及html标签中的可见性指令将被用来构建另一棵树——render树。render树构建好了后来,将会执行布局过程,该过程将确定render树每个节点在屏幕上的恰当坐标。最终是绘制render树,即遍历render树的每个节点并将它们绘制到屏幕上。

偷了一张图片(Chrome和Safari所用内核webkit页面渲染主流程):

图片 1
webkit页面渲染主流程

为了更好的用户体验,渲染引擎将会尽可能早地将内容绘制在屏幕上,而不会等到独具的html都分析完成后再去构建、布局和制图render树,它是分析完部分情节就绘制一部分内容,同时可能还在经过网络下载另外内容(图片,脚本,样式表等)。比如说,浏览器在代码中窥见一个
img
标签引用了一张图纸,于是就向服务器发出图片请求,此时浏览器不会等到图片下载完,而是会持续分析渲染前面的代码,等到服务器重回图片文件,由于图片占用了一定面积,影响了后头段落的布局,浏览器就会回过头来重新渲染这一部分代码。

示例五、给View绑定OnTouchListener和OnClickListener监听器。

在伊夫(Eve)ntDispatchActivity的onCreate()方法里面添加如下代码,并将伊夫(Eve)ntDispatchLinearLayout和伊夫(Eve)ntDispatchTestView的各艺术的重回值都还原成示例一中的状态。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_event_dispatch);
    edtv_test = ViewUtils.find(this, R.id.edtv_test);
    edll_test = ViewUtils.find(this, R.id.edll_test);

    edtv_test.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // 为了log显示的层次更加清晰,这里的TAG使用View的TAG
            Log.i("————View", "onTouch: 返回 " + false);
            return false;
        }
    });

    edtv_test.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // 为了log显示的层次更加清晰,这里的TAG使用View的TAG
            Log.i("————View", "onClick: ");
        }
    });
}

测试log如下:

01-06 19:35:07.563 6737-6737/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/————View: onTouch: 返回 false
01-06 19:35:07.563 6737-6737/cn.codingblock.view I/————View: onTouchEvent: 0 | 是否消耗事件:true

01-06 19:35:07.573 6737-6737/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 2 | 是否拦截:false
01-06 19:35:07.573 6737-6737/cn.codingblock.view I/————View: dispatchTouchEvent: 2 | 分发事件
01-06 19:35:07.574 6737-6737/cn.codingblock.view I/————View: onTouch: 返回 false
01-06 19:35:07.574 6737-6737/cn.codingblock.view I/————View: onTouchEvent: 2 | 是否消耗事件:true

01-06 19:35:07.673 6737-6737/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 1 | 是否拦截:false
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/————View: dispatchTouchEvent: 1 | 分发事件
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/————View: onTouch: 返回 false
01-06 19:35:07.674 6737-6737/cn.codingblock.view I/————View: onTouchEvent: 1 | 是否消耗事件:true
01-06 19:35:07.704 6737-6737/cn.codingblock.view I/————View: onClick: 

接下来再下面修改代码,让onTouch()方法消耗事件,也就是重回true,再观看log:

edtv_test.setOnTouchListener(new View.OnTouchListener() {
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        // 为了log显示的层次更加清晰,这里的TAG使用View的TAG
        Log.i("————View", "onTouch: 返回 " + false);
        return false;
    }
});

log如下:

01-07 11:03:55.411 2757-2757/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:false
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/————View: dispatchTouchEvent: 0 | 分发事件
01-07 11:03:55.412 2757-2757/cn.codingblock.view I/————View: onTouch: 返回 true

01-07 11:03:55.542 2757-2757/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 2 | 是否拦截:false
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/————View: dispatchTouchEvent: 2 | 分发事件
01-07 11:03:55.542 2757-2757/cn.codingblock.view I/————View: onTouch: 返回 true

01-07 11:03:55.560 2757-2757/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 1 | 是否拦截:false
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/————View: dispatchTouchEvent: 1 | 分发事件
01-07 11:03:55.560 2757-2757/cn.codingblock.view I/————View: onTouch: 返回 true

从log中我们可以看到:

  • 为View绑定的OnTouchListener中的onTouch()方法是预先于View的onTouch伊夫nt()方法执行的。倘诺在onTouch()消耗了风波(重回true),那么事件将不在传递给onTouch伊芙(Eve)nt()方法,最后也不会调用onClick()方法。
  • 为View绑定的OnClickListener中的onClick()方法优先级最低,是在方方面面事件流停止后才会被调用,也就是需要通过手指的按下–抬起这些进程才会触发onClick()方法。

IP 寻址

Internet Protocol
是概念网络之间互相互联规则的情商,紧要解决逻辑寻址和网络通用数据传输格式五个问题。

享有连接到因特网上的装备都会被分配一个唯一的 IP
地址,就像网购时填写的收货地址一样。由于一个网络设施的 IP
地址可以转换,可是 MAC
硬件地址(就像身份证号)一般是原则性不变的,所以率先利用 ARP
协议来找到对象主机的 MAC
硬件地址。当通信的两端不在同一个局域网时,需要频繁倒车(路由器)才能找到最后的对象,在转化的进程中还需要通过下一个中转站的
MAC 地址来搜寻下一个转化目标。

传输层传来的 TCP 报文会在这一层被 IP
封装成网络通用传输格式——IP数据包,IP
数据包是真正在网络间开展传输的数额基本单元。

因而逻辑寻址定位到前边应用层 DNS 解析出来的 IP
地址的主机网络地方,然后把数量以 IP 数据包的格式发送到这去。

小结

为了更好的理解,可以把事件流看成是一队人,把ACTION_DOWN类型看做探路人,探路人按规定的线路先走一回,直到走到View的onTouch伊夫(Eve)nt这里,假若onTouch伊夫nt再次来到true,可分晓成此路通,后续部队可以回复。假使回去false,可以了然成此路不通,然后探路人再到Layout(ViewGroup)的onTouch伊芙(Eve)nt中问路通不通,假使通的话后续部队就不要再去View这里了,直接到ViewGroup那来就可以了。而只要ViewGroup那里路也不通,那么探路人就只能去Activity的onTouch伊夫nt这里了,后续部队也直接去Activity的onTouch伊芙(Eve)nt那里就足以了。


说到底想说的是,本体系小说为博主对Android知识举行再一次梳理,查缺补漏的读书过程,一方面是对友好忘记的事物加以复习重新明白,另一方面相信在重新学习的历程中定会有巨大的新拿到,假若你也有跟自己一样的想法,不妨关注自己一块念书,相互探究,共同提升!

参考文献:

  • 《Android开发情势探索》

# 控制缓存

将静态资源强制缓存在客户端,通过添加文(加文(Gavin))件指纹等方法使客户端只请求暴发了改动的资源,可实用降低静态资源请求数量。具体可参考前端静态资源缓存控制策略

dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent

  • boolean dispatchTouchEvent (MotionEvent event):

分发事件,只要事件能传递到当下View就一定会调用此方法,其再次回到值是一个布尔类型表示是否消耗事件。重返true代表消耗事件,事件流的延续部分还会随之传递过来;重临false代表不消耗事件,事件流的继承部分就不再传递于此。

  • boolean onInterceptTouchEvent (MotionEvent ev):

此方法表示是否拦截Motion伊夫nt事件,唯有ViewGroup类型的控件才有此方法。假使此措施再次回到true表示拦截事件,事件将传递给当下View的onTouch伊夫(Eve)nt()方法,而不再向其下属的View传递。假如此办法重回false表示不阻止事件,事件将传递给下级View的dispatchTouch伊夫nt()。

  • boolean onTouchEvent (MotionEvent event):

此方法用来拍卖Motion伊夫(Eve)nt,重临值表示是否消耗事件。重返true表示消耗事件,那么事件流的继承部分还会传送过来;重回false表示不消耗事件,事件将提交上级View的onTouch伊夫(Eve)nt()处理,要是下面View的onTouch伊芙(Eve)nt()依旧重回false,那么事件将再交给上级的顶头上司处理,以此类推,尽管各级View的onTouch伊夫nt()都不消耗事件,那么事件最后将交由Activity的onTouch伊芙(Eve)nt()处理。

上文说了如此多依旧不够具体,先用流程图大体表达一个上述两个格局的涉嫌,及调用流程,下文还会结合实际示例详细表达在事变分发传递中各类艺术的调用规则。

三者关系大体如下图:

图片 2

网络通信走的五层因特网协议栈(OSI标准是七层模型,但实际贯彻普通是五层)。画了一张图:

示例四、假诺在ViewGroup的onInterceptTouch伊芙(Eve)nt中回到了true拦截了事件,整个事件将不再传递给View而是直接交由ViewGroup的onTouch伊芙nt处理。

修改伊夫ntDispatchLinearLayout的onInterceptTouch伊芙(Eve)nt(),让其重回true。

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    Log.i(TAG, "onInterceptTouchEvent: " + event.getAction() + " | 是否拦截:" + true);
    return true;//super.onInterceptTouchEvent(event);
}

测试log如下:

01-05 19:03:21.788 9733-9733/cn.codingblock.view I/Activity: dispatchTouchEvent: 0 | 分发事件
01-05 19:03:21.789 9733-9733/cn.codingblock.view I/——Layout: dispatchTouchEvent: 0 | 分发事件
01-05 19:03:21.789 9733-9733/cn.codingblock.view I/——Layout: onInterceptTouchEvent: 0 | 是否拦截:true
01-05 19:03:21.789 9733-9733/cn.codingblock.view I/——Layout: onTouchEvent: 0 | 是否消耗事件:true
01-05 19:03:21.819 9733-9733/cn.codingblock.view I/Activity: dispatchTouchEvent: 2 | 分发事件
01-05 19:03:21.819 9733-9733/cn.codingblock.view I/——Layout: dispatchTouchEvent: 2 | 分发事件
01-05 19:03:21.819 9733-9733/cn.codingblock.view I/——Layout: onTouchEvent: 2 | 是否消耗事件:true
01-05 19:03:21.877 9733-9733/cn.codingblock.view I/Activity: dispatchTouchEvent: 1 | 分发事件
01-05 19:03:21.877 9733-9733/cn.codingblock.view I/——Layout: dispatchTouchEvent: 1 | 分发事件
01-05 19:03:21.877 9733-9733/cn.codingblock.view I/——Layout: onTouchEvent: 1 | 是否消耗事件:true

# Connection: keep-alive,复用已建立的连接

在 http 早期,每个 http 请求都要开辟一个 tcp
连接,请求完就倒闭这一个连续,导致每个请求都要来几次“一回握手”和“两回挥手”,从而磨磨唧唧多出去大量无谓的等候时间。就好比出去吃饭,等饭等半个钟头,端上来十分钟吃完了,结账排队又等了半个钟头,假如刚进来就吃现成的吃完就跑这多爽啊。keep-alive
干的就是这件事,当第一个请求数据传输截止之后,服务器说“客户端你不要关闭这么些连续,直接换下个请求,我不想再握你的破手了”。那样下个请求就径直传输数据而不用先走“五次握手”的流程了。这好比你又去就餐,吃你最欢喜的红烧肉,酒店在今日先是个客人点红烧肉的时候就炒了一大锅红烧肉,你点餐的时候一直吃现成的就行了,吃完直接跑,哈哈美滋滋。

怎么是事件分发机制?

说了半天的事件分发机制这究竟是个啥东西吧?大家不要把它想象的那么高深莫测,不要在思想上给协调设上阻碍,其实很容易明白,博主的了解是:简单的话,事件分发机制就是Android系统对事件传递过程规定的一种事件传递规则,事件都会依据这多少个规则进行分发传递。

在研讨事件分发机制在此之前,我们先来规定一下解析的步调,化整为零,各类击破:

  • 弄了然分析目的:Motion伊夫nt。
  • 摸底六个法子:dispatchTouch伊芙(Eve)nt(Motion伊芙nt
    event)、onInterceptTouch伊芙nt(Motion
    event)、onTouch伊夫(Eve)nt(Motion伊夫nt event)。
  • Motion伊夫nt事件的传递过程
  • 小结

五层因特网协议栈

MotionEvent

实际上点击事件的散发过程就是对Motion伊芙(Eve)nt事件的分发过程,当用户点击操作按下后,Motion伊夫(Eve)nt事件随后暴发并透过自然的平整传递到指定的View上,这些传递的历程和规则就是事件分发机制。

而点击操作触发Motion伊夫nt事件是一个轩然大波流或者说是一个事件体系,其一流的轩然大波类型有如下两种:

  • MotionEvent.ACTION_DOWN:手指刚点下屏幕时接触此类型。
  • MotionEvent.ACTION_MOVE:手指在屏幕上活动时会多次触及此类型。
  • MotionEvent.ACTION_UP:手指在屏幕上抬起时接触此类型。

要特别注意的是,通常状态下一个Motion伊芙(Eve)nt事件类别包含一个 ACTION_DOWN
若干个 ACTION_MOVE 和 ACTION_UP
是一个完全的事件体系。(点下来立马抬起指头时,会唯有 ACTION_DOWN 和
ACTION_UP,这也是一个完好无缺的事件体系)

事件分发机制是Android中特别首要的一个知识点,同时也是难关,相信到近日停止很多Android开发者对事件分发机制并没有一个分外系统的认识,当然也囊括博主个人在内。可能在平常的付出工作中大家并从未意识到事件分发机制起到的机能,其实它是随时存在的只是我们不了然而已,就像一些滑行争辨、点击事件期间的争执等等大多是因为事件分发处理不当导致的。想起了博主大学时做过一个小项目,里面就现身了滑动争执的题材,固然最终在网上一步步看着外人的课程也糊里糊涂的缓解了,但说到底不知其所以然,那么先天就让大家共同来深入的商量一下事变分发机制吗。

DNS 解析成 IP 地址

DNS属于应用层协议。客户端会先检查本地是否有对应的 ip
地址,要是有就回来,否则就会呈请上级 DNS
服务器,知道找到或到根节点。这一历程可能会异常耗时,使用 dns-prefetch
可使浏览器在悠然时提前将这个域名转化为 ip
地址,真正请求资源时就避免了这么些过程的光阴。例如京东首页的处理:

图片 3

京东首页dns-prefetch处理

dom树和render树的涉嫌

render树节点和dom树节点相呼应,但这种对应关系不是至极的,不可见的dom元素不会被插入render树,例如head元素、script元素等。其余,display属性为none的因素也不会在渲染树中出现(visibility属性为hidden的要素将出现在渲染树中,这是因为visibility属性为hidden的元素即使不可见但保留了元素的占位)。

又偷了一张图:

图片 4

render树与dom树

# 延迟加载,懒加载,按需加载

有的是页面浏览量即便很大,但事实上很大比例用户扫完第一屏就径直跳走了,第一屏以下的始末用户根本就不感兴趣。
对于超大流量的网站,这么些题材越来越首要。这时可遵照用户的作为开展按需加载,用户使用了就去加载,用不到就不去加载。

上述都是从缩小建立tcp连接数量的角度去优化页面性能,之后会享用更多前端性能优化方面的实用方法。

封装成帧

数量链路层负责将 IP
数据包封装成适合在情理网络上传输的帧格式并传导。设计数据链路层的首要目的就是在原来的、有过错的情理传输线路的基础上,拔取差错检测、差错控制与流量控制等艺术,将有不是的大体线路改进成逻辑上无差错的数额链路,向网络层提供高质料的服务。当使用复用技术时,一条物理链路上可以有多条数据链路。

翻阅目录

TCP 传输报文

TCP 将 http
长报文划分为短报文,通过“两回握手”与服务器建立连接,举行保险传输。“两遍握手”建立连接的进程和通话极像:

客户端:喂,我要和 Server 通话
服务端:你好,我是 Server,你是 Client 吗
客户端:没错,我是 Client

连日来建立成功,接下去就足以规范传送数据了。

数据传完之后断开tcp连接还要通过“两回挥手”,大概意思如下:

客户端:Server 小宝贝,我话说完了,你挂电话吗
服务端:我不挂,我不挂,你先挂,你不挂我也不挂
—————- Client 一阵无语 ————–
服务端:你挂了吧
客户端:行,那自己先挂了

迄今结束完成了一遍完整的资源请求响应。

内需小心的是,浏览器对同一域名下并发的tcp连接数是有限量的,2个到10个不等。为了化解这么些资源加载瓶颈,有两种流行的优化方案:

图片 5

事先面试时候时不时被问及这些题材,支支吾吾回答没有底气,仔细探讨了一下,发现内部学问还真不少。

布局render树(layout)

当渲染对象被创设并添加到render树后,它们并没有地方和分寸,统计这多少个值的长河称为layout(布局)。

布局的坐标连串相对于根渲染对象(它对应文档的html标签,可用
document.documentElement 拿到),使用top和left坐标。根渲染对象的岗位是
(0,0),它的深浅是viewport即浏览器窗口的可见部分。布局是一个递归的进程,由根渲染对象起始,然后递归地由此一些或有所的层级节点,为各样需要几何音讯的渲染对象开展测算。

为了不因为每个小变化都全体双重布局,浏览器选拔一个 dirty
bit(页面重写标志位)系统,一个渲染对象爆发了变更或者被添加了,就标志它及它的children为dirty——需要layout。

当layout在整棵渲染树触发时,称为全局layout,这或许在底下这个情状下发出:

  • 一个大局的样式改变影响所有的渲染对象,比如字号的更改。
  • 窗口resize。

layout也可以是增量的,这样只有标志为dirty的渲染对象会另行布局(也将招致有些卓殊的布局)。增量layout会在渲染对象dirty时异步触发,例如,当网络收到到新的始末并添加到dom树后,新的渲染对象会添加到render树中。

# 域名拆分,资源分流储存

当浏览器向服务器请求一个静态资源时,会先发送该域名下的
cookies,服务器对于这个 cookie
根本不会做其他处理,因而它们只是在毫无意义的消耗带宽,所以理应保证对于静态内容的伸手是无
cookie 的请求(也就是所谓的 cookie-free)。将站点的
js、css、图片等静态文件放在一个特其余域名下访问,由于该域名与主站域名不同,所以浏览器就不会把主域名下的
cookies
传给该域,从而收缩网络支付,特别是零星静态文件特别多的意况下效果显然。

一派,由于浏览器是依照域名的并发连接数限制,而不是页面。因而将资源安排在不同的域名下得以使页面的总并发连接数得到线性提高。

# 资源打包,合并请求

比如页面样式全体封装在一个 css 文件内,页面逻辑全体打包在一个 js
文件内,图片拼合成七喜图,这样可有效削减页面的资源请求数量。webpack
是近日最盛行的模块打包工具之一,它可以将页面内具备资源(包括js,css,图片,字体等等)都打包进一个
js 文件,不明觉厉。