如何将位图缓存到本机内存中 [英] How to cache bitmaps into native memory

查看:21
本文介绍了如何将位图缓存到本机内存中的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了我的 10,000 分,我决定在这个很酷的网站上贡献一些东西:一种在本机内存上缓存位图的机制.

For my 10,000 points, i've decided to contibute something with this cool website: a mechanism to cache bitmaps on native memory.

Android 设备为每个应用程序提供的内存量非常有限 - 堆范围从 16MB 到 128MB,具体取决于各种参数 .

Android devices have a very limited amount of memory for each app- the heap ranges from 16MB to 128MB , depending on various parameters .

如果你超过这个限制,你会得到 OOM ,当你使用位图时,这可能会发生很多次.

If you pass this limit, you get OOM , and this can occur many times when you use bitmaps.

很多时候,应用程序可能需要克服这些限制,对巨大的位图执行繁重的操作,或者只是将它们存储以备后用,而您需要

Many times, an app might need to overcome those limitations, perform heavy operations on huge bitmaps or just store them for later use, and you need to

我想出的是一个简单的 java 类,它可以使这些目的变得更容易.

What i've come up with, is a simple java class that would make things a bit easier for those purposes.

它使用 JNI 来存储位图数据,并在需要时能够恢复它.

It's using JNI in order to store the bitmap data , and be able to restore it when needed.

为了支持该类的多个实例,我不得不使用我发现的一个技巧(此处).

In order to support multiple instances of the class, i had to use a trick i've found (here) .

  • 数据仍然存储在 RAM 中,因此如果设备没有足够的 RAM,应用可能会被终止.

  • The data is still stored on the RAM, so if the device doesn't have enough RAM, the app might be killed.

记得尽快释放内存.这不仅是为了避免内存泄漏,也是为了避免在您的应用进入后台时被系统优先杀死.

Remember to free the memory as soon as you can. it's not only to avoid memory leaks, but it's also in order to avoid being prioritized by the system to be killed first, once your app comes to the background.

如果你不想忘记释放内存,你可以在每次恢复位图时释放它,或者让类实现可关闭.

If you don't want to forget freeing the memory, you can either free it each time you restore the bitmap, or make the class implement Closable .

作为一项安全措施,我已经在 finalize() 方法中让它自动释放其本机内存,但不要让它负责这项工作.这太冒险了.当这种事情发生时,我也让它写入日志.

As a safety measure, i've made it automatically free its native memory in the finalize() method, but don't let it be responsible of the job. it's too risky. i've also made it write to the log when such a thing occurs.

它的工作方式是将整个数据复制到 JNI 对象中,为了恢复,它从头开始创建位图并将数据放入其中.

The way it works is by copying the entire data into JNI objects, and in order to restore, it creates the bitmap from scratch and puts the data inside.

正在使用和恢复的位图采用 ARGB_8888 格式.当然,你可以改成你想要的,只是不要忘记改代码...

Bitmaps being used and restored are in ARGB_8888 format. of course, you can change it to whatever you wish, just don't forget to change the code...

存储和恢复大型位图可能需要时间,因此在后台线程上执行此操作可能是明智之举.

Large bitmaps could take time to store and restore, so it might be wise to do it on a background thread.

这不是一个完整的 OOM 解决方案,但它可以提供帮助.例如,您可以将它与您自己的 LruCache 结合使用,同时避免将堆内存用于缓存本身.

This is not a full OOM solution, but it could help. for example, you could use it in conjunction with your own LruCache, while avoiding using the heap memory for the cache itself.

代码仅用于存储和恢复.如果您需要执行某些操作,则需要进行一些研究.openCV 可能是答案,但如果你想执行一些基本的东西,你可以实现他们自己(这里有一个例子 rotatign使用 JNI 的大图像).如果您知道其他替代方案,请告诉我,这里 .

