杰言杰语

以中有足乐者,不知口体之奉不若人也.


  • 首页

  • 分类

  • 关于

  • 归档

  • 标签

  • 搜索

第5章 栈

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

栈(Stack)的用途广泛,通常用于存储局部变量/传递函数参数/保存函数返回地址等.

1. 栈

栈内存在进程中的作用如下:

  • 暂时保存函数内的局部变量.
  • 调用函数时传递参数.
  • 保存函数返回后的地址.

1.1 栈的特征

栈
一个进程中,栈顶指针(ESP)初始状态指向栈底端.执行PUSH命令将数据压入栈时,栈顶指针就会上移到栈顶端.执行POP命令从栈中弹出数据时,若栈为空,则栈顶指针重新移动到栈底端.向栈中压数据就像一层层砌砖,每向上砌一层,砖墙就增高一点儿.

1.2 栈操作示例

栈顶指针的值为19FF84,观察右下角的栈长裤,可以看到ESP指向的地址及其值.
栈的初始状态

在代码窗口中按F7键(Step Into),执行401000地址处的PUSH 100命令.

可以看到ESP值变为19FF80,比原来减少了4个字节,并且当前栈顶指针指向19FF80,该地址中保存着100这个值.换言之,执行PUSH命令时,数值100被压入栈,ESP随之向上移动,即ESP的值减少了4个字节.再次按F7(Step Into),执行401005地址处的POP EAX命令.
PUSH命令

执行完POP EAX命令后,ESP值又增加了4个字节,变为19FF84,栈又变为初始状态.换言之,从栈中弹出数据后,ESP随之向下移动.向栈压入数据从栈中弹出数据时,栈顶指针的变化情形如下:

  • 向栈压入数据时,栈顶指针减小,向低地址移动;从栈中弹出数据时,栈顶指针增高,向高地址移动.

第4章 IA-32寄存器基本讲解

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 什么是CPU寄存器

寄存器(Register)是CPU内部用来存放数据的一些小型存储区域,因为其集成在CPU内部,拥有非常高的读写速度.

2. IA-32 寄存器

IA-32 是英特尔推出的32位元架构,属于复制的指令集构架.

2.1 基本程序运行寄存器

基本程序运行寄存器的组织结构,它由4类寄存器组成.

  • 通用寄存器 ( General Purpose Resgisters, 32位,8个 )
  • 段寄存器 ( Segment Registers, 16位,6个 )
  • 程序状态与控制寄存器 ( Program Status and Control Registers , 32位, 1个 )
  • 指令指针寄存器 ( Instruction Pointer , 32位, 1个 )

基本程序运行寄存器

2.1.1 通用寄存器

通用寄存器是一种通用型的寄存器,用于传送和暂存数据,也可以参与算术逻辑运算,并保持运算结果.IA-32中每个通用寄存器大小都是32位(4个字节),主要用来保存常量与地址等.
通用寄存器

各寄存器的名称如下:

  • EAX : ( 针对操作数和结构数据的 ) 累加器
  • EBX : ( DS段中的数据指针 ) 基址寄存器
  • ECX : ( 字符串和循环操作的 ) 计算器
  • EDX : ( I/O指针 ) 数据寄存器

以上4个寄存器主要用在算术运算( ADD,SUB,XOR,OR)等,常常用来保存变量与变量的值.某些汇编 指令( MUL,DIV,LODS等) 直接用来操作特定寄存器,执行这些命令后,仅改变特定寄存器中的值.

此外,ECX和EAX也可用特殊用途,循环命令( LOOP )中,ECX用来循环计数 ( loop count ),每执行一次循环,ECX都会减1.EAX一般用在函数返回值中,所有Win 32 API函数都会先把返回值保存在EAX再返回.

  • EBP : ( SS段中栈内数据指针 )扩展基址指针寄存器
  • ESI : (字符串操作源指针) 源变址寄存器
  • EDI : (字符串操作目标指针) 目的变址寄存器
  • ESP : (SS段中栈指针) 栈指针寄存器

以上4个寄存器主要用来作保存内存地址的指针.

ESP指示栈区域的栈顶地址,某些指令(PUSH,POP,CALL,RET)可以直接用来操作ESP.

EBP表示栈区域的基地址,函数被调用时保存ESP的值,函数返回时再把值返回ESP,保证栈不会崩溃.ESI和EDI与特定指令(LODS,STOS,REP,MOVS等)一起使用,主要用来内存复制.

2.1.2 段寄存器

段(Segment),IA-32的保护模式中,段是一种内存保护技术,它把内存划分为多个区段,并为每个区段赋予起始地址,范围,访问权限等,以保护内存.此外,它还同分页技术(Paging)一起用于将虚拟内存变更为实际物理内存.段内存记录在SDT(Segment Descriptor Table,段描述符表)中,而段寄存器就持有这些SDT的索引(index).
分段内存模型

它描述了保护模式下的内存分段模型.段寄存器总共由6中寄存器组成,分别为CS,SS,DS,ES,FS,GS,每个寄存器的大小为16位,即2个字节.另外,每个段寄存器指向的段描述符(Segment Descriptor)与虚拟内存结合,形成一个线性地址(Linear Address),借助分页技术,线性地址最终被转换为实际的物理地址(Physical Address).

各段寄存器的名称如下:

  • CS : Code Segment, 代码段寄存器
  • SS : Stack Segment, 栈段寄存器
  • DS : Data Segment, 数据段寄存器
  • ES : Extra ( Data ) Segment,附加(数据)段寄存器
  • FS : Data Segment, 数据段寄存器
  • GS : Data Segment, 数据段寄存器

CS寄存器用于存放应用程序代码所在段的段基址, SS寄存器用于存放栈段的段基址,DS寄存器用于存放数据段的段基址.ES,FS,GS寄存器用来存放程序使用的附加数据段的段基址.

2.1.3 EFLAGS:Flag Register,标志寄存器

3个与程序调试相关的标志,分别为ZF (Zero Flag,零标志),OF(Overflow Flag,溢出标志),CF(Carry Flag,进位标志).
EFLAGS寄存器

  • ZF
    若运算结果为0,则其值为1(true),否则其值为0(False).
  • OF
    有符号整数(signed integer)溢出时,OF值被置为1.此外,MSB( Most Sginificant Bit,最高有效位)改变时,其值也被设为1.
  • CF
    无符号整数(unsigned integer )溢出时,其值也被置为1.

2.1.4 指令指针寄存器

  • EIP : Instruction Pointer,指令指针寄存器
    指令指针寄存器保存着CPU要执行的指令地址,其大小为32位(4个字节).当程序运行时,CPU会读取EIP中的一条指令的地址,传送指令到指令缓冲区,EIP寄存器的值会自动增加,增加的大小即是读取指令的字节大小.

第3章 小端序标记法

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

在计算机领域中,字节序(Byte Ordering)是多字节数据在计算机内存中存储或网络传输时各字节的存储顺序.分为小端序(Little endian)和大端序(Big endian).

1. 字节序

1
2
3
4
BYTE b = 0x12;
WORD w = 0x1234;
DWORD dw = 0x12345678;
char str[ ] = "abcde";

以下为不同数据根据不同字节序保存时的不同:

TYPE Name SIZE 大端序类型 小端序类型
BYTE b 1 [12] [12]
WORD w 2 [12][34] [34] [12]
DWORD dw 4 [12][34][56][78] [78][56][34][12]
char[] str 6 [61][62][63][64][65][00] [61][62][63][64][65][00]

