首页
社区
课程
招聘
移动安全学习笔记(一)
2023-2-4 21:13 11014

移动安全学习笔记(一)

2023-2-4 21:13
11014

写在前面

这是一份笔者学习移动安全的笔记,虽然都是一些基础的东西但也算是能为入门小白指引一些学习路线,本篇的主要内容是 JAVA 和 Android 基础内容,是后续学习的基石,笔记如有缺漏敬请斧正。

JAVA

安装JDK

安装 jdk-8u66-windows-x64.exe

设置环境变量

  • 添加系统环境变量 JAVA_HOME

​ C:\Program Files\Java\jdk1.8.0_66

  • 添加系统环境变量 JRE_HOME

​ C:\Program Files\Java\jre1.8.0_66

  • 添加系统环境变量 CLASSPATH

​ %JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;%JRE_HOME%\lib\rt.jar

  • 系统环境变量 path 中添加变量

​ %JAVA_HOME%\bin;%JRE_HOME%\bin;

  • 测试 java 环境
1
2
3
4
5
6
7
C:\Users\Administrator>java -version
java version "1.8.0_66"
Java(TM) SE Runtime Environment (build 1.8.0_66-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.66-b17, mixed mode)
 
C:\Users\Administrator>javac -version
javac 1.8.0_66

First程序

  1. 使用快捷键win+R,打开运行工具

  2. 输入notepad,打开记事本

  3. 编写代码保存为Hello. java

    1
    2
    3
    4
    5
    public class Hello{
        public static void main(String[] args){
            System.out.println("Hello World");
        }
    }
  4. 在命令行中编译代码,生成class文件

    1
    javac Hello.java
  5. 在命令行中解释执行代码

    1
    java Hello

安装集成环境

  • 安装 IntelliJ IDEA Community Edition 2018.2.4 x64

  • 取消默认的打开最后一个工程的设置

    Settings — Appearance & Behavior — SystemSettings — Reopen last project on startup — 取消选中

  • 设置鼠标滚轮改变字体大小

    Settings — Editor — General — Change font size with Ctrl+Mouse Wheel — 取消选中

  • 关闭第一字母补全代码

    Settings — Editor — General — Code Completion — Match case — 取消选中

  • 开启自动导入

    Settings — Editor — General — Auto Import — Inster import on paste — Ask

  • 设置函数跟踪和返回的快捷键(默认快捷键有冲突)

    Settings — Keymap— Main menu— Navigate — Back — Alt + 向左箭头

    Settings — Keymap— Main menu— Navigate — Forward — Alt + 向右箭头

  • 设置自定义代码块快捷键

    Settings — Editor — Live Templates— Other — +号 — Line Template

    image-20230117110619687

常用快捷键

  • Ctrl + Q 函数参数提示
  • Alt + Insert 呼出菜单
    • Constructor 生成构造方法
    • Getter and Setter 为成员变量生成读写函数(常用于私有变量读写)
  • Alt + Enter 自动排除错误
  • Ctrl + O 呼出重写菜单

Second 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Scanner;
 
public class Main {
    public static void main(String[] args) {
 
        System.out.print("请输入数据:");
 
        Scanner scanner = new Scanner(System.in);
 
        String buff = scanner.next();
 
        System.out.println("您输入的数据:"+buff);
 
    }
}

集合

List和ArrayList

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.List;
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        Zoo zoo = new Zoo();
        zoo.add(new Dog("Dog", 3));
        zoo.add(new Pig("Pig", 5));
        zoo.show();
 
        List<Animal> animals = zoo.search("Dog");
        for (int i = 0; i < animals.size(); i++) {
            System.out.println(animals.get(i).getName());
        }
 
        zoo.clear();
        zoo.show();
    }
}

Zoo.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
import java.util.ArrayList;
import java.util.List;
 
class Zoo {
    private List<Animal> animal = new ArrayList<Animal>();
    public void add(Animal animal){
        this.animal.add(animal);
    }
    public void clear(){
        for (int i = animal.size() - 1; i >= 0; i--) {
            this.animal.remove(animal.get(i));
        }
    }
    public void show(){
        for (int i = 0; i < animal.size(); i++) {
            System.out.println(animal.get(i).getName());
        }
    }
    public List<Animal> search (String name)
    {
        List<Animal> result = new ArrayList<>();
        for (int i = 0; i < animal.size(); i++) {
            if(animal.get(i).getName().contains(name))
            {
                result.add(animal.get(i));
            }
        }
        return result;
    }
}

Animal.java

1
2
3
4
public interface Animal {
    public String getName();
    public int getAge();
}

Dog.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
import javax.naming.Name;
 
public class Dog implements Animal{
    private String name;
    private int age;
 
    public Dog(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getAge() {
        return age;
    }
 
    @Override
    public boolean equals(Object obj){
        if(this == obj)
        {
            return true;
        }
        if(this == null)
        {
            return false;
        }
        if(!(obj instanceof Dog))
        {
            return false;
        }
        if(this.name.equals(((Dog)obj).name))
        {
            return true;
        }
        return false;
    }
}

Pig.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
public class Pig implements Animal{
    private String name;
    private int age;
 
    public Pig(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String getName() {
        return name;
    }
    @Override
    public int getAge() {
        return age;
    }
 
    @Override
    public boolean equals(Object obj){
        if(this == obj)
        {
            return true;
        }
        if(this == null)
        {
            return false;
        }
        if(!(obj instanceof Pig))
        {
            return false;
        }
        if(this.name.equals(((Pig)obj).name))
        {
            return true;
        }
        return false;
    }
}

Set和HashSet

基本实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
 
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        Set<String> all = new HashSet<>();
        all.add("Hello");
        all.add("Hello");   //重复添加无效
        all.add("World");
 
        System.out.println(all);
 
        Iterator<String> iter = all.iterator();
        while(iter.hasNext()){
            System.out.println(iter.next());
        }
    }
}

Map和HashMap

Map提供了一种映射关系,其中的元素是以键值对(key-value)的形式存储的,能够实现根据key快速查找valueMap中的键值对以Entry类型的对象实例形式存在,键(key值)不可重复,value值可以。

 

Map支持泛型,Map<K,V>。

 

Map中常用方法,put(key,value)、putAll添加元素,get(key)获取元素,remove(key)删除。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.util.*;
 
public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        Map<Integer, String> map = new HashMap<>();
        map.put(3,"张三");
        map.put(null,"佚名");
        map.put(5,"王五");
        map.put(0,"赵零");
        map.put(3,"张3");    //key值重复,value覆盖为新内容
 
        //keySet() 方法返回映射中所有 key 组成的 Set 视图。
        Set<Integer> set = map.keySet();
        Iterator<Integer> iter = set.iterator();
        while(iter.hasNext()){
            Integer key = iter.next();
            System.out.println(key + ":" + map.get(key));
        }
    }
}

文件

File 类

常见方法

方法名 说明
File(String pathName) 通过将给定路径的字符串来创建一个表示对应文件的File实例
exists() 判断此File的实例对应的文件是否存在
isFile() 判断此File的实例是否是一个标准文件
isDirectory() 判断此File的实例是否为一个文件夹
getName() 获取此File实例对应的文件或文件夹的名称
getAbsolutePath() 获取File实例对应的文件或文件夹的绝对路径
length() 返回此File实例的长度,单位为字节
creatNewFile() 文件的创建,返回值为布尔型
delete() 删除文件

FileInputStream

  • 从文件系统中的某个文件中获取输入字节

  • 用于读取文件、图像等数据的原始字节流

  • 如果返回-1,则表示到达文件末尾

方法名 描述
public int read() 从输入流中读取一个数据字节
public int read(byte[] b) 从输入流中将最多b.length个字节的数据读入一个byte数组中
public int read(byte[] b,int off,int len) 从输入流中将最多len个字节的数据读入byte数组中
public void close() 关闭此文件输入流并释放与此流有关的所有系统资源

FileOutputStream

常用方法

方法名 描述
public void write(int b) 将指定字节写入此文件输出流
public void write(byte[] b) 将b.length个字节从指定byte数组写入此文件输出流中
public void write(byte[] b,int off,int len) 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流
public void close() 关闭此文件输出流并释放与此流有关的所有系统资源
 

FileEx.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
import java.io.*;
 
public class FileEx {
    public static File CreatFile(String filePath){
        File file = new File(filePath);
        if(!file.exists())
        {
            try{
                file.createNewFile();
                return file;
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
        return file;
    }
 
    public static String ReadFile(File file) throws IOException {
        try {
            FileInputStream inputStream = new FileInputStream(file);
            ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
            byte bytes[] = new byte[1024];
            int nRet = 0;
            while(true)
            {
                nRet = inputStream.read(bytes, 0,1024);
                if(nRet == -1) break;
                arrayOutputStream.write(bytes,0, nRet);
            }
            inputStream.close();
            return new String(arrayOutputStream.toString());
        }catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }catch(IOException e)
        {
            e.printStackTrace();
        }
        return null;
    }
 
    public static boolean WriteFile(File file, String content){
        boolean bRet = false;
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            byte bytes[] = content.getBytes();
            fileOutputStream.write(bytes);
            bRet = true;
            fileOutputStream.close();
        }catch (FileNotFoundException e)
        {
            e.printStackTrace();
        }catch(IOException e)
        {
            e.printStackTrace();
        }
        return bRet;
    }
 
    public static void DeleteFile(File file){
        if(file.exists())
        {
            if(file.isFile())
            {
                file.delete();
            }
            else if(file.isDirectory()) {
                File[] files = file.listFiles();
                for (int i = 0; i < file.length(); i++)
                {
                    DeleteFile(files[i]);
                }
                DeleteFile(file);
            }
        }
    }
}

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.io.File;
import java.io.IOException;
import java.util.*;
 
public class Main {
    public static void main(String[] args) throws IOException {
        System.out.println("Hello World!");
 
        FileEx fileEx = new FileEx();
 
        File file = fileEx.CreatFile("test.txt");
 
        fileEx.WriteFile(file,"liuxingzuo");
 
        String str = fileEx.ReadFile(file);
 
        System.out.println(str);
 
        fileEx.DeleteFile(file);
    }
}

输入输出流

Java常用输入输出流体系分类

分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 lnputStream OutputStream Reader Writer
访问文件 FilelnputStream FileOutputStream FileReader FileWriter
访问数组 ByteArraylnputStream ByteArrayOutputStream CharArrayReader CharArrayWriter
访问管道 PipedIlnputstream PipedOutputstream PipedReader PipedWriter
访问字符串 StringReader stringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectIlnputStream ObjectOutputStream
抽象基类 FilterlnputStream FilterOutputStream FilterReader FilterWriter
打印流 PrintStream PrintWriter
推回输入流 PushbackInputStream PushbackReader
特殊流 DatalnputStream DataOutputStream

粗体字标出的类代表节点流,必须直接与指定的物理节点关联;斜体字标出的类代表抽象基类,无法直接创建实例。

比较器

如果现在要想为—组对象进行排序,那么必须有一个可以区分出对象大小的关系操作,而这个操作在Java之中就是利用比较器完成的
常用比较器:Comparable,如果要为对象指定比较规则,那么对象所在的类必须实现Comparable接口。

Comparable

Person.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
class Person implements Comparable<Person>{
    private String name;
    private int age;
    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString()
    {
        return "Person [name=" + name + ", age=" + age + "]\n";
    }
    @Override
    public int compareTo(Person o) {
        if(this.age > o.age)
        {
            return 1;
        }else if(this.age < o.age){
            return -1;
        }
        return 0;
    }
}

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;
 
public class Main {
    public static void main(String[] args){
        System.out.println("Hello World!");
        Person[] persons = new Person[]{
                new Person("张三", 6),
                new Person("李四", 4),
                new Person("王五", 5),
        };
        Arrays.sort(persons);
        System.out.println(Arrays.toString(persons));
    }
}

Comparator

类在设计之初并没有考虑排序的要求,但后来又需要进行排序,但类无法修改,所以在java里面又提供了另外一种比较器接口,不过它需要为 Comparator 单独定义一个类,所以比较麻烦。

 

PersonComparator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import java.util.Comparator;
 
public class PersonComparator implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        if(o1.getAge() > o2.getAge())
        {
            return 1;
        }
        else if(o1.getAge() < o2.getAge())
        {
            return -1;
        }
        return 0;
    }
}

person.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
class Person{
    private String name;
    private int age;
 
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public Person(String name, int age)
    {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString()
    {
        return "Person [name=" + name + ", age=" + age + "]\n";
    }
}

Main.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Arrays;
 
public class Main {
    public static void main(String[] args){
        System.out.println("Hello World!");
        Person[] persons = new Person[]{
                new Person("张三", 6),
                new Person("李四", 4),
                new Person("王五", 5),
        };
        Arrays.sort(persons, new PersonComparator());
        System.out.println(Arrays.toString(persons));
    }
}

Android

架构

Android架构分为五层

  • Linux 内核层

    Android 的核心系统服务基于Linux 内核,在此基础上添加了部分Android专用的驱动。系统的安全性、内存管理、进程管理、网络协议栈和驱动模型等都依赖于该内核。

  • 硬件抽象层

    硬件抽象层是位于操作系统内核与硬件电路之间的接口层,其目的在于将硬件抽象化,为了保护硬件厂商的知识产权,它隐藏了特定平台的硬件接口细节,为操作系统提供虚拟硬件平台,使其具有硬件无关性,可在多种平台上进行移植。 从软硬件测试的角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,使得软硬件测试工作的并行进行成为可能。通俗来讲,就是将控制硬件的动作放在硬件抽象层中。

  • 系统运行库层

    系统运行库层分为两部分,分别是 C/C++ 程序库和 Android 运行时库

    • C/C++程序库

      | 名称 | 功能描述 |
      | :-------------- | :----------------------------------------------------------- |
      | OpenGL ES | 3D绘图函数库 |
      | Libc | 从BSD继承来的标准C系统函数库,专门为基于嵌入式Linux的设备定制 |
      | Media Framework | 多媒体库,支持多种常用的音频、视频格式录制和回放。 |
      | SQLite | 轻型的关系型数据库引擎 |
      | SGL | 底层的2D图形渲染引擎 |
      | SSL | 安全套接层,是为网络通信提供安全及数据完整性的一种安全协议 |
      | FreeType | 可移植的字体引擎,它提供统一的接口来访问多种字体格式文件 |

    • Android运行时库

      运行时库又分为核心库和ART(5.0系统之后,Dalvik虚拟机被ART取代)。核心库提供了Java语言核心库的大多数功能,这样开发者可以使用Java语言来编写Android应用。相较于JVM,Dalvik虚拟机是专门为移动设备定制的,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。而替代Dalvik虚拟机的ART 的机制与Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。

  • 应用框架层

    应用框架层为开发人员提供了开发应用程序所需要的API,我们平常开发应用程序都是调用这一层所提供的API,当然也包括系统应用。这一层是由Java代码编写的,可以称为Java Framework。下面来看这一层所提供的主要组件:

    | 名称 | 功能描述 |
    | :--------------------------------- | :----------------------------------------------------------- |
    | Activity Manager(活动管理器) | 管理各个应用程序生命周期,以及常用的导航回退功能 |
    | Location Manager(位置管理器) | 提供地理位置及定位功能服务 |
    | Package Manager(包管理器) | 管理所有安装在Android系统中的应用程序 |
    | Notification Manager(通知管理器) | 使得应用程序可以在状态栏中显示自定义的提示信息 |
    | Resource Manager(资源管理器) | 提供应用程序使用的各种非代码资源,如本地化字符串、图片、布局文件、颜色文件等 |
    | Telephony Manager(电话管理器) | 管理所有的移动设备功能 |
    | Window Manager(窗口管理器) | 管理所有开启的窗口程序 |
    | Content Provider(内容提供者) | 使得不同应用程序之间可以共享数据 |
    | View System(视图系统) | 构建应用程序的基本组件 |

  • 应用层

    系统内置的应用程序以及非系统级的应用程序都属于应用层,负责与用户进行直接交互,通常都是用Java进行开发的

adb

添加环境变量

1
C:\Users\Administrator\AppData\Local\Android\Sdk\platform-tools;

常用命令

命令 说明
adb devices 列出所有设备
adb install hello.apk 安装应用hello.apk
adb install -s emulator-5554 D:/hello.apk 安装应用到指定模拟器
adb uninstall hello.apk 卸载hello.apk包
adb pull 获取模拟器中的文件
adb push 向模拟器中写文件
adb shell 进入模拟器的shell模式
adb reboot 重启设备
adb loacat 查看日志
emulator -avd advname 启动模拟器
adb kill-server 关闭服务,常用于adb连接失败
adb start-server 开启服务,常用于adb连接失败

第三方模拟器

这里推荐使用夜神3.8.3.1版本(因为这个版本的Android系统是4.4的),这里说一下,我的电脑安装后启动模拟器会蓝屏,安装高版本的夜神再将其卸载,重新安装3.8.3.1版本可以解决这个问题。

 

替换夜神模拟器的 adb 文件

 

将 AndroidStudio 使用的 sdk 文件夹 C:\Users\Administrator\AppData\Local\Android\Sdk\platform-tools 中的下列三个文件复制到夜神模拟器的 C:\Program Files (x86)\Nox\bin 文件夹中,请注意模拟器的原文件应有必要备份,另外夜神模拟器将 adb.exe 更名为 nox_adb.exe,所以在文件复制后,也虚将复制过来的 adb.exe 重命名为 nox_adb.exe

  • adb.exe
  • AdbWinApi.dll
  • AdbWinUsbApi.dll

替换夜神模拟器的 adb 文件后,打开 cmd输入如下代码表示模拟器连接成功

1
2
3
4
5
6
7
8
9
Microsoft Windows [Version 10.0.19044.1288]
(c) 2019 Microsoft Corporation. All rights reserved.
 
C:\Users\Administrator>adb devices
List of devices attached
127.0.0.1:62001 device
 
C:\Users\Administrator>adb -s 127.0.0.1:62001 shell
root@android:/ #

布局

LinearLayout

新增LinearLayout布局

  • res — layout — 右键单击 — New — Layout Resourse File
  • Root element — LinearLayout
  • Directory name — layout

Demo

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
 
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:src="@drawable/fzzl">
        </ImageView>
 
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
 
        <TextView
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="username:"
            android:textSize="20dp">
        </TextView>
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="please put in username"
            android:textSize="20dp">
        </EditText>
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
 
        <TextView
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="password:"
            android:textSize="20dp">
        </TextView>
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="please put in password"
            android:textSize="20dp">
        </EditText>
 
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="left">
 
        <RadioGroup
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:id="@+id/rg1">
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="sex:">
            </TextView>
            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="boy">
            </RadioButton>
            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="girl">
            </RadioButton>
            <RadioButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="secret">
            </RadioButton>
        </RadioGroup>
 
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="left">
 
        <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="save password"
            android:textSize="20dp">
        </CheckBox>
        <CheckBox
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="keep online"
            android:textSize="20dp">
        </CheckBox>
 
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:textSize="20dp"
            android:text="0">
        </TextView>
        <SeekBar
            android:id="@+id/sb1"
            android:layout_width="300dp"
            android:layout_height="match_parent"
            android:max="100"
            android:progress="0">
        </SeekBar>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:textSize="20dp"
            android:text="100">
        </TextView>
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">
 
        <Button
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:text="Register"
            android:id="@+id/button1"
            android:onClick="clickRegisterButton">
        </Button>
        <Button
            android:layout_width="120dp"
            android:layout_height="wrap_content"
            android:text="Sign in"
            android:layout_marginLeft="50dp"
            android:id="@+id/button2">
        </Button>
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">
 
        <Button
            android:layout_width="290dp"
            android:layout_height="wrap_content"
            android:text="ForgottenPassword"
            android:id="@+id/button3">
        </Button>
    </LinearLayout>
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:gravity="center">
 
        <Button
            android:layout_width="290dp"
            android:layout_height="wrap_content"
            android:text="Message Login"
            android:id="@+id/button4">
        </Button>
    </LinearLayout>
</LinearLayout>

控件

TextView

属性 说明
android:id 控件的id
android:layout_width 控件宽度
android:layout_height 控件的高度
android:text 文本内容
android:textSize 文本大小
android:textColor 文本颜色
android:background 控件背景

EditText

属性 说明
android:id 控件的id
android:layout_width 控件宽度
android:layout_height 控件的高度
android:text 文本内容
android:textSize 文本大小
android:textColor 文本颜色
android:background 控件背景
android:hint 输入提示文本
android:inputType 输入文本类型

ImageView

属性 说明
android:src= "@drawable/ic_launcher" ImageView的内容图像
android:background="@drawable/ic_launcher" lmageView背景图片
android:background= "#0000ff” lmageView的RGB颜色

注意:android: background与android:src可以共存

Button

button按钮点击响应有四种方式

方式一

界面属性设置点击函数

 

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="clickFun"
        android:text="Button"
        android:id="@+id/button1">
    </Button>
</LinearLayout>

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void clickFun(View view) {
        Toast.makeText(MainActivity.this,"Button is click",Toast.LENGTH_SHORT).show();
    }
}

方式二

内部类设置点击函数

 

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:id="@+id/button1">
    </Button>
</LinearLayout>

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        clickFun();
    }
 
    //设置按钮点击:内部类
    //setOnClickListener的第一个参数为一个实现了View.OnClickListener接口的对象
    //故此可以定义一个类,由该类须实现View.OnClickListener接口,再创建成类对象传给setOnClickListener即可
    //
    //观察View.OnClickListener接口原型,其内部仅有一个函数需实现
    //      public interface OnClickListener {
    //          void onClick(View var1);
    //      }
    public class internalClass implements View.OnClickListener
    {
        @Override
        public void onClick(View view)
        {
            Toast.makeText(MainActivity.this,"Button is click",Toast.LENGTH_SHORT).show();
        }
    }
    private void clickFun()
    {
        Button button = findViewById(R.id.button1);
        button.setOnClickListener(new internalClass());
    }
}

方式三

匿名类设置点击函数

 

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:id="@+id/button1">
    </Button>
</LinearLayout>

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        clickFun();
    }
    private void clickFun()
    {
        Button button = findViewById(R.id.button1);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(MainActivity.this,"Button is click",Toast.LENGTH_SHORT).show();
            }
        });
    }
}

方式四

外部类设置点击函数

 

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Button"
        android:id="@+id/button1">
    </Button>
</LinearLayout>

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
// implements View.OnClickListener 为MainActivity类添加一个必须实现的接口函数
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        clickFun();
    }
    @Override
    public void onClick(View view) {
        Toast.makeText(MainActivity.this,
                "Button is click",
                Toast.LENGTH_SHORT).show();
    }
    private void clickFun()
    {
        Button button = findViewById(R.id.button1);
        button.setOnClickListener(this);
    }
}

RadioButton

示例

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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        RadioGroup radioGroup = findViewById(R.id.rg1);
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int i) {
                RadioButton radioButton = findViewById(i);
                String string = radioButton.getText().toString();
                Toast.makeText(MainActivity.this,"RadioGroup is chang" + string,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }
}

ListView

属性名称 说明
divider 设置分割线的颜色
dividerHeight 设置分割线的高度
Scrollbars 设置滚动条的隐藏或显示
Fadescrollbars 设置滚动条的自动隐藏或显示
Entries 引用一个将使用在此ListView里的数组。
 

示例

 

使用 Entries 属性进行布局

 

main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ListView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:entries="@array/nameList"
        android:divider="#000000"
        android:dividerHeight="2dp">
    </ListView>
</LinearLayout>

strings.xml

1
2
3
4
5
6
7
8
9
10
<resources>
    <string name="app_name">MyTestApp</string>
    <string-array name="nameList">
        <item>Tom</item>
        <item>Jack</item>
        <item>Lucy</item>
        <item>Bob</item>
        <item>Alin</item>
    </string-array>
</resources>

Adapter

适配器是数据和视图间的桥梁,它负责把数据所提供的内容显示到视图所定义的外观中

属性名称 说明
ArrayAdapter 适用于简单的文字列表
SimpleAdapter 适用于简单的图文混搭列表
SimpleCursorAdapter 适用于数据源是数据库的列表
自定义Adapter(继承自BaseAdapter) 最灵活的适配器,适用于绝大多数情况

ArrayAdapter

示例

 

使用ArrayAdapter实现文字列表

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.Toast;
 
import java.util.ArrayList;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        ListView listView = findViewById(R.id.lv1);
        String[] data = {"xiaoming","xiaohong","xiaogang","xiaoli"};
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,
                android.R.layout.simple_list_item_1,
                android.R.id.text1,data);
        listView.setAdapter(arrayAdapter);
    }
}

SimpleAdapter

SimpleAdapter是一种简单的适配器(然而并不简单),将静态数据映射到布局 xml 对应的视图上,是BaseAdapter的子类。

 

示例

 

view_list_item.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/logo"
        android:src="@mipmap/ic_launcher">
    </ImageView>
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginLeft="10dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="10dp"
            android:textSize="20dp"
            android:id="@+id/name"
            android:text="王者荣耀">
        </TextView>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/version"
            android:text="版本:">
        </TextView>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/size"
            android:text="大小:">
        </TextView>
    </LinearLayout>
 
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="25dp"
        android:layout_marginLeft="60dp"
        android:text="卸载">
    </Button>
 
</LinearLayout>

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.SeekBar;
import android.widget.SimpleAdapter;
import android.widget.Toast;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        ListView listView = findViewById(R.id.lv1);
 
        Map<String, Object> map1 = new HashMap<>();
        map1.put("logo", R.mipmap.ic_launcher);
        map1.put("title", "王者荣耀");
        map1.put("version", "version:1.2.3");
        map1.put("size", "size:4567M");
 
        Map<String, Object> map2 = new HashMap<>();
        map2.put("logo", R.mipmap.ic_launcher);
        map2.put("title", "绝地求生");
        map2.put("version", "version:1.2.3");
        map2.put("size", "size:4567M");
 
        Map<String, Object> map3 = new HashMap<>();
        map3.put("logo", R.mipmap.ic_launcher);
        map3.put("title", "方舟指令");
        map3.put("version", "version:1.2.3");
        map3.put("size", "size:4567M");
 
        List<Map<String, Object>> mapList = new ArrayList<>();
        mapList.add(map1);
        mapList.add(map2);
        mapList.add(map3);
 
        SimpleAdapter simpleAdapter = new SimpleAdapter(this, mapList,
                R.layout.view_list_item,
                new String[]{"logo","title","version","size"},
                new int[]{R.id.logo, R.id.name,R.id.version,R.id.size});
 
        listView.setAdapter(simpleAdapter);
 
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String name = mapList.get(i).get("title").toString();
                Toast.makeText(MainActivity.this,
                        "listview is click" + name,
                        Toast.LENGTH_SHORT).show();
            }
        });
 
    }
}

自定义Adapter

实现自定义Adapter步骤:

  1. 继承BaseAdapteir或者创建匿名BaseAdapter类

  2. 实现getView方法

  3. 关联getView

注意:自定义Adapter在设置监听器时,当ListView有含有Button、CheckBox等子View时,onclick事件触发是需要获得focus的,而当listView的内容View含有其他子控件也需要获取focus时,就会将focus交给其他子控件,以至于本身无法获得focus,所以就无法触发onclick时间了,可以通过采用子控件中的imageview、textview等onclick时间来替代onitemclick来解决,也可以在Button控件的属性加上android:focusable=”false"

 

示例

 

MyAdapter.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
package com.example.mytestapp;
 
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
 
public class MyAdapter extends BaseAdapter {
    private Context mContext;
    private List<Map<String, Object>> mMapList = new ArrayList<>();
 
    public MyAdapter(Context mContext, List<Map<String, Object>> mMapList) {
        this.mContext = mContext;
        this.mMapList = mMapList;
    }
 
    @Override
    public int getCount() {
        return mMapList.size();
    }
 
    @Override
    public Object getItem(int i) {
        return mMapList.get(i);
    }
 
    @Override
    public long getItemId(int i) {
        return i;
    }
 
    @Override
    public View getView(int i, View convertView, ViewGroup viewGroup) {
 
        View view = null;
        if (convertView == null)
        {
            LayoutInflater layoutInflater = LayoutInflater.from(mContext);
            view = layoutInflater.inflate(R.layout.view_list_item, null);
 
            Map<String,Object> map = mMapList.get(i);
 
            int logoid = (int)map.get("logo");
            String name = (String)map.get("name");
            String version = (String)map.get("version");
            String size = (String)map.get("size");
 
            ImageView logoView = view.findViewById(R.id.logo);
            TextView nameView = view.findViewById(R.id.name);
            TextView versionView = view.findViewById(R.id.version);
            TextView sizeView = view.findViewById(R.id.size);
 
            logoView.setImageResource(logoid);
            nameView.setText(name);
            versionView.setText(version);
            sizeView.setText(size);
        }else{
            view = convertView;
        }
        return view;
    }
}

view_list_item.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <ImageView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:id="@+id/logo"
        android:src="@mipmap/ic_launcher">
    </ImageView>
 
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_marginLeft="10dp">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="10dp"
            android:textSize="20dp"
            android:id="@+id/name"
            android:text="王者荣耀">
        </TextView>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/version"
            android:text="版本:">
        </TextView>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/size"
            android:text="大小:">
        </TextView>
    </LinearLayout>
 
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="25dp"
        android:layout_marginLeft="60dp"
        android:focusable="false"
        android:text="卸载">
    </Button>
 
</LinearLayout>

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        ListView listView = findViewById(R.id.lv1);
 
        Map<String, Object> map1 = new HashMap<>();
        map1.put("logo", R.mipmap.ic_launcher);
        map1.put("name", "王者荣耀");
        map1.put("version", "version:1.2.3");
        map1.put("size", "size:4567M");
 
        Map<String, Object> map2 = new HashMap<>();
        map2.put("logo", R.mipmap.ic_launcher);
        map2.put("name", "绝地求生");
        map2.put("version", "version:1.2.3");
        map2.put("size", "size:4567M");
 
        Map<String, Object> map3 = new HashMap<>();
        map3.put("logo", R.mipmap.ic_launcher);
        map3.put("name", "方舟指令");
        map3.put("version", "version:1.2.3");
        map3.put("size", "size:4567M");
 
        List<Map<String, Object>> mapList = new ArrayList<>();
        mapList.add(map1);
        mapList.add(map2);
        mapList.add(map3);
 
        MyAdapter myAdapter = new MyAdapter(this, mapList);
 
        listView.setAdapter(myAdapter);
 
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
                String name = mapList.get(i).get("name").toString();
                Toast.makeText(MainActivity.this,
                        "listview is click" + name,
                        Toast.LENGTH_SHORT).show();
            }
        });
    }
}

Spinner

示例

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        String[] strings = {"option1","option2","option3"};
        Spinner spinner = findViewById(R.id.s1);
        ArrayAdapter<String> adapter = new ArrayAdapter<>(this,
                android.R.layout.simple_list_item_1, android.R.id.text1,strings);
        spinner.setAdapter(adapter);
        spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
                String string = strings[i];
                Toast.makeText(MainActivity.this,string,Toast.LENGTH_SHORT).show();
            }
            @Override
            public void onNothingSelected(AdapterView<?> adapterView) {
 
            }
        });
    }
}

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Spinner
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/s1">
    </Spinner>
</LinearLayout>

AtuoCompleteTextView

自动补全输入框

 

示例

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
 
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        AutoCompleteTextView autoCompleteTextView = findViewById(R.id.act1);
        String[] strings = new String[]{"text1","text2","text3"};
 
        ArrayAdapter<String> arrayAdapter = new ArrayAdapter<>(this,
                android.R.layout.simple_list_item_1,android.R.id.text1,strings);
 
        autoCompleteTextView.setAdapter(arrayAdapter);
    }
}

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <AutoCompleteTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:completionThreshold="1"
        android:id="@+id/act1">
    </AutoCompleteTextView>
</LinearLayout>

数据存储

方式一