Code is only for storing and restoring. if you need to perform some operations, you will need to perform some research. openCV might be the answer, but if you wish to perform some basic stuff, you could implement them by yourself (here's an example of rotatign large images using JNI). if you know other alternatives, please let me know, here .

希望这对某些人有用.请写下您的意见.

Hope this will be useful for some people. please write down your comments.

另外,如果您发现代码有任何问题或改进建议,请告诉我.

Also, if you find any problem with the code or a suggestion for impovement, please let me know.

如果您希望在 JNI 端执行更多操作,您可以使用 这篇文章,是我写的.它基于我在此处编写的代码,但允许您执行更多操作,并且您可以轻松添加更多自己的操作.

If you wish to perform even more operations on the JNI side, you can use this post that I've made. it's based on the code I've written here, but allows you to do more operations and you can easily add more of your own.

推荐答案

说明

示例代码展示了如何存储 2 个不同的位图(很小的,但它只是一个演示),回收原始的 java ,然后将它们恢复到 java 实例并使用它们.

explanation

the sample code shows how to store 2 different bitmaps (small ones, but it's just a demo), recycle the original java ones, and later restore them to java instances and use them.

正如您可能猜到的,布局有 2 个图像视图.我没有在代码中包含它,因为它很明显.

as you might guess, the layout has 2 imageViews. i didn't include it in the code since it's quite obvious.

如果需要,请记住将代码更改为您自己的包,否则将无法正常工作.

do remember to change the code to your own package if you need, otherwise things won't work.

package com.example.jnibitmapstoragetest;
...
public class MainActivity extends Activity
  {
  @Override
  protected void onCreate(final Bundle savedInstanceState)
    {
    super.onCreate(savedInstanceState);
    //
    Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
    final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
    bitmap.recycle();
    //
    Bitmap bitmap2=BitmapFactory.decodeResource(getResources(),android.R.drawable.sym_action_call);
    final JniBitmapHolder bitmapHolder2=new JniBitmapHolder(bitmap2);
    bitmap2.recycle();
    //
    setContentView(R.layout.activity_main);
      {
      bitmap=bitmapHolder.getBitmapAndFree();
      final ImageView imageView=(ImageView)findViewById(R.id.imageView1);
      imageView.setImageBitmap(bitmap);
      }
      {
      bitmap2=bitmapHolder2.getBitmapAndFree();
      final ImageView imageView=(ImageView)findViewById(R.id.imageView2);
      imageView.setImageBitmap(bitmap2);
      }
    }
  }

JniBitmapHolder.java - JNI 和 JAVA 之间的桥梁":

package com.example.jnibitmapstoragetest;
...
public class JniBitmapHolder
  {
  ByteBuffer _handler =null;
  static
    {
    System.loadLibrary("JniBitmapStorageTest");
    }

  private native ByteBuffer jniStoreBitmapData(Bitmap bitmap);

  private native Bitmap jniGetBitmapFromStoredBitmapData(ByteBuffer handler);

  private native void jniFreeBitmapData(ByteBuffer handler);

  public JniBitmapHolder()
    {}

  public JniBitmapHolder(final Bitmap bitmap)
    {
    storeBitmap(bitmap);
    }

  public void storeBitmap(final Bitmap bitmap)
    {
    if(_handler!=null)
      freeBitmap();
    _handler=jniStoreBitmapData(bitmap);
    }

  public Bitmap getBitmap()
    {
    if(_handler==null)
      return null;
    return jniGetBitmapFromStoredBitmapData(_handler);
    }

  public Bitmap getBitmapAndFree()
    {
    final Bitmap bitmap=getBitmap();
    freeBitmap();
    return bitmap;
    }

  public void freeBitmap()
    {
    if(_handler==null)
      return;
    jniFreeBitmapData(_handler);
    _handler=null;
    }

  @Override
  protected void finalize() throws Throwable
    {
    super.finalize();
    if(_handler==null)
      return;
    Log.w("DEBUG","JNI bitmap wasn't freed nicely.please rememeber to free the bitmap as soon as you can");
    freeBitmap();
    }
  }

Android.mk - JNI 的属性文件:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := JniBitmapStorageTest
LOCAL_SRC_FILES := JniBitmapStorageTest.cpp
LOCAL_LDLIBS := -llog
LOCAL_LDFLAGS += -ljnigraphics

include $(BUILD_SHARED_LIBRARY)
APP_OPTIM := debug
LOCAL_CFLAGS := -g

JniBitmapStorageTest.cpp - 神奇"的东西在这里:

#include <jni.h>
#include <jni.h>
#include <android/log.h>
#include <stdio.h>
#include <android/bitmap.h>
#include <cstring>
#include <unistd.h>

#define  LOG_TAG    "DEBUG"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

extern "C"
  {
  JNIEXPORT jobject JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap);
  JNIEXPORT jobject JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle);
  JNIEXPORT void JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle);
  }

