共计 18825 个字符,预计需要花费 48 分钟才能阅读完成。
Hardcoder 简介
Hardcoder 是腾讯开源的一套 Android APP 与零碎间的通信解决方案,Hardcoder 无效的解决了 APP 只能调用零碎规范 API,无奈间接调用零碎底层硬件资源的问题,让 Android APP 和零碎可能实现实时通信。
依照官网的说法,APP 能充沛调度系统资源如 CPU 频率,大小核,GPU 频率等来晋升 APP 性能,零碎可能从 APP 侧获取更多信息以便更正当提供各项系统资源。同时,对于 Android 不足标准接口实现的性能,APP 和零碎也能够通过该框架实现机型适配和性能拓展。
之所以要研发这一框架,是因为微信在很多的低端机型上,一些罕用的优化伎俩都应用到了极致,所以微信的开发者始终在思考,该如何冲破这个优化的极限?直到有一次与厂商的交换时候理解到,局部厂商会针对微信做一些小改变,其中比拟典型的就是【暴力提频】。零碎在辨认到微信启动,页面切换等场景时,会粗犷地进步 CPU 频率,从而晋升 APP 运行的性能。
但因为厂商无奈精确判断微信场景,暴力提频成果并不现实;而如果过多地进步 CPU 频率,又对手机的功耗有影响。因而,微信的开发者心愿在手机硬件的层面上开掘更多的性能优化空间,于是 Hardcoder 框架应运而生。
Hardcoder 是什么
厂商暴力提频成果不现实是因为在目前 Android 框架下,手机没有方法精确获知 APP 须要资源的机会。如果咱们须要开掘手机硬件层面的性能优化,就须要跳过 Android 操作系统的利用框架,在利用开发者和硬件之间关上一个通道,让硬件能够间接依据利用开发者的须要进行资源的调度。
Hardcoder 构建了 APP 与零碎(ROM)之间牢靠的通信框架,冲破了 APP 只能调用零碎规范 API,无奈间接调用零碎底层硬件资源的问题,让 Android APP 和零碎能实时通信,其架构图如下所示。
利用 Hardcoder,APP 能充沛调度系统资源如 CPU 频率,大小核,GPU 频率等来晋升 APP 性能,零碎可能从 APP 侧获取更多信息以便更正当提供各项系统资源。同时,对于 Android 不足标准接口实现的性能,APP 和零碎间也能够通过该框架实现机型适配和性能拓展。
Hardcoder 通信流程
和很多的通信框架一样,Hardcoder 框架也分为 Server 端和 Client 端。其中 Server 端在厂商零碎侧实现,Client 端以 aar 模式合入到 APP 中,其通信的流程图如下所示。
当 APP 须要额定资源的时候,向 Hardcoder 的 Client 端发出请求。Hardcoder Client 端接管到申请后向 Hardcoder Server 端发出请求。Server 端承受到申请后会依据申请参数向硬件申请不同的资源,比方调整 CPU 频率,把线程绑定到大核运行等,实现了 APP 到零碎的通信。
同时零碎也可把以后零碎的状态通过 Hardcoder Client 在 Server 端注册的接口回调告诉到 Client 端,从而 APP 能够获取到零碎状态,实现零碎到 APP 的通信。
Hardcoder Client 端与 Server 端采纳的是 LocalSocket 的通信形式,对于 LocalSocket,读者能够看这一篇文章的介绍:Android LocalSocket 简介。因为 Hardcoder 采纳 Native 实现,因此在 C 层应用 Linux 的 socket 接口实现了一套 LocalSocket 机制作为 Client 端与 Server 端之间的通信形式。
能够发现,Hardcoder 有如下一些长处或者说个性。
- 零碎服务为 optional,实现上能够齐全反对或者局部反对;
- 框架实现不依赖于特定 Android 零碎,如 API level 限度;
- APP 的性能和业务个性不依赖于该框架。
总而言之,因为 Hardcoder 是腾讯主导的,所以咱们不必太放心兼容性问题,腾讯会和手机厂商进行洽谈并提供解决方案,并且目前曾经反对 Hardcoder 框架的手机厂商有 OPPO、vivo、华为、小米、三星、魅族等。
Hardcoder 性能优化技术计划
Hardcoder 优化根底
Hardcoder 在 Android 零碎侧次要优化的办法有进步 CPU 频率、进步 IO 频率,CPU 锁核以及进步 GPU 频率。
进步 CPU 频率
一般来说,挪动设施为了降低功耗,会不同水平地克制 cpu 频率,同时内核会依据以后的负载动静调整 cpu 频率。这就导致 APP 在执行一些须要资源的操作的时候,不能最大限度利用到 cpu 资源,可能呈现卡顿等状况。
在 Android 零碎中,通过批改 Android 内核配置,就能够达到进步 cpu 频率的指标。咱们能够应用上面的命令来查看以后零碎的 cpu 核数。
ls -l /sys/devices/system/cpu
如果要查看 cpu 反对的频率,能够应用上面的命令。
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
下面的命令是查看某个核(cpu0)反对的频率。支流的 cpu 都会有大小核的辨别,所以并不能保障每个核的反对频率是一样的。一般来说,大核反对更高的频率。要批改某个核的频率,须要有 root 权限。以下操作就是间接 echo 指定频率到 cpu0 上,以达到提频的成果。
echo 1440000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
一般来说,为了不便测试,咱们会禁用某些核,甚至只给零碎留下一个核,以不便做些基准测试,上面的命令可禁用某个指定的内核。
echo 0 > /sys/devices/system/cpu/cpu0/online
同理,咱们能够把 0 改成 1,即可启用对应的内核。
进步 IO 频率
就 Hardcoder 我的项目与局部厂商的沟通过程中,发现厂商并不把 IO 提频看作重点,认为 IO 提频自身成果无限,且必须同时进步 cpu 频率成果才会显著,而局部厂商比方 vivo 则把 IO 频率长期锁定为高频状态。
cat /sys/class/mmc_host/mmc0/clk_scaling/enable
如果值为 1 , 则 emmc 运行在 50MHz 和 200MHz 之间变动;如果值为 0 , 则 emmc 运行在 200MHz 上,也就是高频上了,如应用上面的提频命令即可。
echo 0 > /sys/class/mmc_host/mmc0/clk_scaling/enable
CPU 锁核
锁核操作实际上操作的是 cpu 亲和度,设置某个线程的 cpu 亲和度,即可变相把线程锁定在某个大核上运行。实际操作中,线程可固定在某个核上运行,但不独占,也就是这个核同样能够执行其余线程的指令。这样就缩小了内核切换带来的性能开销。
进步 GPU 频率
GPU 次要用于渲染图像,局部厂商提供了调节 GPU 芯片参数的能力,进步 GPU 频率,从而更好地反对对 GPU 需要较高的场景。
Hardcoder 通信形式 —— LocalSocket
Hardcoder 采纳 LocalSocket 实现手机本地单方 Native 层实时通信机制,对于 LocalSocket 能够自行查阅相干的材料。Android 中的 LocalSocket 其实是 Linux 中 Socket 进行了封装,采纳 JNI 形式调用,进而实现过程间通信。与一般的 Socket 不同,LocalSocket 实现的是 Android 和 Framework 间接的通信。
LocalSocket 通信框架
目前在 Android 上过程通信的 IPC 机制次要有 Binder(Java INTENT/AIDL)、共享内存、Socket(TCP/UDP)等,同时在 Java 层 Android 提供了一套 LocalSocket 的 API。LocalSocket 的实质是基于对 Linux 层 Socket 的封装,用来进行过程间的通信。LocalSocket 依据是否命名分为两种类型:非命名 LocalSocket 只能在父过程和子过程之间通信(因为其余过程无奈取得 Server 端 Socket 名,只有保留了未命名 Socket 的文件描述符的父子过程之间能够应用);命名的 LocalSocket 则能够在任意过程间进行通信。
Android 中的 LocalSocket 在 Unix 域名空间创立一个 Socket 进行通信。Unix 域名空间 Socket 是在 Socket 根底上衍生的 IPC 通信机制,因而 LocalSocket 是为了解决同一台主机上不同过程间的通信问题,其对应接口和 TCP 等跨网络 Socket 形式统一,然而无需实现简单的 TCP/IP 协定栈,不须要打包拆包、计算校验,只需通信单方协商好套接字地址(Android 上即为文件描述符)即可。因此 LocketSocket 在 Android 零碎中作为 IPC 通信伎俩被宽泛应用,如常见的 Binder、Ashmem 和 LocalSocket 等。
IPC 通信机制 | 安全性 | 传输效率 | 实现及推广难易度 |
---|---|---|---|
Binder | 反对 UID 鉴权安全性高 | 单次拷贝 | 短少 C 层公开 API,实现难度大 |
Ashmem | 需实现鉴权 | 无需拷贝,效率最高 | 需额定实现同步,实现难度大 |
Socket(TCP/UDP) | 需实现鉴权 | 两次拷贝 | 技术成熟,零碎侧接入难度小 |
LocalSocket | 需实现鉴权 | 两次拷贝,面向本地通信,无需协定栈 | 技术成熟,面向 Android 设计,零碎侧接入难度小 |
因为 Hardcoder 采纳 Native 实现,天然无奈间接应用 Android SDK 提供的 Java 类的 LocalSocket 和 LocalSocketServer API。当然在 Hardcoder 的设计预期中,使用者无需关怀通信细节,所以我的项目在 C 层应用 Linux 的 Socket 接口实现了一套类 Java 类的 LocalSocket 机制。
Hardcoder 子项目 libapp2sys 中 localsocket.h 定义了 C ++ 类 Localsocket 用于实现对通信流程的接口封装和逻辑管制,工作流程如下所示。
基类 Localsocket
基类 Localsocket 定义了作为 Socket 单方通信的根本行为接口,次要包含创立接口、循环解决收发数据和回调函数。
CreateSocket() 创立接口
int createSocket(const char *localPath, const char *remotePath)
其中,CreateSocket() 创立套接字,传入参数为 Server 端的 Socket 名。
对 Server 端,传入的 Socket 名为空,则默认创立 Server 端。先新建套接字 new Socket(),而后 bind() 绑定地址,再调用 listen() 监听端口,一系列零碎调用胜利后,则进入 loop() 循环期待解决数据。
对 Client 端,传入的 Socket 名无效,则创立 Client 端。先新建套接字 new Socket(),而后 connect() 尝试通过 Socket 名连贯 Server 端。连贯胜利后,Client 端创立子线程进入 loop() 循环期待解决数据。
Loop() 循环解决收发数据
int loop(const int isServer)
Client 端和 Server 端别离保护一个发送队列 sendQueue,当发送队列不为空则发送数据,否则调用 select() 判断是否有待解决的接收数据,如果有则调用 read() 读取数据而后解决接收数据。对 Server 端该函数还会调用 accept() 解决 Client 端的连贯申请。
RecvEvent() 回调函数
int recvEvent(Event event, int fd, uid_t uid, const char *path, uint8_t *data, int len)
RecvEvent() 为虚函数,数据接管解决完后回调下层进行对应的业务解决,具体实现由各自派生类自行实现。
客户端 LocalsocketClient
客户端 LocalsocketClient 继承自 LocalSocket,是 Client 端实现的 LocalSocket 类,实现在 client.h 文件。除了包含基类的根底函数外,次要包含 start() 办法创立连贯。LocalSocketClient 对应实例 Client 由客户端代理类负责创立,次要办法包含初始化和启动。
初始化 init()
int init(const char *remote, const int port, const char *local, IC2JavaCallback *callback)
下层 JNI 入口调用初始化创立 Client 端,以后版本 Localsocket 实现会疏忽 port 和 local 两个参数(UDP 历史实现遗留),remote 为约定的 Server 端 Socket 名,callback 为 APP 端 Java 层监听 server 端回调函数。
启动函数 tryStartEngine()
int tryStartEngine()
启动函数 tryStartEngine() 创立本地的 LocalSocketClient 引擎实例 clientEngine,并调用 LocalSocketClient 类 start() 办法创立连贯并负责断开超时重连逻辑。
服务端 LocalsocketServer
服务端 LocalsocketServer 继承自 LocalSocket,是 Server 端实现的 LocalSocket 类,实现在 server.h 文件,对应实例由服务端代理类负责创立。其创立和 start() 办法由实现了相应服务能力接口的零碎级别过程负责调用。
Native 层实现的 LocalSocket 流程
对 Server 端,创立 Socket 后,调用 bind() 绑定地址和 listen() 监听端口,进入 loop() 循环。首先通过 accept() 解决来自 Client 端的连贯申请,通过 recvEvent() 的回调告诉 Client 端是否连贯胜利;而后查看发送队列 sendQueue 是否有待发送数据,若有则调用 send() 发送数据;再调用 select() 查找是否有待解决的接收数据,有则调用 read() 读取数据进行相应解决;而后从新进行 loop() 循环。
对 Client 端,创立 Socket 后,调用 connect() 通过 Socket 名尝试连贯 Server 端,从 recvEvent() 回调获取是否连贯胜利。若连贯胜利,则进入 loop() 循环,与 Server 端相似循环 send()、select()、read() 过程。
Hardcoder 数据格式 —— proto + JSON
Hardcoder 应用 LocalSocket 机制实现 C/S 双向实时通信,client 端到 server 端传输数据简称为申请(request),server 端到 client 端数据简称为响应(response)。双向数据包均由包头和包体两局部组成,思考到为本地通信且已通过 UID 形式实现鉴权认证,以后版本所有数据均无加密。
申请数据格式
申请的数据格式如下所示。
--------------------------------------------------------
| AMCReqHeaderV2 | body(业务申请构造体序列化数据) |
--------------------------------------------------------
AMCReqHeaderV2 定义在 libapp2sys 子项目的 header.h 中。
const static uint16_t HEADER_PROTOCAL_VERSION_2 = 16;
const static uint32_t HEADER_BEGIN = 0x48444352;
typedef struct AMCReqHeaderV2 {
uint32_t begin; // 包头其起始字段
uint16_t version; // 协定版本
uint16_t funcid; // 申请对应的 function ID
uint32_t bodylen; // 包体序列化数据长度
int64_t requestid; // 以后申请包 ID
uint32_t callertid; // 下层 JNI 调用者所在线程 ID
int64_t timestamp; // 以后申请工夫戳
uint32_t headerlen; // 包头数据长度(Ver2 新增)uint32_t bodyformat; // 包体数据序列化格局枚举值(Ver2 新增)}__attribute__ ((packed)) AMCReqHeaderV2;
其中:
- begin 字段固定标识 Hardcoder 通信。
- version 字段反对协定版本扩大。
- funcid 定义在 protocol.h,表明本次客户端申请对应的零碎操作,例如申请 CPU 提频或者线程锁核等。
const static uint32_t FUNC_BASE = 1000;
const static uint32_t FUNC_CHECK_PERMISSION = FUNC_BASE + 1;
const static uint32_t FUNC_CPU_HIGH_FREQ = FUNC_BASE + 2;
const static uint32_t FUNC_CANCEL_CPU_HIGH_FREQ = FUNC_BASE + 3;
const static uint32_t FUNC_CPU_CORE_FOR_THREAD = FUNC_BASE + 4;
const static uint32_t FUNC_CANCEL_CPU_CORE_FOR_THREAD = FUNC_BASE + 5;
const static uint32_t FUNC_HIGH_IO_FREQ = FUNC_BASE + 6;
const static uint32_t FUNC_CANCEL_HIGH_IO_FREQ = FUNC_BASE + 7;
const static uint32_t FUNC_SET_SCREEN_RESOLUTION = FUNC_BASE + 8;
const static uint32_t FUNC_RESET_SCREEN_RESOLUTION = FUNC_BASE + 9;
const static uint32_t FUNC_REG_ANR_CALLBACK = FUNC_BASE + 10;
const static uint32_t FUNC_REG_PRELOAD_BOOT_RESOURCE = FUNC_BASE + 11;
const static uint32_t FUNC_TERMINATE_APP = FUNC_BASE + 12;
const static uint32_t FUNC_UNIFY_CPU_IO_THREAD_CORE = FUNC_BASE + 13;
const static uint32_t FUNC_CANCEL_UNIFY_CPU_IO_THREAD_CORE = FUNC_BASE + 14;
static const uint32_t FUNC_REG_SYSTEM_EVENT_CALLBACK = FUNC_BASE + 15;
static const uint32_t FUNC_GPU_HIGH_FREQ = FUNC_BASE + 16;
static const uint32_t FUNC_CANCEL_GPU_HIGH_FREQ = FUNC_BASE + 17;
static const uint32_t FUNC_CONFIGURE = FUNC_BASE + 18;
static const uint32_t FUNC_GET_PARAMETERS = FUNC_BASE + 19;
其中,requestid 字段从 0 开始递增放弃唯一性。headerlen 字段为包头长度。bodyformat 为包体序列化格局,以后取值为:1-raw(byte 数组)、2-protobuf 序列化字节流、3-JSON 序列化字节流。
申请 body 局部如果应用 google protobuf 协定格局实现(C 库),请参见我的项目 amc.proto 文件中申请构造体定义,例如申请 CPU 提频的业务申请构造体:
message RequestCPUHighFreq{
required int32 scene = 1;
required int32 level = 2;
required int32 timeoutMs = 3;
required int64 action = 4;
}
编译后会主动在 gen/cpp/amc.pb.h 文件中生成对应的 C++ 类,次要公开成员函数为序列化 / 反序列化接口,以及数据属性的 get/set 拜访接口。
class RequestCPUHighFreq : public ::google::protobuf::MessageLite {RequestCPUHighFreq* New() const;
void CheckTypeAndMergeFrom(const ::google::protobuf::MessageLite& from);
void CopyFrom(const RequestCPUHighFreq& from);
void MergeFrom(const RequestCPUHighFreq& from);
// required int32 scene = 1;
inline bool has_scene() const;
inline void clear_scene();
static const int kSceneFieldNumber = 1;
inline ::google::protobuf::int32 scene() const;
inline void set_scene(::google::protobuf::int32 value);
//...
}
调用 header.h 的动态函数 genReqPack 即能够实现申请数据包的残缺封包逻辑。
static int64_t genReqPack(uint32_t funcid, uint8_t *data, int dataLen, uint8_t **outPack, uint32_t *outLen, uint32_t callertid, int64_t timestamp)
如果采纳 JSON 格局,则应用 key-value 形式,其中 key 对立为字符串,以后版本已有属性定义如下:
- “funcid” 和包头中的 funcid 保持一致,int32 格局。
- “scene” 场景值,int32 格局,示意 APP 具体业务场景,和 Ver1 已有定义保持一致。
- “status” 状态值,int32 格局,示意该操作是一个申请 / 置位(1)还是勾销 / 复位(2)操作。
- “timouts” 超时值,int32 格局,示意该操作工作最长工夫,单位 ms。
- “cpulevel” 申请 cpu level 值,分为 Level 0~3,具体定义请参见 protocol.h。
- “iolevel” 申请 io level 值,分为 Level 0~3,具体定义请参见 protocol.h。
- “gpulevel” 申请 gpu level 值,分为 Level 0 和 Level 1,具体定义请参见 protocol.h。
- “bindtids” 须要绑核的线程,int32 格局数组。
- “unbindtids” 须要解绑的线程,int32 格局数组。
响应数据格式
响应数据的格局如下:
-------------------------------------------------
| AMCRespHeaderV2 | payload |
AMCRespHeaderV2 同样定义在 libapp2sys 子项目的 header.h 中。
typedef struct AMCRespHeaderV2 {
uint32_t begin; // 包头其起始字段
uint16_t version; // 协定版本
uint16_t funcid; // 响应申请对应的 function ID
uint32_t retCode; // 申请处理结果
uint32_t bodylen; // 包体序列化数据长度
int64_t requestid; // 响应对应的申请包 ID
int64_t timestamp; // 以后响应工夫戳
uint32_t headerlen; // 包头数据长度(Ver2 新增)uint32_t bodyformat;// 包体数据序列化格局枚举值(Ver2 新增)}__attribute__ ((packed)) AMCRespHeaderV2;
begin、version、bodylen、timestamp、headerlen 和 bodyformat 等字段含意与 AMCReqHeader2 中各字段一样用于标识响应包本身属性,而 funcid、requestid 则示意其对应解决的申请包属性,便于申请端确认;如果申请包在解包或者校验方面不通过,则对应的 retCode 会返回响应的全局错误码(负值,定义在 protocal.h),否则返回 0 值或者是具体业务处理结果。
const static int32_t RET_OK = 0;
//requestCpuHighFreq,requestHighIOFreq 间接返回 level n
const static int32_t RET_LEVEL_1 = 1;
const static int32_t RET_LEVEL_2 = 2;
const static int32_t RET_LEVEL_3 = 3;
// 预留返回值最初三位作为 level,倒数第四位代表 cpu level,倒数第五位代表 io level,新增值持续左移
const static int32_t RET_CPU_HIGH_FREQ = 1 << 3;// 1000,即 8
const static int32_t RET_HIGH_IO_FREQ = 1 << 4; // 10000,即 16
//requestUnifyCpuIOThreadCore 应用复合标识位
const static int32_t RET_CPU_HIGH_FREQ_LEVEL_1 = RET_CPU_HIGH_FREQ | RET_LEVEL_1; //Unify 接口返回 cpu level 1,1000 | 01 = 1001
const static int32_t RET_CPU_HIGH_FREQ_LEVEL_2 = RET_CPU_HIGH_FREQ | RET_LEVEL_2; //Unify 接口返回 cpu level 2,1000 | 10 = 1010
const static int32_t RET_CPU_HIGH_FREQ_LEVEL_3 = RET_CPU_HIGH_FREQ | RET_LEVEL_3; //Unify 接口返回 cpu level 3,1000 | 11 = 1011
const static int32_t RET_HIGH_IO_FREQ_LEVEL_1 = RET_HIGH_IO_FREQ | RET_LEVEL_1; //Unify 接口返回 io level 1,10000 | 01 = 10001
const static int32_t RET_HIGH_IO_FREQ_LEVEL_2 = RET_HIGH_IO_FREQ | RET_LEVEL_2; //Unify 接口返回 io level 2,10000 | 10 = 10010
const static int32_t RET_HIGH_IO_FREQ_LEVEL_3 = RET_HIGH_IO_FREQ | RET_LEVEL_3; //Unify 接口返回 io level 3,10000 | 11 = 10011
const static int32_t ERR_UNAUTHORIZED = -10001;
const static int32_t ERR_FUNCTION_NOT_SUPPORT = -10002;
const static int32_t ERR_SERVICE_UNAVAILABLE = -10003;
const static int32_t ERR_FAILED_DEPENDENCY = -10004;
const static int32_t ERR_PACKAGE_DECODE_FAILED = -10005;
const static int32_t ERR_PARAMETERS_WRONG = -10006;
const static int32_t ERR_CLIENT_UPGRADE_REQUIRED = -10007;
const static int32_t ERR_CLIENT_DISCONNECT = -20001;
const static int32_t ERR_CLIENT_RESPONSE = -20002;
申请 body 局部如果应用 protobuf 定义,请参见我的项目 amc.proto 文件中响应构造体定义;如果采纳 JSON 格局,属性定义与 AMCReqHeaderV2 保持一致。调用 header.h 的动态函数 genRespPack 即能够实现响应数据包的残缺封包逻辑。
static int64_t genRespPack(uint32_t funcid, uint32_t retCode, uint64_t requestid, uint8_t *data, int dataLen, uint8_t **outPack, uint32_t *outLen)
Native 层实现的 LocalSocket 鉴权形式
因为 Native 层实现的 LocalSocket 通信计划为本地过程间通信,因此只须要在 Server 端接管到 Client 端申请时,通过调用 getsockopt() 办法获取到 Client 端的 UID,而后通过 UID 反查出 Client 端对应的 APP 信息,进而实现响应的鉴权解决。
Hardcoder 厂商接入指南
Hardcoder 我的项目工程中提供了 Server 端的实现例子,代码次要参见 server.h,server.cpp 以及 protocol.h。
设置零碎属性
APP 判断手机是否反对 Hardcoder 会读取 persist.sys.hardcoder.name 的 property,若不为空则手机取 property 字段作为 server 端 socket name 申请建设 socket 连贯。厂商侧需设置 persist.sys.hardcoder.name 属性为零碎 server 侧 socket name。
实现次要接口函数
protocol.h 的 HardCoder 类定义了所有 Hardcoder 接口为虚函数,厂商侧需继承 HardCoder 类实现相干接口。代码例子中 server.h 的 ManufacturerCoder 继承了 HardCoder 实现了相干接口,例子中为空函数,具体实现须要厂商侧编写。此局部接口可同时参照 Hardcoder 接入指南中接口阐明。
int getUidByAddress(const char *ip, int port);
获取 APP UID,每次在 socket 连贯中收到 APP 申请都会查看 APP UID。因为 UID 有唯一性,可作为鉴权用。
bool checkPermission(std::vector<std::string> manufactures, std::vector<std::string> certs, int funcid, int uid, int callertid, int64_t timestamp);
checkPermission 办法的作用是否容许 APP 应用 Hardcoder,容许返回 true,否则返回 false。若容许所有 APP 应用,间接返回 true 即可。
若须要限度 APP 接入,应实现对应的 checkPermission,其中 manufactures 为厂商名数组,certs 为鉴权值数组,可提供鉴权值给容许应用的 APP 作为参数传入。留神若限度利用应用,需告知 APP 开发者如何申请权限接入。
int requestCpuHighFreq(int scene, int64_t action, int level, int timeoutms, int callertid, int64_t timestamp);
requestCpuHighFreq 的次要作用是进步 CPU 频率。其中,scene 参数为 APP 场景值;action 参数为 APP 场景值扩大,为保留字段;level,申请的 CPU level,定义在 protocol.h;目前共分为三个 level,LEVEL 0 为不变,LEVEL 1 最高,默认为 CPU 最高频率,LEVEL 2 次之,LEVEL 3 最低,但仍比以后频率会进步,LEVEL 2 和 LEVEL 3 具体频率可由厂商自行决定。timeoutms,从 timestamp 开始申请 timeoutms 工夫的资源;callertid,申请线程 id;timestamp,申请工夫戳;如果要勾销提频申请,能够应用上面的函数。
int cancelCpuHighFreq(int callertid, int64_t timestamp);
如果要申请绑定指定线程到 cpu 大核,能够应用上面的函数。
int requestCpuCoreForThread(int scene, int64_t action, std::vector<int>bindtids, int timeoutms, int callertid, int64_t timestamp);
其中,bindtids,须要绑定到大核的线程 id 数组。参考实现,局部厂商会间接把申请线程所在过程的所有线程同时绑定到大核。
int cancelCpuCoreForThread(std::vector<int>bindtids, int callertid, int64_t timestamp);
下面的办法用于勾销绑定线程申请。
int requestHighIOFreq(int scene, int64_t action, int level, int timeoutms, int callertid, int64_t timestamp);
requestHighIOFreq 办法用于进步 IO 频率申请。当然,咱们还能够应用混合申请,同时申请进步 CPU 频率,进步 IO 频率,进步 GPU 频率以及线程绑核。
int requestUnifyCpuIOThreadCoreGpu(int scene, int64_t action, int cpulevel, int iolevel, std::vector<int>bindtids, int gpulevel, int timeoutms, int callertid, int64_t timestamp);
勾销的时候应用 cancelUnifyCpuIOThreadCoreGpu 办法。
int cancelUnifyCpuIOThreadCoreGpu(int cancelcpu, int cancelio, int cancelthread, std::vector<int>bindtids, int cancelgpu, int callertid, int64_t timestamp);
Hardcoder 接入指南
接入步骤
- 下载 Hardcoder 工程编译 aar;
- 我的项目 build.gradle 引入 Hardcoder aar;
- 过程启动时调用 initHardCoder 建设 socket
连贯(个别过程启动时须要申请资源,因此举荐在过程启动时调用)。每个过程都是独立的,都须要调用 initHardCoder 建设 socket 连贯,建设连贯后每个过程维持一个 socket,过程退出时 socket 也会断开; - initHardCoder 回调胜利后调用 checkPermission,传入 APP 已申请的各个厂商鉴权值;
- 在须要申请资源的场景调用 startPerformance,传入申请资源的参数。若场景位于过程启动阶段,比方 APP 启动,须要在 initHardCoder 的回调胜利当前再调用 startPerformance,确保连贯已胜利建设,或者判断 HardCoderJNI 的 isConnect() 查看 socket 是否已连贯。
- 场景完结时被动调用 stopPerformance,传入对应场景 startPerformance 时的返回值 hashCode 作为参数,进行本次申请。
- 测试性能,APP 可对关上 / 敞开 Hardcoder 的状况做比照试验,测试性能数据。
编译 Hardcoder aar
Hardcoder 我的项目给开发环境为 AndroidStudio,采纳 gradle 构建,其中 Native 局部代码应用 CMAKE 编译。编译 aar 的步骤如下:
- 根目录下运行命令行
gradlew assembleDebug
触发子工程 libapp2sys 编译,编译胜利后生成 Hardcoder aar,输入目录为 hardcoder/libapp2sys/build/outputs/aar/libapp2sys-debug.aar; - 若须要把 aar publish 到本地 maven 库,以便本身我的项目应用,在根目录下运行如下命令行
./gradlew publishToMavenLocal
,输入 aar 目录为 User
目录 /.m2/repository/com/tencent/mm/hardcoder/app2sys/。
当然,也能够应用 Android Studio 自带的 Gradle 工具进行构建,构建实现后能够在 outputs/aar 目录下看到生成的 aar。
App 引入 Hardcoder aar
Hardcoder 工程以 aar 形式引入,把 hardcoder:app2sys 增加到主工程目录下的 build.gradle 文件的依赖库中。
dependencies {api('com.tencent.mm.hardcoder:app2sys:1.0.0')
}
其中 1.0.0 为以后 Hardcoder 版本号,以后版本号在 Hardcoder 主工程目录下 gradle.properties 文件。
HC_VERSION_NAME=1.0.0
Hardcoder 办法
HardCoderJNI.java 中提供了 App 须要调用的接口。带 native 关键字的函数为 jni 接口,为 Hardcoder Client 端与 Server 端约定的所有通信接口。个别不倡议间接应用 JNI 接口,HardCoderJNI.java 提供了封装的 JAVA 层接口,其中比拟常见的办法有如下一些。
初始化 initHardCoder
InitHardCoder 负责建设 socket 连贯。调用 initHardCoder 前,请确保已正确设置了 hcEnable(下层接口是否开启 Hardcoder 性能开关,默认为 true)和 hcDebug(Hardcoder 组件是否打印 debug log,默认为 false)标记位。所有与零碎间通信都须要依赖 socket 连贯,所以调用申请前确保已调用 initHardCoder 接口 初始化了 InitHardCoder,如下所示。
public static int initHardCoder(String remote, int port, String local, HCPerfManagerThread hcPerfManagerThread, HardCoderCallback.ConnectStatusCallback callback)
InitHardCoder 中参数的含意如下。
- remote 为 Hardcoder server 端 socket name,默认调用 HardCoderJNI.readServerAddr() 读取;
- port 为 Hardcoder server 端端口值,可传入任意 int 值;
- local 为 Hardcoder client 端 socket name,次要用于标识 client 端,可传入任意非空字符串;
- hcPerfManagerThread 为运行 HCPerfManager 的线程,可传入 null,默认由 Hardcoder 新建一个线程运行;若应用 APP 已有线程池中线程,请传入接口 HardCoderJNI.HCPerfManagerThread 的实现类;
- callback 为 initHardCoder 回调,获取连贯建设胜利 / 失败回调,不须要回调时可传入 null。倡议在回调胜利建设 socket 后再发动申请,否则可能存在申请时 socket 未胜利连贯导致申请失败的状况。
- InitHardCoder 为异步执行,返回值不代表连贯是否建设胜利,调用 initHardCoder 胜利返回 0,若返回 ERROR_CODE_NOT_ENABLE = -3,代表 setHcEnable 未设置为 true(默认为 true,下层可用来管制 Hardcoder 是否关上);
InitHardCoder 连贯是否建设胜利通过 HardCoderCallback.ConnectStatusCallback 返回值获取到,ConnectStatusCallback 接口如下所示。
public interface ConnectStatusCallback {void onConnectStatus(boolean isConnect);
}
鉴权 checkPermission
CheckPermission 接口传入厂商名与鉴权值,用于厂商鉴权 APP 是否有权限应用并接入了 Hardcoder 性能。
CheckPermission 必须在 initHardCoder 建设 socket 胜利后调用。此接口用于传输鉴权值给厂商,对须要验证鉴权值的厂商(比方 OPPO),必须调用 checkPermission 把鉴权值传入才可应用 Hardcoder;对没有实现 checkPermission 接口的厂商,系统对 checkPermission 申请没有 callback,因此后续申请调用不应依赖于 checkPermission 的回调,若厂商不返回 checkPermission callback 也能够调用申请。
public static long checkPermission(String[] manufactures, String[] certs, HardCoderCallback.FuncRetCallback callback)
对于 checkPermission 鉴权接口,参数的含意如下。
- manufactures 为字符串数组,传入厂商名;
- certs 为字符串数组,传入对应 manufactures 中的厂商的鉴权值;
- callback 为 checkPermission 回调,如下所示
public interface FuncRetCallback {
void onFuncRet(final int callbackType, final long requestId, final int retCode,
final int funcId, final int dataType, final byte[] buffer);
}
其中,retCode 代表鉴权是否胜利,retCode 为 0 示意胜利,否则失败。
申请资源 startPerformance
StartPerformance 用于向零碎申请进步 cpu 频率、io 频率、gpu 频率、线程绑核等操作,别离对应 JNI 接口 requestCpuHighFreq、requestGpuHighFreq、requestCpuCoreForThread、requestHighIOFreq 以及混合接口 requestUnifyCpuIOThreadCoreGpu,为不便调用,只须要调用对立接口 startPerformance 即可申请所有资源,对不同资源传入不同参数即可。
StartPerformance 在须要申请资源处调用,返回值为申请的 hashcode,下层可保留返回值,在调用 stopPerformance 时传入,指定进行此次申请的资源。
public static int startPerformance(final int delay, final int cpuLevel, final int ioLevel, final int gpuLevel, final int[] tids, final int timeout, final int scene, final long action, final int callerTid, final String tag);
delay 单位 ms,以后申请提早多久调用,个别为 0。当间断调用 initHardcoder 和 startPerformance 时,若 Hardcoder 此时未连贯胜利,申请会被抛弃,此时能够应用 delay 参数推延调用。
cpuLevel 本次申请的 cpu 等级,分为:
public static final int CPU_LEVEL_0 = 0;// 不变
public static final int CPU_LEVEL_1 = 1;// 最高频,个别用于重度场景,会把 cpu 频率提到厂商设置的最高值,实用于能明确晓得完结工夫的场景(比方启动,进入某个界面,完结后会被动调用 stopPerformance)public static final int CPU_LEVEL_2 = 2;// 次高频,个别实用于不晓得明确完结工夫的场景,比方列表滑动等,不调用 stopPerformance,通过手动设置 timeout 值设置完结工夫
public static final int CPU_LEVEL_3 = 3;// 也会比失常运行频率要高
ioLevel 本次申请的 io 等级,分为:
public static final int IO_LEVEL_0 = 0;// 不变
public static final int IO_LEVEL_1 = 1;// 最高级
public static final int IO_LEVEL_2 = 2;
public static final int IO_LEVEL_3 = 3;
gpuLevel 本次申请的 gpu 等级,分为:
public static final int GPU_LEVEL_0 = 0;// 不变
public static final int GPU_LEVEL_1 = 1;// 最高级
参数的含意如下:
- tids 须要绑定到大核上的线程,为 int[] 数组,可传入单个或多个线程线程号。局部厂商会间接把线程对应的过程的所有线程同时绑定到大核;
- timeout 单位 ms,本次申请超时值。若申请未被动调用 stopPerformance,超过 timeout 工夫后零碎会被动开释资源;若申请超过 timeout 工夫仍未被执行(申请过多状况下有可能呈现),申请会被移出申请队列;
- scene int 值,APP 侧定义的场景值;
- action long 值,APP 侧定义的动作值,扩大字段,APP 侧如果只须要应用到 scene 值能够任意传入 action 值;
- callerTid 本次申请方的线程号;
- tag String 类型,APP 侧定义的 tag,次要用于日志。
完结申请 stopPerformance
场景完结时调用 stopPerformance 办法开释本次申请资源的调用,如下所示。
public static int stopPerformance(int hashCode);
hashCode 对应 startPerformance 的返回值,申明完结本次申请的资源;返回值示意此次调用是否胜利,有 ERROR_CODE_SUCCESS 和 ERROR_CODE_FAILED。
参考:腾讯 Hardcoder