【Android 内存优化】Android 原生 API 图片压缩原理 ( 图片质量压缩方法 | 查找 Java 源码中的 native 方法对应的 C++ 源码 )

简介: 【Android 内存优化】Android 原生 API 图片压缩原理 ( 图片质量压缩方法 | 查找 Java 源码中的 native 方法对应的 C++ 源码 )

文章目录

一、 图片质量压缩方法

二、 查找对应的 Native 方法源码

三、 分析 Bitmap.cpp 中动态注册 Native 方法



在博客 【Android 内存优化】图片文件压缩 ( Android 原生 API 提供的图片压缩功能能 | 图片质量压缩 | 图片尺寸压缩 ) 简要介绍了 图片文件压缩格式 , 以及 Android 提供的图片质量 , 尺寸压缩原生 API ;



在博客 【Android 内存优化】Android 原生 API 图片压缩代码示例 ( PNG 格式压缩 | JPEG 格式压缩 | WEBP 格式压缩 | 动态权限申请 | Android10 存储策略 ) 主要使用了上述 Android 原生 API 压缩图片功能进行图片压缩 ;



本博客中将分析 Android 底层源码 , 具体分析图片压缩的原理 ;


先找到源码位置 ;






一、 图片质量压缩方法


在 【Android 内存优化】图片文件压缩 ( Android 原生 API 提供的图片压缩功能能 | 图片质量压缩 | 图片尺寸压缩 ) 三、 Android 原生 API 提供的质量压缩 章节对图片质量压缩方法中的代码进行了简要介绍 , 最终调用的方法是 nativeCompress 方法 , 执行实际的图片压缩逻辑 ;


// 执行 Native 方法, 压缩图片
boolean result = nativeCompress(mNativePtr, format.nativeInt,
  quality, stream, new byte[WORKING_COMPRESS_STORAGE]);



调用的 native 方法 : 查找其在 C++ 代码的对应函数 , 该 Native 函数定义在 Bitmap.cpp 中 ;


private static native boolean nativeCompress(long nativeBitmap, int format,
                                            int quality, OutputStream stream,
                                            byte[] tempStorage);


源码位置 frameworks\base\graphics\java\android\graphics\Bitmap.java , 也可以直接在开发环境中查看该源码 ;



下面开始查找 nativeCompress 方法 , 分析其中的代码 ;






二、 查找对应的 Native 方法源码


1. Native 方法源码查找方法 :



① 文件名相同 : 一般情况下 Java 源码中的 Java 类的类名与对应的定义 Native 方法的 C++ 源码文件名称相同 ;


② 源码搜索 : 如果找不到 , 还是在 Source Insight 中查找对应的 native 方法 , 即可找到对应的 C++ 源码 ; 参考 【Android 系统开发】使用 Source InSight 阅读 Android 源码 博客 ;


image.png

上图是在 Source Insight 中查找 nativeCompress 关键字 , 就可以找到对应的 Bitmap.cpp 源码 ;



2 . 对应构建脚本分析 : 在 Android 源码的 frameworks\base\core\jni 目录下 , 定义了 Bitmap.cpp 编译成动态库的构建脚本 Android.mk , 该构建脚本配置编译了 libandroid_runtime 动态库 , 其中就包含了 Bitmap.cpp , Bitmap.java 中定义的 native 方法的具体实现就在该 frameworks\base\core\jni\android\graphics\Bitmap.cpp 中 ;


LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# ... 
LOCAL_SRC_FILES:= \
# ... 
  android/graphics/Bitmap.cpp \
  android/graphics/BitmapFactory.cpp \
#  ...
LOCAL_C_INCLUDES += \
  $(JNI_H_INCLUDE) \
#  ... 
LOCAL_SHARED_LIBRARIES := \
  libmemtrack 
#  ...
LOCAL_MODULE:= libandroid_runtime
include external/stlport/libstlport.mk
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))




该构建脚本的源码位置 \frameworks\base\core\jni\Android.mk






三、 分析 Bitmap.cpp 中动态注册 Native 方法


参考博客 【Android NDK 开发】JNI 动态注册 ( 动态注册流程 | JNI_OnLoad 方法 | JNINativeMethod 结构体 | GetEnv | RegisterNatives ) 内容 , 在该博客中详细介绍了动态注册的详细细节 ;