提示:
查看ASCII码表,字母a的ASCII码的十六进制表示为0x61,字母e的ASCII码的十六进程表示为0x65. 字符串最好都是以NULL结尾.
因为数据类型为字节型(BYTE),其长度都为1个字节,所以保存在大端序类型或者小端序类型,字节顺序都一致!如果数据长度超过2个字节,则会在大端序和小端序上面存储的数据不同.如果是字符串被保存在数组str中,字符数组在内存中连续,此时无论在大端序还是小端序,存储顺序都相同!

1.1 大端序和小端序

大端序常用于大型UNIX服务器的RISC系列的CPU中,而小端序采用逆向式存
储数据,在Intel x86 CPU常用到.

1.2 在OllyDbg中查看小端序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "windows.h"
BYTE b = 0x12;
WORD w = 0x1234;
DWORD dw = 0x12345678;
char str[] = "abcde";
int main(int argc, char *argv[])
{
BYTE lb = b;
WORD lw = w;
DWORD ldw = dw;
char *lstr = str;
return 0;
}

用OllyDbg调试,用Go to命令(Ctrl+G)跳转到401000地址
main()函数

main()函数地址为401000,全局变量b、w、dw、str地址分别为40AC40、40AC44、40AC48、40AC4C.可以来到数据窗口用Go to命令跳转到40AC40处.
全局变量的内存区域

可以看到,变量w与dw中的数据都采用了小端序存储.

第2章 逆向分析Hello World! 程序

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. Hello World!程序

1
2
3
4
5
6
7
8
9
10
11
12
#include "windows.h"
#include "tchar.h"
int _tmain(int argc, TCHAR *argv[])
{
MessageBox(NULL,
L"Hello World!",
L"www.reversecore.com",
MB_OK);
return 0;
}

不论用那种语言编写的程序,编译后都会生成二进制可执行文件.

2. 调试HelloWorld.exe程序

OllyDbg基本界面

提示

  • 分析时可采用 OllyDbg 的 Win32 专业调试工具.
  • 达到一定水平建议使用 Hex -Rays 公司的 IDA Pro.

    代码窗口 : 默认用于显示反汇编代码,适用于显示各种注释,标签,分析代码时显示循环,跳转位置等信息.
    寄存器窗口 : 实时显示CPU寄存器的值,可用于修改特定的寄存器.
    栈窗口 : 实时显示ESP寄存器指向的进程栈内存,并允许修改.

2.1 入口点

调试器停止的地点即为HelloWorld.exe执行的起始地址(4011A0),它是一段EP(EntryPoint入口点)代码.
地址-指令-反汇编代码-注释]

EP(EntryPoint,入口点)
EP是 Window 可执行文件(EXE,DLL,SYS等)的代码入口点,是执行应用程序最先执行的代码的起始位置,它依赖于CPU.

2.2 跟踪 40270C 函数

OllyDbg基本指令(适用于代码窗)

指令 快捷键 含义
Restart Ctrl+F2 重新开始调试
Step Into F7 执行一句OP code(操作码),若遇到命令(CALL),将进入函数代码内部
Step Over F8 执行一句OP code(操作码),若遇到命令(CALL),仅执行函数自身,不跟随进入
Execute till Return(执行到返回) Ctrl+F9 一直在函数代码内部运行,直到遇到RETN命令,跳出函数

在 EP 代码的40010A0地址处使用Step Into(F7)指令,进入40270C函数.
40270C函数

4027A1地址处有一个RETN指令,它用于返回函数调用者的下一条指令,一般是被调用函数的最后一句.在4027A1地址处的RETN指令上执行SETP over(F8)或Execute till Return(Ctrl+F9)命令.程序会跳转到4011A5地址处.
4011A5函数

2.3 跟踪 40104F 跳转语句

执行4011A5 地址处的跳转命令JMP 0040104F,跳转到40104F.
40104F处的部分代码

2.4 查找main()函数

在40104F地址开始,每执行一次Step Into(F7)命令就下移1行代码,移动到401056地址处的CALL 402524函数调用命令时,执行Step Into(F7)命令,进入402524函数.
调用40104F

402524函数

我们很难把402524函数称为Main()函数,因为在它的代码中没发现调用MessageBox()API的代码.执行Execute till Return(Ctrl+F9)指令,跳出402524函数,返回到40105B地址处.

同样,在40104F地址处执行Step Into(F7)命令调试,遇到函数调用就进入函数查看代码,确认是否是main()函数.若不是main()函数,则使用(Ctrl+F9)命令跳出相关函数,继续以相同方式调试.
调用API

4010E4地址处的CALL Kernel32.GetCommandLineW指令是调用Win32 API的代码.
main()函数

401000函数内部出现了调用MessageBoxW()API的代码,该API的函数参数为与源码内容一致,由此可以判断401000函数就是我们需要找的main()函数.

3. 进一步熟悉调试器

3.1 调试器指令

指令 快捷键 含义
Go to Ctrl+G 移动到指定地址,用来查看代码或内存,运行时不可用
Execute till Cursor F4 执行到光标位置,即直接转到要调试的地址
Comment ; 添加注释
User-defined comment 鼠标右键菜单Search for User-defined comment
Set/Reset BreakPoint F2 设置或取消断点(BP)
Run F9 运行(若设置了断点,则执行到断点处)
Show the previous Cursor - 显示上一个光标的位置
Preview CALL/JMP address Enter 若光标处有CALL/JMP等指令,则跟踪并显示相关地址(运行时不可用,简单查看函数内容时非常好用)

3.2 “大本营”

当每次重新运行调试器时,调试器会返回到EP处,并从此处新开始调试.

3.3 设置”大本营”的四种方法

  • GoTo命令

设置地址40104F.执行Go to(Ctrl+G)命令,在文本框中输入”40104F”
Go to(Ctrl+G)命令

执行Execute till cursor(F4)命令,让调试运行到该处,然后从40104F处开始调试代码.

  • 设置断点

调试代码,还可以设置BP断点,让调试直接流转到”大本营”.设置断点后,程序运行到断点处将会暂停.

在OllyDbg菜单栏中依次选择View-Breakpoints(快捷键ALT+B),打开Breakpoints对话框,列出代码中设置的断点.
断点

  • 注释

键盘上的”;”键可以在指定位置添加注释,还可以通过命令找到它.

在鼠标右键中依次选择Search for User defined comment,这样就能看到用户输入的所有注释.
用户的注释

红色显示的文字即是光标当前所在的位置,当注释位置与光标位置重合时,将仅以红色方式显示.

  • 标签

我们能通过标签功能在指定的地址添加特定名称.移动光标到40104F地址处,按”:”键输入标签.
标签

4. 快速查找指定代码的四种方法

调试代码时,main()函数并不直接位于可执行代码的EP位置上,出现的是开发工具生成的启动函数.

4.1 代码执行法

按F8键逐行执行命令,在某个时刻弹出信息对话框,显示”hello world!”信息.按Ctrl+F2再次载入待调试的可执行文件并重新调试,不断按F8键,某个时刻一定会弹出信息对话框.弹出信息对话框调用时的函数即为main()函数.

提示
Win32 应用程序中,API函数的参数是通过栈传递的.VC++中默认字符串是使用Unicode码表示,并且,处理字符串的API函数也全部变更为Unicode系列函数.

4.2 字符串检索法

鼠标右键菜单 - Search for - All referenced text strings

OllDbg初次载入待调试程序有预分析过程,此过程中会查看进程内存,程序中引用的字符串和调用的API都会被摘录出来,调整到另外一个列表中.
All referenced text strings

地址401007处有一个PUSH 004092A0命令,该命令引用004092A0处即是字符串”helloworld!”双击,光标定位到main()函数中调用MessageBoxW()函数的代码处,在Dump窗口中使用Go to(Ctrl+G)命令,进一步查看内存4092A0地址处的字符串.
4092A0地址处的字符串

