如何将位图缓存到本机内存中
对于我的10,000点,我决定在这个很酷的网站上做出一些贡献:一种将位图缓存在本机内存中的机制。
背景
Android设备为每个应用程序分配的内存非常有限-堆的范围从16MB到128MB,具体取决于各种参数。
如果超过此限制,则会得到OOM,并且在使用位图时可能会发生多次。
很多时候,应用可能需要克服这些限制,对巨大的位图执行繁重的操作,或者只是将其存储以备后用,而您需要
我想出的是一个简单的Java类,它将使实现这些目的变得容易一些。
它使用JNI来存储位图数据,并能够在需要时进行还原。
为了支持该类的多个实例,我不得不使用发现的技巧(here)。
重要笔记
数据仍存储在RAM中,因此,如果设备没有足够的RAM,则该应用可能会被杀死。
记住要尽快释放内存。这不仅是为了避免内存泄漏,而且还是为了避免一旦您的应用程序出现在后台时被系统优先处理。
如果您不想忘记释放内存,则可以在每次还原位图时释放它,或者使该类实现Closable。
为了安全起见,我已使其在finalize()方法中自动释放其本机内存,但不要让它负责这项工作。太冒险了。当发生这种情况时,我也已将其写入日志。
它的工作方式是将整个数据复制到JNI对象中,并且为了还原,它从头开始创建位图并将数据放入其中。
正在使用和还原的位图为ARGB_8888格式。当然,您可以将其更改为所需的任何内容,只是不要忘记更改代码…
大的位图可能需要花费一些时间来存储和恢复,因此在后台线程上进行操作可能是明智的。
这不是完整的OOM解决方案,但可能会有所帮助。例如,您可以将其与您自己的LruCache结合使用,同时避免将堆内存用于缓存本身。
代码仅用于存储和还原。如果需要执行某些操作,则需要进行一些研究。openCV可能是答案,但是如果您希望执行一些基本的工作,则可以自己实现它们(这是使用JNI的可旋转大图像的示例)。如果您知道其他选择,请在此处告诉我。
希望这对某些人有用。请写下您的评论。
另外,如果您发现代码有任何问题或改进建议,请告诉我。
-
说明示例代码显示了如何存储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)