Bitmap.java 中的 nativeCompress 方法 使用的是动态注册的方式 与 Bitmap.cpp 中的 Bitmap_compress 方法对应 ;



1. 动态注册流程 :



① 定义 JNINativeMethod 结构体 : 首先定义了 JNINativeMethod 结构体 , 该结构体由三个成员 , Java 函数名 , java 函数签名 , C++ 函数签名 ;


 

typedef struct {
            const char* name;       //Java 中定义的 Native 方法名 , 注意这是一个 C 字符串
            const char* signature;  //函数签名 , 可以使用 javap 生成
            void*       fnPtr;      //C/C++ 中的 Native 函数签名
        } JNINativeMethod;


② 获取 Java 类 : 获取要注册的 Java 类名称 ;


③ 批量注册 : 最终要调用 JNIEnv 的 RegisterNatives 方法 , 批量注册代码 ; 下面代码中的 android::AndroidRuntime::registerNativeMethods 方法定义在 frameworks\base\core\jni\AndroidRuntime.cpp 中 , 在该方法中又调用了 libnativehelper\JNIHelp.cpp 中的 jniRegisterNativeMethods 方法 , 在该方法中调用了 JNIEnv 的 RegisterNatives 方法注册了这一批 Bitmap.java 的函数 ;




2. Bitmap.cpp 中完整动态注册代码 : 其中对关键代码进行了注释 ;


// 调用的 register_android_graphics_Bitmap 注册函数方法定义在该头文件中
#include <android_runtime/AndroidRuntime.h>
// 定义了 JNINativeMethod 结构体数组
static JNINativeMethod gBitmapMethods[] = {
    {   "nativeCreate",             "([IIIIIIZ)Landroid/graphics/Bitmap;",
        (void*)Bitmap_creator },
    {   "nativeCopy",               "(JIZ)Landroid/graphics/Bitmap;",
        (void*)Bitmap_copy },
    {   "nativeDestructor",         "(J)V", (void*)Bitmap_destructor },
    {   "nativeRecycle",            "(J)Z", (void*)Bitmap_recycle },
    {   "nativeReconfigure",        "(JIIIIZ)V", (void*)Bitmap_reconfigure },
    // nativeCompress 图片压缩方法
    // Java 中的方法名是 nativeCompress 
    // Java 中的方法签名 (JIILjava/io/OutputStream;[B)Z
    // C++ 中的方法签名 (void*)Bitmap_compress
    {   "nativeCompress",           "(JIILjava/io/OutputStream;[B)Z",
        (void*)Bitmap_compress },
    {   "nativeErase",              "(JI)V", (void*)Bitmap_erase },
    {   "nativeRowBytes",           "(J)I", (void*)Bitmap_rowBytes },
    {   "nativeConfig",             "(J)I", (void*)Bitmap_config },
    {   "nativeHasAlpha",           "(J)Z", (void*)Bitmap_hasAlpha },
    {   "nativeIsPremultiplied",    "(J)Z", (void*)Bitmap_isPremultiplied},
    {   "nativeSetHasAlpha",        "(JZZ)V", (void*)Bitmap_setHasAlpha},
    {   "nativeSetPremultiplied",   "(JZ)V", (void*)Bitmap_setPremultiplied},
    {   "nativeHasMipMap",          "(J)Z", (void*)Bitmap_hasMipMap },
    {   "nativeSetHasMipMap",       "(JZ)V", (void*)Bitmap_setHasMipMap },
    {   "nativeCreateFromParcel",
        "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
        (void*)Bitmap_createFromParcel },
    {   "nativeWriteToParcel",      "(JZILandroid/os/Parcel;)Z",
        (void*)Bitmap_writeToParcel },
    {   "nativeExtractAlpha",       "(JJ[I)Landroid/graphics/Bitmap;",
        (void*)Bitmap_extractAlpha },
    {   "nativeGenerationId",       "(J)I", (void*)Bitmap_getGenerationId },
    {   "nativeGetPixel",           "(JII)I", (void*)Bitmap_getPixel },
    {   "nativeGetPixels",          "(J[IIIIIII)V", (void*)Bitmap_getPixels },
    {   "nativeSetPixel",           "(JIII)V", (void*)Bitmap_setPixel },
    {   "nativeSetPixels",          "(J[IIIIIII)V", (void*)Bitmap_setPixels },
    {   "nativeCopyPixelsToBuffer", "(JLjava/nio/Buffer;)V",
                                            (void*)Bitmap_copyPixelsToBuffer },
    {   "nativeCopyPixelsFromBuffer", "(JLjava/nio/Buffer;)V",
                                            (void*)Bitmap_copyPixelsFromBuffer },
    {   "nativeSameAs",             "(JJ)Z", (void*)Bitmap_sameAs },
    {   "nativePrepareToDraw",      "(J)V", (void*)Bitmap_prepareToDraw },
};
#define kClassPathName  "android/graphics/Bitmap"
// 动态注册函数的实际方法
int register_android_graphics_Bitmap(JNIEnv* env)
{
  // 该方法最终调用了 libnativehelper\JNIHelp.cpp 中的  jniRegisterNativeMethods 方法
    return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
                                gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
}