提示:
VC++中,static字符串会被默认保存为Unicode码格式.需要注意的是4092A0地址,它与我们之前代码区地址(401XXX)不同,在helloworld.exe进程中,409XXX地址空间被用来保存程序中的数据, 代码与数据所在的区域是彼此分开的.

4.3 API索引法(1) : 在调用代码中设置断点

鼠标右键菜单 - Search for - All intermodular calls
应用程序想向显示屏输出内容时,需要在程序内部调用Win 32 API.helloworld.exe,它在运行时会弹出一个信息窗口,由此可以判断该程序调用了user32.MessageBox()API.
Intermodular calls

可以查看调用MessageBoxW()的代码,该函数位于40100E地址处,它是user.MessageBoxW()API.

4.4 API索引法(2) : 在API代码中设置断点

鼠标右键菜单 Search for - Name in all calls
如果使用了压缩器/保护器工具对可执行文件进行压缩或保护,因为文件结构的改变.此时用该方法显得十分困难.

DLL代码库会被加载到进程内存中,然后我们可以直接向DLL代码库添加断点,API是操作系统对用户应用程序提供一系列函数,它们位于C:\windows\system32文件夹中的*.dll文件(如kernel32.dll,user32.dll,gdi32.dll,advapi32.dll,ws2_32.dll等)内部.因为编写的应用程序执行某些操作时,必须使用os提供的API向os提出请求,然后与被调用API对用的系统DLL文件就会被加载到应用程序的进程内存.

菜单栏->View-Memory菜单(快捷键ALT+M),打开内存映射窗口.
内存映射窗口

然后通过查找命令将光标定位到MessageBoxW上.

5. 使用”打补丁”方式修改”hello world!”字符串

5.1 “打补丁”

使调试流运行到main函数的起始地址处(401000),在401000地址处按F2设置断点,再按F9执行程序.

5.2 修改字符串的两种方法

直接修改字符缓冲区(buffer).
在其他内存区域生成新字符串并传递给消息函数.

  • 直接修改字符串缓冲区
    在Dump窗口中Ctrl+G快捷键Go to 命令,在窗口中输入4092A0字符串缓冲区.使用鼠标选中4092A0地址的内容,按Ctrl+E快捷键打开编辑窗口 .
    helloworld字符串

在Dump窗口中,选中更改后的字符串,通过鼠标右键,在弹出的菜单中选中Copy to executable file 菜单,然后选中保存即可.

  • 在其他内存区域新建字符串并传递给消息函数
    main()函数

401007地址处有一条PUSH 00409A0命令,它把409A0地址处的”Hello world!”字符串以参数形式传递给MessageBoxW()函数.

我们修改字符串地址为4092A0,下面用Dump窗口查看该部分,相应内存区域由NULL填充(NULL padding)结束.
内存中NULL填充区域

这就是程序中未使用NULL填充区域.

提示
应用程序被加载到内存时有一个最小的内存分配大小,一般为1000.即使程序运行时只占用了100内存,它被加载到内存时依然会分到1000左右的内存,这些内存一部分被程序占用,其余部分分为空余区域,全部被填充为NULL.
从空白区域写入新的字符串

因为新建了缓冲区,接下来把新的缓冲区地址(409F50)作为参数传递给MessageBoxW()函数.为此,我们在代码窗口使用汇编命令修改代码.用空格键打开Assemble窗口.
原字符串地址

在打开的Assemble窗口中输入”PUSH 409F50”指令,那么将把409F50作为新字符串的首地址!
新字符串地址

提示
可执行文件被加载到内存并以进程形式运行时,文件并非原封不动地载入内存,而要遵守一定规则进行.在这一过程中,通常进程的内存地址是存在的,但是相应的文件偏移(offset)并不存在.

6. 小结

  • OllDbg常用命令
指令 快捷键 说明
Step Into F7 执行一条OP code(操作码),遇到CALL命令时,进入函数代码内部.
Step Over F8 执行一条OP code(操作码),遇到CALL命令时,不进入函数代码内部,仅执行函数本身.
Restart Ctrl+F2 再次从头调试(终止调试中的过程,重新载入调试程序)
Go to Ctrl+G 跳转到指定地址(查看代码时使用,非运行时命令)
Run F9 运行(运动断点会暂停)
Execute till return Ctrl+F9 执行函数代码内的命令,直到遇到RETN命令,用于跳出函数整体.
Execute till cursor F4 执行到光标所在的位置(直接调到要指定的位置)
Comment ; 添加注释
User-defined comment 鼠标右键菜单Search for - User-defined comment 查看用户的注释目录
Label : 添加标签
User -defined label 鼠标右键菜单Search for - Usr-defined label 查看用户输入的标签目录
Breakpoint F2 设置或取消断点
All referenced text strings 鼠标右键菜单Search for -All referenced text strings 查看代码中引用的字符串
All intermodular calls 鼠标右键菜单Search for -All intermodular calls 查看代码中调用的所有API函数
Name is all modules 鼠标右键菜单Search for -Name is all modules 查看所有的API函数
Edit data Ctrl+E 编辑数据
Assemble Space 编写汇编代码
Copy to executable file 鼠标右键菜单Copy to executable file 创建文本副本(修改的项目被保留)
  • Assemble(汇编语言)基础指令
指令 说明
CALL XXXX 调用XXXX地址处的函数
JMP XXXX 跳转到XXXX地址处
PUSH XXXX 保存XXXX到栈
RETN 跳转到栈中保持的地址
  • 修改(Path)进程数据与代码的方法
术语 说明
VA(Virtual Address) 进程的虚拟地址
OP code(Operation code) CPU指令(字节码byte code)
PE(Portable Executable) Window可执行文件(EXE,DLL,SYS等)

疑问

  • 快捷键F4与F9最大的不同在于F4可以看做为断点+运行的组合.
  • 启动函数(Stub code)不是用户编写的代码,在调试程序中,我们不需要仔细分析启动函数.

第1章 关于逆向工程

发表于 2017-03-28 | 分类于 ReverseEngineerin | 阅读次数

1. 逆向工程

逆向工程(Reverse Engineering,简称PE).

2. 代码逆向工程

代码逆向工程(简称RCE)

2.1 逆向分析法

  • 1. 静态分析法
    静态分析法是在不执行代码文件的情况下,对代码进行静态分析.靠观察代码文件 的外部特征,获取文件的类型(EXE,DLI,DOC,ZIP等),大小,PE头信息,Import/Export API, 内部字符串,是否运行时解压缩,注册信息,调试信息,数字证书等多种信息.

  • 2. 动态分析法
    动态分析法是在程序文件执行过程中对代码进行动态的分析,它通过调试来分析代码流,获得内存的状态等.

一般先用静态分析法收集代码信息,然后通过收集到的信息推测程序的结构和行为机制.

2.2 源代码,十六进制代码,汇编代码

源代码(Source Code)->十六进制代码(Hex Code)->汇编代码(Assembly Code),即把待分析代码转换为汇编代码后才分析.

2.3 “打补丁”与”破解”

打补丁(Path)与破解(Crack)的区别在于道德问题.

2.4 代码逆向准备

目标&激情&谷歌

2.5 学习逆向分析技术的禁忌

勿贪心勿急躁

Lesson 18 He often does this! 他经常干这种事

发表于 2017-03-28 | 分类于 English | 阅读次数

1. 单词讲解

  • pub n.小酒馆

    • public house
    • They went to the pub for a drink.
  • landlord n.店主

    • The landlord asked Ted to pay the rent(租金).
  • bill n.账单

    • telephone bill
    • gas bill
    • pay one’s bill
    • Bill, please
    • a ten-dollar bill 一张十美元纸币

