2012-09-22 4 views
8

Mam wyciek pamięci w tym pliku, nie mogę znaleźć dokładnego miejsca, ale myślę, że jest to obraz dookoła ->(Bitmap bm = BitmapFactory.decodeFile(filename)), Próbowałem wielu różnych sposoby, ale nie mogę go uruchomić.Przeciek pamięci w systemie Android przy próbie wysłania formularza z obrazem do serwera PHP

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 
+3

Jak duże mogą być obrazy? Jeśli załadujesz zdjęcie o rozdzielczości 5 megapikseli, a następnie zmienisz rozmiar do 1k * 1k, zachowując stare zdjęcie w pamięci, możesz otrzymać 24MB zrobione tylko przez jedno zdjęcie. – Jave

+0

http://stackoverflow.com/a/12819091/726863 –

+0

W twoim poście jest dużo kodu, możesz zmniejszyć go do najbardziej odpowiedniego, LUB rozważ usunięcie nieistotnego kodu. – Siddharth

Odpowiedz

10

bitmapy są bardzo duże konsumenci pamięci. Posiadanie dwóch ładowanych do pamięci może być dużym problemem. Powinieneś rozważyć użycie BitmapFactory.Options podczas dekodowania nowej bitmapy. Ponadto nie potrzebujesz bm2. Zamiast zastąpić tę linię z tym:

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

Wreszcie, jeśli nie ma innych opcji, można zwiększyć wielkość sterty swojej aplikacji za pomocą atrybutu Application android:largeHeap="true" w AndroidManifest.xml. Ta opcja nie powinna być potrzebna - i powinna być brana pod uwagę tylko w przypadku bardzo wymagających graficznie aplikacji.

EDIT

inny link można znaleźć pomocne w optymalizacji zużycia Bitmap: http://developer.android.com/training/tv/optimizing-layouts-tv.html#HandleLargeBitmaps

+0

Aby rozwinąć komentarz Phila, będziesz chciał ustawić opcję 'inJustDecodeBounds' przy ładowaniu pierwszej' Bitmapy': http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html#inJustDecodeBounds – twaddington

+0

Próbowałem i to nie działa, ale w niektórych modelach nie działa, bo w Nexusie HTC hero jest OK, teraz testuję to z HTC One S i zjada całą pamięć , ale jak dotąd twoja odpowiedź jest bardziej przydatna. Dzięki, będę próbować, dopóki nie naprawię, a ja zaktualizuję pytanie. – jonaypelluz

2

Kilka ogólne wskazówki:

  • Jak sugeruje @Phil, Bitmap obiekty jedzą dużo pamięci w systemie Android. Zawsze należy używać SoftReferences do przechowywania map bitowych, aby system operacyjny mógł zwolnić pamięć w razie potrzeby.
  • Powinieneś również użyć metody recycle(), aby wyrzucić mapy bitowe transformacji (tj. Twoją zmienną bm2), gdy skończysz z nimi.
  • Wygląda to na paranoję, więc ustawienie bitmap na NULL po ich skończeniu jest dobrą praktyką, aby wskazać kolektorowi śmieci, że można je zebrać. Jednak generalnie wywołanie gc ręcznie w systemie Android albo pogarsza sprawę, albo nie przynosi żadnego efektu.
  • I wreszcie, profile your application używając ddms!
0

nie mogę powiedzieć na pewno, jak nie jestem programista Java, ale w .NET BitMap calss jest IDisposable i jest zalecany do recyklingu tak szybko, jak to nie jest w użyciu.
Może powinieneś zwolnić swoją pamięć po załadowaniu BitMap?

4

Bitmapy zużywają dużo pamięci są wyraźne. Aby skutecznie używać bitmap, musisz zadbać o następujące funkcje:

  • Pamiętaj, że twoje png w rysunkach są automatycznie zmieniane na podstawie rozmiaru ekranu przez Androida. A to zabiera dużo pamięci. Android nie zmienia rozmiaru obrazów, jeśli znajdzie obrazy o wymaganym rozmiarze/skali w odpowiednim folderze do rysowania. Zacznij więc od skopiowania wszystkich plików ldpi do hdpi. Zmniejszy to zużycie pamięci o co najmniej 40%. Wiem, jak to brzmi, ale to prawda, profiluj swoją aplikację za pomocą debuggable = true w manifeście, a wykorzystanie sterty za pomocą ddms. Dokonaj zmiany i uruchom dokładnie ten sam scenariusz, zauważysz redukcję o 40%.
  • Po przeczytaniu mapy bitowej zmniejsz ją.Nie używaj tylko CreateBitmap, ponieważ odczyta cały plik i zużyje o wiele więcej pamięci. Zamiast tego użyj BitmapOptions i skaluj go. Aby zmniejszyć skalę, najpierw musisz ustawić opcje, a następnie użyć tej opcji jako parametru do połączenia CreateBitmap. Here to ładny link. Szukaj więcej na stackoverflow znajdziesz bardziej interesujące odpowiedzi.
  • Jeśli masz jakieś pliki w swoich zbiorach, które są 1024X512, zmniejsz je. LUB Utwórz nowe pliki, które są wyraźne, ale mają mniejszy rozmiar. Użyj tych plików dla mdpi, usuń ldpi. Użyj 1024X512 do folderu hdpi.
  • Odkryj możliwość korzystania z mniejszych plików, sortuj według rozmiaru i baw się trochę. Widok graficzny na pliki eclipse dla xml jest naprawdę czysty i stosunkowo wolny od błędów. Użyj tego.
  • Edytowane: Nie zapomnij o null swojej bitmapy do zbierania śmieci. To jest najważniejsze.
+0

Używam DDMS i teraz wiem, gdzie jest wyciek pamięci. Kiedy wybrałem obraz z folderu lub biorę, to w tym momencie tracę większość pamięci, z odpowiedzią Phila i twoją próbuję to naprawić, dziękuję! – jonaypelluz

+0

Dodano jeszcze jedną poprawkę, którą musisz wprowadzić. – Siddharth

0

Tak Pamięć podręczna pamięci zapewnia szybki dostęp do bitmap, kosztem wykorzystania cennej pamięci aplikacji. Jeśli LruCache nie może rozwiązać problemu, spróbuj tego: ImageManager, w klasie ImageManager ma metodę recycleBitmaps.

+0

Używanie LRUCache zalecane jest tylko wtedy, gdy ponownie wykorzystuje bitmapy. Tutaj sprawa nie jest taka. Samo użycie bitmapy powoduje awarię. – Siddharth

+0

@jonaypelluz Może, spróbuj tego [ImageManager] (http://blog.pseudoblue.com/2010/08/15/android-bitmaps-and-memory-leaks/) w klasie ImageManager, ma metodę recycleBitmaps . – Jamesprite

+0

Widziałem LruCache, a ja zamierzałem go zaimplementować w innej aplikacji, ale myślę, że spróbuję rozwiązać problem w tym. dzięki! – jonaypelluz

Powiązane problemy