Android 存储方式
SharedPrefence,存储简单的配置数据等
SQLite,存储复杂的关系型数据
File,一般存储日志文件,本地文件缓存, protobuf ,7z
ContentProvider,跨进程数据访问,一般和SQLite结合使用,把数据提供给其他app进程使用。
网络存储 , 网络存储 涉及到序列化/反序列化(protobuf,xml,json)
SharedPrefence
SharedPrefence的commit和apply
- apply没有返回值,而commit返回boolean表明修改是否提交成功 ;
- commit是把内容同步提交到硬盘的,而apply先立即把修改提交到内存,然后开启一个异步的线程提交到硬盘,并且如果提交失败,你不会收到任何通知。
- 所有commit提交是同步过程,效率会比apply异步提交的速度慢,在不关心提交结果是否成功的情况下,优先考虑apply方法。
- apply是使用异步线程写入磁盘,commit是同步写入磁盘。所以我们在主线程使用的commit的时候,需要考虑是否会出现ANR问题。(不适合大量数据存储)
多进程问题 -> mmkv
SQLite
SQLiteStatement
使用事务
使用索引
异步线程,写数据库统一管理
网络
- 序列化
- 反序列化
xml
json
protobuf 与平台无关
7z压缩 (作业)
protobuf
已经有xml/json,为什么要用protobuf
相对于xml,json,protobuf的优点:
- 简洁:xml要写一大堆解析的代码,而protobuf自动生成代码
- 体积小:消息大小只有xml的1/10 - 1/3
- 速度快:解析速度比xml快20~100倍
- 使用Protobuf编译系统: 可以生成更容易在编程中使用的数据访问代码
- 更好的兼容性,Protocol Buffers设计的一个原则就是要能够很好的支持向下或向上兼容。
缺点:
1、二进制格式导致可读性差
为了提高性能,protobuf采用了二进制格式进行编码。这直接导致了可读性差。这个直接影响开发测试时候的效率。当然,一般情况下,protobuf非常可靠,并不会出现太大的问题。
2、缺乏自描述
一般来说,XML是自描述的,而protobuf格式则不是。 给你一段二进制格式的协议内容,不配合你写的结构体是看不出来什么作用的。
项目中使用protobuf
在命令行中使用protobuf
如果要用命令将protobuf文件(protobuf文件的后缀为.proto)编译为java文件,需要安装protobuf工具,然后使用protoc 命令进行编译。
在Android studio项目中使用protobuf
1)首先,我们需要在全局的buid.gradle文件中添加上protobuf工具插件:
classpath ‘com.google.protobuf:protobuf-gradle-plugin:0.8.10’
2)然后,要app/buid.gradle文件中:
①添加上此插件
apply plugin: 'com.google.protobuf'
②添加依赖
implementation 'com.google.protobuf:protobuf-java:3.7.1'
③添加protobuf 配置
protobuf {
//配置protoc编译器
protoc {
artifact = 'com.google.protobuf:protoc:3.7.1'
}
//这里配置生成目录,编译后会在build的目录下生成对应的java文件
generateProtoTasks {
all().each {
task ->
task.builtins {
remove java
}
task.builtins {
java {
}
}
}
}
}
④添加 protobuf文件的目录(在android {}里面)
sourceSets {
main {
proto {
srcDir 'src/main/java/com/protobufdir'
}
}
}
3)在src/main/java/com/protobufdir目录下新建protobufbean.proto文件,添加如下代码:
syntax = "proto3";
package com.example.protobufdemo;//包名
option java_package = "com.example.protobuf02";
option java_outer_classname = "Person";
option csharp_namespace = "android";
message _Person{
string name = 1;
int32 id = 2;
string email = 3;
enum _PhoneType{
MOBILE = 0;
HOME = 1;
WORK = 2;
}
message _PhoneNumber{
string number =1;
_PhoneType type = 2;
}
repeated _PhoneNumber phone = 4;//数组
}
4) 重新build项目,AS build项目时,会将.proto文件生成java文件。
可以在\app\build\generated\source\proto\debug\java\ 目录下找到这些生成的java文件,
这些生成的java文件封装好了一些序列化和反序列化的API ,这些API 可以直接在项目中调用 。
语言规范
消息定义
- 确定消息命名,给消息取一个有意义的名字。
- 指定字段的类型
- 定义字段的编号,在Protocol Buffers中,字段的编号非常重要,字段名仅仅是作为参考和生成代码用。需要注意的是字段的编号区间范围,其中19000 ~19999被Protocol Buffers作为保留字段。
字段约束
- required指定该字段必须赋值,禁止为空(在v3中该约束被移除);
- optional指定字段为可选字段,可以为空,对于optional字段还可以使用[default]指定默认值,如果没有指定,则会使用字段类型的默认值;
- 使用repeated指定字段为集合
字段类型
- 在一个proto文件中可以同时定义多个message类型,生成代码时根据生成代码的目标语言不同,处理的方式不太一样,如Java会针对每个message类型生成一个.java文件
- 可以指定字段的类型为其他message类型
- 使用import关键字导入其他proto文件
- 在proto文件中消息的类型还可以嵌套
- 在proto文件中可以使用extensions关键字预留一部分字段编号出来,以便于后期给第三方扩展时使用
- oneof关键字指定一组字段中,至少要有一个字段必须赋值
在Protocol Buffers中提供了很多的标量类型,供我们在定义字段类型时使用:
编码协议
Base-128变长编码
- 所谓变长编码是和定长编码相对的,定长编码使用固定字节数来表示,如int32类型的数字固定使用4 bytes表示,而变长编码是需要几个字节就使用几个字节,如对于int32类型的数字1来说,只需要1 bytes足够。Base-128变长编码的原则就两条
- 每个字节使用使用低7位表示数字,除了最后一个字节,其他字节的最高位都设置为1。
- 采用Little-Endian字节序
- tag-length-valuetag-length-value
负数编码处理
- 采用ZigZag Encoding
(n <<1)^(n>>31)或者(n<<1)^(n>>63)
大端序列
先写高位,再写低位
小端序列
先写低位,再写高位