源码位置 \frameworks\base\core\jni\android\graphics\Bitmap.cpp


目录
相关文章
|
11月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
1029 4
|
24天前
|
存储 API Android开发
【02】完整的安卓二次商业实战-配置gradle-构建打包原生安卓项目-调试本地运行模拟器-优雅草伊凡
【02】完整的安卓二次商业实战-配置gradle-构建打包原生安卓项目-调试本地运行模拟器-优雅草伊凡
89 4
【02】完整的安卓二次商业实战-配置gradle-构建打包原生安卓项目-调试本地运行模拟器-优雅草伊凡
|
1月前
|
人工智能 API 开发者
图文教程:阿里云百炼API-KEY获取方法,亲测全流程
本文详细介绍了如何获取阿里云百炼API-KEY,包含完整流程与截图指引。需先开通百炼平台及大模型服务,再通过控制台创建并复制API-KEY。目前平台提供千万tokens免费额度,适合开发者快速上手使用。
581 5
|
3月前
|
存储 机器学习/深度学习 API
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
573 31
Android API Level 到底是什么?和安卓什么关系?应用发布如何知道自己的版本?优雅草卓伊凡
|
8月前
|
JSON 数据可视化 API
Python 中调用 DeepSeek-R1 API的方法介绍,图文教程
本教程详细介绍了如何使用 Python 调用 DeepSeek 的 R1 大模型 API,适合编程新手。首先登录 DeepSeek 控制台获取 API Key,安装 Python 和 requests 库后,编写基础调用代码并运行。文末包含常见问题解答和更简单的可视化调用方法,建议收藏备用。 原文链接:[如何使用 Python 调用 DeepSeek-R1 API?](https://apifox.com/apiskills/how-to-call-the-deepseek-r1-api-using-python/)
|
4月前
|
缓存 负载均衡 监控
微服务架构下的电商API接口设计:策略、方法与实战案例
本文探讨了微服务架构下的电商API接口设计,旨在打造高效、灵活与可扩展的电商系统。通过服务拆分(如商品、订单、支付等模块)和标准化设计(RESTful或GraphQL风格),确保接口一致性与易用性。同时,采用缓存策略、负载均衡及限流技术优化性能,并借助Prometheus等工具实现监控与日志管理。微服务架构的优势在于支持敏捷开发、高并发处理和独立部署,满足电商业务快速迭代需求。未来,电商API设计将向智能化与安全化方向发展。
|
自然语言处理 算法 Java
地址描述转换为坐标点不使用API,有什么转换的方法?
地址描述转换为坐标点不使用API,有什么转换的方法?
658 64
|
12月前
|
测试技术 API 项目管理
API测试方法
【10月更文挑战第18天】API测试方法
297 1
|
Android开发
错误记录:调用原生TvSettings 的 com.android.tv.settings.device.storage.ResetActivity 无法启动
本文记录了一个Android TV设置中由于未设置`android:exported="true"`导致`com.android.tv.settings.device.storage.ResetActivity`无法被第三方app启动的错误,并通过添加该属性成功解决了问题。
219 1
|
编译器 API Android开发
Android经典实战之Kotlin Multiplatform 中,如何处理不同平台的 API 调用
本文介绍Kotlin Multiplatform (KMP) 中使用 `expect` 和 `actual` 关键字处理多平台API调用的方法。通过共通代码集定义预期API,各平台提供具体实现,编译器确保正确匹配,支持依赖注入、枚举类处理等,实现跨平台代码重用与原生性能。附带示例展示如何定义跨平台函数与类。
382 0

热门文章

最新文章