class JniBitmap
  {
  public:
    uint32_t* _storedBitmapPixels;
    AndroidBitmapInfo _bitmapInfo;
    JniBitmap()
      {
      _storedBitmapPixels = NULL;
      }
  };

JNIEXPORT void JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniFreeBitmapData(JNIEnv * env, jobject obj, jobject handle)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)
    return;
  delete[] jniBitmap->_storedBitmapPixels;
  jniBitmap->_storedBitmapPixels = NULL;
  delete jniBitmap;
  }

JNIEXPORT jobject JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniGetBitmapFromStoredBitmapData(JNIEnv * env, jobject obj, jobject handle)
  {
  JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
  if (jniBitmap->_storedBitmapPixels == NULL)
    {
    LOGD("no bitmap data was stored. returning null...");
    return NULL;
    }
  //
  //creating a new bitmap to put the pixels into it - using Bitmap Bitmap.createBitmap (int width, int height, Bitmap.Config config) :
  //
  //LOGD("creating new bitmap...");
  jclass bitmapCls = env->FindClass("android/graphics/Bitmap");
  jmethodID createBitmapFunction = env->GetStaticMethodID(bitmapCls, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;");
  jstring configName = env->NewStringUTF("ARGB_8888");
  jclass bitmapConfigClass = env->FindClass("android/graphics/Bitmap$Config");
  jmethodID valueOfBitmapConfigFunction = env->GetStaticMethodID(bitmapConfigClass, "valueOf", "(Ljava/lang/String;)Landroid/graphics/Bitmap$Config;");
  jobject bitmapConfig = env->CallStaticObjectMethod(bitmapConfigClass, valueOfBitmapConfigFunction, configName);
  jobject newBitmap = env->CallStaticObjectMethod(bitmapCls, createBitmapFunction, jniBitmap->_bitmapInfo.height, jniBitmap->_bitmapInfo.width, bitmapConfig);
  //
  // putting the pixels into the new bitmap:
  //
  int ret;
  void* bitmapPixels;
  if ((ret = AndroidBitmap_lockPixels(env, newBitmap, &bitmapPixels)) < 0)
    {
    LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    return NULL;
    }
  uint32_t* newBitmapPixels = (uint32_t*) bitmapPixels;
  int pixelsCount = jniBitmap->_bitmapInfo.height * jniBitmap->_bitmapInfo.width;
  memcpy(newBitmapPixels, jniBitmap->_storedBitmapPixels, sizeof(uint32_t) * pixelsCount);
  AndroidBitmap_unlockPixels(env, newBitmap);
  //LOGD("returning the new bitmap");
  return newBitmap;
  }

JNIEXPORT jobject JNICALL Java_com_example_jnibitmapstoragetest_JniBitmapHolder_jniStoreBitmapData(JNIEnv * env, jobject obj, jobject bitmap)
  {
  AndroidBitmapInfo bitmapInfo;
  uint32_t* storedBitmapPixels = NULL;
  //LOGD("reading bitmap info...");
  int ret;
  if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0)
    {
    LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
    return NULL;
    }
  LOGD("width:%d height:%d stride:%d", bitmapInfo.width, bitmapInfo.height, bitmapInfo.stride);
  if (bitmapInfo.format != ANDROID_BITMAP_FORMAT_RGBA_8888)
    {
    LOGE("Bitmap format is not RGBA_8888!");
    return NULL;
    }
  //
  //read pixels of bitmap into native memory :
  //
  //LOGD("reading bitmap pixels...");
  void* bitmapPixels;
  if ((ret = AndroidBitmap_lockPixels(env, bitmap, &bitmapPixels)) < 0)
    {
    LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
    return NULL;
    }
  uint32_t* src = (uint32_t*) bitmapPixels;
  storedBitmapPixels = new uint32_t[bitmapInfo.height * bitmapInfo.width];
  int pixelsCount = bitmapInfo.height * bitmapInfo.width;
  memcpy(storedBitmapPixels, src, sizeof(uint32_t) * pixelsCount);
  AndroidBitmap_unlockPixels(env, bitmap);
  JniBitmap *jniBitmap = new JniBitmap();
  jniBitmap->_bitmapInfo = bitmapInfo;
  jniBitmap->_storedBitmapPixels = storedBitmapPixels;
  return env->NewDirectByteBuffer(jniBitmap, 0);
  }

这篇关于如何将位图缓存到本机内存中的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