内部储存

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        try {
            fun();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
    private void fun() throws InterruptedException {
        try {
            File file = new File("data/data/"+getPackageName().toString()+"/info.txt");
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write("helloworld".getBytes());
            fileOutputStream.close();
        }catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
        Thread.sleep(1000);
 
        try {
            File file = new File("data/data/"+getPackageName().toString()+"/info.txt");
            FileInputStream fileInputStream = new FileInputStream(file);
            byte bytes[] = new byte[(int)file.length()];
            fileInputStream.read(bytes);
            fileInputStream.close();
            String string = new String(bytes);
 
            Toast.makeText(MainActivity.this,string,Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

方式二

app目录下存储,目录为 /data/data/包名/files,这种方式可以理解为对方式一的简单封装

 

相关API

函数名 功能
FilelntputStream openFileOutput( String name); 读取文件
FileOutputStream openFileOutput( String name, int mode); 模式
 

模式

模式 说明
Context.MODE_PRIVATE 私有方式,第二次读写会覆盖文件
Context.MODE_APPEND 追加方式,判断文件存在,追加到文件末尾
Context.MODE_WORLD_READABLE 其他程序可以读【危险】
Context.MODE_WORLD_WRITEABLE 其他程序可以写【危险】
Context.MODE_WORLD_READABLE 其他程序可以读写【危险】
 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Fun();
    }
    private void Fun()
    {
        try {
            FileOutputStream fileOutputStream = openFileOutput("private.txt", Context.MODE_PRIVATE);
            fileOutputStream.write("hello world".getBytes());
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
        try {
            FileOutputStream fileOutputStream = openFileOutput("append.txt", Context.MODE_APPEND);
            fileOutputStream.write("hello world".getBytes());
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
        try {
            FileOutputStream fileOutputStream = openFileOutput("worldRead.txt", Context.MODE_WORLD_READABLE);
            fileOutputStream.write("hello world".getBytes());
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
        try {
            FileOutputStream fileOutputStream = openFileOutput("worldWrite.txt", Context.MODE_WORLD_WRITEABLE);
            fileOutputStream.write("hello world".getBytes());
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
 
        try {
            FileOutputStream fileOutputStream = openFileOutput("worldReadWrite.txt", Context.MODE_WORLD_READABLE|Context.MODE_WORLD_WRITEABLE);
            fileOutputStream.write("hello world".getBytes());
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

方式三 SP

SharedPreferences方式,保存信息只能是基本数据类型和字符串,适用于保存解锁密码、登陆的用户名密码以及一些配置信息。操作目录为 "/data/data/" + getPackageName() + "/shared_prefs" ,其保存的文件格式是xml。

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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Fun();
    }
    private void Fun()
    {
        SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
        editor.putString("account","jiandan");
        editor.commit();
 
        SharedPreferences sharedPreferences = getSharedPreferences("data",MODE_PRIVATE);
        String string = sharedPreferences.getString("account","");// 参数二为defValue
        Toast.makeText(MainActivity.this,string,Toast.LENGTH_SHORT).show();
    }
}

方式四 Sqlite

相关类

类名 作用
SQLiteOpenHelper 创建数据库,更新数据库版本,打开数据库
SQLiteDatabase 操作数据库中的数据增删改查
 

SQLiteOpenHelper

 

SQLite0penHelper是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新。一般是建立一个类继承它,并实现它的onCreate和onUpgrade方法。

方法名 说明
SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version) 构造方法,一般需要传递一个数据
onCreate(SQLiteDatabase db) 第一次创建数据库时调用
onUpgrade(SQLiteDatabase db,int oldVersion , int newVersion) 版本更新时调用
getWritableDatabase() 创建数据库,并以读写方式打开数据库(磁盘满时不能写时会出错)
getReadableDatabase() 创建数据库,先以读写方式打开磁盘满了再以只读方式打开
 

SQLiteDatabase

 

SQLiteDatabase是一个数据库访问类,这个类封装了一系列数据库操作的APl,可以对数据库增删改查。

方法名 说明
(int) delete(String table,String whereClause,String[] whereArgs) 删除数据行
(long) insert(String table,String nullColumnHack,ContentValues values) 添加数据行
(int)update(String table, ContentValues values, String whereClause, String[] whereArgs) 更新数据行
(void) execSQL(String sql) 执行一个SQL语句,可以是一个select或其他的sql语句
(void)close) 关闭数据库
(Cursor) query(…) 查询指定的数据表返回一个带游标的数据集
(Cursor) rawQuery(String sql, String[] selectionArgs) 运行一个预置的SQL语句,返回带游标的数据集(与上面的语句最大的区别就是防止 SQL注入)
 

Cursor

 

Cursor游标(指针),用于访问查询到的结果集,遍历数据信息

方法名 方法描述
getCount() 总记录条数
isFirst() 判断是否第一条记录
isLast() 判断是否最后一条记录
moveToFirst() 移动到第一条记录
moveToLast() 移动到最后一条记录
move(int offset) 移动到指定的记录
moveToNext() 移动到下一条记录
moveToPrevious() 移动到上一条记录
getColumnlndex(String columnName) 获取指定列索引的int类型值
 

MainActivity.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
package com.example.mytestapp;
 
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        insert();
        update();
        query();
        delete();
    }
 
    public class DbHelper extends SQLiteOpenHelper {
        public DbHelper(@Nullable Context context, @Nullable String name,
                        @Nullable SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }
        @Override
        public void onCreate(SQLiteDatabase sqLiteDatabase) {
            String sql = "create table user(id int, name varchar(20))";
            sqLiteDatabase.execSQL(sql);
            Log.i("DbHelper","Create a Database");
        }
        @Override
        public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
            Log.i("DbHelper","Update a Database");
        }
    }
 
    private void insert() {
        DbHelper dbHelper = new DbHelper(MainActivity.this,"database",null,1);
        SQLiteDatabase sqLiteDatabase = dbHelper.getReadableDatabase();
 
        ContentValues contentValues = new ContentValues();
        contentValues.put("id", "1");
        contentValues.put("name", "xiaoming");
        sqLiteDatabase.insert("user",null,contentValues);;
        sqLiteDatabase.close();
    }
 
    private void delete() {
        DbHelper dbHelper = new DbHelper(MainActivity.this,"database",null,1);
        SQLiteDatabase sqLiteDatabase = dbHelper.getReadableDatabase();
        sqLiteDatabase.delete("user","id=?",new String[]{"1"});
        sqLiteDatabase.close();
    }
 
    private void query() {
        DbHelper dbHelper = new DbHelper(MainActivity.this,"database",null,1);
        SQLiteDatabase sqLiteDatabase = dbHelper.getReadableDatabase();
        Cursor cursor = sqLiteDatabase.query("user", new String[]{"id","name"},"id=?",
                new String[]{"1"},null,null,null,null);
 
        int num = 0;
        while(cursor.moveToNext()){
            int index = cursor.getColumnIndex("name");
            index = index > 0 ? index : 0;
            String string = cursor.getString(index);
            Log.i("Query", "query --> "+string);
            Toast.makeText(MainActivity.this,string,Toast.LENGTH_SHORT).show();
            num++;
        }
        if (num == 0)
        {
            Log.i("Query", "query fail");
            Toast.makeText(MainActivity.this,new String("query fail"),Toast.LENGTH_SHORT).show();
        }
        sqLiteDatabase.close();
    }
 
    private void update()
    {
        DbHelper dbHelper = new DbHelper(MainActivity.this,"database",null,1);
        SQLiteDatabase sqLiteDatabase = dbHelper.getReadableDatabase();
        ContentValues contentValues = new ContentValues();
        contentValues.put("id", "1");
        contentValues.put("name", "xiaobai");
        sqLiteDatabase.update("user",contentValues,"id=?",new String[]{"1"});
        sqLiteDatabase.close();
    }
}

组件

Android中四大组件:

  1. Activity(活动)前台界面
  2. Service(服务)后台服务
  3. Broadcast Receiver(广播接收者) 回调函数
  4. Content Provider(内容提供商) 共享数据

Activity

在开发的应用程序中,对Activity的操作分为以下几种:

  1. 对Activity本身的操作(比如我们需要做个游戏,需要响应鼠标事件,单击、触摸等事件)
  2. 要想程序功能完整,Activity往往都不止一个,多个Activity就涉及到它们之间的切换以及数据传递。
  3. 在系统中往往不止一个应用程序,当我们在使用的过程中,突然电话来了,界面会暂时消失,这涉及Activity的生命周期。
  4. 为了适应多种应用程序的启动和显示,Activity还分几种启动模式。

Activity生命周期

  • Activity 从创建到进入运行态所触发的事件

    onCreate()-->onStart-->onResume ()

  • 从运行态到停止态所触发的事件
    onPause() --->onStop()

  • 从停止态到运行态所触发事件
    onRestart()-->onStart()--->onResume()

  • 从运行态到暂停态所触发事件
    onPause()

  • 从暂停态到运行态所触发事件

    onResume()

QQ截图20230125152042

Acivity的任务栈

Android是通过一种任务栈的方式来管理 Activity 的

  • 当应用运行起来后就会开启一条线程,线程中会运行一个任务栈,当Activity实例创建后就会放入任务栈中。

  • 一个Activity的实例的状态决定它在栈中的位置。

  • 处于前台的 Activity总是在栈的顶端,当前台的Activity因为异常或其它原因被销毁时,处于栈第二层的 Activity将被激活,上浮到栈顶。

  • 当新的 Activity启动入栈时,原Activity会被压入到栈的第二层。

Acivity的启动模式

Activity启动模式的设置在 AndroidManifest.xml 文件中,通过配置Activity的属性 android:launchMode 设置启动模式。

  • standared模式(默认)

    我们平时直接创建的Activity都是这种模式的Activity,这种模式的Activity的特点是:只要你创建了Activity实例,一旦激活该Activity,则会向任务栈中加入新创建的实例,退出Activity则会在任务栈中销毁该实例。

  • singleTop模式

    这种模式会考虑当前要激活的Activity实例在任务栈中是否正处于栈顶,如果处于栈顶则无需重新创建新的实例,会重用已存在的实例调用onNewlntent,否则会在任务栈中创建新的实例。

  • singleTask模式

    如果任务栈中存在该模式的Activity实例,则把栈中该实例以上的Activity实例全部移除,调用该实例的onNew Intent方法重用该Activity,使该实例处于栈顶位置,否则就重新创建一个新的Activity实例。和下面的singleinstance一样,通常在launcher中使用,避免别的程序使用时产生孤岛,也在开销较大的activity中使用节约内存开销。

  • singlelnstance模式

    这种启动模式比较特殊,因为它会启用一个新的栈结构,将Acitvity放置于这个新的栈结构中,并保证不再有其他Activity实例进入。

    修改FirstActivity的launchMode="standard",SecondActivitv的launchMode="singlelnstance"

Activity的响应事件

Android平台的事件处理机制有两种,一种是基于回调机制的,另一种是基于监听接口的。
Android平台中,每个View都有自己的处理事件的回调方法,开发人员可以通过重写View中的这些回调方法来实现需要的响应事件。当某个事件没有被任何一个View处理时,便会调用Activity中相应的回调方法。

 

常用的回调方法

方法 功能
onKeyDown 按下触发
onKeyUp 弹起触发
onTouchEvent 触摸触发
onTrackballEvent 轨迹球处理
onFocusChanged 焦点改变时触发
 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        Log.d("test","onKeyDown");
        return super.onKeyDown(keyCode, event);
    }
    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        Log.d("test","onKeyUp");
        return super.onKeyUp(keyCode, event);
    }
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("test","onTouchEvent");
        return super.onTouchEvent(event);
    }
}

Activity的启动事件

  1. 新建 MainActivity2

    app --- java --- 包名(第一个) --- 右键 --- New --- Acivity --- EmptyAcivity --- Finish

  2. 调整 AndroidMainifest.xml 中acivity标签顺序(MainActivity2 放到 MainActivity下面)

  3. 调整 AndroidMainifest.xml 中acivity标签 MainActivity2 中 exported 属性为 true(默认为false)