2. 语法讲解

  • After I had had lunch at a village pub…

    • had done 过去完成时 (have为助动词,无词义)
    • have lunch (have为实义动词,有”吃,喝”)
    • have coffee
    • Dave has a brother
  • …I looked for(寻找) my bag.

    • Nick looked for his dog everywhere.
    • He didn’t find it.
  • I had left it on a chair beside the door…

    • leave sth. + 介词短语
      He left his bag on the train.
    • beside prep. == next to
      She is sitting beside the woindow.
    • besides prep. == in addition to
      Besides Ellen, five of us have watched the movie.
  • As I was looking for it…

    • as conj. 当…的时候
    • As I was writing a report(报告), George came up to me.
  • I haven’t got my…

    • have got == have
      Jack has got a dog / Jack has a dog.
      否定句 :
      Jack hasn’t got a dog. / Jack hasn’t a dog.
      疑问句 :
      Has Jack got a dog? / Has Jack a dog?
  • … immediately went out.

    • immediately == at once (立即)
  • He often does…

    • he == the dog

3. 知识扩展

  • have
    • 助动词
      He has never been abroad before. 他以前从来没有出过国.
    • 实义动词
      He has two sisters.(有)
      Jim had breakfast at 7 this morning.(吃)
      Would you like to have a cup of coffee?(喝)

11.安卓基础之多媒体

发表于 2017-03-28 | 分类于 Android | 阅读次数

Android 多媒体基础知识

概述

计算机发展到今天,不仅表示数值和符号,已具有了对文本,图形,图像动画及音频视频等多种信息的综合处理能力,我们称之为多媒体技术.

图像与图形

在计算机中,
图像是采用位图形式来表示的;
图形是采用矢量图方式来表示的;

位图图像

称为光栅图或点阵图像,是由许多像小方块一样的”像素”(pixels)组成的图形.
位图图像

  • 图像 : 是由像素点阵组成的画面.
  • 位图 : 由许多点组成的点阵图.构成位图的点称为像素.
  • 色彩深度 : 位图所能达到的最大颜色数,称为色彩深度.(对于黑白两种颜色的图像来说一个像素点可用一个二进制位来表示,如0表示黑色,1表示白色.)

图片的操作

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 准备画纸:(大小和材质需要参考原图)
Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
srcBitmap.getHeight(), srcBitmap.getConfig());
// 2. 准备画板,将画纸放到画板上
Canvas canvas = new Canvas(copyBitmap);
// 3. 准备画笔
Paint paint = new Paint();
// 4. 按照一定的规则
Matrix matrix = new Matrix();// 1:1照着画
// 5. 将原图像按照规则画到画板上
canvas.drawBitmap(srcBitmap, matrix, paint);
  • 图片的缩放

    1
    matrix.setScale(0.6f, 0.8f);
  • 图片的平移

    1
    matrix.setTranslate(50, 50);

位图图像的缺陷

  • 位图放大和缩小都会引起像素的增加或减少,这样会使得原由的图像的线条和形状变得参差不齐,与原图像相比出现失真;出现”锯齿形”.

位图

位图常见的文件格式

  • .bmp
  • .jpg
  • .gif
  • .png

矢量图像

矢量图形是通过计算机将一串线条和图形转换为一系列指令,在计算机中只存储这些指令,而不是像素.矢量图像看起来没有位图图像真实,但矢量图形的存储空间比位图图像要小得多,而且矢量图像通过拉伸,移动,放大等操作,图像不会产生实真.

矢量图像

颜色矩阵

1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0

颜色矩阵的代码表述

1
2
3
4
5
6
7
8
9
10
11
12
13
New Red Value = 1*128 + 0*128 + 0*128 + 0*0 + 0
New Green Value = 0*128 + 1*128 + 0*128 + 0*0 + 0
New Blue Value = 0*128 + 0*128 + 1*128 + 0*0 + 0
New Alpha Value = 0*128 + 0*128 + 0*128 + 1*0 + 0
ColorMatrix cm = new ColorMatrix();
cm.set(new float[] {
2, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
});
paint.setColorFilter(new ColorMatrixColorFilter(cm));

传感器

传感器(英文名称 : sensor)是一种检测装置,能感受到被测量的信息,并能将感受到的信息,按一定规律变换成为电信号或其他所需形式的信息输出,以满足信息的传输,处理,存储,显示,记录和控制等要求.

传感器的特点包括 : 微型化,数字化,智能化,多功能化,系统化,网络化.它是实现自动检测和自动控制的首要环节.传感器的存在和发展,让物体有了触觉,味觉和嗅觉等感官,让物体慢慢变得活了起来.通常根据其基本感知功能分为热敏元件,光敏元件,气敏元件,力敏元件,磁敏元件,湿敏元件,声敏元件,放射线敏感元件,色敏元件和味敏元件等十大类.

传感器

传感器类型 描述
SENSOR_TYPE_ACCELEROMETER 加速度
SENSOR_TYPE_MAGNETIC_FIELD 磁力
SENSOR_TYPE_ORIENTATION 方向
SENSOR_TYPE_GYROSCOPE 陀螺仪
SENSOR_TYPE_LIGHT 光线感应
SENSOR_TYPE_PRESSURE 压力
SENSOR_TYPE_TEMPERATURE 温度
SENSOR_TYPE_PROXIMITY 接近
SENSOR_TYPE_GRAVITY 重力
SENSOR_TYPE_LINEAR_ACCELERATION 线性加速度

练习案例

  • 加载大图片到内存

获取到读写内存权限

1
2
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>

代码实现部分

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv =(ImageView) findViewById(R.id.iv);
}
public void loadBitmap(View v){
String path = "mnt/sdcard/1.jpg";
//通过手机的屏幕的宽高和图片的宽高来计算采样率
//屏幕宽高
DisplayMetrics metrics = getResources().getDisplayMetrics();
int screenWidth = metrics.widthPixels;//获取屏幕宽度
int screentHeight = metrics.heightPixels;//获取屏幕高度
//图片的宽高
try{
ExifInterface exif = new ExifInterface(path);
int picWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH,0);
int picHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH,0);
//用图片的宽度/屏幕的宽度
int widthSample = (int)(picWidth*1f/screenWidth+0.5f);//四舍五入
int heightSample = (int)(picHeight*1f/screentHeight+0.5f);//四舍五入
int sample = (int) (Math.sqrt(widthSample * widthSample
+ heightSample * heightSample) + 0.5f);
//加载图片
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inSampleSize = sample;//采样率
Bitmap bitmap = BitmapFactory.decodeFile(path,opts);
iv.setImageBitmap(bitmap);
}catch (Exception e){
e.printStackTrace();
}
}
}
  • 图片的缩放
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void opts(View v){
String path = "mnt/sdcard/1.jpg";
//显示图片
Bitmap srcBitmap = BitmapFactory.decodeFile(path);
ivSrc.setImageBitmap(srcBitmap);
//准备画纸(大小和材质参照原材料)
Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),srcBitmap.getHeight(),srcBitmap.getConfig());
//准备画板,将画纸放到画板上
Canvas canvas = new Canvas(copyBitmap);
//准备画笔
Paint paint = new Paint();
//按照一定规则
Matrix matrix = new Matrix();//1:1画
//按照比例缩放图片
matrix.setScale(24.6f,22.8f);
//将原图像按照规则画到画板上
canvas.drawBitmap(srcBitmap,matrix,paint);
//画板有数据了
ivDest.setImageBitmap(copyBitmap);
}
  • 图片的位移
1
2
// 位移操作:dx:x方向的增量 dy:y方向的增量
matrix.setTranslate(50, 50);
  • 图片的旋转
1
2
matrix.setRotate(45, srcBitmap.getWidth() / 2f,
srcBitmap.getHeight() / 2f);
  • 对图片颜色的处理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
