Java Android:高效管理位图arraylist而不会导致OutOfMemory异常

Java Android:高效管理位图arraylist而不会导致OutOfMemory异常,java,android,bitmap,android-canvas,android-bitmap,Java,Android,Bitmap,Android Canvas,Android Bitmap,我正在用android实现一本速写本。在这里,我添加了一个画布。为了显示用户有多个页面,当用户单击下一页时,我将画布上当前的位图保存到位图阵列列表中,并清除画布以获得新位图。最后我将整个ArrayList一起保存到图像文件中(工作正常) 问题是,在我的应用程序中,用户可以使用“下一步”和“上一步”按钮移动到先前绘制的位图,在这里,我从位图重新绘制位图。这就是我使用位图数组列表的原因。问题是,在创建了几个页面(比如30个页面)后,我的应用程序崩溃,说OutOfMemory exception 有没

我正在用android实现一本速写本。在这里,我添加了一个画布。为了显示用户有多个页面,当用户单击下一页时,我将画布上当前的位图保存到位图阵列列表中,并清除画布以获得新位图。最后我将整个ArrayList一起保存到图像文件中(工作正常)

问题是,在我的应用程序中,用户可以使用“下一步”和“上一步”按钮移动到先前绘制的位图,在这里,我从位图重新绘制位图。这就是我使用位图数组列表的原因。问题是,在创建了几个页面(比如30个页面)后,我的应用程序崩溃,说OutOfMemory exception

有没有人能提出一种有效的方法来实现我想要实现的目标,这样用户就可以创建任意多的页面,并且仍然可以来回导航

下面是保存到位图arraylist的代码

public void bitArrayStore(int k) {

        if (drawView.canvasBitmap.sameAs(drawView.emptyBitmap)) {
            flag = true;
        } else {

            try {
                if (flag1 == false) {
                    drawView.buildDrawingCache();
                    drawView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
                    bitmaps.set(k, Bitmap.createBitmap(drawView.getDrawingCache()));

                } else {
                    bitmaps.add(k, Bitmap.createBitmap(drawView.getDrawingCache()));
                    flag1 = false;
                }
            } catch (IndexOutOfBoundsException e) {
                bitmaps.add(k, Bitmap.createBitmap(drawView.getDrawingCache()));

            }
            drawView.destroyDrawingCache();

        }
这是当用户单击“上一步”或“下一步”按钮时,我用于将其重新绘制到画布中的代码

public void redraw(ArrayList<Bitmap> bits, int i) {
        try {

                drawCanvas.drawBitmap(bits.get(i), 0, 0, canvasPaint);
                invalidate();


        }catch (NullPointerException e){
            Log.w("DrawingApp","Exception");
        }
    }
public void重画(数组列表位,int i){
试一试{
drawCanvas.drawBitmap(bit.get(i)、0、0、canvasPaint);
使无效();
}捕获(NullPointerException e){
Log.w(“绘图应用”、“例外”);
}
}
下面是onClickListeners中的next和previous按钮的代码

if (v.getId() == R.id.previousbtn) {
            PageNoLayout.setVisibility(View.VISIBLE);
            hideDrawer(colorNsize);
            hideDrawer(eraserDrawer);
            try {
                bitArrayStore(j);
                drawView.startNew();
                j--;
                if (bitmaps.size() > j) {
                    drawView.redraw(bitmaps, j);
                }
            } catch (IndexOutOfBoundsException e) {
                j = 0;
                try {
                    drawView.redraw(bitmaps, j);
                } catch (IndexOutOfBoundsException e1) {
                    drawView.startNew();
                }
            }
            pageNo.setText(String.valueOf(j+1));
            flag = false;
        } else if (v.getId() == R.id.nextbtn) {
            PageNoLayout.setVisibility(View.VISIBLE);
            hideDrawer(colorNsize);
            hideDrawer(eraserDrawer);
            if (j < 50) {
                bitArrayStore(j);
                if (flag == false) {
                    j++;
                }
                if (bitmaps.size() > j) {
                    drawView.startNew();
                    drawView.redraw(bitmaps, j);
                } else {
                    drawView.startNew();
                }
                flag = false;
            } else {
                Snackbar.make(v, "Reached page limit. Please save and start new note", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
            pageNo.setText(String.valueOf(j+1));


        }
if(v.getId()==R.id.previousbtn){
设置可见性(View.VISIBLE);
hideDrawer(彩色);
hideDrawer(橡皮擦绘图器);
试一试{
bitArrayStore(j);
drawView.startNew();
j--;
if(位图.size()>j){
重绘(位图,j);
}
}catch(IndexOutOfBoundsException e){
j=0;
试一试{
重绘(位图,j);
}捕获(IndexOutOfBoundsException e1){
drawView.startNew();
}
}
pageNo.setText(String.valueOf(j+1));
flag=false;
}else if(v.getId()==R.id.nextbtn){
设置可见性(View.VISIBLE);
hideDrawer(彩色);
hideDrawer(橡皮擦绘图器);
如果(j<50){
bitArrayStore(j);
如果(标志==false){
j++;
}
if(位图.size()>j){
drawView.startNew();
重绘(位图,j);
}否则{
drawView.startNew();
}
flag=false;
}否则{
Snackbar.make(v,“已达到页数限制。请保存并开始新注释”,Snackbar.LENGTH\u LONG)
.setAction(“Action”,null).show();
}
pageNo.setText(String.valueOf(j+1));
}

您应该使用ViewPager之类的组件,根据需要管理加载/卸载页面的过程,这样您的内存中就只有将要显示的页面

否则,您可以自己实现相同的逻辑


希望这有帮助。

不要将您的
位图
保存在
阵列列表
中。将它们保存在沙盒内部内存或缓存中(通过
getCacheDir()
)。按“下一步”或“上一步”时,需要从文件中解码位图并通过解码方法之一加载它:
BitmapFactory.decode()

您可以将文件路径保存在
ArrayList
中,以便跟踪
Next
Previous


另外,
BitmapFactory.decode()
可能会占用一些时间,如果您希望用户交互良好,可以提前解码一对(仅一对)位图,可能是异步的。确保使用完
位图后调用
Bitmap.recycle()
,然后转到下一个位图。

唯一的方法是将位图保存在磁盘(PNG或JPG)中,然后重新加载。位图占用大量内存,如果你试图在内存中存储太多的位图,就会耗尽内存,这是一个简单的数学问题。但是我想不出别的办法。这就是我问的原因。谢谢你的回复。就像你说的,如果我保存到磁盘,我怎么知道上一个文件是什么,下一个文件是什么?你能帮我解决这个困惑吗@但是,你只需要记住
寻呼机号码
文件路径
之间的相关性。您可以有一个标准的命名约定,
page_X.png
,或者放入一个SQLite数据库,有一个
ArrayList
,写入共享首选项。@Budius。。。谢谢你的解决方案。。。。我试过了。。就像我想要的那样。请将您的解决方案写为答案,以便我将其标记为已接受答案:)