MainActivity.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
package com.example.mytestapp;
 
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public void click1(View view)
    {
        Intent intent = getIntent();
        intent.setClass(this, MainActivity2.class);
        startActivity(intent);
    }
    public void click2(View view)
    {
        Intent intent = getIntent();
        intent.setClass(this, MainActivity2.class);
        intent.putExtra("text", "hello");
        startActivity(intent);
    }
    public void click3(View view)
    {
        Intent intent = getIntent();
        intent.setClass(this, MainActivity2.class);
        Bundle bundle = new Bundle();
        bundle.putString("text","hello");
        intent.putExtras(bundle);
        startActivity(intent);
    }
    public void click4(View view)
    {
        Intent intent = new Intent();
        intent.setClass(this, MainActivity2.class);
        intent.putExtra("text", "hello");
        startActivityForResult(intent,0x111);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(resultCode == 0x111)
        {
            String string = data.getStringExtra("text");
            Toast.makeText(this,string,Toast.LENGTH_SHORT).show();
        }
    }
}

MainActivity2.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
 
public class MainActivity2 extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Intent intent = getIntent();
        String string = intent.getStringExtra("text");
        if (string == null)
        {
            Bundle bundle = intent.getExtras();
            string = bundle.getString("text");
        }
        Toast.makeText(this,string,Toast.LENGTH_SHORT).show();
    }
    public void click1(View view)
    {
        finish();
    }
    public void click2(View view)
    {
        Intent intent = new Intent();
        intent.putExtra("text","world");
        setResult(0x111,intent);
        finish();
    }
}

activity_main.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="18dp"
        android:id="@+id/tv1"
        android:text="我是第一个页面,我接受到的内容:">
    </TextView>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bt1"
        android:onClick="click1"
        android:text="启动 MainActivity2 无传递参数">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bt2"
        android:onClick="click2"
        android:text="启动 MainActivity2 传递参数 方式一">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bt3"
        android:onClick="click3"
        android:text="启动 MainActivity2 传递参数 方式二">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bt4"
        android:onClick="click4"
        android:text="启动 MainActivity2 接受回传参数">
    </Button>
 
</LinearLayout>

activity_main2.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity2">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="20dp"
        android:id="@+id/tv2"
        android:text="我是第二个页面,我接受到的内容:">
    </TextView>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bt1"
        android:onClick="click1"
        android:text="返回时无传递数据">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/bt2"
        android:onClick="click2"
        android:text="返回时有传递数据">
    </Button>
</LinearLayout>

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".MainActivity2"
            android:exported="true"
            android:launchMode="standard">
            <intent-filter>
                <action android:name="android.intent.action.MAIN2" />
                <category android:name="android.intent.category.LAUNCHER2" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Intent

lntent是组件与组件通信的纽带。一个应用程序的三个核心组件:Activity、Service、Broadcast Receiver ,都是通过intents的消息激活的。

  • Activity:一个intent对象传递startActivity()或startActivityForRestult()去启动一个活动或使一个已存在的活动去做新的事情。
  • Service:一个intent对象传递给startService()去初始化一个service或传递一个新的指令给正在运行的service.类似的,一个intent可以传递给bindService()去建立调用组件和目标服务之间的连接。
  • Broadcast Receiver:一个intent对象传递给任何广播方法(如sendBroadcast(),sendOrderedBroadcast(),
    sendStickyBroadcast()),都将传递到所有感兴趣的广播接收者。

Intent对象的组成

一个Intent对象包含对intent有兴趣的组件的信息,有:

  1. 组件名称(包名或类名)Component、Class

    1. 通过setComponent ()/setClass()或setClassName()设置
    2. 通过getComponent()读取
  2. 动作Action

    1. 通过setAction设置,例如: ACTION_CALL
    2. 通过getAction获取
  3. 数据Data

    1. 数据(data)是将作用于其上的数据的URI和数据的MIME类型。
    2. 通过setData()方法指定数据的URl, setType()指定MIME类型
    3. 如果动作是ACTION_CALL,数据字段将是一个tel:URI和将拨打的号码
    4. 如果动作是ACTION_VIEW,数据字段是一个http:URI,接收活动将被调用去下载和显示URI指向的数据。
  4. 种类 Category

    1. 通过addCategory()添加,常见种类:CATEGORY_LAUNCHER
  5. 附件信息 Extras

    1. 附加信息可以作为一个Bundle使用putExtras()和getExtras ()安装和读取

显式和隐式Intent

显式intent:通过名字指定目标组件。本身程序使用居多

1
2
3
4
5
6
//创建工ntent对象
Intent intent = new Intent();
//设置启动的类
intent.setClass(MainActivity.this,Main2Activity.class);
//启动Actvity
startActivity(intent);

隐式intent:并不指定目标的名字(组件名字字段是空的)而是通过Action启动,其经常用于激活其它应用程序中的组件。

1
2
3
4
5
6
7
8
//创建一个意图对象
Intent intent = new Intent();
//设置打电话动作
intent.setAction( Intent.ACTION_CALL);
//打包数据,放入Intent
Uri data = Uri.parse( "tel: "+num);intent.setData(data);
//根据意图对象启动对应activity
startActivity(intent);

Intent过滤器

Intent Filter 就是用来注册 Activity Service 和 Broadcast Receiver 具有能在某种数据上执行一个动作的能力。

 

使用 Intent Filter,应用程序组件告诉Android,它们能为其它程序的组件的动请求提供服务,包括同一个程序的组件、本地的或第三方的应用程序。

 

为了注册一个应用程序组件为 Intent 处理者,在组件的 manifest 节点添加一个 intent-filter 标签。

 

常见的Intent Filter检测:

  1. 动作检测
    清单文件中的\<intent-filter>元素以\<action>子元素列出动作

  2. 种类检测
    清单文件中的\<intent-filter>元素以\<category>子元素列出种类

  3. 数据检测
    清单文件中的\<intent-filter>元素以\<data>子元素列出数据
    每个\<data>元素指定一个URI和数据类型(MIME类型)。它有四个属性scheme、host、port、path对应于URI的每个部分:scheme://host:port/path

Broadcast Receiver

广播分为无序广播与有序广播

  • 无序广播

    • 与lntent匹配的广播接收者都可以收到该广播,并且是没有先后顺序(同时收到)
    • 广播接收者无法使用setResult系列、getResult系列及abort(中止)系列API,使用sendBroadCast发送广播
  • 有序广播

    • 与Intent匹配的广播接收者都可以收到该广播,但是会按照广播接收者的优先级来决定接收的先后顺序
    • 可以使用 setResult 系列函数来结果传给下一个接收者通过 getResult 系列函数来取得上个接收者
    • 优先级的定义:-1000~1000
    • 最终接收者:所有广播接收者都接收到广播之后,它才接收,并且一定会接收,在发出广播时指定
    • sendOrderedBroadcast
    • 拦截有序广播:abortBroadCast

广播电话拦截

  1. 新建 Broadcast Receiver类

    1. app --- java --- 包名(第一个) --- 右键 --- New --- Other--- Broadcast Receiver --- Finish
    2. MyReceiver.java 文件中注释掉默认的抛出异常代码
  2. 添加 Action 动作

    1. AndroidManifest.xml 文件中 receiver 标签中添加 intent-filter 标签,设置 action 属性
    2. receiver 标签中 intent-filter 标签中 category 属性置为 android.intent.category.DEFAULT
  3. 添加接收电话动作广播权限

    1. manifest 中添加 uses-permission 标签,值为 android.permission.PROCESS_OUTGOING_CALLS

MyReceiver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.mytestapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
 
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        String string = getResultData();
        setResultData("123"+string);
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

广播短信拦截

  1. 新建 Broadcast Receiver类

    1. app --- java --- 包名(第一个) --- 右键 --- New --- Other--- Broadcast Receiver --- Finish
    2. MyReceiver.java 文件中注释掉默认的抛出异常代码
  2. 添加 Action 动作

    1. AndroidManifest.xml 文件中 receiver 标签中添加 intent-filter 标签,设置 action 属性
    2. receiver 标签中 intent-filter 标签中 category 属性置为 android.intent.category.DEFAULT
  3. 提升优先级

    1. intent-filter 中添加priority属性,值为1000(1000是最大优先级)
  4. 添加接收电话动作广播权限

    1. manifest 中添加 uses-permission 标签,值为 android.permission.RECEIVE_SMS

MyReceiver.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
package com.example.mytestapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.widget.Toast;
 
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        Bundle bundle = intent.getExtras();
        Object[] objects = (Object[])bundle.get("pdus");
        for(Object ob:objects)
        {
            SmsMessage smsMessage = SmsMessage.createFromPdu((byte[])ob);
            Toast.makeText(context,smsMessage.getMessageBody(),Toast.LENGTH_SHORT).show();
        }
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter android:priority="1000">
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

SD卡状态监听

  1. 新建 Broadcast Receiver类

    1. app --- java --- 包名(第一个) --- 右键 --- New --- Other--- Broadcast Receiver --- Finish
    2. MyReceiver.java 文件中注释掉默认的抛出异常代码
  2. 添加 Action 动作

    1. AndroidManifest.xml 文件中 receiver 标签中添加 intent-filter 标签,添加 action 标签
    2. action 标签可同时添加多条关于SD卡的动作,分别为:
      1. android.intent.action.MEDIA_MOUNTED
      2. android.intent.action.MEDIA_UNMOUNTED
      3. android.intent.action.MEDIA_REMOVED
    3. receiver 标签中 intent-filter 标签中 category 属性置为 android.intent.category.DEFAULT
  3. 设置动作类型
    1. intent-filter 中添加 data 标签,设置 scheme 属性,值为 file

MyReceiver.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
package com.example.mytestapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
 
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        String string = intent.getAction();
        if(string.equals("android.intent.action.MEDIA_MOUNTED"))
        {
            Toast.makeText(context,"SD卡状态监听:SD卡就绪",Toast.LENGTH_SHORT).show();
        }else if(string.equals("android.intent.action.MEDIA_UNMOUNTED"))
        {
            Toast.makeText(context,"SD卡状态监听:SD卡移除",Toast.LENGTH_SHORT).show();
        }else if(string.equals("android.intent.action.MEDIA_REMOVED"))
        {
            Toast.makeText(context,"SD卡状态监听:SD卡拔出",Toast.LENGTH_SHORT).show();
        }
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_MOUNTED" />
                <action android:name="android.intent.action.MEDIA_UNMOUNTED" />
                <action android:name="android.intent.action.MEDIA_REMOVED" />
                <data android:scheme="file" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

广播开机启动

  1. 新建 Broadcast Receiver类

    1. app --- java --- 包名(第一个) --- 右键 --- New --- Other--- Broadcast Receiver --- Finish
    2. MyReceiver.java 文件中注释掉默认的抛出异常代码
  2. 添加 Action 动作

    1. AndroidManifest.xml 文件中 receiver 标签中添加 intent-filter 标签,添加 action 标签
    2. action 标签可同时添加 name 属性,值为 android.intent.action.BOOT_COMPLETED
    3. receiver 标签中 intent-filter 标签中 category 属性置为 android.intent.category.DEFAULT
  3. 添加权限
    1. manifest 中添加 uses-permission 标签
    2. uses-permission 标签添加 name属性,值为 android.permission.RECEIVE_BOOT_COMPLETED