public class MainActivity extends Activity implements OnSeekBarChangeListener {
private ImageView iv;
private SeekBar skbRed;
private SeekBar skbGreen;
private SeekBar skbBlue;
private float redPercent = 1;
private float greenPercent = 1;
private float bluePercent = 1;
private Bitmap srcBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
skbRed = (SeekBar) findViewById(R.id.skb_red);
skbGreen = (SeekBar) findViewById(R.id.skb_green);
skbBlue = (SeekBar) findViewById(R.id.skb_blue);
skbRed.setOnSeekBarChangeListener(this);
skbGreen.setOnSeekBarChangeListener(this);
skbBlue.setOnSeekBarChangeListener(this);
// 加载图片显示
String path = "mnt/sdcard/img_small_1.jpg";
srcBitmap = BitmapFactory.decodeFile(path);
iv.setImageBitmap(srcBitmap);
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// seekbar进度改变时的回调
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 开始拖动seekbar的回调
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 停止拖动seekbar的回调
// 0-100;//0-2
int progress = seekBar.getProgress();
float percent = progress / 50f;// (0-2f)
if (seekBar == skbRed) {
this.redPercent = percent;
} else if (seekBar == skbGreen) {
this.greenPercent = percent;
} else if (seekBar == skbBlue) {
this.bluePercent = percent;
}
// 去改变图片的颜色
// 1.去获得图片的拷贝
Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(),
srcBitmap.getHeight(), srcBitmap.getConfig());
Canvas canvas = new Canvas(copyBitmap);
Matrix matrix = new Matrix();
// 2.去处理图片的中的颜色数据
Paint paint = new Paint();
// 设置画笔的颜色过滤
// vector:0-2 0:没有 2:最多
float[] cm = new float[] { 1 * redPercent, 0, 0, 0, 0, // red vector
0, 1 * greenPercent, 0, 0, 0, // green vector
0, 0, 1 * bluePercent, 0, 0, // blue vector
0, 0, 0, 1, 0 // alpha vector
};
paint.setColorFilter(new ColorMatrixColorFilter(new ColorMatrix(cm)));
canvas.drawBitmap(srcBitmap, matrix, paint);
// 3.将处理的结果展示
iv.setImageBitmap(copyBitmap);
}
}
  • canvas相关api
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
public class MainActivity extends Activity {
private ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv = (ImageView) findViewById(R.id.iv);
}
public void line(View view) {
// 1.画线
// 准备画纸
Bitmap bitmap = Bitmap.createBitmap(320, 320, Config.ARGB_8888);
// 准备画布
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
canvas.drawLine(10, 10, 200, 200, paint);
iv.setImageBitmap(bitmap);
}
public void rect(View view) {
// 1.画矩形
// 准备画纸
Bitmap bitmap = Bitmap.createBitmap(320, 320, Config.ARGB_8888);
// 准备画布
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
// 设置画笔的颜色
paint.setColor(Color.RED);
// 设置画笔的样式
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(10);// 设置粗细
canvas.drawRect(30, 30, 200, 200, paint);
iv.setImageBitmap(bitmap);
}
public void circle(View view) {
// 1.画圆形
// 准备画纸
Bitmap bitmap = Bitmap.createBitmap(320, 320, Config.ARGB_8888);
// 准备画布
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
// 设置画笔的颜色
paint.setColor(Color.RED);
paint.setAntiAlias(true);// 设置抗锯齿
float cx = 160;// 圆心的坐标X
float cy = 160;// 圆心的坐标Y
float radius = 100;// 半径
canvas.drawCircle(cx, cy, radius, paint);
iv.setImageBitmap(bitmap);
}
public void arc(View view) {
// 1.扇形
// 准备画纸
Bitmap bitmap = Bitmap.createBitmap(320, 320, Config.ARGB_8888);
// 准备画布
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);// 设置抗锯齿
// 1:矩形
RectF oval = new RectF(20, 20, 200, 200);
float startAngle = 0;// 起始角度
float sweepAngle = 120;// 扫过的角度
boolean useCenter = false;// 是否画中心部分
canvas.drawArc(oval, startAngle, sweepAngle, useCenter, paint);
iv.setImageBitmap(bitmap);
}
public void trangle(View view) {
// 1.多边形
// 准备画纸
Bitmap bitmap = Bitmap.createBitmap(320, 320, Config.ARGB_8888);
// 准备画布
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
paint.setColor(Color.RED);
paint.setAntiAlias(true);// 设置抗锯齿
float x1 = 160;
float y1 = 20;
float x2 = 140;
float y2 = 200;
float x3 = 180;
float y3 = 200;
Path path = new Path();
path.moveTo(x1, y1);// 将画笔移动到点1
path.lineTo(x2, y2);// 连线点2
path.arcTo(new RectF(140, 180, 180, 220), 0, 180);
path.lineTo(x3, y3);// 连线点3
path.lineTo(x1, y1);// 连线点1
path.close();
canvas.drawPath(path, paint);
iv.setImageBitmap(bitmap);
}
}
  • 画画板

Menu的写法 :

1
2
3
4
5
6
7
8
9
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:id="@+id/action_save"
android:orderInCategory="100"
android:showAsAction="never"
android:title="保存图片"/>
</menu>

布局写法 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<View
android:id="@+id/color_red"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#ff0000" />
<View
android:id="@+id/color_green"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#00ff00" />
<View
android:id="@+id/color_blue"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#0000ff" />
<View
android:id="@+id/color_yellow"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#ffff00" />
<View
android:id="@+id/color_pink"
android:layout_width="35dp"
android:layout_height="35dp"
android:background="#ff99ff" />
</LinearLayout>
<!-- 画笔的粗细 -->
<SeekBar
android:id="@+id/skb_stroke"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/iv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

Java代码的实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
public class MainActivity extends Activity implements OnClickListener,
OnSeekBarChangeListener, OnTouchListener {
private static final String TAG = "MainActivity";
private View redView;
private View greenView;
private View blueView;
private View yellowView;
private View pinkView;
private SeekBar skbStroke;
private ImageView iv;
private Bitmap bitmap;
private Canvas canvas;
private Paint paint;
private float startX;
private float startY;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
redView = findViewById(R.id.color_red);
greenView = findViewById(R.id.color_green);
blueView = findViewById(R.id.color_blue);
yellowView = findViewById(R.id.color_yellow);
pinkView = findViewById(R.id.color_pink);
skbStroke = (SeekBar) findViewById(R.id.skb_stroke);
iv = (ImageView) findViewById(R.id.iv);
redView.setOnClickListener(this);
greenView.setOnClickListener(this);
blueView.setOnClickListener(this);
yellowView.setOnClickListener(this);
pinkView.setOnClickListener(this);
skbStroke.setOnSeekBarChangeListener(this);
iv.setOnTouchListener(this);
// 准备画纸画布画笔
bitmap = Bitmap.createBitmap(320, 320, Config.ARGB_8888);
canvas = new Canvas(bitmap);
paint = new Paint();
canvas.drawColor(Color.WHITE);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
// 更改画笔的颜色
case R.id.color_red:
paint.setColor(Color.RED);
break;
case R.id.color_green:
paint.setColor(Color.GREEN);
break;
case R.id.color_blue:
paint.setColor(Color.BLUE);
break;
case R.id.color_yellow:
paint.setColor(Color.YELLOW);
break;
case R.id.color_pink:
paint.setColor(0xffff99ff);
break;
default:
break;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// TODO Auto-generated method stub
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 改变画笔的粗细
int progress = seekBar.getProgress();// 0-100(1sp--->10sp)
// paint.setStrokeWidth(progress);
paint.setStrokeWidth(10 * progress / 100f);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// 触摸Imageview的回调
// 触摸的时候需要绘制图像,并且显示
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:// 1次
// 1. 手指按下
startX = event.getX();// 触摸的x坐标
startY = event.getY();// 触摸的y坐标
break;
case MotionEvent.ACTION_MOVE:// 0-多次
// 2. 手指移动
float stopX = event.getX();
float stopY = event.getY();
// 绘制图像,并且显示到imgeview上
canvas.drawLine(startX, startY, stopX, stopY, paint);
// 画纸上有数据了
iv.setImageBitmap(bitmap);
// 更新起始点
startX = stopX;
startY = stopY;
break;
case MotionEvent.ACTION_UP:// 1次
// 3. 手指抬起
break;
default:
break;
}
// 消费触摸事件
return true;
}
// 对应的菜单按钮
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return super.onCreateOptionsMenu(menu);
}
// 菜单按钮的点击事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int itemId = item.getItemId();
switch (itemId) {
case R.id.action_save:
Log.d(TAG, "点击了保存按钮!");
// 将bitmap存储到本地
File file = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + ".jpg");
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
// 将bitmap压缩到流中
bitmap.compress(CompressFormat.JPEG, 100, fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
fos = null;
}
}
// 模拟 sdcard的挂载
Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED);
intent.setData(Uri.fromFile(Environment
.getExternalStorageDirectory()));
sendBroadcast(intent);
break;
default:
break;
}
return super.onOptionsItemSelected(item);
}
}

  • 播放器的同步和异步

