如何将位图缓存到本机内存中

发布于 2021-01-31 15:27:52

对于我的10,000点,我决定在这个很酷的网站上做出一些贡献:一种将位图缓存在本机内存中的机制。

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

如果超过此限制,则会得到OOM,并且在使用位图时可能会发生多次。

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

我想出的是一个简单的Java类,它将使实现这些目的变得容易一些。

它使用JNI来存储位图数据,并能够在需要时进行还原。

为了支持该类的多个实例,我不得不使用发现的技巧(here)。

重要笔记
数据仍存储在RAM中,因此,如果设备没有足够的RAM,则该应用可能会被杀死。

记住要尽快释放内存。这不仅是为了避免内存泄漏,而且还是为了避免一旦您的应用程序出现在后台时被系统优先处理。

如果您不想忘记释放内存,则可以在每次还原位图时释放它,或者使该类实现Closable。

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

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

正在使用和还原的位图为ARGB_8888格式。当然,您可以将其更改为所需的任何内容,只是不要忘记更改代码…

大的位图可能需要花费一些时间来存储和恢复,因此在后台线程上进行操作可能是明智的。

这不是完整的OOM解决方案,但可能会有所帮助。例如,您可以将其与您自己的LruCache结合使用,同时避免将堆内存用于缓存本身。

代码仅用于存储和还原。如果需要执行某些操作,则需要进行一些研究。openCV可能是答案,但是如果您希望执行一些基本的工作,则可以自己实现它们(这是使用JNI的可旋转大图像的示例)。如果您知道其他选择,请在此处告诉我。

希望这对某些人有用。请写下您的评论。

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

关注者
0
被浏览
413
1 个回答
  • 面试哥
    面试哥 2021-01-31
    为面试而生,有面试问题,就找面试哥。

    说明示例代码显示了如何存储2个不同的位图(较小的位图,但这只是一个演示),回收原始的Java位图,然后将它们还原为Java实例并使用它们。

    您可能会猜到,该布局有2个imageViews。我没有在代码中包含它,因为它很明显。

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

    MainActivity.java-如何使用:

    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)
    


知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看