在尝试发送带有图像的表单到PHP服务器时,Android中的内存泄漏

我在这个文件中有一个内存泄漏,我无法find确切位置,但我认为是周围的图像 – > (Bitmap bm = BitmapFactory.decodeFile(filename)) ,我尝试了很多不同的方式,但我无法得到它上class。

 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; } } 

错误日志

 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 

Solutions Collecting From Web of "在尝试发送带有图像的表单到PHP服务器时,Android中的内存泄漏"

位图是非常大的内存消费者。 有两个加载到内存可能是最大的问题。 当你解码一个新的位图时,你应该考虑使用BitmapFactory.Options 。 另外,你不需要bm2 。 相反,用这个replace那一行:

 bm = Bitmap.createScaledBitmap(bm, width, height, true); 

最后,如果没有其他选项,则可以使用AndroidManifest.xml中的Application属性android:largeHeap="true"来增加应用程序的堆大小。 这个选项不应该被需要 – 只应该被认为是非常graphics密集型的应用程序。

编辑

另一个链接,你可能会发现有助于优化位图使用: http : //developer.android.com/training/tv/optimizing-layouts-tv.html#HandleLargeBitmaps

位图占用大量内存很明显。 您需要注意以下几点以有效使用位图

  • 请记住,你的PNG的drawables会根据屏幕大小自动resize的android。 这占用了大量的内存。 如果Android在正确的可绘制文件夹中find所需大小/比例的图像,则不会调整图像大小。 因此,开始将所有ldpi文件复制到hdpi。 这会使内存利用率至less降低40%。 我知道这听起来如何,但是这是真的,在清单中使用debuggable = true来分析您的应用程序,并使用ddms来利用堆。 做出改变并运行完全相同的情况,你会注意到40%的减less。
  • 当你读取你的位图时,将其缩小。 不要只使用CreateBitmap ,因为它会读取整个文件并利用更多的内存。 而是使用BitmapOptions并将其缩小。 要将其缩小,首先需要设置选项,然后将此选项用作CreateBitmap调用的参数。 这是一个不错的链接。 search更多关于stackoverflow你会发现一些更有趣的回应。
  • 如果您的绘图中有任何文件是1024X512,请将其缩小。 或创build清晰但较小的新文件。 使用这些文件为mdpi,删除ldpi。 使用1024X512为您的hdpi文件夹。
  • 探索使用较小的文件的可能性,按大小sorting和玩一下。 eclipse中的xml文件的graphics视图非常整洁,并且相对无bug。 用它。
  • 编辑:不要忘记你的位图为垃圾收集空。 这是最重要的。

几个一般的提示:

  • 正如@Phil所build议的, Bitmap对象往往会在Android中占用大量内存。 您应该始终使用SoftReferences来保存位图,以便操作系统可以在需要时释放内存。
  • 你也应该使用recycle()方法来抛弃转换位图(即你的bm2variables),当你完成它们。
  • 像听起来那样偏执,当你完成它们时,将Bitmaps设置为NULL是向垃圾收集器提示它们可以被收集的好习惯。 但是,一般来说,在Android中手动调用gc会使事情变得更糟或无效。
  • 最后,使用ddms 分析您的应用程序 !

不能肯定地说,因为我不是Java开发人员,但是在.NET中, BitMap calss是IDisposable,build议在不使用的时候尽快回收。
也许你应该在BitMap加载后释放你的内存?

是一个内存caching提供快速访问位图的代价是占用宝贵的应用程序内存。 如果LruCache无法解决问题,您可以试试这个: ImageManager ,在类ImageManager中,它有一个方法recycleBitmaps。