播放网络歌曲权限 :

1
<uses-permission android:name="android.permission.INTERNET"/>

播放器的代码实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
public class MainActivity extends Activity implements OnSeekBarChangeListener {
private EditText etPath;
private SeekBar skbProgress;
private MediaPlayer player;
private boolean tracking = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etPath = (EditText) findViewById(R.id.et_path);
skbProgress = (SeekBar) findViewById(R.id.skb_progress);
skbProgress.setOnSeekBarChangeListener(this);
}
public void play(View view) {
String path = etPath.getText().toString().trim();
if (TextUtils.isEmpty(path)) {
return;
}
// 播放音乐
if (player == null) {
player = new MediaPlayer();
}
// 重置播放器
player.reset();
try {
player.setOnErrorListener(new OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
System.out.println("what : " + what);
return false;
}
});
// 设置播放的资源
player.setDataSource(path);
player.prepare();// 准备播放
player.start();// 开始播放
performProgress();
} catch (Exception e) {
e.printStackTrace();
}
}
private void performProgress() {
skbProgress.setMax(player.getDuration());// 音乐文件的时长
new Thread(new Runnable() {
@Override
public void run() {
while (player != null && player.isPlaying()) {
if (!tracking) {
// 获得当前的进度
int currentPosition = player.getCurrentPosition();
skbProgress.setProgress(currentPosition);//子线程中更新UI
}
}
}
}).start();
}
public void pause(View view) {
// 暂停
if (player != null && player.isPlaying()) {
player.pause();
((Button) view).setText("继续");
} else if (player != null) {
player.start();
performProgress();
((Button) view).setText("暂停");
}
}
public void stop(View view) {
if (player != null) {
player.stop();
player.release();// 释放资源
player = null;
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
// TODO Auto-generated method stub
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
// 开始拖动
tracking = true;
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
// 结束拖动
tracking = false;
// 播放对应的位置
if (player != null) {
player.seekTo(seekBar.getProgress());
}
}
}
  • 声音池
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class MainActivity extends Activity {
private SoundPool pool;
private int soundID;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int maxStreams = 10;// 声音池中同时可以播放声音的数量
int streamType = AudioManager.STREAM_MUSIC;
int srcQuality = 0;
pool = new SoundPool(maxStreams, streamType, srcQuality);
// 加载一个声音文件
soundID = pool.load(this, R.raw.shoot, 1);
}
public void shoot(View view) {
float leftVolume = 1f;
float rightVolume = 1f;
int priority = 0;
int loop = 0;
float rate = 1;// 播放的速率
pool.play(soundID, leftVolume, rightVolume, priority, loop, rate);
}
}
  • 视频播放器

xml界面的简写 :

1
2
3
4
<VideoView
android:id="@+id/vv"
android:layout_width="match_parent"
android:layout_height="match_parent" />

Java代码的实现 :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class MainActivity extends Activity {
private VideoView vv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vv = (VideoView) findViewById(R.id.vv);
MediaController mc = new MediaController(this);
mc.setAnchorView(vv);
vv.setMediaController(mc);
vv.setVideoPath("mnt/sdcard/areyouok.3gp");
vv.start();
}
}

  • 拍照
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class MainActivity extends Activity {
private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void takephoto(View view) {
// create Intent to take a picture and return control to the calling
// application
// 意图
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);// 去打开系统的照相机
// 准备一个接收系统拍好照片后的文件路径
File file = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + ".jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); // set the
// image
// file
// name
// start the image capture Intent
// 请求code
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE) {
// 为你的某个请求返回的结果
// 给你数据的Activity设置的结果标记
// resultCode
switch (resultCode) {
case Activity.RESULT_OK:
// 获取数据成功
System.out.println("ok");
break;
case Activity.RESULT_CANCELED:
// 用户取消操作
System.out.println("cancel");
break;
default:
break;
}
}
}
}
  • 传感器指南针
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
public class MainActivity extends Activity {
private SensorManager manager;
private Sensor sensor;
private SensorEventListener listener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
// 1. 获取 传感器的数据
float[] values = event.values;
// value
// values[0]: Azimuth, angle between the magnetic north direction
// and the y-axis, around the z-axis (0 to 359). 0=North, 90=East,
// 180=South, 270=West
float angle = values[0];
if (angle == 0) {
System.out.println("北");
} else if (angle == 90) {
System.out.println("东");
} else if (angle == 180) {
System.out.println("南");
} else if (angle == 270) {
System.out.println("西");
} else if (angle > 0 && angle < 90) {
System.out.println("东北");
} else if (angle > 90 && angle < 180) {
System.out.println("东南");
} else if (angle > 180 && angle < 270) {
System.out.println("西南");
} else if (angle > 270 && angle < 360) {
System.out.println("西北");
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// 1. 传感器的精确度发送改变时
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 传感器的管理者
manager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
// List<Sensor> list = manager.getSensorList(Sensor.TYPE_ALL);
// for (Sensor sensor : list) {
// System.out.println(sensor.getName());
// }
// 获得光的传感器
sensor = manager.getDefaultSensor(Sensor.TYPE_ORIENTATION);
}
@Override
protected void onResume() {
super.onResume();
// 1:监听传感器
// 2:哪个传感器
// 3:采样频率
manager.registerListener(listener, sensor,
SensorManager.SENSOR_DELAY_NORMAL);
}
@Override
protected void onPause() {
super.onPause();
manager.unregisterListener(listener);
}
}

10.安卓基础之新特性和知识点回顾

发表于 2017-03-28 | 分类于 Android | 阅读次数

1. 使用 ContentProvider 获得系统的联系人

ContentProvider : 后门程序,就是一个继承了ContentProvder的一个类.使用系统已经提供好的后门程序,来获得所有的其他的应用中的数据.
例如 : 获取到系统的短信内容,获取手机联系人的信息.

  • 先获取到读取通讯录权限
