I have a memory leak in this file, I cannot find where exactly, but I think is the image around --> (Bitmap bm = BitmapFactory.decodeFile(filename))
, I have tried many different ways but I can't get it to work.
package prod.vegs;
//All imports here but not need to write them all now :-)
public class ProductForm extends Activity {
private static int TAKE_PICTURE = 1;
private static int SELECT_PICTURE = 2;
//JSON Response node names
private static String KEY_SUCCESS = "success";
private static String ERROR_MSG = "error_msg";
private static String KEY_TYPES = "subtypes";
private static String TYPE_NAME = "name";
private static String TYPE_ID = "id_type";
private static String PRODUCT_ID = "id_product";
private JSONObject json;
private JSONParser jsonParser;
private String barcodeStr;
private String filename;
private int code;
private ProgressDialog dialog;
private TypeClass[] items;
private TypeClass[] sub_items;
//Declare assets objects
Spinner type;
Spinner subtype;
TextView errorMsg;
TextView description;
TextView name;
Button camera;
Button gallery;
Intent intent;
ImageView preview;
Bundle bundle;
LinearLayout errorMsgContainer;
Context context;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.product_form);
context = this;
Bundle b = getIntent().getExtras();
barcodeStr = b.getString("barcode");
jsonParser = new JSONParser();
dialog = new ProgressDialog(this);
dialog.setMessage(getString(R.string.loading));
dialog.setTitle(getString(R.string.progress));
dialog.setCancelable(true);
//Set assets
name = (TextView) findViewById(R.id.productName);
description = (TextView) findViewById(R.id.productDescription);
errorMsg = (TextView) findViewById(R.id.error_msg);
errorMsgContainer = (LinearLayout) findViewById(R.id.error_msg_container);
type = (Spinner) findViewById(R.id.productParentType);
subtype = (Spinner) findViewById(R.id.productType);
camera = (Button) findViewById(R.id.productCamera);
gallery = (Button) findViewById(R.id.productGallery);
preview = (ImageView) findViewById(R.id.productPreview);
filename = Environment.getExternalStorageDirectory() + String.format(getString(R.string.api_product_form_picture_file), barcodeStr);
Boolean fromScanner = b.getBoolean("scanner");
if (fromScanner == true) {
AlertDialog.Builder alertbox = new AlertDialog.Builder(this);
alertbox.setMessage(getString(R.string.insert_product));
alertbox.setPositiveButton(getString(R.string.yes),
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg_1, int arg_num) {
final Functions function = new Functions();
List<NameValuePair> params = new ArrayList<NameValuePair>();
String url = String.format(getString(R.string.api_product_form_types_url), getString(R.string.api_url));
json = function.loadJSONUrl(url, params);
if(json != null){
try {
if (json.getString(KEY_SUCCESS) != null) {
String res = json.getString(KEY_SUCCESS);
if(Integer.parseInt(res) == 1){
JSONArray types = json.getJSONArray(KEY_TYPES);
items = convertJSONArray(types);
SpinAdapter listViewArrayAdapter = new SpinAdapter(context, android.R.layout.simple_spinner_item, items);
listViewArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
type.setAdapter(listViewArrayAdapter);
type.setOnItemSelectedListener(new OnItemSelectedListener(){
public void onItemSelected(AdapterView<?> parent, View v, int pos, long id) {
try {
String url = String.format(getString(R.string.api_subtypes_id_url), getString(R.string.api_url), ((TypeClass) type.getSelectedItem()).getId());
List<NameValuePair> params = new ArrayList<NameValuePair>();
JSONObject json_subtypes = function.loadJSONUrl(url, params);
if (json_subtypes.getString(KEY_SUCCESS) != null) {
JSONArray subtypes = json_subtypes.getJSONArray(KEY_TYPES);
sub_items = convertJSONArray(subtypes);
SpinAdapter subTypeAdapter = new SpinAdapter(context, android.R.layout.simple_spinner_item, sub_items);
subTypeAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
subtype.setAdapter(subTypeAdapter);
subtype.setPrompt("Selecciona la cateogría");
}
} catch (Exception e) {
e.printStackTrace();
}
}
public void onNothingSelected(AdapterView<?> args) {
//Auto-generated method stub
}
});
type.setPrompt("Selecciona la cateogría");
//camera action
camera.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
int timeMili = (int) (System.currentTimeMillis());
filename = Environment.getExternalStorageDirectory() + "/" + timeMili + ".jpg";
Uri output = Uri.fromFile(new File(filename));
intent.putExtra(MediaStore.EXTRA_OUTPUT, output);
code = TAKE_PICTURE;
startActivityForResult(intent, code);
}
});
//gallery action
gallery.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI);
code = SELECT_PICTURE;
startActivityForResult(intent, code);
}
});
//button of the form
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if (!NetworkHelper.CheckNetworkStatus(view.getContext())) {
return;
}
bundle = new Bundle();
bundle.putString("barcode", barcodeStr.toString());
bundle.putString("name", name.getText().toString());
bundle.putString("description", description.getText().toString());
bundle.putString("type_id", ((TypeClass) subtype.getSelectedItem()).getId());
if (_checkFormValues()) {
new SendDataJSON().execute(view);
} else {
Toast.makeText( view.getContext(), getString(R.string.error_form_incomplete), Toast.LENGTH_LONG).show();
}
}
});
} else {
errorMsg.setText(json.getString(ERROR_MSG));
errorMsgContainer.setVisibility(LinearLayout.VISIBLE);
}
}
} catch (JSONException e) {
e.printStackTrace();
}
} else {
}
}
}).setNegativeButton("No",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
Intent myIntent = new Intent(ProductForm.this, CaptureActivity.class);
startActivity(myIntent);
finish();
}
}).show();
} else {
finish();
}
}
class SendDataJSON extends AsyncTask<View, Void, View>{
@Override
protected View doInBackground(View... views) {
String url = String.format(getString(R.string.api_product_form_url),getString(R.string.api_url));
HttpPost httpPost = new HttpPost(url);
try {
// Add your data
MultipartEntity entity = new MultipartEntity();
File photo = new File(filename);
if (photo.exists()) {
//create the compressed image to send
//create the file to send the image
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (!sd.canWrite()) { sd = data; }
String destinationFolderPath = sd + "/" + getString(R.string.app_dir) + "/";
String destinationImageName= "photo_" + bundle.getString("barcode") + ".jpg";
//create the folder to store it
File destinationFolder = new File(destinationFolderPath);
if (!destinationFolder.exists()) {
destinationFolder.mkdirs();
}
File destination = new File(destinationFolder, destinationImageName);
FileOutputStream out = new FileOutputStream(destination);
Bitmap bm = BitmapFactory.decodeFile(filename);
int width = bm.getWidth();
int height = bm.getHeight();
int max_value = 1024;
int max = Math.max(width,height);
if (max > max_value) {
width = width * max_value / max;
height = height * max_value / max;
}
//Make the new image with the new size values
try {
Bitmap bm2 = Bitmap.createScaledBitmap(bm, width, height, true);
//Compress the image
bm2.compress(CompressFormat.JPEG, 75, out);
out.flush();
out.close();
destination = new File(destinationFolder, destinationImageName);
FileBody filePhoto = new FileBody(destination);
entity.addPart("image", filePhoto);
} catch (Exception e) {
Log.w(ProductForm.class.getSimpleName(), e);
}
}
SharedPreferences userSettings = getSharedPreferences("UserPreferences", Context.MODE_PRIVATE);
Charset chars = Charset.forName("UTF-8");
entity.addPart("barcode", new StringBody(bundle.getString("barcode"),chars));
entity.addPart("name", new StringBody(bundle.getString("name"),chars));
entity.addPart("description", new StringBody(bundle.getString("description"),chars));
entity.addPart("id_type", new StringBody(bundle.getString("type_id")));
entity.addPart("uid",new StringBody(userSettings.getString("uid", ""),chars));
httpPost.setEntity(entity);
HttpClient httpclient = new DefaultHttpClient();
httpclient.execute(httpPost);
} catch (IOException e) {
//
}
return views[0];
}
@Override
protected void onPreExecute() {
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
@Override
protected void onPostExecute(View view) {
//redirect to the product page
setContentView(R.layout.product_barcode);
String url = String.format(getString(R.string.api_product_barcode_url), getString(R.string.api_url), bundle.getString("barcode"));
new LoadJSONBarcode().execute(url);
}
}
//Send data to server and receive respond
private class LoadJSONBarcode extends AsyncTask<String, Void, JSONObject>{
@Override
protected JSONObject doInBackground(String... urls) {
List<NameValuePair> params = new ArrayList<NameValuePair>();
json = new JSONObject();
json = jsonParser.getJSONFromUrl(urls[0], params);
return json;
}
@Override
protected void onPreExecute() {
dialog.setMax(100);
dialog.setProgress(0);
dialog.show();
}
@Override
protected void onPostExecute(JSONObject json) {
if (json != null) {
try {
if (json.getString(KEY_SUCCESS) != null) {
String res = json.getString(KEY_SUCCESS);
if(Integer.parseInt(res) == 1){
View view = findViewById(R.id.productBarcodeXML);
Intent myIntent = new Intent(view.getContext(), Product.class);
Bundle b = new Bundle();
b.putString("id", json.getString(PRODUCT_ID));
myIntent.putExtras(b);
view.getContext().startActivity(myIntent);
} else {
errorMsg.setText(json.getString(ERROR_MSG));
errorMsgContainer.setVisibility(LinearLayout.VISIBLE);
}
dialog.dismiss();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == TAKE_PICTURE) {
if (data != null) {
if (data.hasExtra("data")) {
preview.setImageBitmap((Bitmap) data.getParcelableExtra("data"));
preview.setVisibility(ImageView.VISIBLE);
}
} else {
preview.setImageBitmap(BitmapFactory.decodeFile(filename));
preview.setVisibility(ImageView.VISIBLE);
new MediaScannerConnectionClient() {
private MediaScannerConnection msc = null; {
msc = new MediaScannerConnection(getApplicationContext(), this); msc.connect();
}
public void onMediaScannerConnected() {
msc.scanFile(filename, null);
}
public void onScanCompleted(String path, Uri uri) {
msc.disconnect();
}
};
}
} else if (requestCode == SELECT_PICTURE){
if (data != null){
Uri selectedImage = data.getData();
InputStream is;
String[] filePathColumn = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
cursor.moveToFirst();
int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
filename = cursor.getString(columnIndex);
cursor.close();
try {
is = getContentResolver().openInputStream(selectedImage);
BufferedInputStream bis = new BufferedInputStream(is);
Bitmap bitmap = BitmapFactory.decodeStream(bis);
preview.setImageBitmap(bitmap);
preview.setVisibility(ImageView.VISIBLE);
} catch (FileNotFoundException e) {
}
}
}
}
private TypeClass[] convertJSONArray(JSONArray jsonArray){
int len = jsonArray.length();
TypeClass[] t = new TypeClass[len];
if (jsonArray != null) {
for (int i=0;i<len;i++){
try {
JSONObject o = jsonArray.getJSONObject(i);
t[i] = new TypeClass();
t[i].setName(o.getString(TYPE_NAME));
t[i].setId(o.getString(TYPE_ID));
} catch (JSONException e) {
e.printStackTrace();
}
}
}
return t;
}
protected boolean _checkFormValues() {
boolean result = true;
if (name.getText().length() == 0) {
name.requestFocus();
result = false;
}
if (((TypeClass) subtype.getSelectedItem()).getId() == null){
subtype.requestFocus();
result = false;
}
return result;
}
}
Error Log
11-07 23:55:26.914: E/AndroidRuntime(15457): FATAL EXCEPTION: AsyncTask #3
11-07 23:55:26.914: E/AndroidRuntime(15457): java.lang.RuntimeException: An error occured while executing doInBackground()
11-07 23:55:26.914: E/AndroidRuntime(15457): at android.os.AsyncTask$3.done(AsyncTask.java:278)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.lang.Thread.run(Thread.java:864)
11-07 23:55:26.914: E/AndroidRuntime(15457): Caused by: java.lang.OutOfMemoryError: (Heap Size=35491KB, Allocated=27993KB)
11-07 23:55:26.914: E/AndroidRuntime(15457): at android.graphics.BitmapFactory.nativeDecodeFile(Native Method)
11-07 23:55:26.914: E/AndroidRuntime(15457): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:373)
11-07 23:55:26.914: E/AndroidRuntime(15457): at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:443)
11-07 23:55:26.914: E/AndroidRuntime(15457): at prod.vegs.ProductForm$SendDataJSON.doInBackground(ProductForm.java:272)
11-07 23:55:26.914: E/AndroidRuntime(15457): at prod.vegs.ProductForm$SendDataJSON.doInBackground(ProductForm.java:1)
11-07 23:55:26.914: E/AndroidRuntime(15457): at android.os.AsyncTask$2.call(AsyncTask.java:264)
11-07 23:55:26.914: E/AndroidRuntime(15457): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
11-07 23:55:26.914: E/AndroidRuntime(15457): ... 4 more
Bitmaps use up a lot of memory is clear. You need to take care of the following to use bitmaps effectively
CreateBitmap
, since it will read the entire file and utilize a lot more memory. Instead useBitmapOptions
and scale it down. To scale it down you first need to set your options, and then use this options as a parameter to yourCreateBitmap
call. Here is a nice link. Search more on stackoverflow you will find some more interesting responses.Bitmaps are very big memory consumers. Having two loaded into memory could be the big issue. You should consider using BitmapFactory.Options when you decode a new bitmap. Also, you don't need
bm2
. Instead, replace that line with this:Finally, if you have no other options, you can increase your app's heap size using the Application attribute
android:largeHeap="true"
in your AndroidManifest.xml. This option should not be needed - and should only be considered for extremely graphic-intensive applications.EDIT
Another link you may find helpful for optimizing Bitmap usage: http://developer.android.com/training/tv/optimizing-layouts-tv.html#HandleLargeBitmaps
Can't say for sure, as I'm not the Java developer, but in .NET
BitMap
calss is IDisposable and is recommended to be recycled as soon as it is not in use.May be you should free your memory after BitMap loading?
Yes A memory cache offers fast access to bitmaps at the cost of taking up valuable application memory. If LruCache can't resolve the problem you can try this:ImageManager ,in the class ImageManager, it has a method recycleBitmaps.
A few general hints:
Bitmap
objects tend to eat a lot of memory in Android. You should always useSoftReferences
to hold bitmaps so that the OS can free the memory when needed.recycle()
method to throw away transformation bitmaps (ie, yourbm2
variable) when you are finished with them.NULL
when you are finished with them is a good practice to hint to the garbage collector that they can be collected. However, as a general rule calling the gc manually in Android either makes matters worse or has no effect.ddms
!