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

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

问题描述

有关我的10000分,我决定contibute一些同此凉网站:一种机制,将位图缓存在本地内存

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

Android设备有一个非常有限的存储空间为每个APP-堆范围从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.

它使用以存储位图数据,并在需要时能够恢复它JN​​I。

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中,因此,如果设备没有足够的内存,应用程序可能会被杀死。

  • 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格式。当然,你可以把它改成任何你想,只是不要忘了改变code ...

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.

code仅用于存储和恢复。如果你需要执行一些操作,您将需要进行一些研究。 OpenCV的 可能是答案,但如果你想进行一些基本的东西,你可以自己(实现它们< STRONG> 这里是一个例子使用JNI rotatign大量图像) 。如果你知道其他的替代品,请让我知道,<一个href="http://stackoverflow.com/questions/17885201/image-manipulation-and-handling-on-the-jni-side-of-android">here

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.

另外,如果你发现与code或建议的设计改进有任何问题,请让我知道。

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

如果你想在JNI端执行,甚至更多的操作,您可以使用<一个href="http://stackoverflow.com/questions/18250951/jni-bitmap-operations-for-helping-to-avoid-oom">this交 ,我所做的。它是基于在code我在这里写的,但可以让你做更多的操作,您可以轻松地将自己添加更多的。

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.

推荐答案

样品code演示了如何存储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 imageViews。我没有包括在code,因为这是很明显的。

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

千万记得要改变code到你自己的包,如果你需要,否则事情将无法正常工作。

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天全站免登陆