1
<uses-permission android:name="android.permission.READ_CONTACTS"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//ContactsUtils 获得系统的联系人的信息
public class ContactsUtils {
public static List<ContactInfo> displayContact(Context context){
List<ContactInfo> list = new ArrayList<ContactInfo>();
Uri contact_uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri data_uri = Uri.parse("content://com.android.contacts/data");
//获得与后门程序打交道的resolver对象
ContentResolver resolver = context.getContentResolver();
Cursor contact_cursor = resolver.query(contact_uri,
new String[]{"contact_id"},null,null,null);
while(contact_cursor.moveToNext()){
String id = contact_cursor.getString(0);
if(id!=null){
ContactInfo info = new ContactInfo();
Cursor dataCursor = resolver.query(data_uri, new String[] {
"data1", "mimetype" }, "raw_contact_id=?",
new String[] { id }, null);
while(dataCursor.moveToNext()){
String data1 = dataCursor.getString(0);
String type = dataCursor.getString(1);
if ("vnd.android.cursor.item/name".equals(type)) {
info.setName(data1);
} else if ("vnd.android.cursor.item/email_v2".equals(type)) {
info.setEmail(data1);
} else if ("vnd.android.cursor.item/im".equals(type)) {
info.setQq(data1);
} else if ("vnd.android.cursor.item/phone_v2".equals(type)) {
info.setPhone(data1);
}
}
list.add(info);
dataCursor.close();
}
}
contact_cursor.close();
return list;
}
}

2. Fragment 的使用

  • layout 的写法 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<FrameLayout
android:id="@+id/container"
android:layout_width="fill_parent"
android:layout_height="0dip"
android:layout_weight="1" >
</FrameLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:text="@string/hello_world" >
<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="sound"
android:text="声音" />
<Button
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="display"
android:text="显示" />
<Button
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:onClick="storage"
android:text="存储" />
</LinearLayout>
</LinearLayout>
displayfragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#4400ff00"
android:orientation="vertical" >
<TextView
android:text="显示的fragment内容 "
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
soundfragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#44ff0000"
android:orientation="vertical" >
<TextView
android:text="声音的fragment内容 "
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
storagefragment.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#440000ff"
android:orientation="vertical" >
<TextView
android:text="存储的fragment内容 "
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
  • fragmentquickstart的快速入门
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
MainActivity.java
@SuppressLint("NewApi")
public class MainActivity extends Activity {
FragmentManager manager;
FragmentTransaction transaction;
SoundFragment sf;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
manager = getFragmentManager();
//事务
transaction = manager.beginTransaction();
sf = new SoundFragment();
transaction.replace(R.id.container, sf);
transaction.commit(); //提交事务
}
//声音的
public void sound(View v){
//在右侧的 FrameLayout去显示sound相关的fragment数据
transaction = manager.beginTransaction();
// fragment 可以直接 new 出来, 并且不需要到 清单文件中进行注册
SoundFragment sf = new SoundFragment();
//拿到一个frament的manager对象
//事务
// 表示使用 SoundFragment去替换掉之前的framelayout
transaction.replace(R.id.container, sf);
transaction.commit();
}
//显示的
public void display(View v){
transaction = manager.beginTransaction();
DisplayFragment df = new DisplayFragment();
transaction.replace(R.id.container, df);
transaction.commit();
}
//存储的
public void storage(View v){
transaction = manager.beginTransaction();
StorageFragment ssf = new StorageFragment();
transaction.replace(R.id.container, ssf);
transaction.commit();
}
}
DisplayFragment.java
@SuppressLint("NewApi")
public class DisplayFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//为fragment声明layout文件,然后将layout文件的显示转换为一个view对象
return inflater.inflate(R.layout.displayfragment, null);
}
}
SoundFragment.java
@SuppressLint("NewApi")
public class SoundFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//为fragment声明layout文件,然后将layout文件的显示转换为一个 view 对象
//之前为了将layout文件转换为 view 对象时,调用的是
//View.inflate(context, resource, root)
return inflater.inflate(R.layout.soundfragment, null);
}
}
StorageFragment.java
@SuppressLint("NewApi")
public class StorageFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//为fragment声明layout文件,然后将layout文件的显示转换为一个 view 对象
return inflater.inflate(R.layout.storagefragment, null);
}
}

2.1 Fragment 的声明周期

  • Oncreate : 创建的时候执行
  • Onstart : 可见的时候执行
  • Onresume : 获得焦点的时候执行
  • Onpause : 失去焦点的时候执行
  • Onstop : 不可见的时候执行
  • Onrestart : 按了 home 键,重新回到 activity 的时候执行.
  • Ondestory : activity 销毁的时候执行.

9.安卓基础之内容提供者

发表于 2017-03-28 | 分类于 Android | 阅读次数

1. 回顾service

  • 开启服务
  • 绑定服务
  • 调用服务中的方法
  • 解绑服务
  • 关闭服务

2. 远程服务调用代码具体实现步骤

  • 第一步 :
    在远程 service 中去编写一个内部类,让这个内部类集成 Binder,实现 IService 接口,把 IService 接口声明出来 MyAgent extends Binder impelements IService

  • 第二步 :
    将 IService 接口中的public,private给干掉.并且将扩展名改为.aidl

1
2
3
4
interface IService {
void callMethodInService();
}
  • 第三步 :
    回到 service 中的内线类,将其改为继承 IService.Stub类
1
2
3
4
5
6
7
private class MyAgent extends IService.Stub{
@Override
public void callMethodInService() {
methodInService();
}
}
  • 第四步 :
    在这个内部类中实现的方法中去调用服务中的方法
1
2
3
4
5
6
7
8
@Override
public void callMethodInService() {
methodInService();
}
...
public void methodInService(){
System.out.println("远程服务中的方法被调用了 ");
}
  • 第五步 :
    回到远程的调用者应用中, bindService,写法与之前一样.
1
2
3
4
5
6
//绑定远程服务
public void bindservice(View v){
Intent intent = new Intent();
intent.setAction("com.javami.rms");
bindService(intent, new MyConnection(), BIND_AUTO_CREATE);
}
  • 第六步 :
    编写绑定服务的时候,建立的通信频道 MyConnection
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private IService agent;
private class MyConnection implements ServiceConnection{
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// agent = (IService )service
agent = IService.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
}
  • 第七步 :
    在 MyConnection 的方法中,onServiceConnected 中强制类型转换 service 为内线.
1
agent = IService.Stub.asInterface(service);
  • 第八步 :
    通过内线调用服务中的方法
1
2
3
4
5
6
7
8
9
10
11
//调用远程服务中的方法
public void call(View v){
System.out.println("调用者调用远程服务中的方法 ");
try {
agent.callMethodInService();
} catch (RemoteException e) {
e.printStackTrace();
}
}

3. 杂谈题

android 中如何实现 ipc 通信?

常规的情况下,A应用去激活B应用中的组件的时候,这实际上是一个ipc通信的体现,这个时候发送是Intent就是在做ipc通信,intent也是去实现paraceable接口的,ipc通信时传递的非8种基本类型都需要去实现这个接口,如果是进程间通信,除了使用intent之外,还可以使用远程服务调用,谷歌已经对应用中提供了aidl技术技术供应用之间进行通信.

4. ContentProvider 的学习

内容提供者 : 用来提供数据.

4.1 插入删除数据到系统的短信数据库中

  • 先声明读写sms权限
1
2
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>
  • 插入和删除短信的编写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//插入短信到系统的短信数据库中
public void add(View v){
ContentResolver resolver = getContentResolver();
//contentProvider使用的时候必须是以content://打头
Uri uri = Uri.parse("content://sms");
ContentValues values = new ContentValues();
values.put("address", "5201314"); // address
values.put("date", System.currentTimeMillis()); // address
values.put("type", "1"); // address
values.put("body", "亲爱的,我想你了..."); // address
resolver.insert(uri,values);
}
public void delete(View v){
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://sms");
resolver.delete(uri, "address=?", new String[]{"1 008-6"});
}

4.2 手机监听器的实现

  • 先开启相应的权限