MyReceiver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.example.mytestapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;
 
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        Toast.makeText(context,"开机启动",Toast.LENGTH_LONG).show();
        Intent intent1 = new Intent(context, MainActivity.class);
        intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent1);
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

监听应用安装卸载

  1. 新建 Broadcast Receiver类

    1. app --- java --- 包名(第一个) --- 右键 --- New --- Other--- Broadcast Receiver --- Finish
    2. MyReceiver.java 文件中注释掉默认的抛出异常代码
  2. 添加 Action 动作

    1. AndroidManifest.xml 文件中 receiver 标签中添加 intent-filter 标签,添加 action 标签
    2. action 标签可同时添加多条关于SD卡的动作,分别为:
      1. android.intent.action.PACKAGE_ADDED
      2. android.intent.action.PACKAGE_REPLACED
      3. android.intent.action.PACKAGE_REMOVED
    3. receiver 标签中 intent-filter 标签中 category 属性置为 android.intent.category.DEFAULT
  3. 设置动作类型
    1. intent-filter 中添加 data 标签,设置 scheme 属性,值为 package

MyReceiver.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
package com.example.mytestapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        Uri uri = intent.getData();
        String action = intent.getAction();
        if(action.equals("android.intent.action.PACKAGE_ADDED"))
        {
            Toast.makeText(context,"应用程序"+uri+"安装",Toast.LENGTH_SHORT).show();
        }else if(action.equals("android.intent.action.PACKAGE_REPLACED"))
        {
            Toast.makeText(context,"应用程序"+uri+"更新",Toast.LENGTH_SHORT).show();
        }else if(action.equals("android.intent.action.PACKAGE_REMOVED"))
        {
            Toast.makeText(context,"应用程序"+uri+"卸载",Toast.LENGTH_SHORT).show();
        }
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.PACKAGE_REPLACED" />
                <action android:name="android.intent.action.PACKAGE_REMOVED" />
                <data android:scheme="package" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

发送与接收

发送

 

MainActivity.java

 

第17行中的 “CustomBroadcast” 为动作名称,接收方与其一致即可

 

第18行中的 “data” 为动作类型,接收方与其一致即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        fun();
    }
    private void fun() {
        Intent intent = new Intent();
        intent.setAction("CustomBroadcast");
        Uri uri = Uri.parse("data:"+123456);
        intent.setData(uri);
        sendBroadcast(intent);
    }
}

接收

  1. 新建 Broadcast Receiver类
    1. app --- java --- 包名(第一个) --- 右键 --- New --- Other--- Broadcast Receiver --- Finish
    2. MyReceiver.java 文件中注释掉默认的抛出异常代码
  2. 添加 Action 动作
    1. AndroidManifest.xml 文件中 receiver 标签中添加 intent-filter 标签,添加 action 标签
    2. action 标签中添加属性 name ,值为 CustomBroadcast (与发送方动作名称一致即可)
    3. receiver 标签中 intent-filter 标签中 category 属性置为 android.intent.category.DEFAULT
  3. 设置动作类型
    1. intent-filter 中添加 data 标签,设置 scheme 属性,值为 data(与发送方一致即可)

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver
            android:name=".MyReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="CustomBroadcast" />
                <data android:scheme="data" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

MyReceiver.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.example.mytestapp;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.widget.Toast;
public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // TODO: This method is called when the BroadcastReceiver is receiving
        // an Intent broadcast.
        //throw new UnsupportedOperationException("Not yet implemented");
        Uri uri = intent.getData();
        Toast.makeText(context, uri.toString() ,Toast.LENGTH_SHORT).show();
    }
}

Service

生命周期

如果 Service 还没有运行,则 android 先调用 onCreate() 然后调用 onStartCommand() 如果 Service 已经运行,则只调用 onStartCommand(),所以一个 Service 的 onStartCommand 方法可能会重复调用多次。

启动方式

service 有两种启动方式,startService 和 bindService

  • startService方式
    该方法启动的服务所在的进程属于服务进程,Activity一旦启动服务,服务就跟Activity无关
  • binService方式
    该方法启动的服务所在进程不属于服务进程,Activity与服务建立连接,Activity退出,服务也退出

startService

  • 通过调用 Context.startService() 启动服务
  • 通过调用 Context.stopService() 或 Service.stopSelf() 停止服务
  • startService 启动方式可以隐式启动,指定Action即可
  • Service 是由其他的组件启动的,但停止过程可以通过其他组件或自身完成
  • startService 方式服务不能直接与 Activity 直接通信
  • 启动流程:context.startService() —> onCreate() —> onStartCommand() —> Service runningcontext.stopService() —> onDestory() —> Service stop

创建Service类

  • app — java — 包名(第一个) — 右键 —New — Service — Service — Finish
startService 示例1

实现最简单的服务

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
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 click1(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        startService(intent);
    }
    public void click2(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        stopService(intent);
    }
}

MyService.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
package com.example.mytestapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
 
public class MyService extends Service {
    public MyService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    @Override
    public void onCreate() {
        Log.d("lxz", "service is onCreate");
        super.onCreate();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("lxz", "service is onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        Log.d("lxz", "service is onDestroy");
        super.onDestroy();
    }
}

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button1"
        android:onClick="click1"
        android:text="启动服务">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:onClick="click2"
        android:text="停止服务">
    </Button>
</LinearLayout>

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
        </service>
    </application>
</manifest>
sartService 示例2

实现一个电话监听器,其重点在于以下几点:

  • 在 MyService 中定义一个内部类,用于继承并实现 PhoneStateListener 接口

  • 添加读取电话状态的权限

    AndroidManifest.xml 中添加 uses-permission 标签,值为 android.permission.READ_PHONE_STATE

  • 停止服务时将监听状态置为 NONE

MyService.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
package com.example.mytestapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import androidx.annotation.RequiresApi;
 
public class MyService extends Service {
    private TelephonyManager telephonyManager;
    private MyPhoneStateListener mListener;
 
    public MyService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onCreate() {
        Log.d("lxz", "service is onCreate");
        super.onCreate();
        telephonyManager = (TelephonyManager)getSystemService(TELEPHONY_SERVICE);
        mListener = new MyPhoneStateListener();
        telephonyManager.listen(mListener, PhoneStateListener.LISTEN_CALL_STATE);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("lxz", "service is onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        Log.d("lxz", "service is onDestroy");
        super.onDestroy();
        telephonyManager.listen(mListener, PhoneStateListener.LISTEN_NONE);
    }
    private class MyPhoneStateListener extends PhoneStateListener{
        @Override
        public void onCallStateChanged(int state, String phoneNumber) {
            super.onCallStateChanged(state, phoneNumber);
            switch (state)
            {
                case TelephonyManager.CALL_STATE_IDLE:
                    Log.d("lxz", "电话闲置");
                    break;
                case TelephonyManager.CALL_STATE_RINGING:
                    Log.d("lxz", "电话响铃");
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    Log.d("lxz", "电话接听");
                    break;
            }
        }
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
        </service>
    </application>
</manifest>

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
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 click1(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        startService(intent);
    }
    public void click2(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        stopService(intent);
    }
}

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button1"
        android:onClick="click1"
        android:text="启动服务">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:onClick="click2"
        android:text="停止服务">
    </Button>
</LinearLayout>
sartService 示例3

实现一个定时器,其重点在于以下几点:

  • Service 与 Acivity 以广播方式进行的交互
  • 在Activity中以内部类方式创建广播接收者,重写onReceive方法,用于接收广播
  • 在Activity 的 onCreat方法中动态中注册广播接收者

MyService.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
package com.example.mytestapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
 
public class MyService extends Service {
    private Timer timer;
    private int time = 0;
 
    public MyService() {
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    @Override
    public void onCreate() {
        Log.d("lxz", "service is onCreate");
        super.onCreate();
        timer = new Timer();
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                Log.d("lxz", "time is run");
                Intent intent = new Intent();
                intent.setAction("showTime.lxz");
                intent.putExtra("time","时间:" + time);
                sendBroadcast(intent);
                time++;
            }
        };
        timer.schedule(timerTask,0,1000);
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("lxz", "service is onStartCommand");
        time = 0;
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        Log.d("lxz", "service is onDestroy");
        super.onDestroy();
        if(timer != null)
        {
            timer.cancel();
            timer = null;
        }
    }
}

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    private TimerReceiver timerReceiver;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        timerReceiver = new TimerReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction("showTime.lxz");
        registerReceiver(timerReceiver, intentFilter);
    }
 
    public void click1(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        startService(intent);
    }
    public void click2(View view) {
        Intent intent = new Intent();
        intent.setClass(this, MyService.class);
        stopService(intent);
    }
 
    public class TimerReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if(action.equals("showTime.lxz"))
            {
                String time = intent.getStringExtra("time");
                Toast.makeText(MainActivity.this,"time:" + time, Toast.LENGTH_SHORT).show();
            }
        }
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
        </service>
    </application>
</manifest>

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button1"
        android:onClick="click1"
        android:text="启动服务">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:onClick="click2"
        android:text="停止服务">
    </Button>
</LinearLayout>

bindService

  • 通过服务链接(Connection)或直接获取Service中状态和数据信息
  • 服务链接能够获取Service的对象,因此绑定Service的组件可以调用Service中的实现的函数
  • 使用Service的组件通过Context. bindService()建立服务链接,通过Context.unbindService()停止服务链接
  • 如果在绑定过程中Service没有启动,Context.bindService() 会自动启动Service
  • 同一个Service可以绑定多个服务链接,这样可以同时为多个不同的组件提供服务
  • context. bindService() —> onCreate() —> onBind() —> Service runningcontext.stopService() —>onDestory() —> Service stop
bindService 示例1

通过 bindService 实现一个本地的人员查询系统,重点在于以下几点:

  • 创建内部服务类,继承自Binder,重写onCreate方法和onBind方法
  • 在服务类中创建MyBinder类,实现接口
  • 在Activity中编写绑定服务和解绑服务代码
  • 在Activity中创建自定义服务连接类
  • 在Activity中响应按钮,调用Binder对象方法,完成查询

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
 
public class MainActivity extends AppCompatActivity{
    private MyServiceConnection myServiceConnection;
    private MyService.MyBinder myBinder;
    private boolean isBind = false;
    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.d("lxz", "service is onServiceConnected");
            myBinder = (MyService.MyBinder)iBinder;
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.d("lxz", "service is onServiceDisconnected");
        }
    }
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        myServiceConnection = new MyServiceConnection();
    }
 
    public void click1(View view) {
        if (isBind == true)
            return;
        Intent intent = new Intent(this, MyService.class);
        bindService(intent,myServiceConnection,BIND_AUTO_CREATE);
    }
    public void click2(View view) {
        if (isBind == false)
            return;
        Intent intent = new Intent(this, MyService.class);
        unbindService(myServiceConnection);
        stopService(intent);
        myBinder = null;
    }
    public void click3(View view) {
        EditText editText = findViewById(R.id.et1);
        String string = editText.getText().toString();
        if(string.equals(""))
            return ;
        int id = Integer.parseInt(string);
        if(myBinder != null)
        {
            String name = myBinder.queryNameById(id);
            TextView textView = findViewById(R.id.tv1);
            textView.setText(name);
        }
    }
}

