Android性能优化—存储优化

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)

大端序列
先写高位,再写低位
小端序列
先写低位,再写高位

Protocol Buffers 序列化协议及应用
【Protobuf】Protobuf的编解码规则详解

猜你喜欢

转载自blog.csdn.net/yzpbright/article/details/109261927