1
2
3
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  • 启动服务
1
2
3
4
5
6
//开启服务
public void startService(View v){
Intent intent = new Intent();
intent.setAction("com.vrwait.recoder");
startService(intent);
}
  • 实现服务类,先监听电话后实现通话录音
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
public class PhoneService extends Service {
private static final String LOG_TAG = "phoneService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
TelephonyManager tm;
@Override
public void onCreate() {
super.onCreate();
//监听电话的状态,如果电话来临,并且拨通了电话,就后台录音
tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
}
private class MyPhoneStateListener extends PhoneStateListener{
private MyPhoneStateListener(MediaRecorder mRecorder) {
this.mRecorder = mRecorder;
}
@Override
public void onCallStateChanged(int state, String incomingNumber) {
super.onCallStateChanged(state, incomingNumber);
switch (state) {
case TelephonyManager.CALL_STATE_IDLE: // 电话空闲
// 停止 录音
if(mRecorder!=null){
stopRecording();
}
break;
case TelephonyManager.CALL_STATE_OFFHOOK: //电话接通的状态
// 开启 录音
startRecording();
break;
case TelephonyManager.CALL_STATE_RINGING: //电话正在响
break;
default:
break;
}
}
private MediaRecorder mRecorder;
// 开始录音
private void startRecording(){
// 实例化MediaRecorder
mRecorder = new MediaRecorder();
// 指定一个源
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
//输出的数据的格式
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
//文件的保存到哪里
mRecorder.setOutputFile("/mnt/sdcard/yy.3gp");
//使用什么解码器
mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
try{
mRecorder.prepare();
}catch (Exception e){
e.printStackTrace();
}
mRecorder.start();
}
// 停止录音
private void stopRecording() {
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
}
  • 务必指定服务对象(启动的服务是从哪里启动的?)
    1
    2
    3
    4
    5
    <service android:name=".PhoneService">
    <intent-filter>
    <action android:name="com.vrwait.recoder"></action>
    </intent-filter>
    </service>

8.安卓基础之service服务

发表于 2017-03-28 | 分类于 Android | 阅读次数

1. service 服务

Service是一个应用程序组件,它能够在后台执行一些耗时较长的操作,并且不提供用户界面。服务能被其它应用程序的组件启动,即使用户切换到另外的应用时还能保持后台运行。此外,应用程序组件还能与服务绑定,并与服务进行交互,甚至能进行进程间通信(IPC)。 比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与content provider进行交互,所有这些都是后台进行的。

1.1 编写服务的步骤

  • 继承service类,那么就写 了一个服务
  • 到清单中进行配置
  • 启动服务,关闭服务

注意 :
直接开启服务,超时的事,会引发应用程序 ANR(application not responding) ,导致这种问题,是因为主线程中干了耗时的事情,service是运行在主线程中的,主线程是不允许干耗费时间的事儿.

1.2 为什么需要服务

android系统会尽可能的保持应用程序进程的一致存在,即是在应用退出后,也仍然这样.但是,如果发现内存不够用,需要去启动新的进程时,那么会按照顺序的优先级去杀死某些老的进程.

进程 : 就是一块独立的内存空间,用来运行程序.

android中进程的分类 :

  • 前台进程
    Forground process : 可以与用户直接进行交互,就是前台进程.
  • 可视进程
    Visible process : 可以看到,但是不能直接与用户进行交互.
  • 服务进程
    Service process : 进程中运行了一个服务,在运行着.
  • 后台进程
    Background process : 在一个activity,现在可以不见.但是在后台运行着.
  • 空进程
    Empty process : 一个进程中,没有服务,也没有activity,整个程序都已经退出.

重要级别 : 前台进程->可视进程->服务进程->后台进程->空进程

服务可以长期后台运行,是与当前启动服务的activity是没有关系的.

  • 先在清单注册服务

    1
    <service android:name=".QuickStartService"></service>
  • 继承一个服务类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* Created by 诸葛亮 on 2016/12/10.
* Service : 实际上就是一个没有界面的activity
*/
public class QuickStartService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
private boolean flag;
@Override
public void onCreate(){
super.onCreate();
System.out.print("onCreate服务被创建了:"+Thread.currentThread().getName());
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("服务收到了开启的指令了:onStartCommand" );
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
flag =false;
System.out.println("onDestroy服务销毁了 ");
}
}
  • 在主类中写一些暂停开启服务代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.javami.quickstartservice;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//开启服务
public void start(View v){
Intent intent = new Intent();
intent.setClass(this,QuickStartService.class);
startService(intent);
}
//关闭服务
public void stop(View v){
Intent intent = new Intent();
intent.setClass(this,QuickStartService.class);
stopService(intent);
}
}

1.3 启动服务的生命周期

当点击开启服务,服务会创建,点击关闭服务后,服务会销毁.服务只会在第一次创建调用onCreate,多次开启服务,只会去重复调用onStartCommand,并不会去新创建服务.

所以,多次开启服务的方式,去调用服务中的方法是走不通的,调用不了服务中的方法.需要使用绑定服务去调用服务中的方法.

2. 绑定服务

bindService(service,conn,flags)

2.1 编写绑定服务调用服务中的方法的步骤 :

  • 第一步 : 在服务中去编写一个要被外面调用的方法
1
2
3
4
5
//服务中的方法
public void methodInService(String name,int money){
Toast.makeText(this,"服务中的方法被调用...",0).show();
}
  • 在服务中提供一个内线,这个内线是继承了Binder类,实现 IService接口
1
2
private class MyAgent extends Binder implements IService{
}
  • 编写一个IService接口,提供一个方法
1
2
3
4
public interface IService {
public void callMethodInService(String name,int money);
}
  • 进到服务内线中,在内线实现接口方法,调用服务中的方法
1
2
3
4
5
6
7
8
9
private class MyAgent extends Binder implements IService{
@Override
public void callMethodInService(String name, int money) {
//调用服务中的方法
methodInService(name,money);
}
}
  • 在onBind方法中,返回内线
1
2
3
4
5
@Override
public IBinder onBind(Intent intent) {
System.out.println("onBind执行了,绑定了服务");
return new MyAgent();
}
  • 回到百姓activity中,使用绑定服务的方式去开启服务的内线通讯频道,在通讯频道的ServiceConnection的实现类中的,onServiceConnected中拿到内线的引用.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class MyConn implements ServiceConnection {
//当成功绑定了服务,返回内线的引用的方法,用于返回内线的对象
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
agent = (IService) service;
System.out.println("服务绑定的时候执行了onServiceConnected");
}
@Override
public void onServiceDisconnected(ComponentName name) {
agent = null;
System.out.println("服务绑定的时候执行了onServiceDisconnected");
}
}
  • 通过对内线的引用,去调用service中的方法
1
2
3
4
//调用服务中的方法
public void callMethodInService(View v){
agent.callMethodInService("kevin",10000);
}

2.2 绑定服务的声明周期

  • 绑定服务 : 调用oncreate,onbind方法
  • 解绑服务 : 调用onunbind方法,调用ondestroy方法服务销毁

2.3 混合开启服务的介绍

绑定服务可以调用服务中的方法,开启服务可以让服务在后台一直运行.
开启服务->绑定服务->调用服务中的方法->解绑服务->关闭服务

结论 :
如果以后需要去后台一直运行服务,而且又想调用服务中的方法,那么请按以上的过程走!

3. 总结

从前,一位想要办证的用户,需要直接到某部分办证.但是中国式办证太难.只能找到代理,因为代理里面有在政府上班的人,所以代理能利用一切关系来搞掂证件(从服务里面定义代理与方法),那么某用户找到内线(内线通过中间人IService找到),获得内线后.只是钱和事的问题了!

1…567…13
王裕杰

王裕杰

以中有足乐者,不知口体之奉不若人也.

130 日志
13 分类
24 标签
GitHub 微博 豆瓣 知乎
© 2016 - 2017 王裕杰
由 Hexo 强力驱动
主题 - NexT.Muse