精华内容
下载资源
问答
  • 2021-04-05 21:01:22

    Mybatis框架

    1. MyBatis 框架概述

    ​ 框架(Framework)是整个或部分系统的可重用设计,表现为一组抽象构件及构件实例间交互的方法;另一种
    定义认为,框架是可被应用开发者定制的应用骨架。前者是从应用方面而后者是从目的方面给出的定义。
    简而言之,框架其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。简单说就是使用别人搭好的舞台,你来做表演。而且,框架一般是成熟的,不断升级的软件。

    ​ mybatis 是一个优秀的基于 java 的持久层框架,它内部封装了 jdbc,使开发者只需要关注 sql 语句本身,
    而不需要花费精力去处理加载驱动、创建连接、创建 statement 等繁杂的过程。
    mybatis 通过 xml 或注解的方式将要执行的各种 statement 配置起来,并通过 java 对象和 statement 中sql 的动态参数进行映射生成最终执行的 sql 语句,最后由 mybatis 框架执行 sql 并将结果映射为 java 对象并返回。采用 ORM 思想解决了实体和数据库映射的问题,对 jdbc 进行了封装,屏蔽了 jdbc api 底层访问细节,使我们不用与 jdbc api 打交道,就可以完成对数据库的持久化操作。

    2.框架搭建

    ​ mybatis环境搭建

    ​ 第一步:创建maven工程并导入坐标

    ​ 第二步:创建实体类和dao的接口

    ​ 第三步:创建Mybatis的主配置文件 SqlMapConfig.xml 还有映射配置文件,dao下的独立配置文件。

    ​ 第四步:创建映射配置文件 IUserDao.xml

    2.1环境搭建的注意事项:

    ​ 第一个:创建IUserDao.xml和IUserDao.java是名称为了保持一致,Mybatis中他把持久层的操作接口名称和映射文件也叫做:Mapper

    ​ 所以:IUserDao和IUserMapper是一样的

    ​ 第二个:在idea中创建目录的时候,他和包是不一样的

    ​ 包创建时:cn.pluto.dao是三层

    ​ 目录在创建时:cn.pluto.dao是一级目录

    ​ 第三个:mybatis的映射文件位置西部和dao接口的包结构相同

    ​ 第四个:映射配置文件的mapper标签namespce属性的取值必须是dao接口的全限定类名。

    ​ 第五个:映射配置文件的操作配置(select),id属性的取值必须是dao接口的方法名

    ​ 开发中无须再写dao的实现类。

    2.1.2步骤:

    ​ 1.读取配置文件

    ​ 2.创建SqlSessionFactory工厂

    ​ 3.创建SqlSession

    ​ 4.创建Dao接口的代理对象

    ​ 5.执行dao中的方法

    ​ 6.释放资源

    ​ 注意事项:

    ​ 不要忘记在映射配置中告知mybatis要封装到那个实体类中配置的方式:指定实体类的全限定类名

    2.1.3mybatis基于注解的入门案例:

    ​ IUserDao.xml移除,在dao接口的方法上使用@Select注解并且指定sql语句,同时需要在SqlMapConfig.xml中的mapper配置时,使用class属性指定全限定类名

    3.自定义Mybatis的分析

    ​ mybatis在使用代理dao的方式实现增删改查时做什么事呢?

    ​ 只有两件事:

    ​ 第一:创建代理对象

    ​ 第二:在代理对象中调用selectList

    ​ 自定义mybatis能看到的类

    构建者模式:把对象的创建细节隐藏,使用者直接调用方法即可拿到对象
    SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); 相当于包工队
    SqlSessionFactory factory = builder.build(in);;in相当于钱
    SqlSession session = factory.openSession(); 生产SqlSession使用了工厂模式,降低解耦
    IUserDao mapper = session.getMapper(IUserDao.class); 创建Dao接口实现类使用代理模式,不修改源码的基础上对已有的方法增强
    

    PreparedStatement对象它的执行方法:

    ​ execute:他能执行CRUD中任意一种语句,返回值是一个boolean类型,表示是否有结果集。有结果集是true,没有结果集是false。

    ​ executeUpdate:他只能执行CUD语句,查询语句无法执行,它的返回值是影响数据库的行数。

    ​ executeQuery:他只能执行select语句,无法执行增删改。执行结果封装的结果集ResultSet对象。

    4.properties标签的使用

    ​ <!-- 配置properties
    可以在标签内部配置连接数据库的信息。也可以通过属性引用外部配置文件信息
    resource属性: 常用的
    用于指定配置文件的位置,是按照类路径的写法来写,并且必须存在于类路径下。
    url属性:
    是要求按照Url的写法来写地址
    URL:Uniform Resource Locator 统一资源定位符。它是可以唯一标识一个资源的位置。
    它的写法:
    http://localhost:8080/mybatisserver/demo1Servlet
    协议 主机 端口 URI

            URI:Uniform Resource Identifier 统一资源标识符。它是在应用中可以唯一定位一个资源的。
    -->
    <properties url="file:///D:/IdeaProjects/day02_eesy_01mybatisCRUD/src/main/resources/jdbcConfig.properties">
       <!-- <property name="driver" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"></property>
        <property name="username" value="root"></property>
        <property name="password" value="1234"></property>-->
    </properties>
    

    4.1tyoeAliases标签使用

    ​	使用typeAliases配置别名,它只能配置domain中类的别名 -->
        <typeAliases>
            <!--typeAlias用于配置别名。type属性指定的是实体类全限定类名。alias属性指定别名,当指定了别名就再区分大小写 
            <typeAlias type="cn.pluto.domain.User" alias="user"></typeAlias>
    
            <!-- 用于指定要配置别名的包,当指定之后,该包下的实体类都会注册别名,并且类名就是别名,不再区分大小写-->
            <package name="com.itheima.domain"></package>
        </typeAliases>
    带mappers中  package指定dao包的位置
    
    更多相关内容
  • 班组承包合同包工包料.doc
  • PHP的常见语法 PHP代码执行方式: 在服务器端执行,然后返回给用户结果。如果直接使用浏览器打开,就会解析为文本。 意思是说,需要浏览器通过 http请求,才能够执行php页面。 这里只列举常用的PHP语法,...&...

    AJAX 基础教程

    AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
    AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
    AJAX 是与服务器交换数据并更新部分网页的艺术,在不重新加载整个页面的情况下。
    
    XMLHttpRequest 对象
    所有现代浏览器均支持 XMLHttpRequest 对象(IE5 和 IE6 使用 ActiveXObject)。
    XMLHttpRequest 用于在后台与服务器交换数据。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
    

    向服务器发送请求
    如需将请求发送到服务器,我们使用 XMLHttpRequest 对象的 open() 和 send() 方法:

    xmlhttp.open("GET","test1.txt",true);
    xmlhttp.send();
    

    轻松上手Ajax

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title></title>
    	</head>
    	<body>
    		<h1></h1>
    	</body>
    	<script>
    		//第一种异步获取数据的方式
    		let xhr
    		let data
    		if (window.XMLHttpRequest) {
    		    xhr = new XMLHttpRequest();
    		} else {
    		    xhr = new ActiveXObject("Microsoft.XMLHTTP");
    		}
    		
    		xhr.open("GET", 'https://api.myjson.com/bins/djdbh', true);
    		//每当readyState状态发生变化时就会调用此函数
    		//readyState有5个状态 0: 请求未初始化 1: 服务器连接已建立  2: 请求已接收  3: 请求处理中  4: 请求已完成,且响应已就绪
    		//status有两个状态  200: 请求处理成功  404: 未找到页面
    		xhr.onreadystatechange = function () {
    		    //确保再拿取到数据之后再对数据进行处理
    		    if (xhr.readyState === 4 && xhr.status === 200) {
    		        data = xhr.responseText;
    				var nodeData = document.querySelector("h1");
    				nodeData.innerHTML = data;
    		        console.log(JSON.parse(data))
    		    }else{
    		        console.log('change')
    		    }
    		}
    		xhr.send();
    	</script>
    </html>
    

    PHP的常见语法

    PHP代码执行方式

    • 在服务器端执行,然后返回给用户结果。如果直接使用浏览器打开,就会解析为文本。

    • 意思是说,需要浏览器通过 http请求,才能够执行php页面。

    这里只列举常用的PHP语法,更为详细的语法教程可以查阅 api 文档

    第一段 php 代码

    将 WampServer 跑起来,在D:\wamp\www下新建一个1.php文件,代码如下:

    1.php:

    <?php
    	echo "hello smyhvae";
    ?>
    

    在浏览器中输入http://127.0.0.1/2018-02-28/1.php,效果如下:

    代码的编写位置

    上方代码中,注意php语言的格式,第一行和第三行的格式中,没有空格。代码的编写位置在<?php 代码写在这里?>

    注释

    php 注释的写法跟 js 一致。

    <?php
    	//这是单行注释
    	/*
    		这是多行注释
    	*/
    ?>
    

    变量

    • 变量以$符号开头,其后是变量的名称。大小写敏感。

    • 变量名称必须以字母或下划线开头。

    举例:

    	$a1;
    	$_abc;
    

    数据类型

    PHP支持的数据类型包括:

    • 字符串

    • 整数

    • 浮点数

    • 布尔

    • 数组

    • 对象

    • NULLL

    定义字符串时需要注意:

    • 单引号`` :内部的内容只是作为字符串。

    • 双引号"" :如果内部是PHP的变量,那么会将该变量的值解析。如果内部是html代码,也会解析成html。

    说白了,单引号里的内容,一定是字符串。双引号里的内容,可能会进行解析。

    	echo "<input type=`button` value=`smyhvae`>";
    

    上面这个语句,就被会解析成按钮。

    	// 字符串
    	$str = '123';
    
    	// 字符串拼接
    	$str2 = '123'.'哈哈哈';
    
    
    	// 整数
    	$numA = 1; //正数
    	$numB = -2;//负数
    
    	// 浮点数
    	$x = 1.1;
    
    	// 布尔
    	$a = true;
    	$b = false;
    
    	// 普通数组:数组中可以放 数字、字符串、布尔值等,不限制类型。
    	$arr1 = array('123', 123);
    	echo $arr1[0];
    
    	// 关系型数组:类似于json格式
    	$arr2 = $array(`name`=>`smyhvae`, `age`=>`26`);
    	echo $arr2[`name`];  //获取时,通过  key 来获取
    
    

    上方代码中注意,php 中字符串拼接的方式是 .。要注意哦。

    运算符

    PHP 中的运算符跟 JavaScript 中的基本一致,用法也基本一致。

    • 算数运算符:+-/*%

    • 赋值运算符:x = yx += y,x -= y等。

    举例:

    <?php
    	$x = 10;
    	$y = 6;
    
    	echo ($x + $y); // 输出 16
    	echo ($x - $y); // 输出 4
    	echo ($x * $y); // 输出 60
    	echo ($x / $y); // 输出 1.6666666666667
    	echo ($x % $y); // 输出 4
    ?>
    

    函数的定义

    语法格式:

    
    	function functionName() {
    	  //这里写代码
    	}
    

    (1)有参数、无返回值的函数:

    	function sayName($name){
    	    echo $name.'你好哦';
    	}
    	// 调用
    	sayName('smyhvae');
    

    (2)有参数、参数有默认值的函数:

    	function sayFood($food='西兰花'){
    	    echo $food.'好吃';
    	}
    	// 调用
    	sayFood('西葫芦');// 如果传入参数,就使用传入的参数
    	sayFood();// 如果不传入参数,直接使用默认值
    

    (3)有参数、有返回值的函数:

    	function sum($a,$b){
    		return $a+$b
    	}
    	sum(1,2);// 返回值为1+2 = 3
    

    类和对象

    PHP中允许使用对象这种自定义的数据类型。必须先声明,实例化之后才能够使用。

    定义最基础的类:

    	class Fox{
    
    	        public $name = 'itcast';
    	        public $age = 10;
    	}
    
    	$fox = new $fox;
    	// 对象属性取值
    	$name = $fox->name;
    	// 对象属性赋值
    	$fox->name = '小狐狸';
    

    带构造函数的类:

    	class fox{
    	    // 私有属性,外部无法访问
    	    var $name = '小狐狸';
    	    // 定义方法 用来获取属性
    	    function Name(){
    	    return $this->name;
    	    }
    	    // 构造函数,可以传入参数
    	    function fox($name){
    	    $this->name = $name
    	    }
    	}
    
        // 定义了构造函数 需要使用构造函数初始化对象
        $fox = new fox('小狐狸');
        // 调用对象方法,获取对象名
        $foxName = $fox->Name();
    

    内容输出

    • echo:输出字符串。

    • print_r():输出复杂数据类型。比如数组、对象。

    • var_dump():输出详细信息。

    	$arr =array(1,2,'123');
    
    	echo'123';
    	//结果:123
    
    
    	print_r($arr);
    	//结果:Array ( [0] => 1 [1] => 2 [2] => 123 )
    
    	var_dump($arr);
    	/* 结果:
    	array
    	  0 => int 1
    	  1 => int 2
    	  2 => string '123' (length=3)
    	*/
    
    

    循环语句

    这里只列举了foreachfor循环。

    for 循环:

    	for ($x=0; $x<=10; $x++) {
    	  echo "数字是:$x <br>";
    	}
    
    

    foreach 循环:

    	$colors = array("red","green","blue","yellow");
    
    	foreach ($colors as $value) {
    	  echo "$value <br>";
    	}
    

    上方代码中,参数一:循环的对象。参数二:将对象的值挨个取出,直到最后。

    如果循环的是对象,输出的是对象的属性的值。

    输出结果:

    	red
    	green
    	blue
    	yellow
    

    php中的header()函数

    浏览器访问http服务器,接收到响应时,会根据响应报文头的内容进行一些具体的操作。在php中,我们可以根据 header 来设置这些内容。

    header()函数的作用:用来向客户端(浏览器)发送报头。直接写在php代码的第一行就行。

    下面列举几个常见的 header函数。

    (1)设置编码格式:

    	header('content-type:text/html; charset= utf-8');
    

    例如:

    <?php
    	header('content-type:text/html; charset= utf-8');
    	echo "我的第一段 PHP 脚本";
    ?>
    

    (2)设置页面跳转:

    	header('location:http://www.baidu.com');
    

    设置页面刷新的间隔:

    	header('refresh:3; url=http://www.xiaomi.com');
    

    php中的 get 请求和 post 请求

    get 请求

    可以通过$_GET对象来获取。

    举例:下面是一个简单的表单代码,通过 get 请求将数据提交到01.php。

    (1)index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <!-- 通过 get 请求,将表单提交到 php 页面中 -->
    <form action="01.php" method="get">
        <label for="">姓名:
            <input type="text" name="userName"></label>
        <br/>
        <label for="">邮箱:
            <input type="text" name="userEmail"></label>
        <br/>
        <input type="submit" name="">
    </form>
    
    </body>
    </html>
    

    (2)01.php:

    <?php
    	header('content-type:text/html; charset= utf-8');
        echo "<h1>php 的get 请求演示</h1>";
        echo '用户名:'.$_GET['userName'];
        echo '<br/>';
        echo '邮箱:'.$_GET['userEmail'];
     ?>
    

    上方代码可以看出,$_GET是关系型数组,可以通过 **$_GET[key]**获取值。这里的 key 是 form 标签中表单元素的 name 属性的值。

    效果:

    post 请求

    可以通过$_POST对象来获取。

    举例:下面是一个简单的表单代码,通过 post 请求将数据提交到02.php。

    (1)index.html:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <!-- 通过 post 请求,将表单提交到 php 页面中 -->
    <form action="02.php" method="post" >
      <label for="">姓名:
          <input type="text" name= "userName"></label>
          <br/>
      <label for="">邮箱:
          <input type="text" name= "userEmail"></label>
          <br/>
          <input type="submit" name="">
    </form>
    
    </body>
    </html>
    

    (2)02.php:

    <?php
    	header('content-type:text/html; charset= utf-8');
        echo "<h1>php 的 post 请求演示</h1>";
        echo '用户名:'.$_POST['userName'];
        echo '<br/>';
        echo '邮箱:'.$_POST['userEmail'];
     ?>
    

    上方代码可以看出,$_POST是关系型数组,可以通过 **$_POST[key]**获取值。这里的 key 是 form 标签中表单元素的 name 属性的值。

    效果演示:

    [外链图片转存失败(img-lMjRWZuW-1568712427956)(http://img.smyhvae.com/20180228_1145.gif)]

    实际开发中,可能不会单独写一个php文件,常见的做法是:在 html 文件中嵌入 php 的代码。

    比如说,原本 html 中有个 li 标签是存放用户名的:

    	<li>smyhvae</li>
    

    嵌入 php后,用户名就变成了动态获取的:

    	<li><?php
    		echo $_POST[`userName`]
    		?></li>
    

    php 中文件相关的操作

    文件上传 $_FILES

    上传文件时,需要在html代码中进行如下设置:

    (1)在html表单中,设置enctype="multipart/form-data"。该值是必须的。

    (2)只能用 post 方式获取。

    代码如下:

    (1)index.html:

      <form action="03-fileUpdate.php" method="post" enctype="multipart/form-data">
    	  <label for="">照片:
    	      <input type="file" name = "picture" multiple=""></label>
    	  <br/>
    	  <input type="submit" name="">
      </form>
    
    

    (2)在 php 文件中打印 file 的具体内容:

    <?php
      sleep(5);// 让服务器休息一会
      print_r($_FILES);  //打印 file 的具体内容
    ?>
    

    演示效果:

    上方现象可以看出:

    • 点击提交后,服务器没有立即出现反应,而是休息了一会sleep(5)

    • wamp/tmp目录下面出现了一个.tmp文件。

    • .tmp文件一会就被自动删除了。

    • 服务器返回的内容中有文件的名字[name] => computer.png,以及上传文件保存的位置D:\wamp\tmp\php3D70.tmp。服务器返回的内容如下:

    	Array ( [upFile] => Array ( [name] => yangyang.jpg [type] => image/jpeg [tmp_name] => D:\wamp\tmp\phpCC56.tmp [error] => 0 [size] => 18145 ) )
    

    文件保存

    我们尝试一下,把上面的例子中的临时目录下面的文件保存起来。这里需要用到 php 里的 move_uploaded_file()函数。#

    格式如下:

    	move_uploaded_file($_FILES['photo']['tmp_name'], './images/test.jpg');
    

    参数解释:参数一:移动的文件。参数二:目标路径。

    (1)index.html:(这部分的代码保持不变)

    	<form action="03.fileUpdate.php" method="post" enctype="multipart/form-data">
          <label for="">照片:
              <input type="file" name = "picture" multiple=""></label>
          <br/>
          <input type="submit" name="">
      	</form>
    

    (2)PHP代码:

    暂略。

    WampServer 中修改上传文件的大小

    (1)打开 WampServer的文件php.ini

    [外链图片转存失败(img-IzFnpPrp-1568712427958)(http://img.smyhvae.com/20180228_1454.png)]

    (2)修改php.ini中的如下内容:

    设置文件最大上传限制:(值的大小可以根据需求修改)

    	file_uploads = On;         是否允许上传文件 On/Off 默认是On
    	upload_max_filesize = 32M; 设置 上传文件的最大限制
    	post_max_size = 32M;       设置 通过Post提交的最多数据
    

    考虑网络传输快慢:这里修改一些参数:

    	max_execution_time = 30000      ; 脚本最长的执行时间 单位为秒
    	max_input_time = 600            ; 接收提交的数据的时间限制 单位为秒
    	memory_limit = 1024M            ; 最大的内存消耗
    

    C/S架构和B/S架构

    C/S架构

    是Client/Server这两个单词的首字母,指的是客户端,服务器。

    优点:

    • 性能较高:可以将一部分的计算工作放在客户端上,这样服务器只需要处理数据即可。

    • 界面酷炫:客户端可以使用更多系统提供的效果,做出更为炫目的效果。

    缺点:

    • 更新软件:如果有新的功能,就要推出新的版本。

    • 不同设备访问:如果使用其他的电脑,没有安装客户端的话就无法登陆软件。

    B/S架构

    是Browser/Server的这两个单词的首字母。指的是浏览器、服务器,是WEB兴起之后的一种架构。

    现在所有的网站都是B/S架构,较为常见的例子有百度、知乎、网易云音乐Web等等,只需要通过浏览器即可使用.

    优点:

    • 更新简洁:如果需要更新内容了,对开发人员而言需要更改服务器的内容,对用户而言只需要刷新浏览器即可。

    • 多设备同步:所有数据都在网上,只要能够使用浏览器即可登录使用。

    缺点:

    • 性能较低:相比于客户端应用性能较低,但是随着硬件性能的提升,这个差距在缩小。

    • 浏览器兼容:处理低版本的浏览器显示问题一直是前端开发人员头痛的问题之一。移动设备兼容性较好,ie6已经越来越少人用了。

    服务器分类

    项目开发时,有三套环境:

    • Development 开发环境

    • Test 测试环境

    • Production 生产环境

    程序员平时干活儿用开发环境;开发完成后,部署到测试环境;测试完成后,产品上线,部署到生产环境。

    三套环境意味着三个服务器。

    服务器类型

    按类型分:

    • 文件服务器

    • 数据库服务器

    • 邮件服务器

    • Web 服务器等

    按软件分:

    • Apache 服务器

    • Nginx 服务器

    • IIS 服务器

    • Tomcat 服务器

    • Node 服务器等

    按操作系统分:

    • Linux服务器

    • Windows服务器等

    服务器软件

    提供了某种服务的计算器,我们称之为服务器。那么这些赋予计算器各种服务功能的软件主要有哪一些呢?

    常见的服务器软件有:

    • 文件服务器:Server-U、FileZilla、VsFTP等;

    • 数据库服务器:Oracle、MySQL、PostgreSQL、MSSQL等;

    • 邮件服务器:Postfix、Sendmail等;

    • HTTP 服务器:Apache(免费、开源)、Nginx、IIS(微软的.net服务器)、Tomcat(java编程的服务器)、NodeJS 等。

    使用 WampServer 搭建 HTTP服务

    集成环境的分类

    • AMP:Apache + Mysql + PHP。

    • WAMP:windows + Apache + Mysql + PHP。

    • XAMPP:WAMP 是针对windows的,而 XAMPP 可以安装在Linux、Windows、MacOS、Solaris这些操作系统上面。

    在windows平台下,如果想要一步到位安装好这些软件,可是使用软件 WampServer

    WampServer 的安装

    去 WampServer 的官网下载软件。

    测试访问

    打开浏览器输入 127.0.0.1 查看显示的内容,如果是第一次安装,默认显示的应该是如下图片:

    127.0.0.1 是回送地址,指本地机,一般用来测试使用,如果想要让其他电脑也能够访问,需要进行如下配置:

    (1)关闭防火墙:

    (2)修改httpd.conf文件:

    因为 Apache 的配置默认不允许外部访问,我们需要修改配置。

    打开文件c:\wamp\bin\apache\Apache2.2.21\conf\httpd.conf,通过搜索功能找到onlineoffline tag - don't remove这句话,在第234行的 Allow from 127.0.0.1的下面,加一行:Allow from all

    然后将第192行的Deny from all改为Allow from all

    保存,然后重启 wamp 即可。

    配置网站根目录

    网站的根目录默认是在D:\wamp\www。如果想修改这个根目录,可以这样改:

    打开 Apache的配置文件 D:\wamp\bin\apache\Apache2.2.21\conf\http.conf,如果是初次安装,找到178行的DocumentRoot "d:/wamp/www/",以及205行的<Directory "d:/wamp/www/">,改这两个位置的路径即可。我们可以通过搜索关键字documentRoot来定位。

    静态网站和动态网站

    静态网站:

    • 访问的是实实在在保存在服务器上的文件。静态资源包括:html页面、css文件、js文件、图片等。

    • 当内容、图片、界面需要更新时,直接修改.html文件。

    动态网站:

    • 当用户访问网站时,根据某些逻辑,动态生成对应的HTML、CSS、JS代码给用户(这也就是web服务器开发的本质)。

    • 通过某种手段,当有新的消息时,自动的完成网站的更新。

    总结:

    由于静态网站在维护的局限性,所以产生了动态网站。

    实现动态网站的技术:php/jsp/.net/python等。

    动态网站的原理:浏览器请求动态网站的页面(比如*.php),php拼接数据并动态生成html页面,然后将新生成的页面返回给浏览器

    php 之所以被称为最好的语言,是因为:基本上,我们能够想到的功能,它都帮助我们封装成了方法。十分方便。

    HTTP 协议

    请求

    客户端发出的请求,主要由三个组成部分:请求行、请求头、请求主体。如下图所示:

    20180228_1505.jpg

    1、请求行:

    • 请求方法:GET or POST

    • 请求URL

    • HTTP协议版本

    2、请求头:

    常见的请求头如下:

    User-Agent:浏览器的具体类型  如:User-Agent:Mozilla/5.0 (Windows NT 6.1; rv:17.0) Gecko/20100101 Firefox/17.0
    
    Accept:浏览器支持哪些数据类型  如:Accept: text/html,application/xhtml+xml,application/xml;q=0.9;
    
    Accept-Charset:浏览器采用的是哪种编码  如:Accept-Charset: ISO-8859-1
    
    Accept-Encoding:浏览器支持解码的数据压缩格式  如:Accept-Encoding: gzip, deflate
    
    Accept-Language:浏览器的语言环境  如:Accept-Language zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
    
    Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。Host:www.baidu.com
    
    Connection:表示是否需要持久连接。
    属性值可以是Keep-Alive/close,HTTP1.1默认是持久连接,它可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。
    要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。如:Connection: Keep-Alive
    
    Content-Length:表示请求消息正文的长度。对于POST请求来说Content-Length必须出现。
    
    Content-Type:WEB服务器告诉浏览器自己响应的对象的类型和字符集。例如:Content-Type: text/html; charset='gb2312'
    
    Content-Encoding:WEB服务器表明自己使用了什么压缩方法(gzip,deflate)压缩响应中的对象。例如:Content-Encoding:gzip
    
    Content-Language:WEB服务器告诉浏览器自己响应的对象的语言。
    
    Cookie:最常用的请求头,浏览器每次都会将cookie发送到服务器上,允许服务器在客户端存储少量数据。
    
    Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。服务器能知道你是从哪个页面过来的。Referer: http://www.baidu.com/
    
    

    3、请求体:

    指的是提交给服务器的数据。

    需要注意的是,如果是往服务器提交数据,需要在请求头中设置Content-Type: application/x-www-form-urlencoded(在ajax中需要手动设置)。

    响应

    响应报文是服务器返回给客户端的。组成部分有响应行、响应头、响应主体。

    [外链图片转存失败(img-BiGYKQBu-1568712427960)(http://img.smyhvae.com/20180228_1510.jpg)]

    1、状态行:

    HTTP响应行:主要是设置响应状态等信息。

    2、响应头:

    Cookie、缓存等信息就是在响应头的属性中设置的。

    常见的响应头如下:

    Cache-Control
    
    响应输出到客户端后,服务端通过该报文头属告诉客户端如何控制响应内容的缓存。
    
    下面,的设置让客户端对响应内容缓存3600秒,也即在3600秒内,如果客户再次访问该资源,直接从客户端的缓存中返回内容给客户,不要再从服务端获取(当然,这个功能是靠客户端实现的,服务端只是通过这个属性提示客户端“应该这么做”,做不做,还是决定于客户端,如果是自己宣称支持HTTP的客户端,则就应该这样实现)。
    
    Cache-Control: max-age=3600
    
    ETag
    
    一个代表响应服务端资源(如页面)版本的报文头属性,如果某个服务端资源发生变化了,这个ETag就会相应发生变化。它是Cache-Control的有益补充,可以让客户端“更智能”地处理什么时候要从服务端取资源,什么时候可以直接从缓存中返回响应。
    
    ETag: "737060cd8c284d8af7ad3082f209582d"
    
    Location
    
    我们在Asp.net中让页面Redirect到一个某个A页面中,其实是让客户端再发一个请求到A页面,这个需要Redirect到的A页面的URL,其实就是通过响应报文头的Location属性告知客户端的,如下的报文头属性,将使客户端redirect到iteye的首页中:
    
    Location: http://www.google.com.hk
    
    Set-Cookie
    
    服务端可以设置客户端的Cookie,其原理就是通过这个响应报文头属性实现的。
    
    Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
    
    
    

    3、HTTP响应体:

    如果请求的是HTML页面,那么返回的就是HTML代码。如果是JS就是JS代码。

    抓包工具

    常见的抓包工具有:Fiddler、Charles。

    使用Ajax发送http请求(get&post请求)

    同步和异步的概念

    • 同步:必须等待前面的任务完成,才能继续后面的任务。

    • 异步:不受当前任务的影响。

    同源和跨域

    同源

    同源策略是浏览器的一种安全策略,所谓同源是指,域名,协议,端口完全相同。

    跨域问题的解决方案

    从我自己的网站访问别人网站的内容,就叫跨域。

    出于安全性考虑,浏览器不允许ajax跨域获取数据。

    • iframe:处于安全性考虑,浏览器的开发厂商已经禁止了这种方式。

    • JSONP:script 标签的 src 属性传递数据。

    JSONP

    JSONP(JSON with Padding):带补丁的 json,本质是利用了 <script src=""></script>标签具有可跨域的特性,由服务端返回一个预先定义好的JS函数的调用,并且将服务器数据以该函数参数的形式传递过来。此方法需要前后端配合完成。

    我们知道, html标签的 src 属性是支持跨域的:

    	<img src="http://img.smyhvae.com/2016040101.jpg" alt="">
    

    jsonp 就是利用这个特性实现的跨域,但用的是 script 标签。如下:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Document</title>
    </head>
    <body>
    
    <!-- jsonp 就是 利用 src,实现的跨域 用的是 script标签 -->
    <script type="text/javascript"  src='http://192.168.141.137/2018-02-28/myData.php'></script>
    </body>
    </html>
    
    

    上方那一行的代码,意思是:刷新A服务器上的index页面后,会去请求 B 服务器上的 myData.php 这个页面。而且请求的方式是 get 请求。

    但是 B 服务器上的页面不是你想请求就可以请求的,大家一起配合才可以。

    具体实现步骤:

    需要首先声明的是,jsonp 只能通过 GET 方式进行请求。

    (1)A客户端的代码:

    <!DOCTYPE html>
    <html lang="en">
    <head>
    	<meta charset="UTF-8">
    	<title>Document</title>
    </head>
    <body>
    
    </body>
    </html>
    <script type="text/javascript">
    
    	// 定义 eatFood()方法
    	function fn(data) {
    		console.log('我被调用了哦');
    		console.log(data);
    	}
    </script>
    
    <!-- 使用 script标签 发送了 get请求 去到了一个 php页面 -->
    <script type="text/javascript" src='http://192.168.141.137/01.php?callback1=fn'></script>
    

    我们来分析上方代码中的最后一行的那个url:A 客户端请求的是 B服务器上的 01.php页面。url里有个callback1=fn,意思是:callback1是A和B 之间的约定,约定后,将执行方法 fn。

    其实,fn方法已经在最后一行代码中执行了。只不过,fn方法里的data数据,是从 B 服务器中获取的。

    (2)B服务器端的代码:

    <?php
        $mycallBack = $_GET['callback1'];
    
    	$arr = array("zhangsan","lisi","zhaoliu");
    
        echo $mycallBack.`(`.json_encode($arr).`)`;    //字符串拼接
    ?>
    

    代码解释:

    第一行的callback1 是A和B之间的约定,二者必须一致。

    echo语句中输出的内容,即使要返回给A客户端的内容,此内容会保存在 A 客户端的fn方法的data里。 data[0]指的是 zhangsan。

    json_encode指的是,将php对象转化为 json。

    刷新A页面,输出结果为:

    	mycallBack(["zhangsan","lisi","zhaoliu"])
    

    jQuery 中的 JSONP

    我们知道,jQuery 中发送 Ajax 请求,格式是:

    		$("#btn").click(function(){
    			$.ajax({
    				url:"./data.php?callback1=fn",
    				dataType:"jsonp",
    				type:"get",
    				//jsonp:"callback1",   //传递给B服务器的回调函数的名字(默认为 callback)
    				//jsonCallBack:"fn"    //自定义的函数名称。默认为 jQuery 自动生成的随机函数名
    				success:function(data){
    					alert(data);
    					//$("#showInfo").html(data);
    				},
    				error:function(e){
    					console.log(e);
    				}
    			});
    		});
    

    那如果数据是 JSONP,上方代码则改为:

    		$("#btn").click(function(){
    			$.ajax({
    				url:"./data.php?fn",
    				dataType:"text",
    				type:"get",
    				success:function(data){
    					alert(data);
    					//$("#showInfo").html(data);
    				},
    				error:function(e){
    					console.log(e);
    				}
    			});
    		});
    

    参考链接

    模版引擎

    引入

    我们在使用ajax请求数据时,返回的如果是一个 JSON 格式的字符串,我们需要将其包装到对应的HTML代码中,再添加到页面上,才能看到效果。那么这个包装得过程有没有简单的方法呢?

    假设在 js 中有如下数据:

    	var obj = {
    		name:"fox",
    		age:18,
    		skill:"卖萌"
    	};
    

    希望包装为:

    <ul>
      <li>姓名:fox</li>
      <li>年龄:18</li>
      <li>爱好:卖萌</li>
    </ul>
    

    我们可以通过模板插件来实现。

    模版插件的原理

    我们定义一段文本作为模板,读取文本,使用特殊的符号<%= 属性名 %>,通过正则表达式找到这些特殊的符号进行替换,是不是就实现了这样的效果呢?

    常见的模板引擎

    • BaiduTemplate(百度开发)

    • ArtTemplate(腾讯开发):GitHub地址文档地址

    • velocity.js(淘宝开发)

    • Handlebars

    ArtTemplate

    标准语法:

    	{{if user}}
    	  <h2>{{user.name}}</h2>
    	{{/if}}
    

    渲染模板:

    	var data = {
    		title: `标签`,
    		list: [`文艺`, `博客`, `摄影`]
    	};
    	var html = template(`test`, data);
    	document.getElementById(`content`).innerHTML = html;
    

    举例:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script src="js/template-native-debug.js"></script>
        <script src="js/jquery-2.2.0.js"></script>
    
        <script id="tpl" type="text/html">
            <h3><%= className %></h3>
            <ul>
                <% for(var i = 0; i< students.length;i++) { %>
                    <li><%= i+1 %>. <%= students[i] %></li>
                <% } %>
            </ul>
    
        </script>
    
        <script>
            var data = {
                className:"前端1期",
                students:["张飞","刘备","诸葛亮","甄姬","小乔","汪汪"]
            };
    
            $(function (){
                var html = template("tpl",data);
                $("#demo").html(html);
            })
        </script>
    </head>
    <body>
    
        <div id="demo">
    
        </div>
    </body>
    </html>
    

    效果:

    展开全文
  • Android面试必问框架原理

    千次阅读 2021-06-19 16:49:58
    Android面试必问框架原理volatile的实现原理synchronized的实现原理join方法实现原理CAS无锁编程的原理ReentrantLock的实现原理AOP理解IOC理解dagger2注入原理hilt原理组件化通信使用autoService+...

    这些都是一些面试必问的,包括了java一小部分的必问原理知识点,
    平时总结出来供自己面试用,也分享给大家,希望我们一起进步,写的不好的地方望指正,有空还在更新中…感谢大家支持~

    volatile的实现原理

    通过对OpenJDK中的unsafe.cpp源码的分析,会发现被volatile关键字修饰的变量会存在一个“lock:”的前缀。
    Lock前缀:Lock不是一种内存屏障,但是它能完成类似内存屏障的功能。Lock会对CPU总线和高速缓存加锁,可以理解为CPU指令级的一种锁
    同时该指令会将当前处理器缓存行的数据直接写会到系统内存中,且这个写回内存的操作会使在其他CPU里缓存了该地址的数据无效

    synchronized的实现原理

    Synchronized在JVM里的实现都是基于进入和退出Monitor对象来实现方法同步和代码块同步,虽然具体实现细节不一样,但是都可以通过成对的MonitorEnter和MonitorExit指令来实现
    对同步块:
    MonitorEnter指令插入在同步代码块的开始位置,当代码执行到该指令时,将会尝试获取该对象Monitor的所有权,即尝试获得该对象的锁,而monitorExit指令则插入在方法结束处和异常处,JVM保证每个MonitorEnter必须有对应的MonitorExit。
    对同步方法:
    从同步方法反编译的结果来看,方法的同步并没有通过指令monitorenter和monitorexit来实现,相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符

    join方法实现原理

    join方法是通过调用线程的wait方法来达到同步的目的的
    例如A线程中调用了B线程的join方法,则相当于在A线程中调用了B线程的wait方法,当B线程执行完(或者到达等待时间),B线程会自动调用自身的notifyAll方法唤醒A线程,从而达到同步的目的。

    CAS无锁编程的原理

    CAS的基本思路就是:
    如果这个地址上的值和期望的值相等,则给其赋予新值,否则不做任何事儿,但是要返回原值是多少。循环CAS就是在一个循环里不断的做cas操作,直到成功为止。
    还可以说说CAS的三大问题。
    ABA问题
    循环时间长开销大
    (⊙o⊙)…第三个暂时忘记了…

    ReentrantLock的实现原理

    线程可以重复进入任何一个它已经拥有的锁所同步着的代码块,synchronized、ReentrantLock都是可重入的锁。在实现上,就是线程每次获取锁时判定如果获得锁的线程是它自己时,简单将计数器累积即可,每 释放一次锁,进行计数器累减,直到计算器归零,表示线程已经彻底释放锁。
    底层则是利用了JUC中的AQS来实现的

    AQS的大致实现思路

    AQS内部维护了一个CLH队列来管理锁。线程会首先尝试获取锁,如果失败就将当前线程及等待状态等信息包装成一个node节点加入到同步队列sync queue里。 接着会不断的循环尝试获取锁,条件是当前节点为head的直接后继才会尝试。如果失败就会阻塞自己直到自己被唤醒。而当持有锁的线程释放锁的时候,会唤醒队列中的后继线程。

    CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

    AOP理解

    在OOP设计中,它导致了大量的代码的重复,而不利于各个模块的重用
    将程序中的交叉业务逻辑(比如安全、日志、事务等)封装成一个切面,然后注入到目标对象中去

    IOC理解

    思想:由主动创建对象变为被动注入

    实际上就是一个map,里面存各种对象(在xml里配置的bean节点),项目启动时会读取xml中的bean节点,根据全限定类名使用反射创建对象放到map里

    dagger2注入原理

    dagger2采用的是APT技术,在编译期间生成java代码,对于注解而言,有效的避免了运行时注解通过反射解析注解信息而影响效率问题

    场景:A类、B类自己写的,A注入B

    hilt原理

    • 专门面向Android的依赖注入框架
    • 相比dagger2,明显特征:1、简单 2、提供了Android专属的API
    • 初始化过程原理:gradlew插件通过字节码插桩
    • 工厂+双重检测的单例
    • 目前只支持以下Android类:
      Application Activity fragment view service BroadcastReceiver

    场景:
    presenter注入activity,activity退出时,presenter也可以一起销毁

    原理入口:gradlew插件进行字节码插桩
    mainActivity extends Hilt_MainActivity

    onCreate 中的inject开始,调用InjetMainActivity

    APT技术

    全称:annotation process tool 一种处理注释的工具,对源代码文件进行检测,找出其中的Annotation,使用Annotation进行额外的处理;
    通俗讲:编译期的时候,处理注解,生成Java文件
    好处:编译多久不影响界面卡顿

    Dagger2
    Room
    alibaba/ARouter Javapoet来生成java代码
    Butterknife
    DataBinding
    EventBus 早期传统方式一行一行生成java代码

    But,for example,XUtils就是通过运行期,注解,反射,一旦耗费2s,就会让界面卡顿

    组件化通信

    使用autoService+ServiceLoader

    缺点:使用反射去实例化对象
    优点:易配置,易调试,上手快
    ServiceLoader原理:

    思考面试题:为啥给一个类能返回一个实现类

    谷歌开源用来方便生成符合ServiceLoader规范的开源库,使用非常的简单
    首先定义接口,然后使用AutoService注解在实现类声明
    然后就轮到ServiceLoader.load方法出场了

    思考面试题:使用注解以后会发生什么?
    首先会在apk的META-INF文件夹中新建一个service文件夹,然后在下面为我们生成一个配置文件
    (app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes\META-INF\services)
    文件名为接口的全包名+类名,里面的内容为实现类的全包名+类名
    一行为实现类,可以同时有多行,这就是AutoService为我做的全部处理

    类的加载用的是Java的类加载机制,使用ClassLoader读取META-INF\services下指定文件的内容,然后使用反射实例化对象

    参考一下这篇文章:组件化开发新的选择AutoService

    ARouter

    ARouter 阿里巴巴开源的路由框架,将各组件看成不同的局域网,通过路由做中转站,拦截一些不安全的跳转,或者设定一些拦截服务
    ARouter原理:

    Arouter.init()
    在LogisticsCenter中通过编译时注解,生成三个文件,Group(IRoutGroup) Providers(IproviderGroup),Root(IRouteRoot)
    使用Warehouse将文件保存到三个不同的HashMap中,Warehouse相当于路由表,保存着全部模块的跳转关系
    通过ARouter.navagation封装postcard对象
    通过ARouter索引传递到LogisticsCenter(路由中转站)
    询问是否存在跳转对象,如果存在,设置绿色通道开关,判断是否绿色通行和是否能通过拦截服务,
    全部通过就会调用ActivityCompat.startActivity方法来跳转到目的Activity
    所以Arouter实际还是使用原生的FrameWork机制startActivity,只是通过APT注解的形式制造出跳转规则,并认为的拦截跳转和设置跳转条件

    开源路由框架还有哪些?

    • ActivityRouter
    • 天猫统跳协议
    • DeeplinkDispatch

    Binder

    三个维度来理解Binder
    机制:进程间通信的机制
    驱动:Binder是一个虚拟物理设备驱动(文件),/dev/binder
    应用层:是一个能发起通信的Java类
    Binder.java,它实现了IBinder接口,你继承它,就会拥有跨进程的能力

    多进程场景
    自己创建的进程:webview、视频播放、音乐、大图浏览、推送、进程守护
    系统服务:打电话、闹钟等、AMS

    多进程优势
    内存翻倍:变相增大APP的内存
    风险隔离:一个挂了另一个没有影响

    Linux进程间通信有哪些?
    管道、信号量、共享内存、socket

    Binder优势?
    拷贝一次
    性能仅小于共享内存,优于其他IPC(进程间通信)

    Binder一次拷贝图
    在这里插入图片描述
    面试提问:
    如何做到一次拷贝?
    内核和接收方用户空间同时映射一块物理内存
    mmap实现

    mmap原理
    人为指定一块虚拟内存映射到一块已知物理内存

    共享内存无需拷贝,怎么实现的?
    发送发、接收方、内核空间同时映射一块物理内存
    为什么不用共享内存?
    虽然性能好,但不安全

    Binder机制如何跨进程的?
    数据发送方通过系统调用copy_from_user()拷贝到内核空间,
    内核空间有一块虚拟内存和数据接收方虚拟内存共享一块物理内存

    • 步骤1:注册服务

    Server进程 通过Binder驱动 向 Service Manager进程 注册服务
    注册服务后,Binder驱动持有 Server进程创建的Binder实体

    • 步骤2:获取服务

    Client进程 使用 某个 service前(此处是 相加函数),须通过Binder驱动 向 ServiceManager进程获取相应的Service信息
    此时,Client进程与 Server进程已经建立了连接

    • 步骤3:使用服务

    Client进程 根据获取到的 Service信息(Binder代理对象),通过Binder驱动 建立与 该Service所在Server进程通信的链路,并开始使用服务

    对比 Linux (Android基于Linux)上的其他进程通信方式(管道/消息队列/共享内存/信号量/Socket),Binder 机制的优点有:

    • 高效
      Binder数据拷贝只需要一次,而管道、消息队列、Socket都需要2次
      通过驱动在内核空间拷贝数据,不需要额外的同步处理

    • 安全性高
      Binder 机制为每个进程分配了 UID/PID 来作为鉴别身份的标示,并且在 Binder 通信时会根据 UID/PID 进行有效性检测

    • 传统的进程通信方式对于通信双方的身份并没有做出严格的验证
      如,Socket通信 ip地址是客户端手动填入,容易出现伪造

    • 使用简单
      采用Client/Server 架构
      实现 面向对象 的调用方式,即在使用Binder时就和调用一个本地对象实例一样

    ServiceManager启动

    在这里插入图片描述

    SM注册流程

    sm注册

    1. 打开驱动,内存映射(设置大小 128K)
    2. 设置 SM 为大管家 — sm 作用 为了管理系统服务
      1. 创建 binder_node 结构体对象
      2. proc --> binder_node
      3. 创建 work 和 todo --> 类似 messageQueue
    3. BC_ENTER_LOOPER 命令
      1. 写入状态Loop
      2. 去读数据:binder_thread_read:ret = wait_event_freezable_exclusive(proc->wait, binder_has_proc_work(proc, thread)); 进入等待

    source code:
    在这里插入图片描述

    Binder相关类图

    在这里插入图片描述

    bindService流程图

    bindService系统如何处理,最后返回到onServiceConnected()
    在这里插入图片描述
    bindService:经历了6次跨进程
    图中红色序号:
    1、2为一次 1
    3、4各一次 3
    5、6为一次 4
    7、8各一次 6

    手写Binder实现

    先认识两个接口

    Interface source code

    public interface IInterface
    {
        public IBinder asBinder();
    }
    IInterface接口只有一个方法asBinder()
    
    

    IBinder source code

    public interface IBinder {
     public boolean transact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException;
    }
    

    IBinder众多接口中有一个重要的方法transact,接收4个参数:
    code是处理方法的标记
    data是传送数据
    reply是服务端响应数据
    flags 为0代表成功;1代表失败

    定义Person类

    public class Person implements Parcelable {
    
        private String name;
        private int grade;
    
        public Person(String name, int grade) {
            this.name = name;
            this.grade = grade;
        }
    
        protected Person(Parcel in) {
            this.name = in.readString();
            this.grade = in.readInt();
        }
    
        public static final Creator<Person> CREATOR = new Creator<Person>() {
            @Override
            public Person createFromParcel(Parcel in) {
                return new Person(in);
            }
    
            @Override
            public Person[] newArray(int size) {
                return new Person[size];
            }
        };
    
        @Override
        public int describeContents() {
            return 0;
        }
    
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeString(name);
            dest.writeInt(grade);
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", grade=" + grade +
                    '}';
        }
    }
    

    定义接口IPersonManager

    public interface IPersonManager extends IInterface {
        void addPerson(Person person) throws RemoteException;
        List<Person> getPersonList() throws RemoteException;
    }
    

    定义Proxy类

    public class Proxy implements IPersonManager {
    
        private static final String DESCRIPTOR = "com.proxy.binder.common.IPersonManager";
    
        private IBinder mRemote;
    
        public Proxy(IBinder remote) {
            mRemote = remote;
        }
    
        @Override
        public void addPerson(Person person) throws RemoteException {
            //1、数据打包
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                //2、数据校验
                data.writeInterfaceToken(DESCRIPTOR);
                if ((person != null)) {
                    data.writeInt(1);
                    person.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                Log.e("Proxy", "Proxy,addPerson: " + Thread.currentThread());
                //3、数据传送 mRemote就是Ibinder
                mRemote.transact(Stub.TRANSACTION_addPerson, data, reply, 0);
                reply.readException();
            } finally {
                reply.recycle();
                data.recycle();
            }
        }
    
        @Override
        public List<Person> getPersonList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List<Person> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_getPersonList, data, reply, 0);
                reply.readException();
                result = reply.createTypedArrayList(Person.CREATOR);
            } finally {
                reply.recycle();
                data.recycle();
            }
            return result;
        }
    
        @Override //重写了IInterface接口的asBinder方法
        public IBinder asBinder() {
            return mRemote;
        }
    }
    

    Stub类

    public abstract class Stub extends Binder implements IPersonManager {
    
        private static final String DESCRIPTOR = "com.proxy.binder.common.IPersonManager";
    
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        //判断进程,同进程返回stub,不同进程返回proxy
        public static IPersonManager asInterface(IBinder binder) {
            if ((binder == null)) {
                return null;
            }
            IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
            if ((iin != null) && (iin instanceof IPersonManager)) {
                return (IPersonManager) iin;
            }
            return new Proxy(binder);
        }
    
        @Override
        public IBinder asBinder() {
            return this;
        }
    
        @Override
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION:
                    reply.writeString(DESCRIPTOR);
                    return true;
    
                case TRANSACTION_addPerson:
                    Log.e("Proxy", "Stub,TRANSACTION_addPerson: " + Thread.currentThread());
                    data.enforceInterface(DESCRIPTOR);
                    Person arg0 = null;
                    if ((0 != data.readInt())) {
                        arg0 = Person.CREATOR.createFromParcel(data);
                    }
                    this.addPerson(arg0);
                    reply.writeNoException();
                    return true;
    
                case TRANSACTION_getPersonList:
                    data.enforceInterface(DESCRIPTOR);
                    List<Person> result = this.getPersonList();
                    reply.writeNoException();
                    reply.writeTypedList(result);
                    return true;
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        static final int TRANSACTION_addPerson = IBinder.FIRST_CALL_TRANSACTION;
        static final int TRANSACTION_getPersonList = IBinder.FIRST_CALL_TRANSACTION + 1;
    }
    

    AIDL原理

    aidl其实就是利用Binder来实现跨进程调用。而这个系统根据.aidl文件生成的java文件就是对binder的transact方法进行封装。

    onTransact方法是提供给server端用的,transact方法(内部类proxy封装了transact方法)和asInterface方法是给client端用的。系统自动生成不知道你要把哪个当做server端哪个当做client端所以就都生成了。

    Proxy对应客户端(transact),Stub对应服务端(onTransact)

    DESCRIPTION是根据aidl文件位置(包名)来生成的,DESCRIPTION是binder的唯一标识,这也解释了为什么要保证client端和server端.adil文件的包结构一样;

    aidl的transact的调用会导致当前线程挂起,因此如果Server端的方法耗时最好另起线程来调用transact方法;

    aidl通过Parcel来传递参数和返回值,因为binder只支持Binder对象和parcel对象的跨进程调用;

    Binder,IBinder,IInterface三个的关系。Binder是继承自IBinder对象的,IBinder是远程对象的基本接口。另外IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact(),而IInterface主要为aidl服务,里面就一个方法——asBinder,用来获得当前binder对象(方便系统内部调用)。

    最后说白了aidl的核心就是client调用binder的transact方法,server通过binder的onTransact方法了来执行相应的方法并返回。

    Retrofit原理

    1.首先,通过method把它转换成ServiceMethod。
    2.然后,通过serviceMethod,args获取到okHttpCall对象。
    3.最后,再把okHttpCall进一步封装并返回Call对象。

    platform
    这个是Retrofit支持的平台,里面有Android和Java8,这里自然是Android

    callFactory
    执行请求的客户端,这里是OkHttpClient,在创建的时候.client传入

    converterFactories
    json解析处理工厂数组,这里是GsonConverterFactory。进行请求和响应的解析,将json字符串转换为具体的实体类

    callAdapterFactories
    请求和响应的具体处理适配器工厂数组,这里没有传的话默认为ExecutorCallAdapterFactory,如果需要使用rxjava,为RxJava2CallAdapterFactory

    callbackExecutor
    回调处理类,用于对回调数据的处理,这里是Android平台默认的MainThreadExecutor,使用Handler在主线程中处理回调。

    OkHttp

    拦截器流程

    而OkHttp中的getResponseWithInterceptorChain()中经历的流程为

    请求会被交给责任链中的一个个拦截器。默认情况下有五大拦截器

    • RetryAndFollowUpInterceptor

      第一个接触到请求,最后接触到响应;负责判断是否需要重新发起整个请求

    • BridgeInterceptor

      补全请求,并对响应进行额外处理

    • CacheInterceptor

      请求前查询缓存,获得响应并判断是否需要缓存

    • ConnectInterceptor

      与服务器完成TCP连接

    • CallServerInterceptor

      与服务器通信;封装请求数据与解析响应数据(如:HTTP报文)

    • 一、重试及重定向拦截器

    第一个拦截器:RetryAndFollowUpInterceptor,主要就是完成两件事情:重试与重定向

    • 二、桥接拦截器

    BridgeInterceptor,连接应用程序和服务器的桥梁,我们发出的请求将会经过它的处理才能发给服务器,
    比如设置请求内容长度,编码,gzip压缩,cookie等,获取响应后保存Cookie等操作。这个拦截器相对比较简单。

    桥接拦截器的执行逻辑主要就是以下几点

    对用户构建的Request进行添加或者删除相关头部信息,以转化成能够真正进行网络请求的Request
    将符合网络请求规范的Request交给下一个拦截器处理,并获取Response
    如果响应体经过了GZIP压缩,那就需要解压,再构建成用户可用的Response并返回

    • 三、缓存拦截器
      XXXXXXXXXXXXXXXXXXXXXX
    • 四、连接拦截器

    ConnectInterceptor,这个拦截器中的所有实现都是为了获得一份与目标服务器的连接,在这个连接上进行HTTP数据的收发。

    • 五、请求服务器拦截器

    CallServerInterceptor,利用HttpCodec发出请求到服务器并且解析生成Response

    Glide原理

    一、简单总结

    Glide在加载绑定了Activity的生命周期。
    在Activity内新建一个无UI的Fragment,这个特殊的Fragment持有一个Lifecycle。通过Lifecycle在Fragment关键生命周期通知RequestManger进行相关的操作
    在生命周期onStart时继续加载,onStop时暂停加载,onDestory是停止加载任务和清除操作。

    二、with、load、into、都做了什么

    在这里插入图片描述

    三、图片加载策略

    首先从ActivateResource获取,是个值为弱引用的Map
    MemoryCacheDiskCacheLruCache

    缓存机制加载流程图
    在这里插入图片描述

    1. 第一次加载访问活动缓存,有显示,没有就访问内存缓存
    2. 内存缓存有,就将内存缓存中的数据剪切到活动缓存(内存缓存中会删掉),再显示;内存缓存没有,找磁盘缓存;
    3. 磁盘缓存有,复制一份数据给活动缓存,再显示;磁盘缓存没有,会加载外界资源LoadData数据模型,成功以后,保存到磁盘缓存

    四、缓存流程总结——按场景理解

    在这里插入图片描述

    绿色部分:首次加载场景

    • 1.第一次加载,去网络下载图片,保存到磁盘缓存 (/sd/disk_lru_cahe_dir/key),同时存储到活动缓存一份;
    • 第二次加载,直接在活动缓存中找到了资源;
    • 第三次加载,直接在活动缓存中找到了资源;
    • 第N次加载,直接在活动缓存中找到了资源…

    紫色部分:Activity销毁场景

    把Activity返回时,执行onDestroy,fragment监听到以后,通知进行活动缓存释放(活动缓存 ----> 内存缓存),也就是删除活动缓存,移动到内存缓存中

    • 又一次加载时,从内存缓存中获取到了资源
    • 下一次加载时,从活动缓存获取了资源
    • 以后再加载时,从活动缓存获取了资源
    • 第N次加载时,从活动缓存获取了资源

    将APP进程杀死场景
    APP杀掉,整个活动缓存,整个内存缓存,都没有了(运行内存缓存)
    首次冷启动 所以从磁盘缓存中获取资源
    第二次加载时,从活动缓存获取了资源
    第三次加载时,从活动缓存获取了资源
    第N次加载时,从活动缓存获取了资源

    面试准备

    Glide的优点

    使用简单,链式调用比较方便

    Glide.with(context)
    .load(uri)
    .into(imageView);

    • 占用内存较小
    • 默认使用RGB_565格式,是Picasso的内存占用的一半(Picasso使用RGB_8888)
    • 无代码侵入
    • 相对Picasso来说,接入很简单,无需将ImageView替代为自定义View,也不需要其他配置,只需要将库引入即可
    • 支持gif
    • ImageLoader不支持gif图片加载
    • 缓存优化
      1、支持内存分级缓存:正在使用的图片,弱引用缓存;已使用过的图片LruCache缓存
      2、Glide会为不同的ImageView尺寸缓存不同尺寸的图片
    • 与Activity生命周期绑定,不会出现内存泄露

    实现原理

    基于在Activity中添加无UI的Fragment,通过Fragment接收Activity传递的生命周期。Fragment和RequestManager基于LifeCycle接口建立联系,并传递生命周期事件,实现生命周期感知。

    Glide怎么监听生命周期?
    通过无UI的Fragment

    如何绑定生命周期?
    在调用Glide.with(Activity activity)的时候,总结如下:

    1. Glide绑定Activity时,生成一个无UI的Fragment
    2. 将无UI的Fragment的LifeCycle传入到RequestManager中
    3. 在RequestManager的构造方法中,将RequestManager存入到之前传入的Fragment的LifeCycle,在回调LifeCycle时会回调到Glide的相应方法

    项目中大量使用Glide,偶尔出现内存溢出问题,请说说大概原因?
    答:尽量在with的时候,传入有生命周期的作用域(非Application作用域),尽量避免使用Application作用域,因为Application作用域不会对页面绑定生命周期机制

    有几种缓存模式?

    1. DiskCacheStrategy.ALL:原始图片和转换过的图片都缓存
    2. DiskCacheStrategy.RESOURCE:只缓存原始图片
    3. DiskCacheStrategy.NONE:不缓存
    4. DiskCacheStrategy.DATA:只缓存使用过的图片

    有几层缓存?
    三层,DiskLruCache LruCache ActiveCache
    DiskLruCache是硬盘缓存;
    LruCache是内存缓存;
    ActiveCache是活动缓存

    为什么有了 内存缓存 还需要 活动缓存 ?

    活动:正在使用的图片,都放在活动缓存 (弱引用 GC 没有使用了 已回收 被动回收) 【资源封装 Key Value】

    内存:LRU管理的,临时存放 活动缓存 不使用的Value(LRU最少使用算法) 【资源封装 Key Value】

    为什么要活动缓存?
    内存:LRU管理的,maxsize,如果最少使用,内部算法会回收(不安全,不稳定)

    你正在使用的图片—【活动缓存】如果不用了 才会扫描时回收,[存入 移除 非常快]

    活动缓存回收机制:
    弱引用 (GC全盘扫描,判断,弱引用没有被使用,回收)

    with
    6个重载函数都调用了getRetriever().get()

    EvenetBus原理

    参考:https://blog.csdn.net/qq_38859786/article/details/80285705

    EvenetBus是一种发布-订阅事件总线.代码简洁,开销小,并很好的实现了发送者和接收者的解耦.(是一种观察者模式)

    发布者(Publisher)使用post()方法将Event发送到Event Bus,而后Event Bus自动将Event发送到多个订阅者(Subcriber)。
    3.0以前订阅者的订阅方法为onEvent()、onEventMainThread()、onEventBackgroundThread()和onEventAsync()
    Event Bus3.0之后统一采用注解@Subscribe的形式

    A,Event:事件,
    B,Publisher:发布者,可以在任意线程发布事件
    C,Subscrible:订阅者

    基本使用

    1 添加依赖

    compile 'org.greenrobot:eventbus:3.1.1'
    

    2 在接受消息的代码

      //注册成为订阅者
        EventBus.getDefault().register(this);
        //解除注册
        EventBus.getDefault().unregister(this);
    
        //订阅方法,当接收到事件的时候,会调用该方法
        @Subscribe(threadMode = ThreadMode.MAIN)
        public void onEvent(MessageEvent messageEvent){
            Log.e("date","receive it");
            Toast.makeText(ViewPageStep1Activity.this, messageEvent.getMessage(), Toast.LENGTH_SHORT).show();
        }
    

    3 在发送消息的地方
    EventBus.getDefault().post(new MessageEvent(“从fragment将数据传递到activity22222222”));

    源码解析

    1. getDefault--------采用单利双重锁模式创建Eventbus对象
    static volatile EventBus defaultInstance;
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    
    1. 构造方法中,粘性事件,保存到ConCurrenHashMap集合
      stickyEvents = new ConcurrentHashMap<>(); //线程安全,
    2. register()方法主要做了3件事(过程):

    3.1,从缓存中获取订阅方法列表,
    3.2,如果缓存中不存在,则通过反射获取到订阅者所有的函数,
    3.3,遍历再通过权限修饰符.参数长度(只允许一个参数).注解(@Subscribe)来判断是否是具备成为订阅函数的前提
    3.4,具备则构建一个SubscriberMethod(订阅方法,其相当于一个数据实体类,包含方法,threadmode,参数类型,优先级,是否粘性事件这些参数),循环结束订阅函数列表构建完成添加进入缓存

    在RunTime期间使用反射对程序运行的性能有较大影响,EventBus3.0中增加了一个新特性:通过在编译期创建索引(SubscriberInfoIndex)就不会用反射获取订阅者方法,而是直接从getSubscriberInfo这个方法里面获取,提高程序运行性能,自己添加索引进行优化。

    //通过CopyOnWriteArrayList保存Subscription,Subscription是一个封装类,封装了订阅者、订阅方法这两个类。
    //Arraylist效率高,但线程不安全,在多线程的情况下,使用CopyOnWriteArrayList,避免多线程并发异常

    1. post()

    4.1,发送事件:从缓存中获取目标页面接收信息的方法,循环遍历,找到对应的方法,就将信息传递过去.
    4.2,post调用后先使用ThreadLocal来存储事件,他可以隔离多个线程对数据的访问冲突(ThreadLocal是每个线程独享的,其数据别的线程是不能访问的,因此是线程安全的。
    4.3,根据事件类型(也就是Post参数的类型)为key从subscriptionsByEventType获取订阅方法和订阅者
    此时,我们已经具备了订阅者.订阅方法.待发送事件.post线程.threadmode等信息
    4.4,根据线程和threadmode来判断

    面试题:EventBus 如何实现线程转换的

    EventBus 中生产者和消费者模式的实现主要是在 PendingPostQueue里面。

    PendingPostQueue 的实现比较简单,主要是通过在 enqueue 和 poll 的时候进行 synchronized 同步来实现的。

    EventBus3.0对象池缓存减少了创建对象的开销

    okhttp

    拦截器就是基于责任链模式每个节点有自己的职责,同时可以选择是否把任务传递给下一个拦截器

    责任链模式,这是InterceptorChain的源码以及方法的调用关系,有点像递归,不过两者还是有区别的, 递归函数是不断调用自身
    而这种拦截器的逻辑是两个类(或者说是类内部的方法)不断相互调用,以某个条件作为终结。

    面试题:Android类似的设计模式还有什么

    • 事件传递机制
    • 自定义控件的点击事件拦截
    • ClassLoader的双亲委派机制

    封装的Chain的内部逻辑

    public class MyChain implement Interceptor.Chain{
        Arraylist<Interceptor> incpts;
        int index = 0;
        
        public MyChain(Arrlist<Interceptor> incpts){
            this(incpts, 0);
        }
        
        public MyChain(Arrlist<Interceptor> incpts, int index){
            this.incpts = incpts;
            this.index =index;
        }
        
        public void setInterceptors(Arrlist<Interceptor> incpts ){
            this.incpts = incpts;
        }
     
        @override
        Response proceed(Request request) throws IOException{
                Response response = null;
                ...
                //取出第一个interceptor来处理
                Interceptor incpt = incpts.get(index);
                //生成下一个Chain,index标识当前Interceptor的位置。
                Interceptor.Chain nextChain = new MyChain(incpts,index+1);
                response =  incpt.intercept(nextChain);
                ...
                return response;
        }
      }
    
    public class MyInterceptor implement Intercetpor{
        @Override 
        public Response intercept(Chain chain) throws IOException {
            Request request = chain.request();
            //前置拦截逻辑
            ...
            Response response = chain.proceed(request);//传递Interceptor
            //后置拦截逻辑
            ...
            return response;
        }
    }
    

    Jetpack相关

    Lifecycle

    感知生命周期的组件框架,响应Activity和Fragment生命周期变化并通知观察者,让代码符合生命周期规范,减少内存泄漏。

    设计模式:观察者
    在这里插入图片描述

    Lifecycle 是一个抽象类。它内部定义了两个枚举;Event 需要分发的事件的类型,State 宿主的状态。

    • LifecycleRegistry

    它是 Lifecycle 的唯一实现类。主要用来注册观察者(LifecycleObserver),以及分发宿主状态给它们(可以处理多个观察者)。

    • LifecycleOwner
    AppCompatActivity  impl  LifeCycleOwer
    

    用来声明它是一个能够提供生命周期事件的宿主,Activity/Fragment 都实现了该接口。内部只有一个 getLifecycle 方法

    public interface LifecycleOwner {
        @NonNull
        Lifecycle getLifecycle();
    }
    
    • LifecycleObserver

    用来定义观察者。

    看图总结

    说明:

    • Activity/Fragment 都默认实现了 LifecycleOwner 接口;
    • LifecycleRegistry 是 Lifecycle 唯一的实现类;
    • 实现观察者(Observer)有三种方式:LifecycleObserver 配合 @OnLifecycleEvent
      注解、DefaultLifecycleObserver 拥有宿主所有生命周期事件、LifecycleEventObserver
      将宿主生命周期事件封装成 Lifecycle.Event
    • 在 Activity/Fragment 中通过 getLifecycle() 方法获取到一个 LifecycleRegistry 对象;
    • 通过调用 LifecycleRegistry 对象的 addObserver() 添加一个观察者(该观察者通过三种方式实现都可以)。

    在这里插入图片描述
    面试题

    • LifeCycle 是如何监听到 Activity/Fragment 生命周期变化的?
      1、在Activity中创建了ReportFragment
      2、ReportFragment生命周期中都调用dispatch()方法
      3、dispatch中判断Activity是否实现了LifecycleOwer接口,调用LifecycleRegistry的handleLifecycleEvent()
      总结:这样生命周期状态就会借由LifecycleRegistry通知各个LifecycleObserver从而调用对应的Lifecycle.Event方法

    • LifeCycle 如何将生命周期变化的事件分发给观察者的?
      通过LifecycleRegistry分发,核心代码在dispatch中的 ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);

      @SuppressWarnings("deprecation")
        static void dispatch(@NonNull Activity activity, @NonNull Lifecycle.Event event) {
            ...
            if (activity instanceof LifecycleOwner) {
                Lifecycle lifecycle = ((LifecycleOwner) activity).getLifecycle();
                if (lifecycle instanceof LifecycleRegistry) {
                    //最终还是通过 LifecycleRegistry 来处理事件的分发
                    ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
                }
            }
        }
    
    • Activity 如何实现 LifyCycle?
      Activity 实现 Lifecycle 需要借助于 ReportFragment 往 Activity 上添加一个 fragment。ReportFragment 没有任何的页面,它只负责在生命周期变化时利用 LifecycleRegistry 进行相应事件的分发

    之所以需要借助 ReportFragment ,目的是为了兼顾不是继承自 AppCompactActivity 的场景, 同时也支持我们自定义 LifecycleOwner 的场景。

    • Fragment 如何实现 LifyCycle

    在 Fragment 各个生命周期方法内部会利用 LifecycleRegistry 进行相应的事件分发。

    Livedata

    LiveData本身是观察者,观察组件的Lifecycle,也是被观察者,数据变化时要通知数据的观察者。

    How it works:

    • LiveData::observe(lifecycleOwner, observer)
    • LiveData::setValue VS LiveData::postValue

    LiveData::observe(lifecycleOwner, observer)

    在activity/fragment中观察LiveData,我们一般都会创建一个observer实例,调用liveData.observe(lifecycleOwner, observer)方法,在observer的回调方法onChanged中,实现UI的更新,无需再判断当前lifecycle来决定是否要更新。

    observe source code

    
    @MainThread
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
            assertMainThread("observe");
            if (owner.getLifecycle().getCurrentState() == DESTROYED) {
                // ignore
                return;
            }
            LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
            ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
            if (existing != null && !existing.isAttachedTo(owner)) {
                throw new IllegalArgumentException("Cannot add the same observer"
                        + " with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        }
    

    liveData的成员变量mObservers保存了所有observer和它们各自的相关的lifecycleowner。

    在observe方法中,observer和lifecycleOwner包裹成一个新的lifecycleBoundObserver对象,在检查有效性后,observer和包裹后的lifecycleBoundObserver分作为key、value存入mObservers中。

    lifecycleBoundObserver实现了LifecycleEventObserver接口

    执行lifecycle.addObserver(lifecycleBoundObserver)后,即可在lifecycle状态(state)变化时,触发lifecycleBoundObserver.onStateChanged,根据lifecycle当前状态,决定observer是否活跃(active), 从而决定是否要接收livedata中的数据更新。

    lifeBoundObserver.shouldBeActive方法,定义了lifeCycle至少处于STARTED以及之后状态,observer才是active的。

    状态从inactive变成active时,lifeBoundObserver会调用 liveData.dispatchingValue(this) 方法,让liveData把mData中的告知自己,从而通知其包含的observer。

    例如,一个在background的activity回到foreground时,其相关的observer也从inactive变成active,也就是onStart后,马上会收到当前livedata中的值。

    需要知道的是,lifeCycle不仅会在state真正改变时通知lifecycleBoundObserver。调用 lifecycle.addObserver(lifecycleBoundObserver) 后,它会把达到当前状态前经历的所有LifeCycle.Event都发送给lifecycleBoundObserver。

    例如,当前lifeCycle状态为RESUMED, lifecycle.addObserver(lifecycleBoundObserver) 后,

    lifeBoundObserver.onStateChanged 马上连续收到ON_CREATE,ON_START,ON_RESUME三个事件。可以想象成“补收”之前的事件。在这种情况下,因为onStateChanged在收到ON_CREATE时,lifecycle已经处于RESUME状态,再STARTED之后,所以observer已是活跃的。所以比如在activity onResume后,onPause前,让一个obsever开始观察livedata,那么这个observer的onChange方法会立马被调用(如果mData已经设置过的情况)。

    所以,observer并不只是再数据变化时收到“推送”,也可在lifecycle状态变化时收到通知。

    参考 LiveData实现原理

    LiveData::setValue VS LiveData::postValue

    更新一个MutableLiveData实例中值,需要通过setValue或postValue方法,其中setValue只能在主线程调用。为什么有这样的设计呢?

    阅读LiveData(MutableLiveData的父类)源码,其内部使用了俩个volatile修饰的成员变量,mData和mPendingData

    mData保存了最终数据。LiveData实例暴露给外部取值的getValue方法,以及其内部推送数据给观察者时,使用的都是mData。mData只能通过setValue方法更新,即只能由main thread写入,加上volatile的特性(直写main memory而非cpu cache),写入的新值将对所有线程可见。所以不会出现mData更新后,还有线程读取到更新前数据的情况。

    那主线程外更新数据怎么实现呢?
    就由postValue利用mPendingData完成。

    mPendingData中保存了将要但还未被写入mData中的值。postValue方法中,新的数据先被写入mPendingData,然后post一个runnable task到主线程。task中调用setValue方法将mPendingData中的值写入mData,接着清除mPendingData中的值。在postValue和task中,mPendingData的读写都由synchronized block包裹。即postValue中和task中对mPendingData的操作不会并行,避免了postValue对mPendingData的赋值正好被task中mPendingData清除覆盖的情况。

    postValue source code

    protected void postValue(T value) {
            boolean postTask;
            synchronized (mDataLock) {
                postTask = mPendingData == NOT_SET;
                mPendingData = value;
            }
            if (!postTask) {
                return;
            }
            ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
        }
    

    另外,postValue时,如果当前已有task就不会再post新的task。所以task被执行前,无论postValue被调用多少次,只有最后一次postValue中传入的值,会被更新入mData。也就是说,观察者们不会观察到之前多次postValue中的数据。

    task source code

    private final Runnable mPostValueRunnable = new Runnable() {
            @SuppressWarnings("unchecked")
            @Override
            public void run() {
                Object newValue;
                synchronized (mDataLock) {
                    newValue = mPendingData;
                    mPendingData = NOT_SET;
                }
                setValue((T) newValue);
            }
        };
    

    所以,LiveData 不能直接 被当作 事件线 使用。它的事件是会“丢失”的。它更像是Rx中的BehaviorSubject而非Observerable。除了上述原因,观察者从inActive状态切换到active状态时,会“主动”观察当前LiveData中的值,也是LiveData不可作事件线用的另一个原因。

    viewmodel

    只是一个容器,数据保存到viewmodel里,当生命周期改变不会丢数据

    room

    paging 分页组件 recycleview做分页需要自己写代码
    做导航 在多fragment,比多个activity跳来跳去好一些
    workmanager 管理常见任务,wifi非wifi切换做不同处理,或者一个小时以后上传网络数据

    Lifecycle

    使用:用一个类作为观察者来观察被观察者MainActivity的生命周期 这个类要实现LifecycleObserver
    1)被观察者怎么实现的呢? MainActivity 和fragmentActivity 都 extends ComponentActivity implenments LifecycleOwner
    2)被观察者怎么通知观察者,被观察者和观察者怎么绑定在一起? 在被观察者中 getLifecycle().addObserver(presenter)
    3) 被观察者生命周期发生变化,观察者在哪里收到消息呢? 类似观察者的update @onLifeCycleEvent注解、反射

    原理: getLifecycle().addObserver(presenter)
    ComponentActivity的onCreate ReportFragment.injectifNeededIn(this); 不带UI的fragment绑定到activity上面,谷歌抄袭glide源码

      反射 状态机
    

    livedata
    使用:在ViewModel ViewModelProviders.of(this).get(NamgeViewModel.class);
    然后将model和观察者绑定,需要一个观察者来观察数据
    Observer observer = new Observer(){
    onChanged(String str){

            }
        }
        然后订阅
        model.getCurrentName().observe(this,observer);
    
        只要string改变,就会onChanged激活一次
    
    
     MutableLiveData<String>
    
     LiveDataBus 存放订阅者
    
     需要注册订阅者 提供with方法(key,MutibLivedata)
     发送数据 LiveDataBus.getInstance().with("data",String.class).postValue("jett");
     接收端怎么接收数据呢? LiveDataBus.getInstance().with("data",String.class).observe(this,new Observer<String>(){
        @Override
        onChanged(String s){
    
        }
     })
    

    Viewmodel相关

    在Activity重建时会执行destory生命周期事件,那么为什么ViewModel没有销毁呢?

    onRetainNonConfigurationInstance中会存储ViewModelStore实例

    fragment之间怎么共享viewmodel?
    通过activity的viewmodel

    http://www.yanfriends.com/blog/23/

    dataBinding

    databinding 双向绑定,UI和数据绑定,数据改变,UI一起改变

    dataBinding使用了 APT(Annotation Processing Tool)即注解处理器的技术,编译期间会扫描res/layout目录下所有的布局文件,只要有data标签,都会生成两个布局文件:配置文件带标签的布局文件

    其中配置文件主要是通过tag能够快速定位绑定的控件,生成的布局文件主要是生成控件的tag值

    结合生成文件和XML的位置和时间节点,大致可以看出生成的原理是Gradle插件+APT

    面试题:dataBinding如何实现双向绑定?

    1. model->view的过程? setText

    ActivityMainBindlmpl -> executeBindings
    android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView2, userPasswordGet);
    用model的属性赋值给控件,这不就是Model到View的过程嘛!model的所有值都会直接刷新控件UI赋值。

    1. View怎么刷新Model呢? setTextWatcher

    一旦你的view发生了变更,被监听到了,那么它view的值就会被get出来:
    android.databinding.adapters.TextViewBindingAdapter.getTextString(mboundView2);拿到这个值之后,就会到model对象赋值,也就是说,View的值输入发生了任何变更,都可以改变Model! 这就是双向绑定!和反射没有任何关系。
    ActivityMainBindlmpl.java是通过APT技术自己生成出来的。

    HTTPS

    可能存在的面试题总结如下:

    1. 用一段话来总结HTTPS:

    HTTPS要使客户端与服务器端的通信过程得到安全保证,必须使用的对称加密算法,但是协商对称加密算法的过程,需要使用非对称加密算法来保证安全,然而直接使用非对称加密的过程本身也不安全,会有中间人篡改公钥的可能性,所以客户端与服务器不直接使用公钥,而是使用数字证书签发机构颁发的证书来保证非对称加密过程本身的安全。这样通过这些机制协商出一个对称加密算法,就此双方使用该算法进行加密解密。从而解决了客户端与服务器端之间的通信安全问题。

    1. http和https区别?

    0.9 1991 get请求
    1.0 1996 PUT PATCH HEAD OPTIONS DELETE
    1.1 1997 持久连接、节约带宽、HOST域、管道机制、分块传输
    2.0 2015 多路复用 一个TCP链接可以请求响应多次

    1. SSL握手过程?

    1、 client请求server 发送信息随机值1+客户端支持的加密算法
    2、 server响应握手信息,包括随机值2+匹配好的协商加密算法
    3、 server给client发送第二个响应报文是“数字证书”,这套证书就是一对公钥和私钥,传送证书,这个证书就是公钥,只是包含了很多信息,如颁发机构,过期时间,服务端公钥,第三方认证机构的签名,服务端的域名信息
    4、 client解析证书,TLS来完成,首先验证公钥是否有效,如果没问题,生成一个随机值(预主密钥)
    5、 clinet接下来通过随机值1、随机值2和预主密钥组装会话密钥,然后通过公钥加密会话密钥
    6、 client传送加密信息,也就是证书加密的会话秘钥,目的就是让server使用秘钥解密得到随机值1、随机值2和预主秘钥
    7、 server解密得到随机值1、随机值2、预主秘钥,然后组装会话秘钥,跟client会话秘钥相同
    8、 client通过会话秘钥加密一条消息发送Server,验证server是否正常接收加密的消息
    9、 server也会加密一条消息传送client,如果client正常接收,证明SSL层连接建立完成

    TCP和UDP那点事

    1. 灵魂拷问1——TCP和UDP区别?

    TCP 是可靠通信协议, 而 UDP 是不可靠通信协议。
    TCP 的可靠性含义: 接收方收到的数据是完整, 有序, 无差错的。
    UDP 不可靠性含义: 接收方接收到的数据可能存在部分丢失, 顺序也不一定能保证。

    1. 灵魂拷问2——TCP 协议怎么实现可靠传输?

    通信双方需要判断自己已经发送的数据包是否都被接收方收到, 如果没收到, 就需要重发。
    为了实现这个需求, 很自然地就会引出序号(sequence number) 和 确认号(acknowledgement number) 的使用。

    1. 灵魂拷问3——TCP为什么需要握手这个操作, 能不能不握手?

      UDP就不用握手,不可靠传输
      为了实现可靠传输,发送方和接收方始终需要同步( SYNchronize )序号,所以需要握手确认是否收到消息
      

    三次握手过程

    SYN =1 , seq = x
    SYN =1, ACK = 1, seq = y , ack = x+1
    ACK =1 , seq = x+1, ack = y+1

    四次挥手呢?

    1. 灵魂拷问4——为什么三次握手最后一次握手中, 在上面的示意图中回复的 seq = x+1 。

    TCP 协议规定SYN报文虽然不携带数据, 但是也要消耗1个序列号, 所以前两次握手客户端和服务端都需要向对方回复 x+1 或 y+1 。
    最后一次握手在默认不携带数据的情况下, 由于SYN 不是 1 , 是不消耗序列号的。 所以三次握手结束后, 客户端下一个发送的报文中 seq 依旧是 x+1
    TCP标准规定,ACK报文段可以携带数据,,但如果不携带数据则不消耗序号

    1. 灵魂拷问5——为什么连接的时候是三次握手,关闭的时候却是四次握手?

      关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。
      只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送

    2. 灵魂拷问6——为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?

       我们必须假象网络是不可靠的,有可以最后一个ACK丢失
       lient会设置一个计时器,等待2MSL,如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
      
       (所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间)
      
    3. 灵魂拷问7——为什么不能用两次握手进行连接?

       3次握手完成两个重要的功能,
           既要双方做好发送数据的准备工作(双方都知道彼此已准备好),
           也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
      `两次握手,可能发生死锁`
      

    为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤
    重点结论:如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认

    1. 灵魂拷问8——如果已经建立了连接,但是客户端突然出现故障了怎么办?

    TCP还设有一个保活计时器,`服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。

    1. 灵魂拷问9——Client为什么要有心跳连接?

    如果客户端断线,服务器监听不到吗?
    答:能

    那为什么要有心跳连接呢?
    运营商会定期检查,因为可能出现NAT超时问题

    1. Https抓包原理

    思路:中间人攻击(MITM)

    需要考虑:
    MITM Server如何伪装成真正的Server;
    MITM Client如何伪装成真正的Client。

    第一个问题,MITM Server要成为真正的Server,必须能够给指定域名签发公钥证书,且公钥证书能够通过系统的安全校验。比如Client发送了一条https://www.baidu.com/的网络请求,MITM Server要伪装成百度的Server,必须持有www.baidu.com域名的公钥证书并发给Client,同时还要有与公钥相匹配的私钥。

    MITM Server的处理方式是从第一个SSL/TLS握手包Client Hello中提取出域名www.baidu.com,利用应用内置的CA证书创建www.baidu.com域名的公钥证书和私钥。创建的公钥证书在SSL/TLS握手的过程中发给Client,Client收到公钥证书后会由系统会对此证书进行校验,判断是否是百度公司持有的证书,但很明显这个证书是抓包工具伪造的。为了能够让系统校验公钥证书时认为证书是真实有效的,我们需要将抓包应用内置的CA证书手动安装到系统中,作为真正的证书发行商(CA),即洗白。这就是为什么,HTTPS抓包一定要先安装CA证书。

    第二个问题,MITM Client伪装成Client。由于服务器并不会校验Client(绝大部分情况),所以这个问题一般不会存在。比如Server一般不会关心Client到底是Chrome浏览器还是IE浏览器,是Android App还是iOS App。当然,Server也是可以校验Client的,这个后面分析。

    1. OkHttp如何防止抓包

    1.描述

    客户端请求接口数据时,可以使用一些apk完成抓包操作。会获取我们请求的参数和请求成功后的报文。常见的抓包工具有HttpCanary和Fiddler。那么我们在使用OkHttp时,如何防止被这些apk转包呢?其实OkHttp已经为我们提供了相应的方法。

    2.原理

    OkHttp使ProxySelector来获取代理信息,在构造OkHttpClient时是可以设置的,其默认值是ProxySelector.getDefault(),该默认值会反应出系统的代理信息。
    那么我们就可以提供自己的ProxySelector实现来达到绕过系统代理的能力。

    3.代码

    //配置okHttp的参数
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .connectTimeout(nettimeout, TimeUnit.SECONDS)
     
     
            //proxySelector方法设置防止抓包
            .proxySelector(new ProxySelector() {
                @Override
                public List<Proxy> select(URI uri) {
                    return Collections.singletonList(Proxy.NO_PROXY);
                }
     
                @Override
                public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
     
                }
            })
     
     
            .build();
    

    DNS解析过程

    首先,客户端发出DNS请求翻译IP地址或主机名。DNS 服务器在收到客户机的请求后:

    1. 检查DNS 服务器的缓存,若查到请求的地址或名字,即向客户机发出应答信息;
    2. 若没有查到,则在数据库中查找,若查到请求的地址或名字,即向客户机发出应答信息;
    3. 若没有查到,则将请求发给根域DNS
      服务器,并依序从根域查找顶级域,由顶级查找二级域,二级域查找三级,直至找到要解析的地址或名字,即向客户机所在网络的DNS服务器发出应答信息,DNS
      服务器收到应答后先在缓存中存储,然后,将解析结果发给客户机。
    4. 若没有找到,则返回错误信息。

    总结:DNS服务器缓存->数据库->根域DNS服务器->顶级域->二级域->三级域->存入缓存

    加密

    加密方式和用途

    1、不可逆加密。比如 MD5、SHA、HMAC
    典型用途:

    密码总不能明文存到数据库吧,所以一般加密存起来,只要用户的输入经过同样的加密算法 对比一下就知道密码是否正确了,所以没必要可逆。

    2、可逆加密。

    对称加密。比如:AES、DES、3DES、IDEA、RC4、RC5、RC6
    典型用途:

    用同一个密码加密和解密,太常见了,我用密码加密文件发给你,你只能用我的密码才能解开。

    非对称加密(就是公私钥)。比如:RSA、DSA、ECC

    典型用途:
    加密(保证数据安全性)使用公钥加密,需使用私钥解密
    认证(用于身份判断)使用私钥签名,需使用公钥验证签名

    如何选择加密方式,怎么加密?

    • 用不可逆加密可行吗?

    想想目的,这个肯定不可行

    • 用对称加密可以行?

    不行,秘钥都可能被截获

    • 用非对称加密(rsa)可行吗?

    试想一下:如果服务器生成公私钥,然后把公钥明文给客户端(有问题,下面说),那客户端以后传数据用公钥加密,服务端用私钥解密就行了,这貌似能保证浏览器到服务端的通道是安全的,那服务端到浏览器的通道
    如何保证安全呢?
    那既然一对公私钥能保证,那如果浏览器本身也生成一对公私钥匙,然后把公钥明文发给服务端,抛开明文传递公钥的问题,那以后是不是可以安全通信了,的确可以!但https本身却不是这样做的,最主要的原因是非对称加密非常耗时,特别是加密解密一些较大数据的时候有些力不从心,当然还有其他原因。既然非对称加密非常耗时,那只能再考虑对称加密了。

    • 用非对称加密 + 对称加密可行吗?(行也得行,不行也得行,因为也没有其他方式了)

    上面提到浏览器拥有服务器的公钥,那浏览器生成一个密钥,用服务器的公钥加密传给服务端,然后服务端和浏览器以后拿这个密钥以对称加密的方式通信不就好了!完美!
    所以接下来说一下上面遗留的一个问题:服务端的公钥是明文传过去的,有可能导致什么问题呢?
    如果服务端在把明文公钥传递给浏览器的时候,被黑客截获下来,然后把数据包中的公钥替换成自己伪造的公钥(当然他自己有自己的私钥),浏览器本身是不知道公钥真假的,所以浏览器还是傻傻的按照之前的步骤,生成对称密钥,然后用假的公钥加密传递给服务端,这个时候,黑客截获到报文,然后用自己的私钥解密,拿到其中的对称密钥,然后再传给服务端,就这样神不知鬼不觉的,对称密钥被黑客截取,那以后的通信其实就是也就全都暴露给黑客了。
    公钥不能明文传输,那要怎么办?

    淡定的考虑一下,上面的流程到底哪里存在问题,以致使黑客可以任意冒充服务端的公钥!
    其实根本原因就是浏览器无法确认自己收到的公钥是不是网站自己的。

    如何保证浏览器收到的公钥一定是该网站的公钥

    现实生活中,如果想证明某身份证号一定是小明的,怎么办?看身份证。这里国家机构起到了“公信”的作用,身份证是由它颁发的,它本身的权威可以对一个人的身份信息作出证明。互联网中能不能搞这么个公信机构呢?给网站颁发一个“身份证”?当然可以,这就是平时经常说的数字证书

    什么是数字证书?证书都包含什么?

    身份证之所以可信,是因为背后是国家,那数字证书如何才可信呢?这个时候找CA(Certificate Authority)机构。办身份证需要填写自己的各种信息,去CA机构申请证书需要什么呢?至少应该有以下几项吧:
    1、网站域名
    2、证书持有者
    3、证书有效期
    4、证书颁发机构
    5、服务器公钥(最主要)
    6、接下来要说的签名时用的hash算法
    那证书如何安全的送达给浏览器,如何防止被篡改呢?给证书盖个章(防伪标记)不就好了?这就又引出另外一个概念:数字签名

    未完待续......

    展开全文
  • 但应在拱圈砂浆达到设计强度后方可卸。 箱涵通道、涵洞施工流程图 路堤整修施工流程图 施工工艺 (1)测量放样,洒白灰标示出路堤两侧超填宽度,路堤顶面纵横向坡面标高采用埋砖法控制。 (2)填土路堤两侧超填的宽度...

    路基工程

    01

    拆除工程

    拆除浆砌石:18~25元/m³

    机械拆除素砼:40~60元/m³

    爆破拆除素砼:40~60元/m³

    机械拆除钢筋砼:80~90元/m³

    机械挖除水泥砼路面:20~30元/m³

    机械挖除沥青砼路面:10~15元/m³

    机械挖除碎石路面或粒料基层:10~12元/m³

    人工凿除沥青砼路面:35~50元/m³

    人工拆除路缘石:15~20元/m³

    拆除砖墙:15~20元/m³

    02

    机械土方

    挖装土方:2~2.8元/m³

    运输第1km:2.8~3.5元/m³

    增运0.5km:0.4~0.5元/m³

    碾压及路基修整(含翻挖晾晒):3~3.5元/m³

    碾压及路基修整(含翻挖晾晒):2.5~3.5元/m³

    掺灰5%处理(旋耕机、小宝马):19~23元/m³

    掺灰6%处理(旋耕机、大宝马):22~25元/m³

    每增减1%生石灰:1.9~2.5元/m³

    冲击碾压:0.12~0.15元/m³

    推土机推表土:1.0~1.8元/m³

    推土机平整弃渣场:0.5~0.8元/m³

    砍伐树木:5~8元/m³

    挖除树根:10~15元/m³

    接管吹填中粗砂:27元/m³

    一般堆载及超载填砂:25元/m³

    砂垫层(含Φ100PVC泄水管及外包反滤上工布):40元/m³

    堆载顶压填砂(沉降及加宽):26元/m³

    填砂袋(反压护道):60元/m³

    机械素土回填:5~8元/m³

    03

    路基石方

    软石(松动爆破):5.5~7元/m³

    次坚石(松动爆破):6~7.5元/m³

    特坚石、坚石(松动爆破):7~8.5元/m³

    孤石:10~12元/m³

    控制爆破增加费:5~8元/m³

    装石方:2.5~3.5元/m³

    运输第1km:3~4元/m³

    碾压石方:3~4元/m³

    增运0.5km:0.5~0.6元/m³

    添AB组料:5~6.5元/m³

    04

    路基附属工程

    干砌石边沟:45~60元/m³

    浆砌石边沟:65~75元/m³

    预制混凝土边沟:150~170元/m³

    现浇混凝土边沟:150~160元/m³

    05

    边坡防护

    干砌石:45~65元/m³

    浆砌片石护坡:60~70元/m³

    浆砌块石护坡:50~60元/m³

    砼预制块护坡:150~160元/m³

    铺草皮:3.5~5元/m³

    喷播植草:2.5~5.5元/m³

    客土喷播(10cm厚):25~27元/m³

    喷混植生:25~32元/m³

    预应力锚索:100~120元/m³

    现浇砼边坡:130~150元/m³

    Φ=22、25锚杆:8~10元/m³

    框架梁:120~170元/m³

    主动防护网:110~120元/m³

    06

    软基处理

    泥浆泵清淤:4~5元/m³

    挖掘机清楚淤泥:7~12元/m³

    砂垫层:55~60元/m³

    碎石垫层:55~60元/m³

    抛填片石:45-50元/m³

    水泥搅拌桩(Φ500mm):9~11元/m

    单管高压旋喷机(Φ500mm):50~60元/m

    单管高压旋喷机(Φ600mm):55~65元/m

    素砼桩(50cm):50~60元/m

    静压或打入桩(网点):13~18元/m

    砂桩、碎石桩(80cm):16~18元/m

    塑料排水板:0.9~1.2元/m

    土工格室:0.9~1.2元/m

    土工网垫:0.9~1.2元/m

    土工格栅:0.9~1.2元/m

    复核土工膜(含土工布):0.9~1.2元/m

    袋装砂井:1.5~2元/m

    长螺旋CFG桩(50cm):12~18元/m

    CFG桩桩帽混凝土:45~55元/m³

    CFG桩桩板混凝土:55~65元/m³

    CFG桩桩板、桩帽钢筋:400~420元/t

    07

    挡土墙

    浆砌片石护坡:50~65元/m³

    钢筋混凝土:110~130元/m³

    片石混凝土墙身:90~120元/m³

    片石混凝土基墙:45~60元/m³

    钢筋混凝土:430~480元/t

    混凝土预制块墙身:140~160元/m³

    08

    桩板挡墙

    就地灌注钢筋混凝土桩柱(地下):25~30元/m³

    就地灌注钢筋混凝土桩柱(地上):110~130元/m³

    挡土板预制:80~90元/m³

    挡土板运输1km:50~60元/m³

    挡土板安装:50~60元/m³(安装)

    钢筋:400~450元/t

    09

    抗滑桩

    挖桩土方:60~70元/m³

    石方:120~140元/m³

    护壁:120~140元/m³

    桩身:30~35元/m³

    钢筋:450~480元/t

    ab218faa2313be42f57f464f271f04ab.png

    土方路基、石方路基、路基摊铺、路基开挖、路基回填:路基的种类和工程类型复杂而多变,路基的施工方法也是各种各样。不同路基类型一般对应着不同的施工方法,而不同的方法又有不一样的施工流程。

    填土路基施工流程图

    580e300732af1173b51b6e074b2ac672.png

    施工工艺

    (1)路基每层填料铺设前,下一层底面必须石灰打格,并且挂线,以控制施工层铺厚度。

    (2)若填方分几个作业段施工,两段交接处不在同一时间填筑,则先填地段应按1:1分层留台阶。若两个地段同时填,则应分层相互交叠衔接,其搭接长度不得小于2m。

    (3)机械作业时,应根据工地地形、路基横断面形状和土方调配图等,合理地规定机械运行路线。土方集中工点,应有全面、详细的机械运行作业图。

    (4)压路机进行路基压实作业行驶速度在4km/h以内为宜,压实路线,直线段宜先两侧后中间,小半径曲线段由内侧向外侧,纵向进退式进行;横向接头,对振动压路机重叠0.4~0.5m,对三轮压路机重叠轮宽的1/2,前后相邻两区段宜纵向重叠1.0~1.5m,使路基各点都得到压实,避免土基产生不均匀沉陷。

    填挖交界处施工流程图

    74ad00b90d7eedc09029ce7b6c9a42f4.png

    结构物处回填施工流程图

    e04c25995ecd396812011380b08c49fa.png

    施工工艺

    (1)结构物回填前应在台背用油漆画好每一层的松铺厚度标志线,分层回填压实。

    (2)涵洞缺口填土,应在两侧对称均匀分层回填压实。如使用机械回填,则涵台胸腔部分及检查井周围应先用小型压实机械压实后,方可用机械进行大面积回填。

    (3)填土过程中,应防止水的浸害,回填结束后,顶部应及时封闭。

    (4)在涵洞两侧缺口填土未完成前,不得进行涵顶标高以上的填方施工。

    冲击式压路机施工流程图

    bb7e4f611223116d3a79c95284733877.png

    施工工艺

    (1)冲击式压路机最大瞬间冲击功率不小于25KJ,轮重为16T,动力不小于400马力,行走时速不小于12KM。

    (2)冲碾时注意避免对涵洞或其他构造物的损坏。

    填石路基施工流程图

    5fb1d7114b684fb95aabb4ead87b38d8.png

    施工工艺

    (1)填石路堤逐层填筑时,应安排好石料运输路线,专人指挥,按水平分层,先低后高,先两侧后中央上料,并用大功率推土机摊平。个别不平处应配合细石块、石屑找平。

    (2)当石块级配较差、料径较大、填层较厚、石块间空隙较大时,可在每层表面的空隙里扫入石渣、石屑、中粗砂,再以压力将砂冲入下部,反复数次,使空隙填满。

    (3)人工铺填石料时,应先铺填写大块石料,大面向下,小面向上,摆平放稳,再用小石块找平,石屑塞缝、最后压实。

    (4)填石路堤压实时应先两侧(即靠路肩部分)后中间,压实路线对于轮碾应纵向互相平行,反复碾压。行与行之间应重叠40-50cm,前后相邻区段应重叠1.0-1.5m。

    土质路堑开挖施工流程图

    8848213dfc48ac0e384f0ad89a4d0778.png

    软土地基施工流程图

    0c2f3d538af07b54cb3a7f2ffd49fba7.png

    河、塘、湖(库)、海地区路基施工流程图

    93b88a3c7d797a72e736aa41d3cc1038.png

    施工工艺

    (1)填料与取土:宜设置集中取土场。常水位以下路堤的施工材料,宜选用矿渣、块石、砾石等水稳性良好的材料,其粒径不宜大于30cm。

    (2)受水位涨落影响的部分,也宜选用水稳性好的材料,如具有天然级配的砂砾、卵石、粗(中)砂,石质坚硬不宜风化的片、碎石等。

    (3)严格按设计图纸及文件并根据水流对路基破坏作用的性质、程度进行防护和加固施工。当施工现场的实际情况与设计防护形式不符,应提请监理工程师设计代表变更设计。防护方式一般可采用植物防护、石砌防护、砼板防护、石笼、抛石、挡土墙等措施或综合采用两种及两种以上的措施。

    (4)山区沿河路基,应针对水流冲刷情况进行加固和防护,施工期间注意防洪,防洪工程宜在洪水期前完成;穿越地质不良陡峻沟谷时,还应查清有无泥石流影响,并相应采取排导,拦截措施。

    铺种草皮防护施工流程图

    cee572965f9f23507478fec0de8f5d9d.png

    喷播草籽防护施工流程图

    61d53335a5b1c004294da7e98941b8ed.png

    浆砌片(块)石或混凝土预制块防护施工流程图

    1dce2a0dc9d3696f91332335157067ef.png

    一般路段边沟施工流程图

    c65efb6449dccf27f75cfe50a233b2cb.png

    盲沟、渗沟施工流程图

    674b5d822b63e21bbcc3d13e72514068.png

    圆管涵施工流程图

    8ab4b36f177eae992884d16d9657b7c3.png

    倒虹吸施工流程图

    f7dc41e7b1ccc590754a94dab18381bd.png

    施工工艺

    (1)倒虹吸管施工与圆管涵的施工技术与工艺基本一致,但重点处理在管节之间的接缝处理。

    (2)倒虹吸在填土覆盖前必须做灌水试验,符合要求后,方可填土。

    盖板涵施工流程图

    e26418a017d9999ac08ec23c2fcad873.png

    拱涵施工流程图

    a80faa9d7e2af0650879ac75180a2af5.png

    施工工艺

    (1)拱涵有关砌体工程必须按砌体工程相关规范要求施工。

    (2)基坑开挖及基底承载力检测如盖板涵。

    (3)拱圈支架必须经设计验算确定,搭设拱圈时必须预留相应的预拱度,支架必须有足够的强度和刚度。

    (4)拱圈砌筑或采用砼浇筑时,应由两侧向中间同时对称进行,以防拱架失稳,进出水口的拱上端墙应待拱圈合拢砂浆强度达到设计强度的30%以上后方可进行施工。

    (5)拱圈砂浆强度达到设计强度的70%时方可拆除拱架,砂浆强度必须达到设计强度后方可进行拱上填土。

    (6)当拱架未拆除,拱圈砂浆强度达到设计强度的70%时,可进行拱顶填土。但应在拱圈砂浆达到设计强度后方可卸架。

    箱涵通道、涵洞施工流程图

    8ab4b36f177eae992884d16d9657b7c3.png

    路堤整修施工流程图

    93d207a8fd93bb8e4f3c1a39d844e252.png

    施工工艺

    (1)测量放样,洒白灰标示出路堤两侧超填宽度,路堤顶面纵横向坡面标高采用埋砖法控制。

    (2)填土路堤两侧超填的宽度应予切除,如遇边坡缺土,必须挖成台阶分层填补夯实。

    (3)边沟整修应挂线进行,如遇边沟缺损,不可随意用土贴补。

    (4)修整的路基表层厚150mm以内,松散的或半埋的尺寸大于100mm的石块,应从路基表面层移走,并按规定填平压实。

    (5)在路堑边沟和路堤拱形护坡,每隔20米应设20*20cm临时排水孔,排水孔底面低路床标高10cm,在凹形竖曲线低处应增设临时排水孔。

    (6)在路面施工前,应检查临时排水、永久排水设施是否设置、有效。

    (7)路基修整完毕后,堆弃路基范围内的废土料应予清除。

    展开全文
  • MyBatis框架

    2019-08-26 21:10:21
    构建者模式:就相当于你要修房子的时候,你自己不用修,你找来了包工队,由这个包工队修,你只需要给钱。这个包工队就相当于是构建者。 构建者模式的优势:把对象的创建细节隐藏,使得使用者直接调用方法即可拿到...
  • 但是现在不用了,我们有了一个包工队:builder,我们只需要把钱:in 给它,它就把把工厂:factory 建好 三、工厂模式 //3、使用工厂生产SqlSession对象 SqlSession session = factory.openSession(); 生产SqlSession...
  • 所以最简单的就是找一个包工队,所以SqlSessionFactoryBuilder就是那个包工队,in就是钱 ) 优点:把对象的创建细节隐藏,使使用者直接调用方法即可拿到对象。屏蔽了很多繁琐的操作 SqlSession 生产...
  • (2)构建者:就像盖房子,找的包工队。 (3)优点:把对象的创建细节隐藏,让使用者直接调用方法即可拿到对象;减少繁琐的操作。 工厂模式: (1)生产SQLSession使用了工厂模式。 (2)优点:使用工厂去生产一个...
  • } //创建工厂 //创建工厂时,mybatis使用了构建者模式,即找包工队盖房子,而不是自己亲自处理一系列问题 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = ...
  • 这时我们找一个包工团队,只要给钱,就能办好。代码里面的SqlSessionFactoryBuilder就是这样的团队。里面所有繁琐的事都不用管了。 为什么要使用工厂模式创建Session对象呢?直接用new不行吗? 比如我们刚开始new了1...
  • //创建工厂 //创建工厂时,mybatis使用了构建者模式,即找包工队盖房子,而不是自己亲自处理一系列问题 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder...
  • 像我们自己盖厂房一样,不是事事亲为,我们找个包工队,告诉需求,给钱即可: SqlSessionFactoryBuilder类似于包工队; in类似于我们给的钱; 3)创建SqlSession对象 使用了工厂模式,降低了类之间的依赖关系 4)...
  • //创建工厂 //创建工厂时,mybatis使用了构建者模式,即找包工队盖房子,而不是自己亲自处理一系列问题 SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder(); SqlSessionFactory factory = builder...
  • 2020=1024+996,程序员本命年,职场很艰辛

    万次阅读 多人点赞 2020-01-03 00:20:28
    一个打杂的外包工,一般都是10k-20k 深圳招.Net的外包数来数去就那几家的(中软国际、软通动力、文思海辉、博彦科技、武汉佰钧成、塔塔集团、印孚瑟斯…),以前经常打电话让我去面试,后来再没有打过了,当时...
  • 我在国企外包一年的经历和感受

    千次阅读 2021-07-24 00:14:24
    本文来自一位读者朋友的投稿~坐标北京,在国有 4 大行之一的某银行做外包。不知不觉,本人已经入职外包公司一年多。一年的经历让我对整个外包领域算是已经有了一点了解。今天这篇文章,我就简单谈一...
  • 网络抓包工具

    2019-08-08 20:44:59
    来源:blog.csdn.net/xjpdf10/article/details/84656739 前段时间,《亲爱的,热爱的》刚播完,其中剧情中涉及网络攻防大赛,你是不是也喜欢看,羡慕枪神他们从事的职业,飞快的敲击键盘,屏幕黑白代码快速闪过,...
  • Vue脚手架搭建以及创建Vue项目流程

    千次阅读 2022-04-25 22:20:41
    但是有两个概念需要区分开: 我们学习的 Vue 版本的 2.X,脚手架的版本是 4.5.12 Vue 可以理解为中式建筑风格,经过更新升级,现在是 2.X 版本 Vue 脚手架可以理解为盖房包工队,也在不断改造,现在是 4.5.12 Vue...
  • 晾衣:用普通晾衣就行; 洗衣机:用一线品牌,如海尔。拼多多上10公斤的滚筒洗衣机1380 橱柜+吊柜:橱柜用大理石的,3延米,吊柜用2延米的,包工包料大约三千; 油烟机+灶台:用一线品牌,老板或方太。大约3000...
  • 包工:将做好的饭菜打包;出纳员:收款并提交食品。每个职员可被看作一个进程。 问:试用信号量实现四类工作人员正确并发运行的机制。 作业1: 桌上有个能盛得下六块饼干的空盘子。爸爸不停地向盘中放梳打饼干或威化...
  • IaaS是包硬不包软,面对集成商,PaaS是包硬包软不包工,面对开发者,SaaS是全包,面对消费者。   三大阵营都在不断演进中,互相取长补短,甚至模糊了彼此的界限。 PaaS最新的发展就是: 1、BaaS(后端即...
  • 第二点就是介绍介绍一个专业的最常见的抓包工具 - Fiddler (感兴趣的话可以参考我之前写的这篇 《一文学会 - Fiddler抓包快速实战》) 抓包工具介绍 抓包工具有很多,比如程序员常用的 Fiddler、Charles ,安全领域...
  • 建筑工人是一个特殊的群体。每年春节过后,许多人都不情愿地离开家乡,前往...木工在工地上一般是包工,按照面积或者模板展开面来算,价格一般在35至60元每平方左右。木工一天大概能挣600元以上(早6点到晚7点左右)。
  • 总价:各地区不同,有些地区价格做烂了,用包工包料三十多的也有,有些地方价格上到六七十一个平方,低于六十一个平方的都没人做; 工价:一般十元左右一个平方,一百起算;如果是全屋墙布,建议门套窗套踢脚线柜子...
  • 六、App爬取相关库安装 本书介绍的抓包工具有mitmproxy 和mitmdump。一些简单的接口可以通过mitmproxy 分析,找出规律,然后直接用程序模拟来抓取了。但是如果遇到更复杂的接口,就需要利用mitmdump对接Python来对...
  • 牵头同学和参与同学之间的协作方式,由“中心化分配”与“被动完成”(包工队模式),转变为辅助与输出(PVP战队模式),即牵头同学提供更全面、更具体、更具有指导性的分析能力、工具,以及用于降低改造成本和上线...
  • 5)抓包工具 常见的抓包工具有Fiddler,Charles,Wireshark,这三种都比较常用。 抓包,也是为了更好的协助开发同学排查问题,出现了Bug,通过抓包,可以更清晰地排查是前端问题还是后端问题,比如未发送请求,或者...
  • (5)抓包工具 常见的抓包工具有Fiddler,Charles,Wireshark,这三种都比较常用。 抓包,也是为了更好的协助开发同学排查问题,出现了Bug,通过抓包,可以更清晰地排查是前端问题还是后端问题,比如未发送请求,...
  •  掌握一些基本的测试工具 在测试工具的掌握里,最重要的肯定学会网络抓包,这是测试工程师的必修课,抓包工具有很多,一般抓HTTP请求的最常用的是fiddler ,借口调试工具可以使用postman和jmeter,App的测试工具...
  • ( D ) A、-0.030 B、-0.040 C、-0.050 D、-0.060 52、【单选题】超过( )高的塔机,必须在起重机的最高部位(臂、塔帽或人字顶端)安装红色障碍指示灯,并保证供电不受停机影响。( B ) A、20m B、30m C、40...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 266
精华内容 106
关键字:

包工架