大多数时候,在Android上获取OOM是由于使用了太多位图和/或创建大位图.
最近我决定试用JNI,以便通过将数据本身存储在JNI端来避免OOM.
在与JNI搞砸了一段时间后,我在SO上创建了一些帖子,寻求帮助并分享我的知识,现在我决定与你分享更多代码.如果有人有兴趣阅读调查结果或做出贡献,这里有帖子:
这一次,我添加了存储,恢复,裁剪和旋转位图的功能.它应该很容易添加更多选项,如果其他人在这里将自己的代码添加到更有用的功能,我会很高兴.
所以我要展示的代码实际上是合并了我创造的所有东西.
Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.ic_launcher);
final int width=bitmap.getWidth(),height=bitmap.getHeight();
// store the bitmap in the JNI "world"
final JniBitmapHolder bitmapHolder=new JniBitmapHolder(bitmap);
// no need for the bitmap on the java "world", since the operations are done on the JNI "world"
bitmap.recycle();
// crop a center square from the bitmap, from (0.25,0.25) to (0.75,0.75) of the bitmap.
bitmapHolder.cropBitmap(width/4,height/4,width*3/4,height*3/4);
//rotate the bitmap:
bitmapHolder.rotateBitmapCcw90();
//get the output java bitmap , …Run Code Online (Sandbox Code Playgroud) java-native-interface android bitmap out-of-memory android-ndk
对于我的10,000点,我决定用这个很酷的网站来点缀一些东西:一种在本机内存上缓存位图的机制.
Android设备的每个应用程序的内存量非常有限 - 堆的范围从16MB到128MB,具体取决于各种参数.
如果您通过此限制,则会获得OOM,当您使用位图时,这可能会发生多次.
很多时候,应用程序可能需要克服这些限制,对巨大的位图执行繁重的操作,或者只是存储它们以供以后使用,并且您需要
我想出的是一个简单的java类,它可以使事情变得更容易.
它使用JNI来存储位图数据,并能够在需要时恢复它.
为了支持该类的多个实例,我必须使用我发现的技巧(这里).
数据仍然存储在RAM中,因此如果设备没有足够的RAM,应用程序可能会被杀死.
记得尽快释放记忆.它不仅可以避免内存泄漏,而且还可以避免在您的应用程序到达后台时首先被系统优先处理.
如果您不想忘记释放内存,可以在每次还原位图时释放它,或者使类实现Closable.
作为一种安全措施,我已经使它在finalize()方法中自动释放其本机内存,但不要让它负责这项工作.风险太大了.当这样的事情发生时,我也写了它写入日志.
它的工作方式是将整个数据复制到JNI对象中,为了还原,它从头开始创建位图并将数据放入其中.
正在使用和恢复的位图采用ARGB_8888格式.当然,您可以将其更改为您想要的任何内容,只是不要忘记更改代码...
大位图可能需要一些时间来存储和恢复,因此在后台线程上执行它可能是明智的.
这不是一个完整的OOM解决方案,但它可能有所帮助.例如,您可以将它与您自己的LruCache结合使用,同时避免将堆内存用于缓存本身.
代码仅用于存储和恢复.如果您需要执行某些操作,则需要进行一些研究.openCV可能就是答案,但是如果你想要执行一些基本的东西,你可以自己实现它们(这里是使用JNI旋转大图像的例子).如果你知道其他选择,请在这里告诉我.
希望这对某些人有用.请写下你的意见.
此外,如果您发现代码有任何问题或建议填补,请告知我们.
如果您希望在JNI端执行更多操作,您可以使用我所做的这篇文章.它基于我在这里编写的代码,但允许您进行更多操作,您可以轻松添加更多自己的代码.
创建一个具有大量高质量图像的应用程序,我决定将图像缩小到所需的大小(这意味着如果图像大于屏幕,我会缩小它的尺寸).
我注意到在某些设备上,如果图像缩小,它们变得模糊/像素化,但在同一设备上,对于相同的目标imageView大小,如果图像没有缩小尺寸,它们看起来很好.
我已经决定进一步检查这个问题,并创建了一个显示问题的小型POC应用程序.
在向您展示代码之前,这里是我正在谈论的演示:

看到差异有点难,但你可以看到第二个有点像素化.这可以在任何图像上显示.
public class MainActivity extends Activity
{
@Override
protected void onCreate(final Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final ImageView originalImageView=(ImageView)findViewById(R.id.originalImageView);
final ImageView halvedImageView=(ImageView)findViewById(R.id.halvedImageView);
final ImageView halvedBitmapImageView=(ImageView)findViewById(R.id.halvedBitmapImageView);
//
final Bitmap originalBitmap=BitmapFactory.decodeResource(getResources(),R.drawable.test);
originalImageView.setImageBitmap(originalBitmap);
halvedImageView.setImageBitmap(originalBitmap);
//
final LayoutParams layoutParams=halvedImageView.getLayoutParams();
layoutParams.width=originalBitmap.getWidth()/2;
layoutParams.height=originalBitmap.getHeight()/2;
halvedImageView.setLayoutParams(layoutParams);
//
final Options options=new Options();
options.inSampleSize=2;
// options.inDither=true; //didn't help
// options.inPreferQualityOverSpeed=true; //didn't help
final Bitmap bitmap=BitmapFactory.decodeResource(getResources(),R.drawable.test,options);
halvedBitmapImageView.setImageBitmap(bitmap);
}
}
Run Code Online (Sandbox Code Playgroud)
XML:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools:context=".MainActivity"
android:fillViewport="true">
<HorizontalScrollView android:layout_width="match_parent"
android:fillViewport="true" android:layout_height="match_parent">
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent" …Run Code Online (Sandbox Code Playgroud) 好吧,这一切折磨了我好几个星期,我将图像设置为 227 像素高,将其缩放到 170 像素,即使我希望它在任何时候都是 wrap_content。
好的。在这里,我采用了 1950 像素长的我的图像(我把它的一部分放在这里,这样你就可以理解它应该是什么样子)。

首先,我想将其缩放回 227 像素高,因为这就是它的设计方式以及应该如何
Bitmap bitmapOrg = BitmapFactory.decodeResource(getResources(),R.drawable.ver_bottom_panel_tiled_long);
int width = bitmapOrg.getWidth();
int height = bitmapOrg.getHeight();
int newWidth = 200; //this should be parent's whdth later
int newHeight = 227;
// calculate the scale
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
// create a matrix for the manipulation
Matrix matrix = new Matrix();
// resize the bit map
matrix.postScale(scaleWidth, scaleHeight);
// recreate the new Bitmap …Run Code Online (Sandbox Code Playgroud)