MyService.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
package com.example.mytestapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
 
public class MyService extends Service {
    private MyBinder myBinder;
    private int time = 0;
    String strings[] = {"小明","小白","小红","小刚"};
 
    public MyService() {}
    public class MyBinder extends Binder implements IPerssonListeren{
        @Override
        public String queryNameById(int id) {
            if(id > 0 && id < 4)
                return strings[id];
            return "";
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        //throw new UnsupportedOperationException("Not yet implemented");
        Log.d("lxz", "service is onBind");
        return myBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("lxz", "service is onUnbind");
        return super.onUnbind(intent);
    }
    @Override
    public void onCreate() {
        Log.d("lxz", "service is onCreate");
        super.onCreate();
        myBinder = new MyBinder();
    }
    @Override
    public void onDestroy() {
        Log.d("lxz", "service is onDestroy");
        super.onDestroy();
        if(myBinder != null)
            myBinder = null;
    }
}

IPerssonListeren.java

1
2
3
4
5
package com.example.mytestapp;
 
public interface IPerssonListeren {
    String queryNameById(int id);
}

activity_main.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button1"
        android:onClick="click1"
        android:text="绑定服务">
    </Button>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button2"
        android:onClick="click2"
        android:text="解绑服务">
    </Button>
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:textSize="20dp"
        android:id="@+id/et1">
    </EditText>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/button3"
        android:onClick="click3"
        android:text="查询">
    </Button>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20dp"
        android:id="@+id/tv1">
    </TextView>
</LinearLayout>

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
        </service>
    </application>
</manifest>
bindService 示例2

通过 bindService 实现一个跨进程的人员查询系统,基本原理及步骤如下:

  • 在服务中创建自定义Binder类提供查询服务,使用AIDL导出接口,创建单独的客户端,在服务连接对象中获取服务中的AlDL Binder对象从而完成查询。
  • 创建PersonSelectService,重写onCreate、onBind方法。
  • 抽取接口,创建AIDL接口文件,IPersonL.istenerAlDL、queryNameByld
  • 创建内部类MyBinder类,继承自Stub,提供查询方法,返回名称。
  • 在onCreate方法中创建MyBinder对象
  • 在onBind方法中返回MyBinder对象
  • 在清单文件中配置服务,对外暴露,需使用意图过滤器指定动作
  • 创建另一个APP界面,将AIDL接口文件连带包都复制过来
  • 在Activity中调用bindService方法,重写
  • 在ServiceConnection中重写onServiceConnected方法
  • 通过Stub.asInterface获取Binder
  • 通过AIDL对象调用查询方法,完成查询功能。

其重点如下:

  • 创建AIDL文件后,需点击IDE上方的的小锤子进行编译,编译后才能在MyService文件中继承该接口
  • 跨进程访问是没有办法直接访问类名的,需要在清单文件中服务标签注册动作,以指定Action这种方式进行隐式启动
  • 在将AIDL文件复制到另一个APP中后,该AIDL文件还需在新的工程中重新编译一次

创建AIDL文件

 

app — java — 包名(第一个) — 右键 —New — AIDL— AIDL File— Finish

 

复制AIDL接口文件及包

 

文件列表切换到Project模式,目录如下 app — src — main — aidl,复制该文件夹至新APP同文件夹目录下,注意AIDL文件复制后需重新编译一次

 

APP1

 

MyService.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
package com.example.mytestapp;
 
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
 
public class MyService extends Service {
    private MyBinder myBinder;
    private int time = 0;
    String strings[] = {"小明","小白","小红","小刚"};
 
    public MyService() {}
    public class MyBinder extends IPersonListenerAIDL.Stub{
        @Override
        public String queryNameById(int id) {
            if(id > 0 && id < 4)
                return strings[id];
            return "";
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        //throw new UnsupportedOperationException("Not yet implemented");
        Log.d("lxz", "service is onBind");
        return myBinder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("lxz", "service is onUnbind");
        return super.onUnbind(intent);
    }
    @Override
    public void onCreate() {
        Log.d("lxz", "service is onCreate");
        super.onCreate();
        myBinder = new MyBinder();
    }
    @Override
    public void onDestroy() {
        Log.d("lxz", "service is onDestroy");
        super.onDestroy();
        if(myBinder != null)
            myBinder = null;
    }
}

IPersonListenerAIDL.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// IPersonListenerAIDL.aidl
package com.example.mytestapp;
 
// Declare any non-default types here with import statements
 
interface IPersonListenerAIDL {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    //void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
    //        double aDouble, String aString);
    String queryNameById(int id);
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.MyBindService" />
            </intent-filter>
        </service>
    </application>
</manifest>

APP2

 

MainActivity.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
package com.example.bindtestapp;
 
import androidx.appcompat.app.AppCompatActivity;
 
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
 
import com.example.mytestapp.IPersonListenerAIDL;
 
public class MainActivity extends AppCompatActivity {
    IPersonListenerAIDL aidl;
    MyServiceConnection myServiceConnection;
    private class MyServiceConnection implements ServiceConnection {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.d("lxz", "service is onServiceConnected");
            aidl = IPersonListenerAIDL.Stub.asInterface(iBinder);
        }
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.d("lxz", "service is onServiceDisconnected");
        }
    }
 
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myServiceConnection = new MyServiceConnection();
    }
 
    public void click1(View view) {
        Intent intent = new Intent("com.MyBindService");
        bindService(intent,myServiceConnection,BIND_AUTO_CREATE);
    }
    public void click2(View view) {
        Intent intent = new Intent("com.MyBindService");
        unbindService(myServiceConnection);
        stopService(intent);
    }
    public void click3(View view) {
        EditText editText = findViewById(R.id.edit1);
        String string = editText.getText().toString();
        if(string.equals(""))
            return ;
        int id = Integer.parseInt(string);
        if(aidl != null)
        {
            String name = null;
            try {
                name = aidl.queryNameById(id);
            } catch (RemoteException e) {
                throw new RuntimeException(e);
            }
            TextView textView = findViewById(R.id.text1);
            textView.setText(name);
        }
    }
}

IPersonListenerAIDL.aidl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// IPersonListenerAIDL.aidl
package com.example.mytestapp;
 
// Declare any non-default types here with import statements
 
interface IPersonListenerAIDL {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    //void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
    //        double aDouble, String aString);
    String queryNameById(int id);
}

activity_main.xml

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
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="绑定服务"
        android:onClick="click1"
        android:id="@+id/button1">
    </Button>
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="解绑服务"
        android:onClick="click2"
        android:id="@+id/button2">
    </Button>
 
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="查询"
        android:onClick="click3"
        android:id="@+id/button3">
    </Button>
    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:id="@+id/edit1">
    </EditText>
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:id="@+id/text1">
    </TextView>
</LinearLayout>

ContentProvider

Android应用程序运行在不同的进程空间中,因此不同应用程序的数据是不能够直接访问的。为了增强程序之间的数据共享能力,Android系统虽然提供了像 SharedPreferences 这类简单的跨越程序边界的访问方法,但这些方法都存在一定的局限性。

 

ContentProvider 提供了应用程序之间共享数据的方法。应用程序通过 ContentProvider 访问数据而不需要关心数据具体的存储及访问过程,这样既提高了数据的访问效率,同时也保护了数据。

 

Android系统附带的ContentProvider包括:

  • Browser:存储如浏览器的信息
  • CallLog:存储通话记录等信息
  • Contacts:存储联系人等信息
  • MediaStore:存储媒体文件的信息
  • Settings:存储设备的设置和首选项信息

关于ContentProvider数据集

 

ContentProvider数据集类似于数据库的数据表,每行是一条记录,每列具有相同的数据类型。

 

ContentResolver 内容解析者

 

应用程序使用 ContentResolver 对象,利用URI才能访问 ContentProvider 提供的数据集。一个ContentProvider可以提供多个数据集可以为多个ContentResolver服务。

 

URI

 

Uniform Resource ldentifier 通用资源标志符,用来定位远程或本地的可用资源。

 

URI基本格式

 

content://authority/data_path/id

  • content 固定前缀
  • authority 授权者名称,应保证唯一性,用于确定具体又 ContentProvider 提供资源
  • data_path 数据路径,用于确定请求的数据集
  • id 数据编号,用于匹配数据集中 _ID 字段的值(确定数据集中唯一的一条数据),如果需请求的数据不只限于一条,则该项省略

URI示例

  • content://contacts/people/ 表示全部联系人信息的URI
  • content://contacts/people/1 表示ID=1的联系人信息的URI
  • content://com. android.contacts/contacts/ 全部联系人信息的URI(原生写法)
  • ContactsContract.Contacts.CONTENT_URI 全部联系人信息的URI(常量写法)

由于URI通常比较长,而且容易写错,所以,在Android系统中定义了一些辅助类和常量来代替这些长字符串。

 

URI和URL

  • 在Android中广泛应用URI,而不是URL。
  • URL是标识资源的物理位置,相当于文件的路径(例如 http://www.163.com/logo.png)
  • URI则是标识资源的逻辑位置,并不提供资源的具体位置。
    • 比如说电话薄中的数据,如果用URL来标识的话:可能是一个很复杂的文件结构,而且一旦文件的存储路径改变,URL也必须得改动。
    • 但若是URI,则可以用诸如content://contract /people这样容易记录的逻辑地址来标识,而且并不需要关心文件的具体位置,即使文件位置改动也不需要做变化。
    • 当然这都是对于用户来说,后台程序中URI到具体位置的映射还是需要程序员来改动的。

常见的URI

 

具体的还有很多CONTENT_URI,可以到ContactsContract.class文件中去查看源码

常量值 作用
ContactsContract.Contacts.CONTENT_URI 管理联系人的Uri
ContactsContract.CommonDataKinds.Phone.CONTENT_URI 管理联系人电话的Uri
ContactsContract.CommonDataKinds.Email.CONTENT_URl 管理联系人邮箱的Uri
ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_URI 管理联系人地址的Uri
 

创建ContentProvider类

 

app — java — 包名(第一个) — 右键 — New — Other — ContentProvider — 指定URI Authorities — Finish

示例1

实现最简单的ContentProvider

 

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Toast;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://myContentProvider");
        Cursor cursor = resolver.query(uri,null,null,null,null,null);
        Toast.makeText(this,"cursor:"+cursor,Toast.LENGTH_SHORT).show();
    }
}

MyContentProvider.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
package com.example.mytestapp;
 
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
 
public class MyContentProvider extends ContentProvider {
    public MyContentProvider() {
    }
 
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }
 
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        //throw new UnsupportedOperationException("Not yet implemented");
        Log.d("lxz","MyContentProvider  query");
        return null;
    }
 
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
 
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <provider
            android:name=".MyContentProvider"
            android:authorities="myContentProvider"
            android:enabled="true"
            android:exported="true">
        </provider>
    </application>
 
    <uses-permission android:name="android.permission.INTERNET" />
 
