作者: HalfSugar

  • 射频识别与传感器技术

    头大,学的什么东西。By the way,图还得我自己来。

    PDF放前面,Markdown放这里。

    题型

    1. 选择题 10 分
    2. 名词解释 25 分
    3. 简答题 36 分
    4. 计算分析 15 分
    5. 论述 14 分

    测量方法

    根据获得测量值的方法

    1. 直接测量 实际结果 = 测量结果 y = x
    2. 间接测量 实际结果 y = 一个测得值 x 或多个测得值 x1,x2,…,xn 的函数。 y = f(x) y = f(x1,x2,…,xn)
    3. 组合测量 若实际结果必须经过求解联立方程组求得,称为组合测量。 如:$$
      \left\{\begin{matrix}x_{1}=f_{1}(y_{1},y_{2},…,y_{m}) \\x_{2}=f_{2}(y_{1},y_{2},…,y_{m}) \\\vdots \\x_{3}=f_{3}(y_{1},y_{2},…,y_{m}) \end{matrix}\right.
      $$

    根据测量方式

    1. 偏差式测量 仪表指针的位移决定被测量的量值。精度较低。
    2. 零位式测量 用指零仪表的零位反映测量系统的平衡状态,测量系统平衡时,用已知的标准量决定被测量的量值。
    3. 微差式测量 将被测量与已知标准量相比较,取得差值后,用偏差法测得此值。

    根据测量条件

    1. 等精度测量 影响和决定误差大小的全部因素始终保持不变。
    2. 不等精度测量 不同测量条件下,用不同精度的仪表,不同的测量方法,不同的测量次数,不同的测量者。

    根据被测量变化快慢

    1. 静态测量 被测量在测量过程中固定不变。不需要考虑时间因素对测量的影响。
    2. 动态测量 被测量在测量过程中不断变化。

    根据测量敏感元件是否与被测介质接触

    1. 接触式测量
    2. 非接触式测量

    根据测量系统是否向被测对象施加能量

    1. 主动式测量
    2. 被动式测量

    检测系统的构成

    传感器感受被测量的大小,并输出相对应的输出信号。

    信号调理电路对传感器输出的电信号进行处理。

    显示器将所得信号变成人的感官可以接受的信号。

    数据处理装置对测得的数据进行处理、运算、分析、对动态测试结果进行频谱分析。

    执行机构通断、控制、调节、保护。

    传感器的静态特性和动态特性

    静态特性

    1. 灵敏度$$
      S=\frac{\Delta y}{\Delta x}
      $$
      输出量增量与输入量增量之比。S 越大越灵敏。输入输出特性曲线上某点的斜率就是灵敏度
    2. 线性度 传感器的输出与输入之间数量关系的线性程度。
    3. 迟滞 相同工作条件下,输入量由小到大(正行程)及输入量由大到小(反行程)变化期间其输入输出特性曲线不重合的现象称为迟滞。差值称为迟滞差值。最大迟滞差值 ΔHmax 与满量程输出值 YFS 称为迟滞误差,用 γH 表示。$$
      \gamma_{H}=\frac{\Delta H_{max}}{Y_{FS}}\times 100\%
      $$
    4. 重复性 相同工作条件下,输入量按同一方向做全量程连续多次变化时,所得特性曲线不一致的程度。
    5. 漂移 输入量不变,输出量随时间变化。

    动态特性

    输入量随时间变化时传感器的响应特性。


    金属应变片的工作原理

    电阻应变效应:导体在外界作用下产生机械变形(拉伸 / 压缩)时,其电阻值相应发生变化。

    被测量使弹性元件产生应变,应变再由应变片产生相应的电阻变化,因而通过测量电阻,可以得到被测量的大小。

    半导体应变片工作原理

    半导体材料的电阻率 ρ 随作用应力的变化而变化。

    电阻应变片测量电路

    不平衡电桥

    $$
    易知,E_{D} = E,E_{A} = 0. \\ 初状态为平衡,故有 \\ E_{B} = E_{C} \\ 即有 \\ \frac{R_{2}}{R_{1}}=\frac{R_{4}}{R_{3}} \\ 当 R_{1} 变化,有 \\ E_{B}=E \cdot (\frac{R_{1}+\Delta R_{1}}{R_1+R_2+\Delta R_1})\\ E_C=E \cdot (\frac{R_3}{R_3+R_4})=E \cdot (\frac{R_1}{R_1+R_2})\\ 此时 U_0=E_B-E_C\\ 由糖水不等式,显然 E_B>E_C\\ \begin{split} U_0&=E\cdot \frac{(R_1+\Delta R_1)(R_1+R_2)-R_1(R_1+R_2+\Delta R_1)}{(R_1+R_2+\Delta R_1)(R_1+R_2)}\\ &=E\cdot\frac{\Delta R_1\cdot R_2}{(R_1+R_2)^2+\Delta R_1(R_1+R_2)} \end{split}\\ 相对于 (R_1+R_2)^2 来说,\Delta R_1(R_1+R_2) 可忽略不计\\ 分数上下同除以 R_1^2,令 n=\frac{R_2}{R_1}\\ 故\\ U_0=\frac{n}{(1+n)^2} \cdot \frac{\Delta R_1}{R_1}\cdot E
    $$

    自感式电感传感器工作原理

    利用线圈自感量的变化测量。

    传感器的运动部分与衔铁相连,被测量变化时,衔铁产生位移,引起磁路中电阻变化,使得电感线圈的电感量变化,测量出这种变化,就能确定衔铁位移量大小和方向。

    差动变压器式传感器工作原理

    变压器的次级线圈产生感应电压,两个相同的次级线圈接成差动的形式,即反向串联,即成为差动变压器。铁芯做成活动的,则变成差动变压器式传感器。铁芯运动时,在次级绕组中产生感应电势,测量这种电势即可得到被测量。

    衔铁向上运动,则 U2 > 0。

    电涡流式传感器工作原理

    块状金属导体置于变化的磁场中或在磁场中做切割磁感线运动时,导体内将产生旋涡状的感应电流,称为电涡流。

    当传感器激励线圈通过交变电流 I1 ,线圈周围产生交变磁场 H1 ,使得置于此磁场中的被测导体产生电涡流 I2 ,产生反向的新的交变磁场 H2。使得传感器激励线圈的电感量、阻抗、品质因数变化,即线圈等效阻抗发生变化。

    线圈等效阻抗 Z的变化与被测金属导体电阻率 ρ磁导率 μ几何形状/尺寸因子 r激磁电流频率 f线圈与被测金属导体的距离 x有关,若使得其中一个为变量,其它因素不变,则可通过测量 Z 来测量这个变化量。

    电容式传感器的工作原理

    将被测非电量的变化转换成电容量变化。

    平行板电容器的电容如下

    $$
    C=\frac{\varepsilon A}{d}\\ \\ \varepsilon-平行板间介质介电常数\\ A-极板所覆盖的面积\\ d-极板间距离
    $$

    当被测参数变化使得式中某一参数变化,就可以把参数变化转换为电容变化,通过测量电路就可转换为电量输出。

    分类

    1. 变极距型
    2. 变面积型
    3. 变介质型

    压电效应、逆压电效应

    某些电介质当沿着一定方向对其施力而使其变形时,内部产生极化现象,同时在它两个表面上产生符号相反的电荷,外力去掉后,又重新恢复到不带电的状态,称为 压电效应

    当在电介质极化方向施加电场时,电介质也会产生几何变形,称为 逆压电效应

    霍尔传感器原理与应用

    霍尔效应

    置于磁场中的静止载流导体,当它的电流方向与磁场方向不一致时,载流导体上垂直于电流和磁场的方向上将产生电势差,该电势差称为霍尔电势差。

    应用

    1. 霍尔式位移传感器
    2. 霍尔式转速传感器
    3. 霍尔计数装置

    光电式传感器

    外光电效应

    在光线作用下,物体内电子逸出物体表面向外发射的现象。

    这种电子称为光电子。

    内光电效应

    在光线作用下,物体的导电性能发生变化或产生光生电动势的效应。

    两类

    1. 光电导效应 在光线作用下,半导体材料吸收入射光子能量,若能量大于等于半导体材料的禁带宽度,就激发出 电子 – 空穴对,使载流子浓度增加,半导体导电性增加,阻值减低。是光敏电阻的原理。
    2. 光生伏特效应 光线的作用下能够使物体产生一定方向的电动势的现象。是光电池的原理。

    光敏电阻

    无光照时,光敏电阻阻值很大,电路中电流很小。被一定波长的光范围的光照后,其阻值急剧减小,电路中电流迅速增大。

    光敏二极管

    没有光照射时,反向电阻很大,反向电流很小。被光照射时,形成光电流,光照强度越大,光电流越大。

    因此不受光照射时,光敏二极管处于截止状态,而受到光照射时,光敏二极管处于导通状态。

    光敏三极管

    集电极电流是光电流的 β 倍,因此光敏晶体管具有放大作用。

    光电池

    原理是光生伏特效应,其本质是一个大面积的 PN 结,当光照射到 PN 结的一个面,光子能量大于半导体材料的禁带宽度,电池每吸收一个光子就产生一对自由电子和空穴,电子 – 空穴对从表面向内扩散,建立一个和光照强度有关的电动势。

    光电耦合器件

    是由发光元件(如发光二极管)和光电接收元件(如光敏二极管、光敏三极管等)合并使用,以光作为媒介把输入端的电信号耦合到输出端的一种器件。

    1. 用于实现电隔离的光电耦合器 有时可以取代继电器、变压器、斩波器。
    2. 用于检测物体位置或有无物体的光电开关

    体积小、寿命长、无触点、抗干扰能力强、输出输入绝缘、可以单向传输 模拟信号或数字信号。

    光电开关的工作原理

    光电开关是一种利用感光元件对变化的入射光加以吸收,并进行光电转换,同时加以某种形式的放大和控制,从而获得最终的控制输出“开”“关”信号的器件。

    气敏传感器的工作原理

    原理:利用气体在半导体表面的氧化和还原反应导致敏感元件阻值变化。当氧化型气体吸附到 N 型半导体上,还原型气体吸附到 P 型半导体上时,半导体载流子减少,使电阻值增大。将上述条件反过来则使其电阻值减小。

    湿敏传感器的工作原理

    湿敏传感器是能够感受外界湿度变化,并通过器件材料的物理或化学性质变化,将湿度转化成有用信号的器件。

    超声波传感器的工作原理与应用

    利用超声波在超声场中的物理特性和各种效应而研制的装置称为超声波传感器、换能器或探测器。

    超声波探头:是超声波的发射器和接收器。

    压电式:利用压电材料的逆压电效应把高频电振动转换成高频机械振动,产生超声波。利用压电效应把超声振动转换为电信号,接收超声波。

    应用:

    1. 超声波物位传感器:超声波物位传感器是利用超声波在两种介质的分界面上的反射特性制成的。如果从发射超声脉冲开始,到接收探头接收到反射波为止的这个时间间隔为已知,就可以求出分界面的位置,利用这种方法可以对物位进行测量。
    2. 超声波流量传感器:超声波流量传感器的测定方法是多样的,但目前应用较广的主要是超声波传播速度变化法。超声波在流体中传播时,在静止流体和流动流体中的传播速度是不同的,利用这一特点可以求出流体的速度,再根据横截面积,便可以知道流体的流量。

    红外感应系统的工作原理

    红外辐射的本质是热辐射,温度低的物体辐射的红外线波长长,温度高的物体辐射的红外线波长短。在一般常温下,所有物体都是红外辐射的发射源,但发射的红外波长不同。红外感应实际就是根据物体因表面温度不同会发出不同波段的红外光这一特性进行检测的。

    编码器的原理与应用

    原理

    编码器是将角位移和线位移转换成数字量的一种数字传感器,它以高精度、高分辨率和高可靠性被广泛用于各种位移的测量。

    应用

    1. 角度位置测量
    2. 数字测速

    热电传感器

    热电偶测温原理

    两种不同材料的导体(或半导体)组成一个闭合回路,当两接点温度 T 和 T0 不同时,则在该回路中就会产生电动势,这种现象称为热电效应,该电动势称为热电势。这两种不同材料的导体或半导体的组合称为热电偶,导体 A、B 称为热电极。

    三个基本定律

    中间温度定律 在热电偶测温回路中,tc为热电极上某一点的温度,热电偶 AB 在接点温度为 t、t0 时的热电势 EAB( t, t0) 等于热电偶 AB 在接点温度 t、tc 和 tc、t0 时的热电势 EAB( t, tc ) 和 EAB( tc, t0) 的代数和,即$$
    E_{AB}(t,t_0)=E_{AB}(t,t_C)+E_{AB}(t_C,t_0)
    $$

    均质导体定律 由两种均质导体组成的热电偶,其热电动势的大小只与两材料及两接点温度有关,与热电偶的大小尺寸、形状及沿电极各处的温度分布无关。这条定理说明:热电偶必须由两种不同性质的均质材料构成。

    中间导体定律 在热电偶测温回路内,接入第三种导体时,只要第三种导体的两端温度相同,则对回路的总热电势没有影响。

  • 八段数码管显示解析

    “8段数码管显示”工程解析

    现象:数码管显示“12345678”。

    源代码如下:

    int main()
    {
        Stm32_Clock_Init( 6 );  //传入一个8位二进制数,是倍频系数,使得PLL倍频。也对时钟进行了使能。
        delay_init( 72 );       //延时初始化,就是对系统滴答定时器的初始化。设置了延时倍乘数。
        LED_Init();             //LED使能和初始化。
        LED_SEL = 0;
        show_w1=1;
        show_w2=2;
        show_w3=3;
        show_w4=4;
        show_w5=5;
        show_w6=6;
        show_w7=7;
        show_w8=8;
        while(1)
        {
    
            SetLed(0, show_w1%10);
            delay_ms(1);
            SetLed(1, show_w2%10);
            delay_ms(1);
            SetLed(2, show_w3%10);
            delay_ms(1);
            SetLed(3, show_w4%10);
            delay_ms(1);
            SetLed(4, show_w5%10);
            delay_ms(1);
            SetLed(5, show_w6%10);
            delay_ms(1);
            SetLed(6, show_w7%10);
            delay_ms(1);
            SetLed(7, show_w8%10);
            delay_ms(1);
        }
    }

    此为库函数版。

    库函数解析:

    Stm32_Clock_Init();

    //定义
    //时钟初始化
    void Stm32_Clock_Init(u8 PLL)//传入一个8位的二进制数
    {
        unsigned char temp=0;   
        MYRCC_DeInit();       //复位并配置向量表
        RCC->CR|=0x00010000;  //外部高速时钟使能HSEON
        /*
        HSEON:外部高速时钟使能 (External high-speed clock enable) 
        由软件置’1’或清零。
        */
        while(!(RCC->CR>>17));//等待外部时钟就绪
        RCC->CFGR=0X00000400; //APB1=DIV2;APB2=DIV1;AHB=DIV1;
        PLL-=2;//抵消2个单位
        RCC->CFGR|=PLL<<18;   //设置PLL值 2~16
        RCC->CFGR|=1<<16;     //PLLSRC ON 
        FLASH->ACR|=0x32;     //FLASH 2个延时周期
        //repare
    
        RCC->CR|=0x01000000;  //PLLON
        while(!(RCC->CR>>25));//等待PLL锁定
        RCC->CFGR|=0x00000002;//PLL作为系统时钟    
        while(temp!=0x02)     //等待PLL作为系统时钟设置成功
        {   
            temp=RCC->CFGR>>2;
            temp&=0x03;
        }    
    }            

    总结:

    传入一个8位二进制数,是倍频系数(范围2~16),使得PLL倍频。也对时钟进行了使能和初始化。配置了向量表,复位和配置了相关外设。

    传参类型:u8

    u8是unsigned char类型,8位二进制。

    时钟树

    HSE(High Speed External Clock signal):高速外部时钟信号

    HSI(High Speed Internal Clock signal):高速内部时钟信号

    LSE(Low Speed External Clock signal):低速外部时钟信号

    LSI(Low Speed Internal Clock signal):低速内部时钟信号

    USB预分频器:48MHz,连接USB。

    APB1(低速)外设:UART2,TIMER2等。

    APB2(高速)外设:UART1,SPI1等。

    PLL锁相环:
    作用:

    ​ 抬高频率。

    输入:

    ​ HSE和HSI,通过PLLSRC选择(通过寄存器PLLCFGR的第22位决定:0:HSI;1:HSE)。

    输出:

    ​ PLLCLK(系统时钟)和PLL48CLK。PLLCLK在多路选择开关处,根据需要传给SYSCLK。

    MYRCC_DeInit(void)

    //把所有时钟寄存器复位
    void MYRCC_DeInit(void)
    {                                                               
        RCC->APB1RSTR = 0x00000000;//复位结束             
        RCC->APB2RSTR = 0x00000000; 
    
        RCC->AHBENR = 0x00000014;  //睡眠模式闪存和SRAM时钟使能.其他关闭.      
        RCC->APB2ENR = 0x00000000; //外设时钟关闭.               
        RCC->APB1ENR = 0x00000000;   
        RCC->CR |= 0x00000001;     //使能内部高速时钟HSION                                                                 
        RCC->CFGR &= 0xF8FF0000;   //复位SW[1:0],HPRE[3:0],PPRE1[2:0],PPRE2[2:0],ADCPRE[1:0],MCO[2:0]                     
        RCC->CR &= 0xFEF6FFFF;     //复位HSEON,CSSON,PLLON
        RCC->CR &= 0xFFFBFFFF;     //复位HSEBYP          
        RCC->CFGR &= 0xFF80FFFF;   //复位PLLSRC, PLLXTPRE, PLLMUL[3:0] and USBPRE 
        RCC->CIR = 0x00000000;     //关闭所有中断
        //配置向量表    
    
        #ifdef  VECT_TAB_RAM
        MY_NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
        #else   
        MY_NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
        #endif
        //配置中断向量表基址和偏移量
    }

    实际上是设置了向量表的复位,外设的复位,关闭所有中断。。

    RCC结构体
    /*------------- Reset and Clock Control --------------*/
    typedef struct
    {
      vu32 CR;        //时钟控制寄存器
      vu32 CFGR;    //时钟配置寄存器
      vu32 CIR;        //时钟中断寄存器
      vu32 APB2RSTR;//APB2 外设复位寄存器
      vu32 APB1RSTR;//APB1 外设复位寄存器
      vu32 AHBENR;    //AHB外设时钟使能寄存器
      vu32 APB2ENR;    //APB2 外设时钟使能寄存器
      vu32 APB1ENR;    //APB1 外设时钟使能寄存器
      vu32 BDCR;    //备份域控制寄存器
      vu32 CSR;        //控制/状态寄存器
    } RCC_TypeDef;    

    向量表的定义。

    vu32(无符号长整型)
    typedef volatile unsigned long  vu32;

    注意到RCC结构体中寄存器的格式是 vu32 。vu32 即 volatile unsigned long

    volatile即“易变的”。

    C++会对代码进行优化,内部不影响IO的代码并不会被编译。

    int main() {
        int i = 0;
        i++;
        cout << "hello world" << endl;
    }

    按照代码,这个程序会在内存中预留int大小的空间,初始化这段内存为0,然后这段内存中的数据加1,最后输出“hello world”到标准输出中。

    但是根据这段代码编译出来的程序(加-O2选项),不会预留int大小的内存空间,更不会对内存中的数字加1。他只会输出“hello world”到标准输出中。

    编译器为了优化代码,修改了程序的逻辑。实际上C++标准是允许写出来的代码和实际生成的程序不一致的。

    虽说优化代码是件好事情,但是也不能让编译器任意修改程序逻辑,不然的话我们没办法写可靠的程序了。所以C++对这种逻辑的改写是有限制的,这个限制就是在编译器修改逻辑后,程序对外界的IO依旧是不变的。

    怎么理解呢?实际上我们可以把我们写出来的程序看做是一个黑匣子,如果按照相同的顺序输入相同的输入,他就每次都会以同样的顺序给出同样的输出。这里的输入输出包括了标准输入输出、文件系统、网络IO、甚至一些system call等等,所有程序外部的事物都包含在内。

    所以对于程序使用者来说,只要两个黑匣子的输入输出是完全一致的,那么这两个黑匣子是一致的,所以编译器可以在这个限制下任意改写程序的逻辑。这个规则又叫as-if原则。

    volatile关键字的作用

    volatile让该变量的地位相当于输入输出,不能改变顺序,不能忽略。所以类似取消优化。

    RCC->CR|=0x00010000;

    第16位:外部高速时钟使能HSEON

    HSEON:外部高速时钟使能 (External high-speed clock enable)

    由软件置’1’或清零。

    while(!(RCC->CR>>17));

    RCC_CR中第17位是HSERDY,即外部时钟就绪位,当外部时钟就绪时,HSERDY位就等于1了,RCC-CR>>17语句将第17位移到第0位,于是RCC-CR就等于0x00000001了,(!(RCC-CR>>17))值即为0了,while(!(RCC-CR>>17))就执行完了。

    RCC->CFGR=0X00000400;

    CFGR的第10位设为1。

    PPRE1[2:0]:低速APB预分频(APB1) (APB low-speed prescaler (APB1))

    由软件置’1’或清’0’来控制低速APB1时钟(PCLK1)的预分频系数。

    警告:软件必须保证APB1时钟频率不超过36MHz。

    0xx:HCLK不分频

    100:HCLK 2分频

    101:HCLK 4分频

    110:HCLK 8分频

    111:HCLK 16分频

    此处是置为100,即HCLK2分频。

    PLL-=2; RCC->CFGR|=PLL<<18;

    此两步是设置了倍频系数,倍频系数设置如下图

    RCC->CFGR|=1<<16;

    设置了CFGR第16位。

    PLLSRC:PLL输入时钟源 (PLL entry clock source)

    由软件置’1’或清’0’来选择PLL输入时钟源。

    只能在关闭PLL时才能写入此位。

    0:HSI振荡器时钟经2分频后作为PLL输入时钟

    1:HSE时钟作为PLL输入时钟。

    RCC->CR|=0x01000000;

    第24位置1,作用是PLL使能。

    PLLON:PLL使能 (PLL enable)

    由软件置’1’或清零。

    当进入待机和停止模式时,该位由硬件清零。

    当PLL时钟被用作或被选择将要作为系统时钟 时,该位不能被清零。

    0:PLL关闭;

    1:PLL使能。

    while(!(RCC->CR>>25));

    等待PLL时钟锁定。

    PLLRDY:PLL时钟就绪标志 (PLL clock ready flag)

    PLL锁定后由硬件置’1’。

    0:PLL未锁定;

    1:PLL锁定。

    RCC->CFGR|=0x00000002;

    PLL输出作为系统时钟;

    SW[1:0]:系统时钟切换 (System clock switch)

    由软件置’1’或清’0’来选择系统时钟源。

    在从停止或待机模式中返回时或直接或间接作为系统时钟的HSE出现故障时,由硬件强制选择 HSI作为系统时钟(如果时钟安全系统已经启动)

    00:HSI作为系统时钟;

    01:HSE作为系统时钟;

    10:PLL输出作为系统时钟;

    11:不可用

    delay_init();

    延时初始化,就是对系统滴答定时器的初始化。设置了延时倍乘数。

    传参:8位二进制数。

    推挽输出

    推挽输出既可以输出低电平,也可以输出高电平,可以直接驱动功耗不大的数字器件。可以理解成,输出0,1。

    CPU 写 1,外部引脚输出高电平。

    CPU 写 0,外部引脚输出低电平。

    开漏输出

    CPU 写 0,外部引脚接地。

    CPU 写 1,外部引脚悬空。

    LED_Init()

    LED初始化。

    LED_SEL = 0;

    总结:

    操作 用 GPIOB_ODR_Addr 和 n 初始化的地址 的内存空间。n = 3 。赋值为0

    BITBAND(addr, bitnum)

    #define BITBAND(addr, bitnum) ((addr & 0xF000 0000)+0x200 0000+((addr &0xFFFFF)<<5)+(bitnum<<2)) 
    1. 将 addr 高四位(28~31)设为1。第25位+1。
    2. 只保留addr的低20位(0-19),左移5位(5-24)。加上bitnum左移2位的数值。

    两步相加。为BITBAND(addr, bitnum)。是一个初始化的地址。

    MEM_ADDR(addr)

    #define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 

    MEM_ADDR(addr)表示addr地址处的值。

    BIT_ADDR(addr, bitnum)

    #define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum))

    BIT_ADDR(addr, bitnum) 表示用addr和bitnum初始化的地址的值。

    PBout(n)

    #define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n)

    表示用 GPIOB_ODR_Addr 和 n 初始化的地址的值。或者说取地址处的内存空间进行操作。

    GPIOB_ODR_Addr是 (GPIOB_BASE+12) 即 0x40010C0C 。n仍然是一个“偏移量”,实际上是寄存器内的“地址”。

    什么是偏移量?

    ​ 例如GPIOA_BASE = (APB2PERIPH_BASE + 0x0800) 。而 GPIOA 又包含几个寄存器。

    ​ 所以每个寄存器都有一定偏移量。相当于寄存器在这个 GPIO 中的位置。

    LED_SEL

    #define LED_SEL PBout(3) 

    即 n = 3 ;

    show_w

    定义:无符号字符类型,8位二进制。

    void SetLed(u8 w, u8 value)

    void SetLed(u8 w, u8 value)
    {
        SEL0 = w%2;             //取w第0位
        SEL1 = w/2%2;           //取w第1位
        SEL2 = w/4;             //取w第2位
        LedValue(segTable[value]);
    }
    #define SEL0 PBout(0)
    #define SEL1 PBout(1)
    #define SEL2 PBout(2)
    /***************************数码管段选***************************/
    u8 segTable[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
    void LedValue(u8 value)
    {
        GPIOE->ODR &= ~(0xff<<8);       //清除数据,只保留低8位
        GPIOE->ODR |= value<<8;         //设置8~15位
    }

    SetLed 函数传入两个参数,第一个是位选,第二个是段选

    void delay_ms(u16 nms)

    void delay_ms(u16 nms)
    {                  
        u32 temp;          
        SysTick->LOAD=(u32)nms*fac_ms;//时间加载(SysTick->LOAD为24bit)
        SysTick->VAL =0x00;           //清空计数器
        SysTick->CTRL=0x01 ;          //开始倒数  
        do
        {
            temp=SysTick->CTRL;
        }
        while(temp&0x01&&!(temp&(1<<16)));//等待时间到达   
        SysTick->CTRL=0x00;       //关闭计数器
        SysTick->VAL =0X00;       //清空计数器           
    }

    延时函数,以ms为单位。

  • 软件工程复习

    燃尽了,写到半夜两点。

  • 计算机网络 复习

    这是个重量级的复习资料,因为疫情提前放假,我足足复习了一个月,只能说薄纱期末了。

  • Android 课设(记录不完整)

    由于开发任务重,开发部分记录缺失。


    一、目的与要求

    1. 掌握基于 Android 的网络编程的相关知识。
    2. 学会如何定义网络协议。
    3. 学会如何传输表情、图片等特殊信息。

    二、实验内容

    1. 建立简单的 SOCKET 通信服务端和客户端。服务端 ServerAddress=InetAddress.getByName(“192.168.1.142”);
       socket=new Socket(ServerAddress,500);
       commsThread=new CommsThread(socket);
       ​
       public class CommsThread extends Thread {
           private final Socket socket;
           private final InputStream inputStream;
           private final OutputStream outputStream;
       ​
           public CommsThread(Socket sock) {
               socket = sock;
               InputStream tmpIn = null;
               OutputStream tmpOut = null;
               try {
                   // —创建用于通过套接字读写的输入流和输出流对象—
                   tmpIn = socket.getInputStream();
                   tmpOut = socket.getOutputStream();
              } catch (IOException e) {
       ​
                   Log.d(“SocketChat”, e.getLocalizedMessage());
              }
               inputStream = tmpIn;
               outputStream = tmpOut;
          }
       ​
           public void run() {
               // —流的缓冲区存储—
               byte[] buffer = new byte[1024];
               // —bytes returned from read()—
               int bytes;
               // —一直监听 InputStream 直到异常发生—
               while (true) {
                   try {
                       // —从inputStream读取—
                       bytes = inputStream.read(buffer);
       ​
                       // —更新主活动UI—
                       SocketsActivity.UIupdater.obtainMessage(0, bytes, -1, buffer).sendToTarget();
                  } catch (IOException e) {
                       break;
                  }
              }
          }
       ​
           // —从主活动调用此函数将数据发送到远程设备—
           public void write(byte[] bytes) {
               try {
                   outputStream.write(bytes);
              } catch (
               IOException e) {
              }
          }
       ​
           // —从主活动调用此函数以关闭连接—
           public void cancel() {
               try {
                   socket.close();
              }
               catch (IOException e) {
              }
          }
       }注意在 androidManifest 中添加用户权限: <uses-permission android:name=*”android.permission.INTERNET”* />客户端 ServerAddress = InetAddress.getByName(“192.168.1.142”); socket = new Socket(ServerAddress, Serverport);
       Socet.connect(SocketAddress remoteAddress);
       InputeStream tmpIn = socket.getInputStream();
       OutputStream tmpOut = socket.getOutputStream();
       ​
       while(bRunning) {
           tmpIn.read(byte[] buf,int offset,int length);
           tmpOut.write(byte[] buffer,offset,int length);
       }

    登录完成后,进行前面类似的 Presence、Message、IQ 报文进行信息交互;

    三、思路

    建立一个服务器,服务器暂定用 Node.js 来写。

    用到的工具:

    1. Node.js
    2. Android
    3. Json
    4. xml 存疑

    看得出来建议使用的是 xml 配合 xmpp 协议。但我希望使用的是Json,因为服务端好解析。可以研究 XMPP 协议 的原理,再移植到 Json 上。

    四、大致过程

    测试

    编好布局

    写好主界面的功能

    编写安卓 TCP 服务

    在服务器上建立相对应的 TCP 服务端

  • 汇编复习

    考试范围

    1、题型:简答(6 个,24 分);计算写出 OF SF ZF CF 值;指令执行题;内存绘制题;编程题;论述题

    2、第一、二、三章(汇编语言的组合;计算机中数和符号的表示;补码的加减运算;逻辑运算规则;汇编程序的优势;寄存器的作用和组成;8086 结构特征;段;有效地址、物理地址;外设和接口;32 位 CPU 工作模式;汇编编程所需文件;上机步骤)

    第四章(寻址方式)

    第五章(指令系统)

    第六章(伪指令,变量属性,赋值伪指令,过程定义伪指令,数值回送伪指令)

    第七章(分支,循环)

    第一章 基础

    1.1 简介

    1.1.1 机器语言与汇编语言

    三类计算机程序设计语言:

    1. 机器语言
    2. 汇编语言
    3. 高级语言

    汇编指令也称为符号指令。汇编语言也称为符号语言。符号也称为助记符。

    汇编程序 是把汇编语言源程序翻译成机器码的程序。这个过程称为 汇编

    1.1.2 汇编语言的组成

    1. 汇编指令:机器码助记符,是汇编语言的核心
    2. 伪指令:没有对应机器码,计算机不执行
    3. 其他符号:如 +、-、*、/ 编译器识别,没有对应机器码

    1.1.3 学习意义

    1. 对于计算机应用开发有重要作用
    2. 是从根本上认识和理解计算机工作过程的最好办法

    1.2 数据表示

    1.2.1 进制转换

    1. B 二进制
    2. D 十进制
    3. H 十六进制

    101101(B)=45(D)=101101B=45D=1011012=4510

    1.2.2 二进制和十六进制的运算

    1.2.3 带符号数的补码

    补码符号位数据位
    正数0
    000
    负数1其正数数据位反码再把最低位加一

    性质:补码的补码是原码。

    1.2.4 补码加减法

    [ X + Y ] = [ X ] +[ Y ]

    [ X – Y ] = [ X ] +[ – Y ]

    不必考虑数字的正负,符号位直接参与运算。最高位的进位丢弃。

    1.2.7 逻辑运算

    1. 与 AND
    2. 或 OR
    3. 非 NOT
    4. 异或 XOR异或就是异,不一样则 true。

    小结

    1. 汇编语言的优势?因为用汇编语言设计的程序最终被转换成机器指令,故能够保持机器语言的一致性,直接、简捷,并能像机器指令一样访问、控制计算机的各种硬件设备,如磁盘、存储器、CPU、I/O 端口等。使用汇编语言,可以访问所有能够被访问的软、硬件资源。目标代码简短,占用内存少,执行速度快,是高效的程序设计语言,经常与高级语言配合使用,以改善程序的执行速度和效率,弥补高级语言在硬件控制方面的不足,应用十分广泛。

    第二章 计算机基本原理

    2.1 计算机系统组成

    基本工作原理

    1. 存储程序
    2. 程序控制

    这种原理称为冯诺依曼原理,这样的计算机称为冯诺依曼计算机,这种计算机的体系结构称为冯诺依曼结构。

    典型冯诺依曼结构如图所示。

    主要部分:

    1. 中央处理器
    2. 存储器
    3. 输入/输出子系统

    三部分由系统总线连在一起。

    2.2 存储器

    2.2.2 存储器

    1. 基本存储单元
      • 最小单位:一个二进制位 bit
      • 一个字节:八位二进制 Byte
      • 一字 (Word):2 字节 Word
      • 双字:2 字 32 位
      80×86 微机内存储器以字节为基本单位。即读写至少是一个字节。(隐藏知识点,地址按照字节编码)
    2. 字的存储一字 16 位,高八位为高位字节,低八位为低位字节。规定高位字节在高地址单元,低位字节在低地址单元。

    2.2.3 存储器分段

    1. 分段概念8086 系统有 20 根地址线,可以寻址 1MB。但 8086 系统是 16 位机,16 位结构解决 20 位地址,采用分段法。段的划分并不是在内存,是来自 CPU。计算物理地址 段地址 * 16 + 偏移地址 = 物理地址。 (注意,* 16 是左移 4 位,不是左移 16 位!)每个段 64KB,因为偏移地址 16 位。意义:使得程序设计时,程序保持相对的完整性。
    2. 段的类型存储器逻辑分段类型:
      • 代码段 存放指令,段基址存放在段寄存器 CS(Code Segment)
      • 数据段 存放数据,段基址存放在段寄存器 DS(Data Segment)
      • 附加段 辅助存放数据,段基址存放在段寄存器 ES(Extra Segment)
      • 堆栈段 重要的数据结构,可用来保存数据、地址和系统参数,段基址存放在段寄存器 SS(Stack Segment)
      编写汇编时必须有代码段,而数据段、堆栈段、附加段可以根据需要选择。

    2.2.4 逻辑地址

    用户编程时使用的地址。

    段地址和偏移地址都是 16 位二进制数。

    有可能多个逻辑地址组织对应到同一个物理单元上。因此逻辑地址不是唯一的。

    CPU 要想读写数据,需要进行下面三类交互:

    1. 存储单元地址
    2. 器件选择,读/写命令
    3. 读/写数据

    地址、数据、控制信息传送是通过 总线 的。总线有三类:

    1. 地址总线
    2. 控制总线
    3. 数据总线

    2.3 寄存器

    2.3.1 寄存器介绍

    典型 CPU 由 运算器、控制器、寄存器 等器件构成。

    8086 CPU 有 14 个寄存器。

    按使用类别划分,寄存器分三类:

    1. 通用寄存器
    2. 段寄存器
    3. 专用寄存器

    寄存器组如下:

    1. 通用数据寄存器
      • AX Accumulator,累加器,运算时较多使用
      • BX Base,基址寄存器,除了存储数据,一般存放一段内存的起始偏移地址
      • CX Count,计数寄存器,除了存储数据,一般存放重复操作的次数
      • DX Data,数据寄存器,除了存储数据,有时存放 32 位数据的高 16 位
      这四个寄存器可以分成两个独立的 8 位寄存器来使用,AX 的高八位寄存器为 AH(Accumulator High),低八位为 AL(Accumulator Low)。其它寄存器同理。
    2. 地址寄存器
      • SP Stack Pointer,堆栈指针寄存器,存放堆栈栈顶偏移地址
      • BP Base Pointer,基址指针寄存器,存放内存中数据的偏移地址
      • SI Source Index,源变址寄存器,经常用来存放内存中源数据区的偏移地址。变址寄存器指的是,在某些指令作用下它可以自动递增或递减其中的值。
      • DI Destination Index,目的变址寄存器,经常用来存放内存中目的数据区的偏移地址,在某些指令作用下它可以自动递增或递减其中的值。
      这四个寄存器不能拆分使用。
    3. 段寄存器
      • CS Code Segment,代码段寄存器,存放当前正在执行的程序段的地址。
      • SS Stack Segment,堆栈段寄存器,存放堆栈段的段基址。
      • DS Data Segment,数据段寄存器,用来存放数据段的段基址。
      • ES Extra Segment,附加段寄存器,用来存放另一个数据段的段基址。 32 位 8086 处理器增加了两个段寄存器 FS、GS,功能类似 ES(EFG 可还行)。
      32 位的 8086 处理器仍然使用 16 位的段寄存器,但是存储内容变化了。
    4. 指令指针寄存器
      • IP Instruction Pointer,指令指针寄存器,存放即将执行指令的偏移地址。
    5. 标志寄存器
      • FLAGS 存放 CPU 的两类标志:
        • 状态标志反映处理器当前的状态(溢出,进位等)。六个状态标志:
          • CF Carry Flag 进位/借位标志,表示加法有进位或者减法有借位。
          • PF Parity Flag 奇偶标志,1 表示运算结果低八位有偶数个“1”。
          • AF Assistent carry Flag 辅助进位标志。
          • ZF Zero Flag 零标志,1 则运算结果为 0。
          • SF Sort Flag 符号标志,是运算结果最高位(符号位),如果未溢出,1 为负数,0 为非负数。如果溢出了,结果就因错误而无意义,但符号标志仍然可以标志正负,溢出后,1 为正,0 为负(与正常结果相反)。
          • OF Overflow Flag 溢出标志,1 则结果错误。无符号数也有这个标志但可以不处理。
        • 控制标志控制 CPU 的工作方式,如 是否响应可屏蔽中断。三个控制标志:
          • TF
          • IF Interrupt Flag 中断允许,1 表示允许处理器响应可屏蔽中断请求信号,称为开中断。反之则关中断。
          • DF Direction Flags 方向标志,0 时用增加的方式修改源或目的地址。见 SI/DI 寄存器
        标志名标志为 1标志为 0OF 溢出 是/否OVNVDF 方向 减/增DNUPIF 中断 允许/不允许EIDISF 符号 负/正NGPLZF 零 是/否ZRNZAF 辅助进位 有/无ACNAPF 奇偶 偶/奇PEPOCF 进位 有/无CYNC状态标志每次运算后自动产生,控制标志的值由指令设定。

    2.3.2 CS 和 IP

    CS 是 Code Segment 代码段寄存器,IP 是 Instruction Pointer 指令指针寄存器。

    提供了 CPU 要执行的指令的地址。因此如果内存中的一段信息被 CPU 执行过,就一定被 CS:IP 指向过。

    2.3.3 堆栈

    数据存入 堆栈 是以 的方式存入(16 位),后存入的数据,物理地址小/低。栈指针 SP 指向栈顶,压栈则 -2。

    常常用于保存调用的程序的返回地址和现场参数,也可以作为一种临时的数据存储区。

    2.4 外设和接口

    外设(外部设备)也称为 输入/输出设备,通过 输入/输出接口 与主机连接。

    接口一方面连接外设,一方面通过总线与主机相连。接口内有若干寄存器,用于在 CPU 与外部设备之间传递信息。系统对外设接口中的寄存器进行统一编号,称为端口号。CPU 可以通过端口地址来区分和访问不同的外设。

    端口/寄存器分为以下3类

    1. 数据端口 用来存放需要传递的数据。起数据缓冲作用。方向可以是输入/输出。
    2. 控制端口 传递 CPU 对外部设备的控制信号。例如启动磁盘工作。方向总是输出。
    3. 状态端口 协调外设与主机的同步。反映外设工作状态。例如某设备还未准备好接收数据,则不能向它发送数据。状态端口的传送方向对于 CPU 而言总是输入。

    CPU与I/O接口中的端口的信息传输是通过数据总线进行的。

    2.5 32 位 8086CPU 的工作模式

    1. 实模式 兼容 16 位机的特点,内存寻址范围 0 ~ 0FFFFFH 的 1MB 空间,地址线低 20 位有效。可以使用 32 位寄存器和 32 位操作数,MS DOS 只能在实模式下运行。
    2. 保护模式 是 32 位 8086CPU 的主要工作模式,特点是 全部地址线参与寻址 ,程序使用 逻辑地址,或者称为虚拟地址。虚拟地址的段选择符存放在寄存器中,应用程序不能更改,操作系统决定。 物理地址 是 16 位段选择符对应的 32 位段基址 和 32 位偏移地址组成。32 位即 4GB 大小。 采用虚拟地址使得各程序之间有严格的内存保护和隔离。
    3. 虚拟 8086 模式 就是生成多个虚拟的 8086CPU,以便运行实模式下的 8086 程序。 支持内存保护和隔离,可以同时运行多个程序。 Windows 下在 DOS 窗口运行一个 DOS 应用程序,则该程序运行在虚拟 8086 模式下。

    小结

    1. 8086 结构特征
      1. 数据总线为 16 位;
      2. 运算器一次最多可以处理 16 位的数据;
      3. 寄存器的最大宽度为 16 位;
      4. 寄存器和运算器之间的通路为 16 位

    第三章 汇编语言程序实例及上机操作

    3.1 汇编语言的工作环境

    3.1.1 汇编语言的系统工作文件

    代码到程序三步骤

    1. 编辑
    2. 汇编
    3. 连接

    编辑形成 .ASM 文件(代码文件),汇编成为 .OBJ 文件,连接形成 .EXE 文件。

    因此需要有四种程序:

    1. 编辑程序如记事本,EDIT.COM。
    2. 汇编程序如 MASM.EXE。
    3. 连接程序如 LINK.EXE
    4. 调试程序如 DEBUG.EXE

    3.2 实例

     code    segment         ;
     assume cs:code ; 说明语句,指定CODE段与CS寄存器关联。
     start: mov ah,1 ; START:是一个标号,MOV AH,1指令表示把1送AH寄存器,这是因为DOS系统功能的1号功能是键盘输入,所以要把功能号1送AH。
      int 21h ; INT 21H 指令即调用DOS系统功能,其1号功能被执行,程序等待键盘输入,从键盘输入一个字符后,程序才继续执行。注意,这个从键盘输入的字符到哪里去了呢?它被放到寄存器AL中,是该字符的ASCII码。如果是字符“A”,则AL中就为41H。
      mov dl,al ; 把AL送DL寄存器。
      add dl,1 ; DL内容加1。为后面的2号功能调用准备输出的字符。
      mov ah,2 ; 调用DOS系统功能的2号功能,显示DL中的字符。
      int 21h ; 执行
      mov ah,4ch ; 调用DOS系统功能的4CH号功能,4CH号功能是程序结束并返回到操作系统。
      int 21h ; 执行
     code ends ;
             end start ; 说明语句,告诉汇编程序,汇编到此结束,程序的启动地址为标号为START的那条指令(第3行)。

    解析:

    CODE 是段名,SEGMENTENDS 是关键字。

    第 1 行 CODE SEGMENT 和第 11 行 CODE ENDS 这 一对说明语句,定义了代码。

    第四章 操作数的寻址方式

    1. 立即寻址方式 MOV AL,6H 指令 目的 数字 源操作数是数字。直接把数字复制进入目的操作数。 只适用于源操作数,类型要和目的操作数相同(同样是字/字节)
    2. 寄存器寻址 MOV AX,BX 源操作数是寄存器。读出寄存器的值,写入目的操作数。
    3. 直接寻址方式 MOV AX,DS:[4050H] MOV AX,[4050] 源操作数是逻辑地址。读出地址的存储单元的值,写入目的操作数。
    4. 寄存器间接寻址方法 MOV AX,[BX] 操作数的地址 EA 在寄存器中。 读入 BX 的值,计算物理地址,再读物理地址的值,写入目的操作数。这里与直接寻址方式的区别在于读了两次,且第一次是寄存器,第二次也是寄存器。 注意,寄存器间接寻址中只允许
      1. BX(DS) Base 基址寄存器,Data Segment 数据段
      2. BP(SS) Base Pointer 基址指针寄存器,Stack Segment 堆栈段
      3. SI(DS) Source Index 源变址寄存器,Data Segment 数据段
      4. DI(DS) Destination Index 目的变址寄存器,Data Segment 数据段
      以上使用逻辑地址的计算方法即段地址 * 16 + 偏移地址 = 物理地址段地址就是括号内的地址,偏移地址就是括号外的寄存器的值。
    5. 寄存器相对寻址方法 MOV AX,TOP[SI] MOV [BX+2623H],AX 以上第一句,源操作数 有效地址 为 EA = SI + TOP(TOP 取其偏移地址若是寄存器或常量,多是 16 位,与 16 位寄存器匹配) 物理地址 为 DS * 10H + EA 然后读取这个物理地址的存储单元的值 这种方式常用于查表。适合访问一维数组。 注意:默认搭配 DS 段寄存器和 BX、SI、DI。SS 段寄存器和 BP
    6. 基址变址寻址方式 MOV AX,[BX+DI] 源操作数物理地址为 DS * 10H + BX +DI 取出该地址的存储单元的值。 允许使用的基址寄存器为
      1. BX(DS) Base 基址寄存器
      2. BP(SS) Base Pointer 基址指针寄存器
      变址寄存器为
      1. SI Source Index 源变址寄存器
      2. DI Destination Index,目的变址寄存器
    7. 相对基址变址寻址方式 MOV AX,[BX+DI+MASK] 物理地址为 DS * 10H + BX + DI + MASK 注意:默认 DS 为数据端 允许使用的基址寄存器为
      1. BX(DS) Base 基址寄存器
      2. BP(SS) Base Pointer 基址指针寄存器
      变址寄存器为
      1. SI Source Index 源变址寄存器
      2. DI Destination Index,目的变址寄存器
      可用于二维数组的处理。

    小结

    1. 选择寻址方式的两个原则
      1. 实用
      2. 有效
    2. 双操作数指令的提示
      1. 双操作数的两个操作数长度需要匹配。
      2. 两个操作数不能同时是内存单元。 因为一条指令需要使用地址线传输地址,如果都是地址,那么一条指令就需要传输两个地址,所以是不现实的。

    第五章 常用指令系统

    汇编语言的一般格式为:

    [ 标号 : ] 指令助记符 [ 操作数 ] [ ; 注释 ]

    例如:

     START: MOV AX,DATA ; DATA 送AX

    上述一般格式内的方括号为可选内容,由此可知,一条指令中只有 指令助记符 是必不可少的。

    意义说明:

    1. 标号 标号是一个符号地址,用来表示指令在内存中的位置。 令使用标号时,应加冒号 :
    2. 指令助记符 指令助记符表示指令名称,是指令功能的英文缩写。
    3. 操作数 操作数表示指令要操作的数据或数据所在的地址。 可以是 寄存器、常量、变量,也可以由表达式构成。 80×86 指令一般带有 0 个、1 个或 2 个操作数。 双操作数指令的 第一个 操作数称为 目的操作数 DST,存放操作结果。第二个 称为 源操作数 SRC。操作数之间用 , 分开。
    4. 注释 注释由分号 ; 开始。 注释超过一行则在每行都必须以分号开头。

    5.1 数据传送指令

    5.1.1 通用数据传送指令

    1. MOV(move) 传送 格式:MOV DST,SRC 操作:(DST)←(SRC),将源操作数传送到目的操作数。 注意:
      1. DSTSRC 长度必须明确且一致
      2. DSTSRC 不能同为存储器,不允许在两个存储单元之间直接传送数据。这是因为一条指令最多发送一个地址。
      3. DST 不能为 CSIP ,因为 CS:IP 指向的是当前要执行的指令。
      4. DST 不允许是立即数。
      更多信息见 P62
    2. PUSH(push onto the stack) 进栈 格式:PUSH SRC 操作:(SP)←(SP)-2 ((SP)+1,(SP))←(SRC) 表示将源操作数压入堆栈(目的操作数),目的操作数地址由SS:SP指定,指令中无需给出。 SP总是指向栈顶,堆栈操作以 字(16 位) 为单位进行操作。
    3. POP(pop from the stack) 出栈 格式:POP DST 操作:(DST)←((SP)+1,(SP))(SP)←(SP)+2 其中DST表示目的操作数。将堆栈中源操作数弹出到目的操作数,堆栈中源操作数地址由 SS:SP 指定,指令中无需给出。 源操作数弹出后,SP加 2,下移一个字,指向新的栈顶。
    4. XCHG(exchange) 交换 格式:XCHG OPR1,OPR2 操作:(OPR1)← →(OPR2) 其中OPR1OPR2为操作数。把 2 个操作数互换位置。 XCHG为双操作数指令,两个操作数均是目的操作数,除了遵循双 操作数指令的规定,也不能用立即数寻址。

    5.1.2 累加器专用传送指令

    1. IN(input)输入 把端口号 PORT 或由 DX 指向的端口的数据输入到累加器,根据端 口号的长度,有长格式和短格式两种形式。
      1. 长格式:IN AL,PORT(字节) IN AX,PORT(字) 操作:AL ←(PORT)AX ←(PORT) PORT 为端口号,端口号范围为 00~FFH 时,可以使用长格式指令 长格式指令,是指其机器指令长度为 2 个字节(端口号占 1 个字节)
      2. 短格式:IN AL,DX(字节) IN AX,DX(字) 操作:AL ←((DX))AX ←((DX)) 端口号范围为 0100H~0FFFFH 时,必须使用短格式指令。 短格式指令长度为 1 个字节,因为端口号存放在 DX 寄存 器中。
    2. OUT(output)输出 把累加器的数据输出到端口 PORT 或由 DX 指向的端口。与输入指 令相同,根据端口号的长度,分为长格式和短格式两种形式。
      1. 长格式:OUT PORT,AL(字节) OUT PORT,AX(字节) 操作:PORT ← AL PORT ← AX
      2. 短格式:OUT DX,AL(字节) OUT DX,AX(字节) 操作:(DX)← AL(DX)← AX
    3. XLAT(translate)换码 格式:XLAT 操作:AL ←(BX+AL) 把 BX+AL 的值作为有效地址,取出其中的一个字节送 AL。

    5.1.3 地址传送指令

    1. LEA(Load Effective Address)有效地址送寄存器 格式:LEA REG,SRC 操作:REG ← SRC 把源操作数的有效地址 EA 送到指定的寄存器。
    2. LDS(Load DS with Pointer)指针送寄存器和 DS 格式:LDS REG,SRC 操作:REG ←(SRC)DS ←(SRC+2) 把源操作数 SRC 所指向的内存单元中 2 个字送到指定的寄存器 REG 和 DS。
    3. LES(Load ES with Pointer)指针送寄存器和 ES 格式:LDS REG,SRC 操作:REG ←(SRC)ES ←(SRC+2) 把源操作数 SRC 所指向的内存单元中 2 个字送到指定的寄存器 REG 和 ES。

    5.1.4 标志寄存器传送指令

    1. LAHF(Load AH with Flags)标志送 AH 寄存器
    2. SAHF(Store AH into Flags)AH 送标志寄存器
    3. PUSHF(Push Flags)标志入栈
    4. POPF(Pop Flags)标志出栈

    以上 4 条指令的格式相同,只有操作码部分,操作数为固定默认值。

    5.2 算术运算指令

    5.2.1 类型扩展指令

    1. CBW 字节扩展成字:convert byte to word
    2. CWD 字扩展成双字:convert word to double word

    这两条指令的格式相同,只有操作码部分,无操作数部分。

    操作数默认为累加器,无需在指令中给出。

    当执行 CBW 时,默认将 AL 寄存器的内容扩展到 AX 寄存器中,扩展方法为符号扩展。

    即如果 AL 的最高位为 1(负数),则 CBW 指令扩展时使 AH=FFH,如果 AL 的最高位为 0(正数),则 CBW 指令扩展时使 AH=00H。

    当执行 CWD 时,默认将 AX 寄存器的内容扩展到(DX,AX)中,其中 DX 存放双字中的高位,AX 存放双字中的低位。

    如果 AX 的最高位为 1(负数),则 CWD 指令扩展时使 DX=FFFFH,如果 AX 的最高位为 0(正数),则 CWD 指令扩展时使 DX=0000H。

    5.2.2 加法指令

    1. ADD(add)加法 格式:ADD DST,SRC 操作:(DST)←(DST)+(SRC) ADD 指令将源操作数与目的操作数相加,结果存入目的操作数中。 特别需要注意,加法指令执行后会影响标志寄存器中的 CF 和 OF 标志位。
    2. ADC(add with carry)带进位加法 格式:ADD DST,SRC 操作:(DST)←(DST)+(SRC)+CF 其中上式中的 CF 为运算前 CF 标志位的值。
    3. INC(increment)加 1 格式:INC OPR 操作:(OPR)←(OPR)+1 该指令不影响 CF 标志位。

    5.2.3 减法指令

    1. SUB(subtract)减法 格式:SUB DST,SRC 操作:(DST)←(DST)-(SRC)
    2. SBB(subtract with borrow)带借位减法 格式:SBB DST,SRC 操作:(DST)←(DST)-(SRC)-CF
    3. DEC(decrement)减 1 格式:DEC OPR 操作:(OPR)←(OPR)-1 该指令不影响 CF 标志位。
    4. NEG(negate)求补 格式:NEG OPR 操作:(OPR)←(OPR)
    5. CMP(compare)比较 格式:CMP OPR1,OPR2 操作:(OPR1)-(OPR2)

    5.2.4 乘法指令

    1. MUL(unsigned mulutiple)无符号数乘法 格式:MUL SRC 操作:当操作数为字节时,(AX)←(AL)×(SRC) 当操作数为字时,(DX,AX)←(AX)×(SRC)
    2. IMUL(signed mulutiple)有符号数乘法 格式和操作与 MUL 相同,用来作有符号数乘法。

    乘法指令中,目的操作数默认为累加器 AX,不必在指令中写出。

    两个相乘的数必须长度相同,根据 SRC 的长度,默认参与运算的是 AL 寄存器的值(即为 AX 寄存器的第八位)或者是 AX 寄存器的值。

    SRC 可以是寄存器或变量,但不能是立即数,因为立即数的长度是不明确的。

    5.2.5 除法指令

    1. DIV(unsigned divide)无符号数除法 格式:DIV SRC 操作: SRC 为字节时,(AL)←(AX)/(SRC)的商,(AH)←(AX)/(SRC)的余数。 SRC 为字时,(AX)←(DX,AX)/(SRC)的商,(DX)←(DX,AX)/(SRC)的余数。该指令将参与运算的数据默认为无符号数,则商和余数都是无符号数。
    2. IDIV(signeddivide)有符号数除法 指令格式和操作与无符号数除法相同,用来作有符号数除法。 最终商的符号应是两个操作数符号的异或,而余数的符号和被除数符号一致。

    在除法指令里,目的操作数必须是累加器 AX 和 DX,不必在指令中写出。

    被除数长度应为除数长度的两倍,余数放在目的操作数的高位,商放在目的操作数的低位。其中 SRC 不能是立即数。

    另外,和作乘法时相同,作除法时需考虑是无符号数还是有符号数,从而选择不同的指令。

    由于除法指令的字节操作要求被除数为 16 位,字操作要求被除数为 32 位,因此往往需要用符号扩展指令使得被除数长度比除数长度扩大一倍。

    需要注意的是,在进行乘法运算时,不会发生溢出问题,但在使用除法指令时,会产生溢出现象。

    当除数是字节类型时,除法指令要求商为 8 位。此时如果被除数的高 8 位绝对值≥除数的绝对值,则商会产生溢出。

    当除数是字类型时,除法指令要求商为 16 位。此时如果被除数的高 16 位绝对值≥除数的绝对值,则商会产生溢出。

    商出现溢出时,系统转 0 号类型中断处理,提示“divide overflow”,并退出程序,返回到操作系统,程序已经崩溃了。

    要想避免出现这种情况,必须在作除法前对溢出作出预判。

    5.2.6 BCD 码的十进制调整指令

    BCD 码是二进制转十进制比较简单的编码,4 位二进制表示 1 位十进制,其转换表如下。

    1. DAA(Decimal Adjust for Addition)加法的十进制调整指令 格式:DAA 操作:加法指令中,以 AL 为目的操作数,当加法运算结束后,使用本指令可以把 AL 中的和调整为正确的 BCD 码格式。 即:
      1. 如果 AL 低 4 位 > 9,或 AF=1,则 AL = AL + 6;
      2. 如果 AL 高 4 位 > 9,或 CF=1,则 AL = AL + 60H, CF = 1。
    2. DAS(Decimal Adjust for Subtraction)减法的十进制调整指令 格式:DAS 操作:减法指令中,以 AL 为目的操作数,当减法运算结束后,使用本指令可以把差调整为 BCD 码格式。 即:
      1. 如果 AL 低 4 位 > 9,或 AF = 1,则 AL = AL – 6,AF = 1;
      2. 如果 AL 高 4 位 > 9,或 CF = 1,则 AL = AL – 60H, CF = 1。

    5.3 逻辑与移位指令

    5.3.1 逻辑指令

    1. AND(and)与 格式:AND DST,SRC 操作:(DST)←(DST)∧(SRC)
    2. OR(or)或 格式:OR DST,SRC 操作:(DST)←(DST)∨(SRC)
    3. NOT(not)非 格式:NOT OPR 操作:(OPR)←(OPR)
    4. XOR(exclusive or)异或 格式:XOR DST,SRC 操作:(DST)←(DST)∀(SRC)
    5. TEST(test)测试 格式:TEST OPR1,OPR2 操作:(OPR1)∧(OPR2) 说明:TEST 指令的两个操作数相与的结果不保存,只根据结果置标志位。

    逻辑运算指令只会对部分标志位产生影响,其中 NOT 指令不影响任何标志位,其他指令将使 CF 位和 OF 位为 0,AF 位无定义,其他位则根据运算结果设置。

    逻辑指令除了常规的逻辑运算功能外,通常还可以用来对操作数的某些位进行处理,例如屏蔽某些位(将这些位置 O),或使某些位置 1,或测试某些位等。

    1. SHL(Shift Logical Left)逻辑左移
    2. SAL(Shift Arithmetic Left)算术左移
    3. SHR(Shift Logical Right)逻辑右移
    4. SAR(Shift Arithmetic Right)算术右移
    5. ROL(Rotat Left)循环左移
    6. ROR(Rotat Right)循环右移
    7. RCL(Rotat Left with Carry)带进位循环左移
    8. RCR(Rotat Right with Carry)带进位循环右移

    移位指令均是双操作数指令,指令的格式相同,以 SHL 为例,

    则:

    格式:

    1. SHL OPR,1
    2. SHL OPR,CL,其中 CL 寄存器的值大于 1。

    其中 OPR 为寄存器或内存单元,移位次数可以是 1 或 CL 寄存器,如需移位的次数大于 1,则可以在该移位指令前把移位次数先送 CL 寄存器中。

    当执行逻辑或算术左移时,操作结果相同,均是最低位补 0,移出的最高位送 CF 标志位;

    当执行逻辑右移时,最高位补 0,移出的最低位送 CF 标志位;

    当执行算术右移时,OPR 被认为是有符号数,则最高位补符号位自身,移出的最低位送 CF 标志位;

    当执行循环左移时,OPR 整体向左移一位,最高位移出,同时送 CF 标志位和最低位;

    当执行循环右移时,OPR 整体向右移一位,最低位移出,同时送 CF 标志位和最高位;

    当执行带进位循环左移时,OPR 整体向左移一位,此时最高位移出送 CF 标志位,而 CF 标志位原始的数值送 OPR 最低位;

    当执行带进位循环右移时,OPR 整体向右移一位,此时最低位移出送 CF 标志位,而 CF 标志位原始的数值送 OPR 最高位。

    5.4 串操作(我赌一波,不考)

    1. MOVS(Move String)串传送
      1. MOVSB:以字节为单位传送; 操作: (ES:DI)←(DS:SI),DI±1,SI±1
      2. MOVSW:以字为单位传送; 操作: (ES:DI)←(DS:SI),DI±2,SI±2
      3. MOVS DST,SRC:将源串 SRC 传送到目的串 DST 中。
      MOVS指令的寻址方式是固定的,目的串地址为ES:[DI],源串地址为DS:[SI],因此前两种格式都将操作数直接省略了。若采用第三种格式指令,则以字节为单位传送时,可以表示为:MOVS ES:BYTE PTR[DI],DS:[SI] 目的操作数指出了是字节的传送,如果源串不在数据段,也可加前缀,如ES:[SI]。上述操作中,当方向标志 DF=0 时 SI、DI 用+;DF=1 时 SI、DI 用-。方向标志 DF 的设置有两条指令:
      1. CLD(clear direction flag)设置正向(向前,使 DF=0,SI 或 DI 自动加)
      2. STD(set direction flag)设置反向(向后,使 DF=1,SI 或 DI 自动减)
    2. CMPS(Compare String)串比较
      1. CMPSB(字节) 操作: (ES:DI)-(DS:SI),DI±1,SI±1
      2. CMPSW(字) 操作: (ES:DI)-(DS:SI),DI±2,SI±2
      3. CMPS DST,SRC。
      本条串操作指令把两个串的对应位置的字节或字相减,不保存结果,只是根据结果设置标志位。该指令与前缀 REPE 联用时,可比较两个串是否相等。在每次比较过程中,一旦发现不相等,ZF=0,则终止重复执行,而不必等到整个串全部比较结束,此时 CX≠0,ZF=0。该指令终止执行后,可根据标志 ZF 判断两个串是否相等。其他指令格式与串传送指令相同。
    3. SCAS(Scan String)串扫描
      1. SCASB(字节) 操作: AL-(ES:DI),DI±1
      2. SCASW(字) 操作: AX-(ES:DI),DI±2
      3. SCAS DST。
      串扫描指令是把AL/AX寄存器中的内容与附加段中的由目的变址寄存器DI所指向的内存单元内容相比较,与CMPS比较指令相似,并不保存结果,只是根据结果设置标志位。该指令与前缀 REPNE 联用时,可在目的串中查找有无和 AL/AX 寄存器中的内容相同的字节或字。在每次执行串扫描指令过程中,一旦发现相等,即 ZF=1,则终止执行,此时 CX≠0,ZF=1,说明已找到相同的内容,而不必等到整个串全部扫描结束。该指令终止执行后,可根据标志位 ZF 判断是否找到。指令相关格式要求同串传送指令。
    4. STOS(Store in to String)存入串
      1. STOSB(字节) 操作: (ES:DI)←AL,DI±1
      2. STOSW(字) 操作: (ES:DI)←AX,DI±2
      3. STOS DST
      该指令把AL/AX寄存器的内容存入由目的变址寄存器指向的附加段的某单元中,并根据DF的值及数据类型修改目的变址寄存器的内容。当它与 REP 联用时,可把累加器的内容存入一个连续的内存缓冲区,该缓冲区长度由 CX 指定,因此 STOS 串指令可用于初始化某一块内存区。上述有关串处理指令的特性也适合本指令。
    5. LODS(Load from String)从串取
      1. LODSB(字节) 操作: AL←(DS:SI),SI±1
      2. LODSW(字) 操作: AX←(DS:SI),SI±2
      3. LODS SRC。
      该指令意义不大,一般不和 REP 联用,因为重复执行多次的结果也 只是使累加器为最后一次的值。该指令可以由 MOV 指令代替。

    串操作指令用以处理内存中的数据串,但该操作每一次执行处理的只是单个字节或字,因此对于数据串来说,需要重复执行串操作指令才能处理完整个串。

    串操作指令的重复有特定的前缀指令配合,下面先介绍前缀指令:

    1. REP(repeat)重复 前缀 REP 的作用是重复执行串操作指令,直到寄存器 CX=0 为止,而每执行一次串操作指令,会使 CX 的内容自动减 1,因此总的重复次数等于 CX 寄存器的初始值。
    2. REPE/REPZ(repeat while equal/zero)相等/为零则重复 前缀 REPE 也叫 REPZ,只有当 CX 寄存器的值≠0 并且标志位 ZF=1 时,重复执行串操作指令。 若用以比较两个字符串是否相等,每次的串操作指令把源串中的一个字节和目的串中的一个字节进行比较,如果相等(即 ZF=1),则还需继续执行串操作指令,若不相等或者比较全部串的数据(CX=0),则停止。
    3. REPNE/REPNZ(repeat while not equal/not zero)不相等/不为零则重复 前缀 REPNE 也叫 REPNZ,只有当 CX 寄存器的值≠0 并且标志位 ZF=0 时,重复执行串操作指令。 若在一个字符串中查找是否存在某一个字符,串操作指令把字符串中的一个字节和要找的这个字符进行比较,如果不相等(即 ZF=0),则还需继续执行串操作指令,直到找到(ZF=1)或者查找完整个串的数据(CX=0),才停止。

    串处理指令的特性及用法

    5.5 程序转移指令

    5.5.1 无条件转移指令与程序的可重新定位

    JMP(jmp):该指令无条件转移到指令指定的地址去执行程序。

    1. 段内直接转移 格式:JMP NEAR PTR OPR 操作:IP←IP+16 位位移量
    2. 段内间接转移 格式:JMP WORD PTR OPR 操作:IP←(EA) 有效地址 EA 值由 OPR 的寻址方式确定。它可以使用除立即数 方式以外的任何一种寻址方式。
    3. 段间直接转移 格式:JMP FAR PTR OPR 操作:IP←OPR 的偏移地址 CS←OPR 所在段的段地址
    4. 段间间接转移 格式:JMP DWORD PTR OPR 操作:IP←(EA) CS←(EA+2)

    待续,敢考就敢寄。

    第六章 伪指令与源程序格式

    6.1 伪指令

    6.1.1 处理机选择

    随着处理器的升级,增加了新的指令。

    处理机选择伪指令有以下几种:

    1. ·8086 选择 8086 指令系统
    2. ·286 选择 80286 指令系统
    3. ·286P 选择保护方式下的 80286 指令系统
    4. ·386 选择 80386 指令系统
    5. ·386P 选择保护方式下的 80386 指令系统
    6. ·486 选择 80486 指令系统
    7. ·486P 选择保护方式下的 80486 指令系统
    8. ·586 选择 Pentium 指令系统
    9. ·586P 选择保护方式下的 Pentium 指令系统

    指令中的点 · 是需要的。这类伪指令一般放在代码段中的第一条指令前即可。如不给出,则汇编程序认为其默认选择是 8086 指令系统。

    6.1.2 段定义伪指令

    1. 段定义伪指令汇编程序在把源程序转换为目标程序时,只能自动确定标号和变量(代码段和数据段的符号地址)的偏移地址,程序中对于段地址也要作出说明,段地址一旦说明,该段内的指令、标号和变量都属于这个段。段定义伪指令格式:  segment_name  SEGMENT
       ​
       segment_name  ENDSsegment_name由用户确定,大写的为关键字。段定义伪指令两句成对出现,两句之间为其他指令。
    2. 为了确定用户定义的段和哪个段寄存器相关联,用 ASSUME 伪指令 来实现。 ASSUME 伪指令格式: ASSUME register_name: segment_name …,
        register_name: segment_name register_name 为段寄存器名,必须是 CS,DS,ES 和 SS。而 segment_name 则必须是由段定义伪指令定义的段中的段名。ASSUME 伪指令只是指定把某个段分配给哪一个段寄存器,它并不能把段地址装入段寄存器中,所以在代码段中,还必须把段地址装入相应的段寄存器中。还需要用两条 MOV 指令完成这一操作。但是,代码段不需要这样做,代码段的这一操作是在程序初始化时完成的。

    6.1.3 程序开始和结束伪指令

    源程序结束的伪操作的格式为:

     END [label]

    汇编程序将在遇到 END 时结束汇编。其中标号 label 指示程序开始执行的起始地址。

    如果是多个程序模块相连接,则只有主程序需要使 用标号,其他子程序模块则只用 END 而不能指定标号。

    6.1.4 数据定义与存储器单元分配伪指令

    伪指令的一般格式是:

    [变量] 操作码 N个操作数 [; 注释]

    操作码字段说明所用伪操作的助记符,即伪操作,说明所定义的数据类型。常用的有以下几种。

    1. DB:伪操作用来定义字节,其后的每个操作数都占有一个字节(8 位)。
    2. DW:伪操作用来定义字,其后的每个操作数占有一个字(16 位,其低位字节在第一个字节地址中,高位字节在第二个字节地址中,即数据低位在低地址,数据高位在高地址)。
    3. DD:伪操作用来定义双字,其后的每个操作数占有两个字(32 位)。
    4. DF:伪操作用来定义 6 个字节的字,其后的每个操作数占有 48 位。
    5. DQ:伪操作用来定义 4 个字,其后的每个操作数占有 4 个字(64 位),可用来存放双精度浮点数。
    6. DT:伪操作用来定义 10 个字节,其后的每个操作数占有 10 个字节,为压缩的 BCD 码。

    这些伪操作可以把其后跟着的数据存入指定的存储单元,形成初始化数据;或者只分配存储空间而并不确定数值。

    6.1.5 类型属性操作符

    1. WORD PTR ; 字类型
    2. BYTE PTR ; 字节类型

    就是按照某种类型读取,但不会改变变量本身的类型。

    对一个 8 位(或 16 位)的变量可以用 16 位(或 8 位)方式访问。

    6.1.6 THIS 操作符和 LABEL 伪操作

    通过 THIS 操作符或 LABEL 伪操作,将变量定义为不同的访问类型。

    1. 使用 THIS 操作符: 格式:THIS type
    2. 使用 LABEL 伪操作: 格式:name LABEL type

    type在这里是BYTE或者WORD

    6.1.7 表达式赋值伪指令“EQU”和“=”

    可以用赋值伪操作给表达式赋予一个常量或名字。其格式如下:

     Expression_name EQU Expression
     Expression_name=Expression

    后略

    第七章 分支与循环程序设计

    18 级试卷

  • Android 复习

    第一章 Android 基础入门

    Android 是基于 Linux 的开源操作系统,最初由 Andy Rubin 开放。

    1.1 简介

    1.1.1 通信技术

    1G 很容易被窃听

    2,3,4,5G 技术的本质区别是传输速度不同。

    2019 年,5G 商用元年。

    1.1.3 体系结构

    1. 应用程序层应用程序的集合。
    2. 应用程序框架层提供 API。
    3. 核心类库系统库和 Android 运行时库(包含了 Dalvik 虚拟机,使得每个 Android 应用程序都能运行在独立的进程中)。
    4. Linux 内核层底层驱动的集合,如显示驱动,音频驱动,照相机驱动等。

    1.1.4 Dalvik 虚拟机

    基于寄存器架构。执行特有的 dex文件实现其功能。

    功能:

    1. 对象生命周期管理
    2. 堆栈管理
    3. 线程管理
    4. 安全异常管理
    5. 垃圾回收

    虚拟机编译文件的过程(P4):

    ART 模式

    启用后,安装 APP 时预编译,并将代码转换成机器语言存储在本地。

    提高执行效率。


    1.4 Android 程序结构

    四大组件在 src/main/AndroidManifest.xml 里注册,这是整个程序的配置文件。 四大组件:

    1. Activity 活动
    2. Service 服务
    3. BroadCastReceive 广播接收器
    4. ContentProvider 内容提供商

    接下来介绍程序工程的结构:

    • app 代码和资源
      • libs 第三方 jar 包
      • src 代码和资源的主目录
        • androidTest 调试代码
        • main
          • java 程序代码
          • res 程序资源
          • AndroidManifest.xml 整个程序的配置文件,在其中可以配置程序所需的权限和注册程序用到的四大组件。注意这里是申请网络权限和存储权限的地方,不申请就用不了,而且似乎不会报错。
      • build.gradle app 构建脚本,包含编译的 SDK 版本,Tools 版本,支持的最低 SDK 版本,支持的目标 SDK 版本。
    • build.gradle 安卓程序的构建脚本
    • local.properties 指定程序的 SDK 路径。可以通过 sdk.dir 的值指定,一般不需要修改
    • settings.gradle 配置子项目

    1.5 资源管理与使用

    res 目录中。

    1.6 调试

    输出信息使用 logcat。 v:verbose 详细 d:debug i:info w:warning e:error a:assert 断言 vdiwea

    习题

    一、填空题

    1. Dalvik 中的 Dx 工具会把部分 class 文件转换成(dex)文件。
    2. 如果希望在 XML 布局文件中调用颜色资源,可以使用(@color)调用。
    3. Android 程序入口的 Activity 是在(AndroidMainifest.xml)文件中注册的。
    4. Android 中 查看应用程序日志的工具是(LogCat)。

    二、判断题

    1. Dalvik 是 Google 公司设计的用于 Android 平台的虚拟机。 √
    2. Android 应用程序的主要语言是 Java。√
    3. Android 系统采用分层架构,分别是应用程序层、应用程序框架层、核心类库和 Linux 内核。√
    4. 第三代移动通信技术 (3G) 包括 TD-LTE 和 FDD-LTE 两种制式。× 应是 4G。
    5. Android 程序中,Log.e() 用于输出警告级别的日志信息。× e-error
    6. 每个 Dalvik 虚拟机实例都是一个独立的进程空间,并且每个进程之间不可以通信。× 可以通信。

    三、选择题

    1. Dalvik 虚拟机是基于(C)的架构。 A. 栈 B. 堆 C. 寄存器 D. 存储器
    2. Android 项目中的主题和样式资源,通常放在 (C) 目录。 A. res/drawable B. res/layout C. res/values D. assets
    3. 下列关于 AndroidManifest.xml 文件的说法中,错误的是 (D)。 A. 它是整个程序的配置文件 B. 可以在该文件中配置程序所需的权限 C. 可以在该文件中注册程序用到的组件 D. 该文件可以设置 UI 布局
    4. Dalvik 虚拟机属于 Android 系统架构中的(C) A. 应用程序层 B. 应用程序框架层 C. 核心类库层 D. Linux 内核层
    5. Android 中 短信、联系人管理、浏览器等属于 Android 系统架构中的(A) A. 应用程序层 B. 应用程序框架层 C. 核心类库层 D. Linux 内核层

    四、简答题

    简述 Android 体系结构的层次和特点

    见 1.1.3。书 P3。

    第二章 Android 常见界面布局

    2.1 View 控件

    每个界面必须有且只有一个 ViewGroup 容器。

    2.2 界面布局编写方式

    2.2.1 XML 布局文件 中编写布局

    布局文件放在 res/layout ,用标签定义布局,例如 <RelativeLayout></RelativeLayout>。

    2.2.2 Java 代码中编写布局

    P32

    2.3 布局通用属性(P32)

     android:id
     // 标识
     ​
     android:layout_width
     // 布局的宽度
     ​
     android:layout_height
     // 布局的高度
     ​
     android:background
     // 布局背景
     ​
     android:layout_margin
     // 边界距离,外距
     ​
     android:padding
     // 内距

    2.4 线性布局

     <LinearLayout></LinearLayout>
    1. 排列方式:水平或者竖直排列。 android:orientation=”vertical”
       // 控件自上到下
       ​
       android:orientation=”horizontal”
       // 控件从左到右
    2. 权重 android:layout_weight=”1″
       // 设置权重

    2.5 相对布局

     <RelativeLayout></RelativeLayout>

    相对定位指定子控件的位置。

    2.6 表格布局

    行列方式的布局。

     <TableLayout>
        <TableRow>
        </TableRow>
     </TableLayout>

    2.7 帧布局

     <FrameLayout></FrameLayout>

    习题

    一、填空题

    1. Android 的常见布局都直接或者间接的继承自(ViewGroup)类。
    2. Android 中 的 TableLayout 继承自(LinearLayout)。
    3. 表格布局 TableLayout 通过(TableRow)布局控制表格的行数。
    4. (RelativeLayout)布局通过相对定位的方式指定子控件的位置。
    5. 在 R.java 文件中,android:id 属性会自动生成对应的(int)类型的值。

    二、判断题

    1. ViewGroup 是盛放界面控件的容器。√
    2. 如果在帧布局 FrameLayout 中放入三个所有属性都相同的按钮,那么能够在屏幕上显示的是第 1 个被添加的按钮。× 第三个,因为会被覆盖。
    3. Android 中的布局文件通常放在 res/layout 文件夹中。√
    4. TableLayout 继承自 LinearLayout, 因此它完全支持 LinearLayout 所支持的属性。√
    5. LinearLayout 布局中的 android:layout_weight 属性用于设置布局内控件所占的权重。√

    三、选择题

    1. 下列属性中,用于设置线性布局方向的是(A)。 A. orientation B. gravity C. layout gravity D. padding
    2. 下列选项中,不属于 Android 布局的是(C)。 A. FrameLayout B. LinearLayout C. Button D. RelativeLayout
    3. 帧布局 FrameLayout 是将其中的组件放在自己的(A)。 A. 左上角 B. 右上角 C. 左下角 D. 右下角
    4. 对于 XML 布局文件,android:layout_width 属性的值不可以是(D)。 A. match_parent B. fill_parent C. wrap_content D. match_content
    5. 下列关于 RelativeLayout 的描述,正确的是(C)。 A. RelativeLayout 表示绝对布局,可以自定义控件的 x、y 的位置 B. RelativeLayout 表示帧布局,可以实现标签切换的功能 C. RelativeLayout 表示相对布局,其中控件的位置都是相对位置 D. RelativeLayout 表示表格布局,需要配合 TableRow 一起使用

    第三章 Android 常见界面控件

    3.1 简单控件

    1. Textview 文本框
    2. EditText 文本输入框
    3. Button 按钮
    4. ImageView 图片
    5. RadioButton 单选按钮(圆按钮)
    6. CheckBox 多选按钮(方按钮)
    7. Toast 类 轻量级信息提醒机制。

    3.2 列表控件

    ListView 是类似微信主界面和淘宝搜索界面的列表。

    这种列表需要配合数据适配器 Adapter,常见的 Adapter 如下

    1. BaseAdapter
      1. getCount 获取 Item 个数
      2. getItem(int position) 获取对应位置的 Item 对象
      3. getItemId(int position) 获取 id
      4. getView(int position,View convertView,ViewGroup Parent) 获取视图
    2. SimpleAdapter
    3. ArrayAdapter

    注意学习一下 Adapter 怎么定义。

    习题

    一、判断题

    1. Android 的控件样式,每一个 XML 属性都对应一个 Java 方法。√
    2. 当指定 RadioButton 按钮的 android:checked 属性为 true 时,表示未选中状态。× 当然是选中了。
    3. AlertDialog 对话框能够直接通过 new 关键字创建对象。× 要通过 Builder 对象。
    4. Toast 是 Android 系统提供的轻量级信息提醒机制,用于向用户提示即时消息。√
    5. ListView 列表中的数据是通过 Adapter 加载的。√

    二、选择题

    1. 在 XML 布局中定义了一个 Button, 决定 Button 按钮上显示文字的属性是(B) A. android:value B. android:text C. android:id D. android:textvalue
    2. 下列选项中,(C)用于设置 TextView 中文字显示的大小。 A. android:textSize=”18″ B. android:size=”18″ C. android:textSize=”18sp” D. android:size=”18sp”
    3. 使用 EditText 控件时,当文本内容为空时,如果想做一些提示,那么可以使用的属性是(D)。 A. android:text B. android:background C. android:inputType D. android:hint
    4. 为了让一个 imageView 显示一张图片,可以通过设置的属性是(A)。 A. android:src B. android:background C. android: img D. android:value
    5. 下列关于 ListView 的说法中,正确的是(C) A. ListView 的条目不能设置点击事件 B. ListView 不设 置 Adapter 也能显示数据内容 C. 当教据超出能显示范围时,ListView 自动具有可滚动的特性 D. 若 ListView 当前能显示 10 条,一共有 100 条数据,则产生了 100 个 View
    6. CheckBox 被选择的监听事件通常使用(B)方法。 A. setOnClickListener B. setOnCheckedChangeListener C. setOnMenuItemSelectedListener D. setOnCheckedListener
    7. 当使用 EditText 控件时,能够使文本框设置为多行显示的属性是(A)。 A. android:lines B. android:layout_height C. android:textcolor D. android:textsize
    8. 下列关于 AlertDialog 的描述,错误的是(A) A. 使用 new 关键字创建 AlertDialog 的实例 B. 对话框的显示需要调用 show() 方法 C. setPositiveButton() 方法是 用来设置确定按钮的 D. setNegativeButton() 方法是用来设置取消按钮的

    第四章 程序活动单元 Activity

    4.1 Activity 生命周期

    5 种生命周期的状态:

    1. 启动状态 十分短暂
    2. 运行状态 可见的、有焦点的。运行状态的 Activity 具有高优先级,即使内存不足,系统也会先销毁栈底的 Activity 来确保当前的 Activity 正常运行。
    3. 暂停状态 可见,但无焦点不可操作,例如主界面出现一个弹窗,则主界面就是暂停状态。
    4. 停止状态 完全不可见就是停止状态。
    5. 销毁状态 会被清理出内存。

    生命周期方法:

    1. onCreate() 创建界面的时候初始化的函数,一开始就会调用,可以重写这个方法来做一些初始化工作,例如给控件写数据等
    2. onStart() 即将可见时调用
    3. onResume() 获取焦点时调用
    4. onPause() 暂停,被遮挡时调用
    5. onStop() 不可见时调用
    6. onRestart() 停止状态启动
    7. onDestory() 销毁时调用

    4.2 Activity 创建、配置、启动、关闭

    创建

    编译器左上角 new 就行。

    配置

    编写类继承 ( extend ) activity 类

    启动

     public void startActivity (Intent intent)

    关闭

     public void finish() 

    4.3 Intent 意图和 IntentFilter 匹配

    显式和隐式 Intent

    1. 显式 intent Intent intent = new Intent(this, SecondActivity.class);
       startActivity(intent);那么就用 intent 的属性来确定需要打开的 Activity 了。
    2. 隐式 intent 主要有三个属性值:
      1. action 要完成的动作
      2. data 传递的数据
      3. category action 属性的额外信息

    IntentFilter

    隐式 Intent 会使得系统将其和每一个组件的过滤器相匹配,三个属性值都匹配成功,则唤起相应的组件。

    匹配规则:

    1. action 清单文件中设置 一些 action 属性(<intent-filter></intent-filter>),只要 action 匹配上了一条就算匹配上。
    2. data 清单文件中设置 一些 data 属性(<intent-filter></intent-filter>),只要 data 匹配上了一条就算匹配上。
    3. category 清单文件中设置 一些 category 属性(<intent-filter></intent-filter>),隐式的 intent 里的 category 必须包含于(小于等于)清单文件里的 category 属性,否则匹配不上。因此 intent 的 category 如果是空则和所有的 IntentFilter 匹配上

    4.4 Activity 之间的跳转

    4.4.1 数据传递

    1. 用 Intent 来做数据传递。 putExtra(String name, …);
       // 加入一个属性,第一个参数是属性名,第二个是属性值第二个参数的类型是变化的,因为这个函数有很多的重载。但应该都是基本数据类型。不能传递一个对象的引用。另一个 Activity 取出这个属性这样操作。 getIntent().getStringExtra(String name)
       // 如果这个属性的值是 int,就把 String 改成 Int。Boolean→Boolean
    2. 用 Bundle 类传递数据 例如 Intent intent = new Intent() ;
       Bundle bundle = new Bundle() ;
       bundle.putString(“account”, “小米”);
       intent.putExtras(bundle);
       startActivity(intent);另一个 Activity 取出 Bundle 这样操作。 getIntent() .getExtras() .getString(“account”);

    4.4.2 数据回传

    三个方法:

    1. startActivityForResult( Intent intent, int requestCode); 第一个参数是意图对象,第二个是请求码,标识请求来源。这个函数用来开启一个 Activity,当开启的 Activity 被销毁时会返回数据。
    2. setResult( int resultCode, Intent intent); 用于携带数据回传。
    3. onActivityResult( int requestCode, int resultCode, Intent data); 用于接收回传的数据。

    startActivityForResult( Intent intent, int requestCode); 用于在 MainActivity 开启 SecondActivity。

    setResult( int resultCode, Intent intent); 用于 SecondActivity 设置 Result,自身销毁后 Result 会返回到 MainActivity。

    onActivityResult( int requestCode, int resultCode, Intent data); 用于对回传的 Result 解析,是在 SecondActivity 销毁后自动调用的。需要重写。

    4.5 任务栈和启动模式

    任务栈

    任务栈是存放 Activity 实例的容器,销毁就弹出栈,用户操作的永远是栈顶。

    启动模式

    1. standard 默认的启动模式,启动一个就创建一个新的实例。
    2. singleTop 如果启动的是栈顶的任务则复用,而不是创建新的实例。
    3. singleTask 如果启动的是栈内的任务则复用,而不是创建新的实例。
    4. singleInstance 启动新的栈来管理新的实例,不管哪个栈调用这个 Activity,整个系统里只有这一个 Activity,例如安卓的系统桌面。

    4.6 Fragment (P95)

    嵌入 Activity 的 UI 片段。

    4.6.2 生命周期

    受其所属的 Activity 影响,也和 Activity 类似,有 启动、运行、暂停、停止、销毁五种状态,但 Activity 暂停,Fragment 也暂停,停止、销毁也是跟随 Activity 的。

    在 Activity 运行的时候,可以单独对每个 Fragment 操作,如添加(启动状态)删除(销毁状态)等。

    习题

    一、填空题

    1. Activity 的启动模式包括 standard, singleTop,singleTask 和(singleintance)。
    2. 启动一个新的 Activity 并且获取这个 Activity 的返回教据,需要重写(startActivityForResult() )方法。
    3. 发送隐式 Intent 后,Android 系统会使用(IntentFilter)匹配相应的组件。
    4. 在清单文件中为 Activity 添加 <intent-filter> 标签时,必须添加的属性名为(action)否则隐式 Intent 无法开启该 Activity.
    5. Activity 的(finish() )方法用于关闭当前的 Activity。

    二、判断题

    1. 如果 Activity 不设置启动模式,则默认为 standard.√
    2. Fragment 与 Activity 的生命周期方法是一致的。× 方法当然不一样 P80 和 P95
    3. 如果想要关闭当前的 Activity, 可以调用 Activity 提供的 finish() 方法。√
    4. <intent-filter> 标签中间只能包含一个 action 属性。× 多个
    5. 默认情况下,Activity 的启动方式是 standard。√

    三、选择题

    1. 下列选项中,不属于 Android 四大组件的是(C) A. Service B. Activity C. Handler D. ContentProvider
    2. 下列关于 Android 中 Activity 管理方式的描述中,正确的是(B) A. Android 以堆的形式管理 Activity B. Android 以栈的形式管理 Activity C. Android 以树的形式管理 Activity D. Android 以链表的形式管理 Activity
    3. 下列选项中,(B)不是 Activity 生命周期方法。 A. onCreate() B. startActivity() C. onStart() D. onResume()
    4. 下列方法中,(A)是启动 Activity 的方法。 A. startActivity() B. goToActivity() C. startActivityResult() D. 以上都是
    5. 下列关于 Intent 的描述中,正确的是(B) A. Intent 不能够实现应用程序间的数据共享 B. Intent 可以实现界面的切换,还可以在不同组件间直接进行数据传递 C. 使用显式 Intent 可以不指定要跳转的目标组件 D. 隐式 Intent 不会明确指出需要激活的目标组件,所以无法实现组件之间的数据跳转

    第五章 数据存储

    5 种存储方式:

    • 文件存储 openFileInput() openFileOutput() 类似 Java。
    • SharedPreferences 存储 存储一些简单的配置信息,采用 xml 格式。
    • SQLite 数据库存储 支持基本 SQL 语法,一般使用其作为复杂数据的存储引擎。
    • ContentProvider 四大组件之一,用于应用程序间的数据交换。可以将自己的数据共享给其它的应用程序使用。
    • 网络存储 存储在网络服务器上。

    5.2 文件存储(P108)

    5.2.1 写入

    内部存储

    内部存储是存在应用程序中的,文件会被该应用程序私有化,其它应用程序需要申请权限才可以操作。卸载应用程序时,这些文件也会删除。

     FileOutputStream fos = openFileOutput( String fileName, int mode);
     String word = "Hello World!";
     fos.write(word.getBytes());
     fos.close();

    写入( FileOutputStream )有四种读写模式,以枚举的方式定义,填入第二个参数。

    1. MODE_PRIVATE只能被当前文件读写。
    2. MODE_APPEND可以追加。
    3. MODE_WORLD_READABLE该文件可以被其它程序读。
    4. MODE_WORLD_WRITEABLE可以被其它程序写。

    外部存储

    外部存储是存入外部的设备(SD卡或内嵌的存储卡)的。永久性的存储方式。可以被其它应用程序共享,可能会被浏览、修改、删除,是不安全的存储方式。

    需要使用 Environment.getExternalStorageState() 方法确认外部设备是否可用。

     String state = Environment.getExternalStorageState();
     // 获取外存状态
     if(state.equals(Environment.MEDIA_MOUNTED)) {
         //状态判断
         File SDPath = Environment.getExternalStorageDirectory();
      // 获取 SD 卡路径
      File file = new File( SDPath, "data.txt");
         // 创建文件对象
         FileOutputStream fos = new FileOutputStream(file);
         // 打开文件流
         fos.write( data.getBytes());
         // 写入
      fos.close();
         // 关闭流
     }

    5.2.2 读取

    内部存储

     String content = "";
     // 创建 存储的字符串
     FileInputStream fis = openFileInput( "data.txt");
     // 打开文件输入流
     byte[] buffer = new byte[fis.available()];
     // 创建缓冲区
     fis.read(buffer);
     // 读入缓冲区
     content = new String( buffer);
     // 读入字符串
     fis.close();
     //关闭流

    外部存储

     String state = Environment.getExternalStorageState();
     // 获取外存状态
     if(state.equals(Environment.MEDIA_MOUNTED)) {
         //状态判断
         File SDPath = Environment.getExternalStorageDirectory();
      // 获取 SD 卡路径
      File file = new File( SDPath, "data.txt");
         // 创建对象文件
         FileInputStream fis = new FileInputStream(file);
         // 打开流
         BufferedReader br = new BufferedReader( new InputStreamReader( fis));
         // 创建字符输入缓冲流对象
         String data = br.readLine();
         // 读取数据
         br.close();
         fis.close();
     }

    5.3 SharedPreferences 存储(P115)

    持久化存储。存一些配置,类似账号密码。采用 xml 格式。

    5.4 SQLite 数据库存储(P118)

    可以存储大量数据。

    习题

    一、判断题

    1. SQLite 是 Android 自带的一个轻量级的数据库,支持基本 SQL 语法。√
    2. Android 中的文件存储方式,分为内部存储方式和外部存储方式。√
    3. 使用 openFileOutput() 方式打开应用程序的输出流时,只需指定文件名。× 还有读写模式。这是内部存储的输出流,外存用 new FileOutputStream(file)。Input 只要文件名(从文件里面读到程序里面当然不用什么追加什么的)
    4. 当 Android SDK 版本低于 23 时,应用程序想要操作 SD 卡数据,必须在清单文件中添加权限。√
    5. SQLiteDatabase 类的 update() 方法用于删除数据库表中的数据。× update 当然是更新
    6. SQLite 数据库的事 务操作满足原子性、一致性、隔离性和持续性。√

    二、选择题

    1. 下列关于 SharedPreferences 存取文件的描述中,错误的是(C)。 A. 属于移动存储解决方式 B. SharedPreferences 处理的就是 key-value 对 C. 读取 xml 的路径是 /sdcard/shared_prefs D. 文本的保存格式是 xml
    2. 下列选项中,不属于 getSharedPreferences 方法的文件操作模式参数是(B)。 A. Context.MODE_PRIVATE B. Context.MODE_PUBLIC C. Context.MODE_WORLD_ READABLE D. Context.MODE_WORLD_WRITEABLE
    3. 下列方法中,(B)方法是 shardPreferences 获取其编辑器的方法。 A. getEdit() B. edit() C. setEdit() D. getAll()
    4. Android 对教据库的表进行查询操作时,会使用 SQLietDatabase 中的(C)方法。 A. insert() B. execSQL() C. query() D. updata()
    5. 下列关于 SQLite 数据库的描述中,错误的是(C) A. SqliteOpenHelper 有创建数据库和更新数据库版本的的功能 B. SqliteDatabase 类是用来操作数据库的 C. 每次调用 SqliteDatabase 的 getWritableDatabase 方法时,都会执行 SqliteOpenHelper 的 onCreate() 方法 D. 当数据库版本发生变化时,会调用 SqliteOpenHelper 的 onUpgrade() 方法更新数据库
    6. 下列初始化 SharedPreferences 的内代码中,正确的是(D) A. SharedPreferences sp = new SharedPreferences(); B. SharedPreferences sp = SharedPreferences.getDefault(); C. SharedPreferences sp = SharedPreferences.Factory(); D. SharedPreferences sp = getSharedPreferences();

    第六章 内容提供者和内容观察者

    6.1 内容提供者

    A 程序通过 ContentProvider 来暴露数据,B 程序通过 ContentResolve 操作 A 程序暴露的数据。A 程序会将操作结果返回给 ContentResolver,然后 ContentResolver 再将操作结果返回给 B。

    两个重要的部分

    1. 数据模型 使用基于数据库模型的 简单表格,其中每个数据的唯一标识是 _ID,数据类型是 int。查询字段数据用 getInt() /getString() /getLong() 等
    2. Uri ContentResolver 的增删查改方法以 Uri 的形式对外提供数据。 Uri 的三部分:
      1. scheme content:// 表示操作的数据被 ContentProvider 控制,不会被修改。
      2. authority 表示 ContentProvider 设置的唯一标识。主要用来区分程序,其一般是表示程序包名。
      3. path 表示资源或数据。可以动态修改。
      例如 content:// cn.itcast.mycontentprovider/ person

    6.4 内容观察者

    实时监听 ContentProvider 共享的数据是否变化。

    观察指定的 Uri 代表的数据的变化。

    变化时触发 ContentObserver 的 onChange() 方法。

    详细解释 P138

    习题

    一、判断题

    1. Uri 主要由三部分组成,分别是 scheme,authority 和 path。√
    2. 内容观察 ContentObserver 用于观察指定 URI 代表的数据的变化。√
    3. 内容提供者主要功能是实现跨程序共享数据的功能。√
    4. Android 中通过内容解析者查询短信数据库的内容时不需要加入读短信的权限。× 危险权限一共九个:
      1. 位置
      2. 日历
      3. 照相机
      4. 联系人
      5. 存储卡
      6. 传感器
      7. 麦克风
      8. 电话
      9. 短信 这九个需要动态申请。也就是说不仅需要在清单中注册,还需要在代码里面申请!
    5. Android 系统的 UriMatcher 类用于匹配 Uri。√

    二、选择题

    1. 如果一个应用程序想要访问另外一个应用程序的数据库,那么需要通过(C)实现。 A. BroadcastReceiver B. Activity C. ContentProvider D. AIDL
    2. 下列方法中,(B)能够得到 ContentResolver 的实例对象。 A. new ContentResolver() B. getContentResolver() C. newInstance() D. ContentUris.newInstance()
    3. 自定义内容观察者时,需要继承的类是(B)。 A. BaseObserver B. ContentObserver C. BasicObserver D. DefalutObserver
    4. 对查询手机系统短信时,内容提供者对应的 Uri 为(C)。 A. Contacts.Photos.CONTENT_URI B. Contacts.People.CONTENT_URI C. content://sms/ D. Media.EXTERNAL_CONTENT_URI
    5. 下列关于 ContentProvider 的描述,错误的是(D)。 A. ContentProvider 是一个抽象类,只有继承后才能使用 B. ContentProvider 只有在 AndroidManifest.xml 文件中注册后才能运行 C. ContentProvider 为其他应用程序提供了统一的访问数据库的方式 D. 以上说法都不对

    第七章 广播机制

    7.1 广播机制的概述

    广播机制:每个程序可以根据自己的兴趣来注册广播,广播可能是系统发送的也可能是其他应用发送的。

    消息订阅者(广播订阅者)注册广播接收者(Binder 机制),广播发送者发送广播(Binder 机制),处理中心接收,根据消息发送者的要求,在已注册列表内寻找合适的消息订阅者,寻找依据是(IntentFilter/Permission)。然后由处理中心发送广播到消息订阅者。

    广播接收者默认重写类的构造函数和 onReceive() 方法。

    Android 8.0 后,必须用动态注册才可以接收广播,静态接收无效。

    1. 无序广播 异步执行,所有监听这个广播的广播接收者都会收到,但接收/执行顺序不确定。效率高,但无法拦截。
    2. 有序广播 按照广播接收者声明的优先级别依次接收,只有一个广播接收者能接收到。在这个广播接收者中的逻辑执行完,再继续传递。效率较低,有接收先后顺序,可以被拦截(终止)。优先级相同,先注册的先接收。setPriority(int priority) 函数,参数值越大,优先级越高。
    3. 指定广播 有序广播的一种,保证某个接收者一定会接收到这个广播。

    习题

    一、填空题

    1. (BroadcastReceiver)用来监听来自系统或者应用程序的广播。
    2. 广播接收者的注册方式有两种,分别是(动态注册)和(静态注册)。

    二、判断题

    1. Broadcast 表示广播,它是一种运用在应用程序之间传递消息的机制。√
    2. 在清单文件注册广播接收者时,可在 <intent-filer> 标签中使用 priority 属性设置优先级别,属性值越大优先级越高。√
    3. 有序广播的广播效率比无序广播更高。× 当然是无序的高,因为有序的需要按顺序接收和执行。
    4. 动态注册的广播接收者的生命周期依赖于注册广播的组件。√
    5. Android 中广播接收者必须在清单文件里面注册。× 动态注册不需要在清单里注册。但注意危险权限注册,既要在清单注册也要代码里面动态注册。

    三、选择题

    1. 关于广播类型的说法,错误的是(BC)。(多选) A. Android 中的广播类型分有序广 播和无序广播 B. 无序广播是按照一定的优先级进行接收 C. 无序广播可以被拦截,可以被修改数据 D. 有序广播按照一定的优先级进行发送
    2. 广播作为 Android 组件间的通信方式,使用的场景有(ABCD)。(多选) A. 在同一个 APP 内部的同一组件内进行消息通信 B. 不同 APP 的组件之间进行消息通信 C. 在同一个 APP 内部的不同组件之间进行消息通信(单个进程) D. 在同一个 APP 具有多个进程的不同组件之间进行消息通信

    第八章 服务

    8.1 服务概述

    四大组件之一,关于四大组件见 1.4。

    在后台长时间执行操作并且不提供用户界面的应用程序。

    一般由 Activity 启动,但不依赖于 Activity,有自己的生命周期。

    应用场景:

    1. 后台运行 后台长时间运行而不提供界面,系统必须回收内存资源的时候,会被销毁,否则一直在后台运行。
    2. 跨应用访问 服务被启动时,即使用户切换到其它应用程序,服务也会后台继续运行

    例如多媒体后台播放是服务,后台记录地理位置也是服务。

    注意,服务并不是运行在子线程中,而是在主线程中,只是没有界面而已。它的耗时操作会开启子线程来处理,否则会出现 ANR(程序无响应)异常。

    8.3 服务的生命周期

    与启动方式有关,有两种启动方式。

    1. startService() 方法(其他组件调用 stopService()停止,服务自身调用 stopSelf() ) 依次调用 onCreate() onStartCommand() onDestory() 在后台长时间运行,启动服务的组件与服务之间没有关联。即使组件被销毁,服务也会继续运行。
    2. bindService() 方法( unbindService() 解绑) 依次调用 onCreate() onBind() onUnbind() onDestory() 服务与组件绑定,允许组件与服务交互,组件退出/调用 unbindService() 方法,服务就会销毁(当所有组件都解绑的时候,这个服务就会被销毁)。 多个组件可以绑定一个服务。第一个组件绑定是会回调 onCreate() 生命周期方法,后续的绑定只会调用 onBind() 方法,返回 IBinder 给客户端。

    8.5 服务的通信

    通过调用 bindService() 方法开启服务后,服务与绑定服务的组件之间可以通信,通过组件可以控制服务操作服务。

    通信可以先用 startService() 来开启服务,再 bind 绑定组件来通信,也可以直接以 bindService() 来开启服务。

    服务通信方式有两种

    1. 本地服务通信 应用程序内部通信。 Service 类onBind() 方法会返回一个 IBinder 对象,传递给 ServiceConnection 类onServiceConnected( ComponentName Name, IBinder service) 方法,然后绑定服务的组件就通过 IBinder 对象Service 通信。
    2. 远程服务通信 应用程序之间的通信。通过 AIDL(一种接口定义语言) 实现。由于每个应用程序都有自己的进程,因此远程服务通信,是不同进程之间的通信。注意进程与线程的区别,线程是更小的概念,前面提到后台服务在主线程内,耗时操作在子线程内。而主线程和子线程都在进程内。

    习题

    一、填空题

    1. 如果想要停止 bindService() 方法启动的服务,需要调用(onUnbind() )方法。
    2. Android 系统的服务的通信方式分为(本地通信)和(远程通信)。
    3. 远程服务通过(AIDL)实现服务的通信。

    二、判断题

    1. Service 服务是运行在子线程中的。× 是主线程,但耗时操作放到子线程,否则出现程序无响应。
    2. 不管使用哪种方式启动 Service, 它的生命周期都是一样的。× 生命周期的方法都不一样,生命周期也不一样
    3. 使用服务的通信方式进行通信时,必须保证服务是以绑定的方式开启的,否则无法通信。√
    4. 一个组件只能绑定一个服务。× 多个组件可以绑定一个服务,一个组件可以绑定多个服务(据说)
    5. 远程服务和本地服务都运行在同一个进程中。× 远程服务是不同进程之间的数据通信,因此是多进程。

    三、选择题

    1. 如果通过 bindService 方式开启服务,那么服务的生命周期是(C)。 A. onCreate() →onstart() →onBind() →onDestroy() B. onCreate() →onBind() →onDestroy() C. onCreate() →onBind() →onUnBind() →onDestroy() D. onCreate() →onStart() →onBind() →onUnBind() →onDestroy()
    2. 下列关于 Service 服务的描述中,错误的是(D) A. Service 是没有用户可见的界面,不能与用户交互 B. Service 可以通过 Context.startService() 来启动 C. Service 可以通过 Context.bindService() 来启动 D. Service 无须在清单文件中进行配置
    3. 下列关于 Service 的方法描述,错误的是(D)。 A. onCreate() 表示第一次创建服务时执行的方法 B. 调用 startService() 方法启动服务时执行的方法是 onStartCommand() C. 调用 bindService() 方法启动服务时执行的方法是 onBind() D. 调用 startService() 方法断开服务绑定时执行的方法是 onUnbind()
  • Device Association Service占用CPU 蓝牙导致卡顿 更换网卡后游戏卡顿 Win10/Win11打不开5G热点 蓝牙设备删除失败

    最近碰到一系列的问题,在此做个总结。

    首先是Win10/Win11打不开5GHz频段热点。显示 所选频段不可用。这是政策原因,导致Intel网卡不能打开热点,不清楚是驱动层面还是系统层面的设置。
    解决方法是,让电脑先连接5GHz的热点,例如手机开热点,电脑连接。如果手机恰好开到了高信道,那电脑就可以开5G热点。
    这样太麻烦了,我选择抛弃Intel网卡。
    对了,网上会说部分Intel型号网卡受限,这个,本人不可能测试全部的网卡,反正在自己的电脑上测试了AX210,这是现在最新款Intel通用网卡(其它可能有主板限制),仍然不能打开。

    一劳永逸的办法半糖原创。
    然后我换了个AMD/联发科网卡,MT7922/RZ616,花了99大洋。
    这个网卡在本地机器上是不免驱的,需要下载专门的驱动(不确定大家的机器是否免驱,因为官方淘宝店说是免驱的)。
    驱动下载
    这是驱动下载的网址。
    实测这个网卡能开5GHz热点。据说中国只有Intel受限。半糖原创。

    然后发现新的问题,系统的Device Association Service占用很大,我这边游戏本,基本没开什么东西,居然让这个东西拉满了CPU,甚至半个小时不带停的。我甚至怀疑是中毒了。
    不是中毒,搜索一番后发现,这个进程占用大,可能是系统在自动清理缓存,也可能是其它问题。
    它的作用是和新的有线/无线设备连接。半糖原创。
    然后发现是蓝牙问题,但除了关闭蓝牙别无办法。关闭蓝牙以后我的电脑风扇几乎就静音了,打开就呼呼转。
    这边顺便提一句,换网卡,网卡上除了无线网络设备,还有蓝牙设备。所以是WLAN和蓝牙一起换了。
    其实拉满了CPU也就略卡,还能忍忍。
    最后发现问题,因为我的蓝牙设备删除失败。
    删除失败的解决方法:进入设备管理器,手动卸载相关的蓝牙设备。
    而解决卡顿和Device Association Service服务占用的方法也是这样。半糖原创。

    在设备管理器里面点菜单栏的查看显示隐藏的设备,然后把蓝牙里面半透明的设备全部卸载,就可以解决卡顿的问题。其原因是旧的驱动程序干扰了新的驱动程序。导致功能异常。


    最后我还是推荐换RZ616网卡的,体验上还行,对比Intel,似乎感知不强,没有所谓的AMD不如Intel的感知。也支持5GHz或者6GHz。半糖原创。

    这边一次把所有的问题都写一起了,没有分开写文章,是因为,这几个问题刚好串联在一起了。因此一次性写完。半糖原创。