Internal memory full of images, probably caused by Bitmap.compress (format, int, stream)

My application is a Wifi chat application with which you can chat between two Android blocks with text messages and take pictures of the camera and send them. Images are saved on the SD card.

I used OutOfMemoryError after several images sent, but I solved this problem by sending

 options.inPurgeable = true; 

and

 options.inInputShareable = true; 

to the BitmapFactory.decodeByteArray method. This makes the pixels "deallocatable", so new images can use memory. Thus, the error is no longer saved.

But the internal memory is still full of images, and the warning “Low space: phone storage space is getting low” appears. The application will no longer work, but after the application finishes, there is no more memory on the phone. I need to manually clear the application data in the "Settings"> "Applications"> "Application Management" section.

I tried to process the bitmaps and even tried to clear the application cache explicitly, but it doesn't seem to do what I expect.

This function receives an image through a TCP socket, writes it to an SD card, and launches my custom Activity PictureView:

 public void receivePicture(String fileName) { try { int fileSize = inStream.readInt(); Log.d("","fileSize:"+fileSize); byte[] tempArray = new byte[200]; byte[] pictureByteArray = new byte[fileSize]; path = Prefs.getPath(this) + "/" + fileName; File pictureFile = new File(path); try { if( !pictureFile.exists() ) { pictureFile.getParentFile().mkdirs(); pictureFile.createNewFile(); } } catch (IOException e) { Log.d("", "Recievepic - Kunde inte skapa fil.", e); } int lastRead = 0, totalRead = 0; while(lastRead != -1) { if(totalRead >= fileSize - 200) { lastRead = inStream.read(tempArray, 0, fileSize - totalRead); System.arraycopy(tempArray, 0, pictureByteArray, totalRead, lastRead); totalRead += lastRead; break; } lastRead = inStream.read(tempArray); System.arraycopy(tempArray, 0, pictureByteArray, totalRead, lastRead); totalRead += lastRead; } BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pictureFile)); bos.write(pictureByteArray, 0, totalRead); bos.flush(); bos.close(); bos = null; tempArray = null; pictureByteArray = null; setSentence("<"+fileName+">", READER); Log.d("","path:"+path); try { startActivity(new Intent(this, PictureView.class).putExtra("path", path)); } catch(Exception e) { e.printStackTrace(); } } catch(IOException e) { Log.d("","IOException:"+e); } catch(Exception e) { Log.d("","Exception:"+e); } } 

Here is a PictureView . It creates byte [] from the file on the SD card, decodes the array in Bitmap, compresses the bitmap image and writes it back to the SD card. Finally, in Progress.onDismiss image is set as a full-screen image:

 public class PictureView extends Activity { private String fileName; private ProgressDialog progress; public ImageView view; @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); Log.d("","onCreate() PictureView"); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); view = new ImageView(this); setContentView(view); progress = ProgressDialog.show(this, "", "Laddar bild..."); progress.setOnDismissListener(new OnDismissListener() { public void onDismiss(DialogInterface dialog) { File file_ = getFileStreamPath(fileName); Log.d("","SETIMAGE"); Uri uri = Uri.parse(file_.toString()); view.setImageURI(uri); } }); new Thread() { public void run() { String path = getIntent().getStringExtra("path"); Log.d("","path:"+path); File pictureFile = new File(path); if(!pictureFile.exists()) finish(); fileName = path.substring(path.lastIndexOf('/') + 1); Log.d("","fileName:"+fileName); byte[] pictureArray = new byte[(int)pictureFile.length()]; try { DataInputStream dis = new DataInputStream( new BufferedInputStream( new FileInputStream(pictureFile)) ); for(int i=0; i < pictureArray.length; i++) pictureArray[i] = dis.readByte(); } catch(Exception e) { Log.d("",""+e); e.printStackTrace(); } /** * Passing these options to decodeByteArray makes the pixels deallocatable * if the memory runs out. */ BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true; options.inInputShareable = true; Bitmap pictureBM = BitmapFactory.decodeByteArray(pictureArray, 0, pictureArray.length, options); OutputStream out = null; try { out = openFileOutput(fileName, MODE_PRIVATE); /** * COMPRESS !!!!! **/ pictureBM.compress(CompressFormat.PNG, 100, out); pictureBM = null; progress.dismiss(); } catch (IOException e) { Log.e("test", "Failed to write bitmap", e); } finally { if (out != null) try { out.close(); out = null; } catch (IOException e) { } } } }.start(); } @Override protected void onStop() { super.onStop(); Log.d("","ONSTOP()"); Drawable oldDrawable = view.getDrawable(); if( oldDrawable != null) { ((BitmapDrawable)oldDrawable).getBitmap().recycle(); oldDrawable = null; Log.d("","recycle"); } Editor editor = this.getSharedPreferences("clear_cache", Context.MODE_PRIVATE).edit(); editor.clear(); editor.commit(); } } 

When the user clicks the back button, the image should not be accessible anymore from the application. Just saved on the SD card.

In onStop() I recycle the old Bitmap and even try to clear the application data. However, the warning "Low on space" appears. How can I be sure that images will no longer allocate memory when they are not needed?

EDIT: Looks like the problem is the compression method. If everything is compiled after compilation, the problem remains. If I remove the compression, the problem will disappear. Compression seems to set aside memory that has never been released, and it's 2-3 MB per image.

+4
source share
2 answers

Ok, I decided. The problem was that I was passing the OutputStream to compress , which is a stream for a private file in the application’s internal memory. This is what I set as the image later. This file is never highlighted.

I did not understand that I had two files: one on the SD card and one in the internal memory, both with the same name.

Now I just set the SD card file as an ImageView image. I never read the file in internal memory as a byte [], so I never decrypted the array into a bitmap, thereby never compressing the bitmap in internal memory.

This is the new PictureView:

 public class PictureView extends Activity { public ImageView view; private String path; @Override public void onCreate(Bundle bundle) { super.onCreate(bundle); Log.d("","onCreate() PictureView"); path = getIntent().getStringExtra("path"); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); view = new ImageView(this); setContentView(view); Uri uri = Uri.parse( new File(path).toString() ); view.setImageURI(uri); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { Log.d("","Back key pressed"); Drawable oldDrawable = view.getDrawable(); if( oldDrawable != null) { ((BitmapDrawable)oldDrawable).getBitmap().recycle(); oldDrawable = null; Log.d("","recycle"); } view = null; } return super.onKeyDown(keyCode, event); } } 

Is it wrong to use an external file as an ImageView image? Do I have to load it into internal memory first?

+1
source

If you specifically want the image to be completely erased from memory when the user presses back, you can override the "Back" button and make your cleaned images there. I do this in some of my applications and it seems to work. maybe something like this:

 @Override protected void onBackPressed() { super.onBackPressed(); view.drawable = null; jumpBackToPreviousActivity(); } 

I am sure there are some browsing methods that clear other caches and the like. You can process the bitmap, but this does not guarantee that it will be reset immediately, but only at some point when gc gets to it ..... but I'm sure you probably already know that :)

EDIT: You can also do the same in the onPause method. It is guaranteed to work out. The other two can never be called according to android docs. http://developer.android.com/reference/android/app/Activity.html

0
source

Source: https://habr.com/ru/post/1396530/


All Articles