</manifest>

示例2

在自己的ContentProvider中访问数据库,其重点在于使用UriMatcher对象保存uri,并使用其match方法匹配uri

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://myContentProvider");
        ContentValues values = new ContentValues();
        for(int i = 0; i < 10; i++)
        {
            values.put("_id",""+i);
            values.put("name","简单"+i);
            resolver.insert(uri,values);
        }
 
        Uri uri2 = Uri.parse("content://myContentProvider/person");
        Cursor cursor = resolver.query(uri2,null,null,null,null,null);
        Log.d("lxz","cursor"+cursor);
        while (cursor.moveToNext())
        {
            int id = cursor.getInt(0);
            String name = cursor.getString(1);
            Log.d("lxz","id = "+ id + " name = " + name);
        }
    }
}

MyContentProvider.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
package com.example.mytestapp;
 
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
 
public class MyContentProvider extends ContentProvider {
    private DBHelper mDBHelper;
    private SQLiteDatabase mSQLiteDatabase;
    public MyContentProvider() {
    }
 
    private static UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int PERSONS = 0X123;
    private static final int PERSON_ID = 0X456;
 
    static{
        sUriMatcher.addURI("myContentProvider","person", PERSONS);
        sUriMatcher.addURI("myContentProvider","person/#", PERSON_ID);
    }
 
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }
 
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO: Implement this to handle requests to insert a new row.
        //throw new UnsupportedOperationException("Not yet implemented");
        mSQLiteDatabase = mDBHelper.getReadableDatabase();
        long id = mSQLiteDatabase.insert("person",null,values);
        return ContentUris.withAppendedId(uri,id);
    }
 
    @Override
    public boolean onCreate() {
        // TODO: Implement this to initialize your content provider on startup.
        mDBHelper = new DBHelper(getContext());
        return true;
    }
 
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        //throw new UnsupportedOperationException("Not yet implemented");
        Log.d("lxz","MyContentProvider  query");
 
        int code = sUriMatcher.match(uri);
        switch (code)
        {
            case PERSONS:
            {
                return mSQLiteDatabase.query("person",new String[]{"_id","name"},null,null,null,null,null);
 
            }
            case PERSON_ID:
            {
                long id = ContentUris.parseId(uri);
                return mSQLiteDatabase.query("person",new String[]{"_id","name"},"_id=?",new String[]{""+id},null,null,null);
            }
        }
 
        return null;
    }
 
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

DBHelper.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
package com.example.mytestapp;
 
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import androidx.annotation.Nullable;
 
public class DBHelper extends SQLiteOpenHelper {
    public DBHelper(@Nullable Context context) {
        super(context, "mydb.db", null, 1);
    }
 
    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        String sql = "create table person(_id integer primary key autoincrement, name varchar(20))";
        sqLiteDatabase.execSQL(sql);
        Log.i("DbHelper","Create a Database");
    }
 
    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
 
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
 
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <provider
            android:name=".MyContentProvider"
            android:authorities="myContentProvider"
            android:enabled="true"
            android:exported="true">
        </provider>
    </application>
 
</manifest>

示例3

读写系统应用通讯录的ContentProvider,其重点在于以下几点:

  • 读写系统应用通讯录的ContentProvider需要权限,分别为

    android.permission.READ_CONTACTS 和 android.permission.READ_CONTACTS

  • 数据库中直接看到的 mimetype_id 项并不存在,该项为多表查询,真实字段为 mimetype,可以通过在代码中遍历列名观察到

  • mimetype 是 String类型,而不是在数据库中看到的 int 类型

  • 添加联系人时,应先在 raw_contacts 中添加一个空项,然后再在 data 中添加各种数据

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InsertData("小红","123456","xiaohong.com","tianjin");
        InsertData("xiaoli","121156","xiaoli.com","北京");
        QueryData();
    }
 
    private void InsertData(String name, String number, String email, String adress)
    {
        // 在 raw_contacts 中添加一个空项
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        ContentValues values = new ContentValues();
        uri = resolver.insert(uri, values);
        long raw_contact_id = ContentUris.parseId(uri);
 
        // 在 data 中添加各种数据
        uri = Uri.parse("content://com.android.contacts/data");
        values.clear();
        values.put("raw_contact_id", raw_contact_id);
        values.put("data1", number);
        values.put("mimetype", "vnd.android.cursor.item/phone_v2");
        values.put("data2", 1);
        resolver.insert(uri, values);
 
        values.clear();
        values.put("raw_contact_id", raw_contact_id);
        values.put("data1", adress);
        values.put("mimetype", "vnd.android.cursor.item/postal-address_v2");
        values.put("data2", 1);
        resolver.insert(uri, values);
 
        values.clear();
        values.put("raw_contact_id", raw_contact_id);
        values.put("data1", email);
        values.put("mimetype", "vnd.android.cursor.item/email_v2");
        values.put("data2", 1);
        resolver.insert(uri, values);
 
        values.clear();
        values.put("raw_contact_id", raw_contact_id);
        values.put("data1", name);
        values.put("mimetype", "vnd.android.cursor.item/name");
        resolver.insert(uri, values);
    }
 
    private void QueryData()
    {
        List<PersonInfo> personInfoList = new ArrayList<>();
        ContentResolver resolver = getContentResolver();
        Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
        Cursor cursor = resolver.query(uri,new String[]{"_id","display_name"},null,null,null,null);
 
        if(cursor != null)
        {
            while (cursor.moveToNext())
            {
                int id = cursor.getInt(0);
                String name = cursor.getString(1);
                Log.d("lxz","id = " + id + "    name = " + name);
                uri = Uri.parse("content://com.android.contacts/raw_contacts/"+id+"/data");
 
                // 这里需要注意的是数据库中直接看到的 mimetype_id 项并不存在
                // 该项为多表查询,真实字段为 mimetype,可以通过在代码中遍历列名观察到
                // 并且还需注意 mimetype 是 String类型,而不是在数据库中看到的 int 类型
                //Cursor cursor2 = resolver.query(uri,null,null,null,null,null);
                //for(int i = 0; i < cursor2.getColumnCount(); i++)
                //    Log.d("lxz",cursor2.getColumnName(i));
 
                Cursor cursor2 = resolver.query(uri,new String[]{"mimetype","raw_contact_id","data1"},null,null,null,null);
                PersonInfo personInfo = new PersonInfo();
                while(cursor2.moveToNext())
                {
                    String mimetype = cursor2.getString(0);
                    int raw_contact_id = cursor2.getInt(1);
                    String data1 = cursor2.getString(2);
                    Log.d("lxz", "minetype = " + mimetype + " address = " + data1);
                    personInfo.set_id(raw_contact_id);
 
                    if(mimetype.equals("vnd.android.cursor.item/phone_v2"))
                    {
                        personInfo.setNumber(data1);
                    } else if (mimetype.equals("vnd.android.cursor.item/postal-address_v2")) {
                        personInfo.setAddress(data1);
                    } else if (mimetype.equals("vnd.android.cursor.item/email_v2")) {
                        personInfo.setEmail(data1);
                    } else if (mimetype.equals("vnd.android.cursor.item/name")) {
                        personInfo.setName(data1);
                    }
                }
                personInfoList.add(personInfo);
            }
        }
 
        for (PersonInfo personInfo : personInfoList)
        {
            Log.d("lxz",personInfo.toString());
        }
    }
}

PersonInfo.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
package com.example.mytestapp;
 
public class PersonInfo {
    public int _id;
    public String name;
    public String number;
    public String email;
    public String address;
 
    public int get_id() {
        return _id;
    }
    public void set_id(int _id) {
        this._id = _id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    @Override
    public String toString() {
        return "PersonInfo{" +
                "_id='" + _id + '\'' +
                ", name='" + name + '\'' +
                ", number='" + number + '\'' +
                ", email='" + email + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}

AndroidManifest.xml

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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.READ_CONTACTS"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

多线程

Android 的多线程有很多种方式,这里学习基础的两种:Thread 和 Runnable

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        // 方式一
        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                for (int i = 0; i < 10; i++)
                {
                    Log.d("lxz","Thread:" + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        thread.start();
 
        // 方式二
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++)
                {
                    Log.d("lxz","Runnable:" + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        };
        Thread thread1 = new Thread(runnable);
        thread1.start();
    }
}

网络操作

Android 中的网络操作有以下几点需要注意:

  • 网络操作需要在清单中添加权限 android.permission.INTERNET

  • 网络操作属于耗时操作,耗时操作不允许在主线程中执行

  • 网络操作很有可能报错,所以很多地方都需要try-catch

MainActivity.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
package com.example.mytestapp;
 
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 
public class MainActivity extends AppCompatActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                String path = "http://www.baidu.com";
                try {
                    URL url = new URL(path);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setConnectTimeout(5000);
                    int code = connection.getResponseCode();
                    if(code == 200)
                    {
                        InputStream inputStream = connection.getInputStream();
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        byte bytes[] = new byte[1024];
                        int retCount = -1;
                        while(true)
                        {
                            retCount = inputStream.read(bytes,0,1024);
                            if (retCount == -1)
                                break;
                            outputStream.write(bytes,0,retCount);
                        }
                        String string = new String(outputStream.toString());
                        Log.d("lxz",string);
                    }
                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        thread.start();
 
    }
}

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

消息机制

在 Android 中界面操作界面操作只能在主线程中操作,所以子线程中的数据如果想显示在界面上就必须通过消息机制发送给主线程

 

MainActivity.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
package com.example.mytestapp;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.TextView;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 
public class MainActivity extends AppCompatActivity{
    public Handler mHandle = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            if(msg.what == 0x123)
            {
                TextView textView = findViewById(R.id.text1);
                textView.setText(msg.obj.toString());
            }
        }
    };
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        Thread thread = new Thread(){
            @Override
            public void run() {
                super.run();
                String path = "http://www.baidu.com";
                try {
                    URL url = new URL(path);
                    HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                    connection.setConnectTimeout(5000);
                    int code = connection.getResponseCode();
                    if(code == 200)
                    {
                        InputStream inputStream = connection.getInputStream();
                        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
                        byte bytes[] = new byte[1024];
                        int retCount = -1;
                        while(true)
                        {
                            retCount = inputStream.read(bytes,0,1024);
                            if (retCount == -1)
                                break;
                            outputStream.write(bytes,0,retCount);
                        }
                        String string = new String(outputStream.toString());
                        Log.d("lxz",string);
                        Message message = new Message();
                        message.obj = string;
                        message.what = 0x123;
                        mHandle.sendMessage(message);
                    }
                } catch (MalformedURLException e) {
                    throw new RuntimeException(e);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        };
        thread.start();
    }
}

activity_main.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <ScrollView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="18dp"
            android:id="@+id/text1">
        </TextView>
    </ScrollView>
</LinearLayout>

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyTestApp"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2023-2-16 18:06 被简单的简单编辑 ,原因:
收藏
点赞5
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回