Note that there are some explanatory texts on larger screens.

plurals
  1. POOOM exception - bitmap size exceeding VM budget
    text
    copied!<p>I guess I have a memory leak. I have a list activity that uses the camera.... Only working now with taking pictures... But I guess some list inflating does some of the memory leak... I guess I missed some resource freeing (guess of images....)</p> <p>I could not find it.</p> <p>Really need some help in this one.</p> <p>Here are the classes: The List Activity</p> <p>package org.BJ.Food4All.Activities.NewRecipe;</p> <pre><code>import org.BJ.Food4All.R; import org.BJ.Food4All.Recipe; import org.BJ.Food4All.Recipe.Instruction; import org.BJ.Food4All.Activities.RecipeBook.RecipeInstructionsListViewAdapter; import org.BJ.Food4All.Activities.RecipeBook.SharedData; import org.BJ.Food4All.utils.CameraUtil; import org.BJ.Food4All.utils.ImageUploadItem; import android.app.ListActivity; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.view.ContextMenu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.EditText; public class Instructions extends ListActivity implements OnClickListener { private final static String mTAG = "Instructions"; private EditText mInstructionEditText = null; private RecipeInstructionsListViewAdapter mListViewAdapter = null; private Recipe mEditRecipe = PrivateResources.GetRecipe(); private CameraUtil mCameraUtil = new CameraUtil( this ); private int mSelectedEntryIndex = -1; @Override protected void onCreate( Bundle savedInstanceState ) { super.onCreate(savedInstanceState); setContentView( R.layout.new_recipe_instruction_tab ); mInstructionEditText = (EditText)findViewById(R.id.newRecipeInstructionEditTextId); View addInstructionButton = findViewById( R.id.naddInstructionButtonId ); // Sanity check if( mInstructionEditText == null || addInstructionButton == null ) { Log.e( mTAG, "NULL pointers"); // secure exit finish(); } // Set up click listeners for all the buttons addInstructionButton.setOnClickListener( this ); mListViewAdapter = new RecipeInstructionsListViewAdapter( this, R.layout.recipes_instruction_list_single_view_entry, mEditRecipe.GetInstructions() ); setListAdapter( mListViewAdapter ); registerForContextMenu( getListView() ); } public void onClick( View v ) { switch( v.getId() ) { case R.id.naddInstructionButtonId: AddInstructionToRecipe( v ); break; default: Log.e( mTAG, "Invalid ID:" + v.getId() ); // secure exit finish(); } } private void AddInstructionToRecipe( View v ) { String instructionText = mInstructionEditText.getText().toString(); if( instructionText == null ) { return; } Instruction newInstruction = new Instruction( mEditRecipe.GetInstructions().size() + 1, // Index instructionText, // The instruction null, true ); if( mEditRecipe.AddInstruction( newInstruction ) != true ) { // TODO - ERROR } else { mListViewAdapter.notifyDataSetChanged(); } } /* * (non-Javadoc) * @see android.app.Activity#onCreateContextMenu(android.view.ContextMenu, android.view.View, android.view.ContextMenu.ContextMenuInfo) */ @Override public void onCreateContextMenu( ContextMenu menu, View v, ContextMenuInfo menuInfo) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.instructions_ctx_menu, menu); super.onCreateContextMenu( menu, v, menuInfo ); } /* * (non-Javadoc) * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) */ @Override public boolean onContextItemSelected(MenuItem item) { super.onContextItemSelected(item); AdapterView.AdapterContextMenuInfo menuInfo; menuInfo = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo(); mSelectedEntryIndex = menuInfo.position; switch( item.getItemId() ) { case R.id.deleteId: mEditRecipe.RemoveInstruction( mSelectedEntryIndex ); mListViewAdapter.notifyDataSetChanged(); return true; case R.id.takePictureId: mCameraUtil.TakePicture(); return true; } return false; } /* * (non-Javadoc) * @see android.app.Activity#onActivityResult(int, int, android.content.Intent) */ @Override protected void onActivityResult( int requestCode, int resultCode, Intent data ) { String imageLocation = mCameraUtil.onActivityResult( requestCode, resultCode, data ); // TODO - switch to parameter passed in the intent!!!! like TakePicture( index ); mEditRecipe.GetInstructions().get( mSelectedEntryIndex ).SetInstructionImageLocation( imageLocation ); mSelectedEntryIndex = -1; // Update the listviewitem with the picture mListViewAdapter.notifyDataSetChanged(); } } </code></pre> <p>The Adapter:</p> <pre><code>package org.BJ.Food4All.Activities.RecipeBook; import java.util.ArrayList; import org.BJ.Food4All.R; import org.BJ.Food4All.Recipe.Instruction; import org.BJ.Food4All.utils.GlobalDefs; import android.content.Context; import android.graphics.Color; import android.graphics.Typeface; import android.net.Uri; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class RecipeInstructionsListViewAdapter extends ArrayAdapter&lt;Instruction&gt; { private Context mContext; private ArrayList&lt;Instruction&gt; mItems; private LayoutInflater mInflater; public RecipeInstructionsListViewAdapter( Context context, int textViewResourceId, ArrayList&lt;Instruction&gt; items) { super( context, textViewResourceId, items ); mContext = context; mItems = items; mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public View getView( int position, View convertView, ViewGroup parent ) { ViewHolder holder = new ViewHolder(); if (convertView == null) { convertView = mInflater.inflate(R.layout.recipes_instruction_list_single_view_entry, null); } if( super.getItem(position) != null ) { holder.instructionIndex = (TextView) convertView.findViewById( R.id.listUp_RecipeInstructionNumberTextBoxId ); holder.instructionText = (TextView) convertView.findViewById( R.id.listUp_RecipeInstructioTextTextBoxId ); holder.instructionImage = (ImageView)convertView.findViewById( R.id.listUp_RecipeInstructionImageViewId ); Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "Eras_Bold.ttf"); holder.instructionIndex.setTypeface(tf); holder.instructionIndex.setTextSize(30); holder.instructionIndex.setTextColor( GlobalDefs.GetHeadlineColor() ); holder.instructionIndex.setText( Integer.toString(mItems.get(position).getIndex() ) ); tf = Typeface.createFromAsset(mContext.getAssets(), "Arial.ttf"); holder.instructionText.setTypeface(tf); holder.instructionText.setTextSize(14); holder.instructionText.setTextColor( Color.BLACK ); holder.instructionText.setText( mItems.get(position).getText() ); String imageLocation = mItems.get(position).GetInstructionImageLocation(); if( imageLocation != null ) { holder.instructionImage.setImageURI( Uri.parse( imageLocation ) ); holder.instructionImage.setVisibility( View.VISIBLE ); } else { holder.instructionImage.setVisibility( View.GONE ); } convertView.setTag(holder); convertView.setLayoutParams( new ListView.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); } else { } return convertView; } @Override public boolean isEnabled(int position) { return true; } static class ViewHolder { TextView instructionIndex; TextView instructionText; ImageView instructionImage; } } </code></pre> <p>The camera util:</p> <pre><code>package org.BJ.Food4All.utils; import java.io.File; import org.BJ.Food4All.DB.DBManager; import android.app.Activity; import android.content.ContentValues; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Environment; import android.provider.MediaStore; import android.util.Log; //import android.widget.ImageView; import android.widget.Toast; public class CameraUtil { private static final String mTAG = "CameraUtil"; private static final int PICK_IMAGE = 1; private static final int CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE = 2; private Activity mParentActivity = null; private String mFileName = null; // Storage filename // private Uri mImageUri = null; // mImageUri is the current activity attribute, define and save it for later usage (also in onSaveInstanceState) private Bitmap mBitmap = null; // private ImageView mImageView = null; private DBManager mDBManager = null; public CameraUtil( Activity parentActivity ) { mParentActivity = parentActivity; mDBManager = new DBManager( parentActivity ); } /** * Used by the camera button - for taking a new picture */ public void TakePicture() { mFileName = mDBManager.GetCurrentImageFilename() + ".jpg"; ContentValues contentValues = new ContentValues(); contentValues.put( MediaStore.Images.Media.TITLE, mFileName ); contentValues.put( MediaStore.Images.Media.DESCRIPTION, "Image capture by camera" ); // TODO- update description for recipe name description // mImageUri = mParentActivity.getContentResolver().insert( // MediaStore.Images.Media.EXTERNAL_CONTENT_URI, // contentValues ); //create new Camera Intent Intent intent = new Intent( MediaStore.ACTION_IMAGE_CAPTURE ); intent.putExtra( MediaStore.EXTRA_OUTPUT, Uri.fromFile(getImageFile( mFileName )));//mImageUri ); intent.putExtra( MediaStore.EXTRA_VIDEO_QUALITY, 1 ); try { mParentActivity.startActivityForResult( intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE ); } catch( Exception e ) { Toast.makeText( mParentActivity.getApplicationContext(), "Error while starting Camera!", Toast.LENGTH_LONG ).show(); Log.e( mTAG, "Failed to start camera" ); Log.e( mTAG, e.getMessage(), e ); } } /** * * @param requestCode * @param resultCode * @param data */ public String onActivityResult(int requestCode, int resultCode, Intent data) { String fileManagerString = null; String selectedImagePath = null; switch( requestCode ) { case PICK_IMAGE: // Used if we want to choose a picture from the gallery if( resultCode == Activity.RESULT_OK ) { Uri selectedImageUri = data.getData(); String filePath = null; try { // OI FILE Manager fileManagerString = selectedImageUri.getPath(); // MEDIA GALLERY selectedImagePath = getPath( selectedImageUri ); if( selectedImagePath != null ) { filePath = selectedImagePath; } else if( fileManagerString != null ) { filePath = fileManagerString; } else { Toast.makeText( mParentActivity.getApplicationContext(), "Unknown path", Toast.LENGTH_LONG ).show(); Log.e( mTAG, "Unknown image path"); } if( filePath != null ) { DecodeFile( filePath ); } else { mBitmap = null; } } catch( Exception e ) { Toast.makeText( mParentActivity.getApplicationContext(), "Internal error", Toast.LENGTH_LONG ).show(); Log.e( mTAG, e.getMessage(), e); } } break; case CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE: if( resultCode == Activity.RESULT_OK ) { // Uri selectedImageUri = mImageUri; String filePath = null; try { // OI FILE Manager fileManagerString = mFileName;//selectedImageUri.getPath(); // MEDIA GALLERY // selectedImagePath = mFileName;//getPath( selectedImageUri ); //GlobalData.setUploadedImagePath(selectedImagePath); // TODO - for uploading the image // Add image to the recipe images // ImageUploadItem uploadItem = new ImageUploadItem( selectedImagePath ); // GlobalData.imageUploads.add(uploadItem); // Get image path on the image // if( selectedImagePath != null ) // { // filePath = selectedImagePath; // } // else if( fileManagerString != null ) // { filePath = fileManagerString; // } // else // { // // Toast.makeText( mParentActivity.getApplicationContext(), // "Unknown path", // Toast.LENGTH_LONG ).show(); // // Log.e( mTAG, "Unknown image path" ); // } if( filePath != null ) { String p = getImageFile( mFileName ).getPath(); DecodeFile( p );//filePath ); } else { mBitmap = null; } } catch( Exception e ) { Toast.makeText( mParentActivity.getApplicationContext(), "Internal error", Toast.LENGTH_LONG ).show(); Log.e( mTAG, e.getMessage(), e); } } break; default: return null; } // TODO Here is where the image is received from either the camera or the gallery and is in the async task // TODO to go the next activity return getImageFile( mFileName ).getPath();//mFileName;//selectedImagePath; } /** * * @param uri * @return */ private String getPath( Uri uri ) { String[] projection = { MediaStore.Images.Media.DATA }; Cursor cursor = mParentActivity.managedQuery( uri, projection, null, null, null ); if( cursor != null ) { // HERE YOU WILL GET A NULLPOINTER IF CURSOR IS NULL // THIS CAN BE, IF YOU USED OI FILE MANAGER FOR PICKING THE MEDIA int column_index = cursor.getColumnIndexOrThrow( MediaStore.Images.Media.DATA ); cursor.moveToFirst(); return cursor.getString( column_index ); } else { return null; } } /** * * @param filePath */ private void DecodeFile( String filePath ) { // Decode image size BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); bitmapOptions.inJustDecodeBounds = true; BitmapFactory.decodeFile( filePath, bitmapOptions ); // The new size we want to scale to final int REQUIRED_SIZE = 1024; // Find the correct scale value. It should be the power of 2. int width_tmp = bitmapOptions.outWidth; int height_tmp = bitmapOptions.outHeight; int scale = 1; while( true ) { if( width_tmp &lt; REQUIRED_SIZE &amp;&amp; height_tmp &lt; REQUIRED_SIZE ) { break; } width_tmp /= 2; height_tmp /= 2; scale *= 2; } // Decode with inSampleSize BitmapFactory.Options newBitmapOptions = new BitmapFactory.Options(); newBitmapOptions.inSampleSize = scale; mBitmap = BitmapFactory.decodeFile( filePath, newBitmapOptions ); } /** * Gets the picture taken by the camera - to be used in ImageView * * @return */ public Bitmap GetTakenPictureBitmap() { return mBitmap; } /** * Get the image FILE to be used for the picture taken by the camera - from filename String * * @param filename - the filename String * @return The File representing the image file */ private File getImageFile( final String filename ) { //it will return /sdcard/image.tmp final File path = new File( Environment.getExternalStorageDirectory(), mParentActivity.getPackageName() ); if( !path.exists() ) { path.mkdir(); } return new File( path, filename ); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload