2012-08-13 19 views
16

के बिना बड़ी छवियों को लोड करना मेरे पास 5000 x 4000 पीएक्स छवि है जिसे मैं कैनवास पर आकर्षित करना चाहता हूं।आउटऑफमेमरीरर

सबसे पहले मैंने इसे संसाधनों से लोड करने का प्रयास किया। मैंने इसे /res/drawable में रखा है।

InputStream input = getResources().openRawResource(R.drawable.huge_image); 
Drawable d = Drawable.createFromStream(input, "image"); 
d.setBounds(...); 
d.draw(canvas); 

यह एक आकर्षण की तरह काम किया:

मैं निम्न विधि का इस्तेमाल किया।

इस मामले में InputStream एक AssetManager.AssetInputStream है।

तो अब मैं इसे एसडीकार्ड से लोड करना चाहता हूं।

यहाँ मैं करने की कोशिश की है:

File f = new File(path); 
Uri uri = Uri.fromFile(f); 
InputStream input = mContext.getContentResolver().openInputStream(uri); 
Drawable d = Drawable.createFromStream(input, "image"); 

इस मामले InputStream में एक FileInputStream है और जब Drawable बनाने मैं एक OutOfMemoryError मिला है।

तो मैं सोच रहा हूँ:

वहाँ उस त्रुटि हो रही बिना छवि को लोड करने के लिए एक रास्ता है? या FileInputStream को AssetInputStream में परिवर्तित करने का कोई तरीका है?

नोट:

मैं छवि का आकार बदलने क्योंकि मैं ज़ूम/कार्यक्षमता पैन को लागू कर रहा हूँ नहीं करना चाहती। कृपया मुझे Loading Large Bitmaps Efficiently पढ़ने के लिए मत कहें।

आप पूर्ण श्रेणी here देख सकते हैं। setImageUri() का उपयोग करते समय त्रुटि होती है।

यहाँ मेरी त्रुटि लॉग है:

08-13 11:57:54.180: E/AndroidRuntime(23763): FATAL EXCEPTION: main 
08-13 11:57:54.180: E/AndroidRuntime(23763): java.lang.OutOfMemoryError: bitmap size exceeds VM budget 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.BitmapFactory.nativeDecodeStream(Native Method) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:468) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:332) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:697) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.graphics.drawable.Drawable.createFromStream(Drawable.java:657) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.benitobertoli.largeimagezoom.ZoomImageView.setDrawablefromUri(ZoomImageView.java:187) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.benitobertoli.largeimagezoom.ZoomImageView.setImageUri(ZoomImageView.java:588) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.benitobertoli.largeimagezoom.TestActivity.onKeyDown(TestActivity.java:30) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.KeyEvent.dispatch(KeyEvent.java:1257) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.app.Activity.dispatchKeyEvent(Activity.java:2075) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1673) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.ViewRoot.deliverKeyEventToViewHierarchy(ViewRoot.java:2493) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2463) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.view.ViewRoot.handleMessage(ViewRoot.java:1752) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.os.Handler.dispatchMessage(Handler.java:99) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.os.Looper.loop(Looper.java:144) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at android.app.ActivityThread.main(ActivityThread.java:4937) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at java.lang.reflect.Method.invokeNative(Native Method) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at java.lang.reflect.Method.invoke(Method.java:521) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 
08-13 11:57:54.180: E/AndroidRuntime(23763): at dalvik.system.NativeStart.main(Native Method) 

संपादित करें:

मैं एक एचटीसी डिजायर A8181 पर अपने कोड का परीक्षण किया गया था। बताया जाने के बाद कि पहला कोड स्निपेट कुछ अन्य उपकरणों पर काम नहीं कर रहा था, मैंने सैमसंग गैलेक्सी एस 2 और एम्यूलेटर पर परीक्षण किया।

परिणाम: जब संसाधनों से लोड हो रहा है, एमुलेटर एक OutOfMemoryError, Galaxy S2 एक अपवाद फेंक नहीं था लेकिन उसके परिणाम Drawable बातिल था दे दी है।

तो मुझे लगता है कि समय का एकमात्र समाधान छवि को कम करना है।

+0

मैंने एम एम्यूलेटर पर अपना कोड चलाने की कोशिश की और मुझे "Res से" विधि पर आउटऑफमेमरी त्रुटि मिल रही है :) आप किस डिवाइस और/या एमुलेटर सेटिंग्स का उपयोग करते हैं जो बिना त्रुटि के इसे चलाने में सक्षम था? – Joe

उत्तर

13

पर एक नजर डालें।

आपको सही विकल्प सेट करने की आवश्यकता है।मेरे लिए

BitmapFactory.Options options = new BitmapFactory.Options(); 

options.inSampleSize = 4; 

Bitmap myBitmap = BitmapFactory.decodeFile(mUri.getPath(),options); 
Drawable d = new BitmapDrawable(Resources.getSystem(),myBitmap); 
updateDrawable(d); 

काम करता है:

http://developer.android.com/reference/android/graphics/BitmapFactory.Options.html

inSampleSize 

मैं अपने कोड के साथ यह कोशिश की।

+0

जैसा कि मैंने अपने प्रश्न में कहा था, मुझे संसाधनों से छवि लोड करने में कोई समस्या नहीं है। इसके अलावा मैं छवि का आकार बदलना नहीं चाहता हूं। –

+0

खेद है कि मुझे याद आया कि फिर डीकोडस्ट्रीम (इनपुटस्ट्रीम है, रेक्ट आउट पैडिंग, बिटमैप फैक्ट्री। ऑप्शन ऑप्ट्स) विधि – n3utrino

+0

दोहराना * मैं छवि * का आकार बदलना नहीं चाहता हूं। –

0

मुझे यकीन नहीं है कि यह करने का तरीका सही है या नहीं, लेकिन यह आपकी समस्या का समाधान कर सकता है।

अपनी छवि को वेबदृश्य में खोलने का प्रयास करें। यह आपको अंतर्निर्मित ज़ूम-इन/आउट और पैनिंग सुविधा प्रदान करेगा और आपको किसी भी इनपुट स्ट्रीम या किसी अन्य चीज़ को खोलने की चिंता करने की आवश्यकता नहीं है।

परिसंपत्तियों से वेबव्यू में एक छवि खोलने के लिए:

WebView wv = (WebView) findViewById(R.id.webView1); 
    wv.loadUrl("file:///android_asset/abc.png"); 

आप WebSettings कर सकते हैं ज़ूम नियंत्रण अनुमति देने के लिए।

आशा है कि यह आपकी मदद करेगा !!

+0

आपके उत्तर के लिए धन्यवाद। मैं इशारा पहचान के साथ एक कस्टम व्यू विकसित कर रहा हूं और एक वेब व्यू बस पर्याप्त नहीं है। –

3

मेरी राय में, छवि को छोटे टुकड़ों में विभाजित करने का सबसे अच्छा विकल्प है। यह एप्लिकेशन को आउटऑफमेमरी अपवाद देने से रोक देगा। यहाँ मेरी उदाहरण कोड

पहले से एसडी कार्ड छवि प्राप्त एक यह एक बिटमैप

Bitmap b = null; 
     try { 
      File sd = new File(Environment.getExternalStorageDirectory() + link_to_image.jpg); 
      FileInputStream fis; 

      fis = new FileInputStream(sd); 

      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.inPurgeable = true; 
      options.inScaled = false; 

      b = BitmapFactory.decodeStream(fis, null, options); 
      fis.close(); 

     } catch (FileNotFoundException e) { 
      e.printStackTrace(); 
     } catch (IOException e) { 
      e.printStackTrace(); 
     } 

स्प्लिट में डाल दिया बिटमैप (ख) छोटे टुकड़ों में है। इस मामले में SPLIT_HEIGHT 400 है।

double rest = (image_height + SPLIT_HEIGHT); 
     int i = 0; 
     Bitmap[] imgs = new Bitmap[50]; 

     do { 
      rest -= SPLIT_HEIGHT; 
      if (rest < 0) { 
       // Do nothing 
      } else if (rest < SPLIT_HEIGHT) { 
       imgs[i] = Bitmap.createBitmap(b, 0, (i * SPLIT_HEIGHT), image_width, (int) rest); 
      } else { 
       imgs[i] = Bitmap.createBitmap(b, 0, i * SPLIT_HEIGHT, image_width, SPLIT_HEIGHT); 
      } 

      i++; 

     } while (rest > SPLIT_HEIGHT); 

अब आपके पास छोटी छवियों का संग्रह है। आप इसे perfecly क्या करना चाहते हैं, तो आप इस बिटमैप रीसायकल कर सकते हैं:

// Not needed anymore, free up some memory 
     if (b != null) 
      b.recycle(); 

इस बिंदु से आप कैनवास पर छोटे आकार के चित्रों डाल सकते हैं। मेरे अगले कोड में मैंने छवियों को स्क्रॉलव्यू में रखा। छवियों को कैनवास पर रखने के लिए कोड मुझे लगता है कि बहुत अलग नहीं है।

// Add images to scrollview 
     for (int j = 0; j < imgs.length; j++) { 
      Bitmap tmp = imgs[j]; 
      if (tmp != null) { 
       // Add to scrollview 
       ImageView iv = new ImageView(activity); 

       String id = j + "" + articlenumber; 
       iv.setId(Integer.parseInt(id)); 
       iv.setTag(i); 

       iv.setImageBitmap(tmp); 

       RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(globalwidth, tmp.getHeight()); 
       lp.addRule(RelativeLayout.BELOW, previousLongPageImageId); 
       lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT); 

       container.addView(iv, lp); 
       previousLongPageImageId = iv.getId(); 
      } 
     } 

मुझे आशा है कि यह आपकी मदद करेगा।

नोट: छवियों के लिए अधिकतम संकल्प 2048 * 2048 (ओपनजीएल प्रतिबंध या कुछ) है, इसलिए आपको छवियों को छोटे टुकड़ों में विभाजित करना होगा।

+1

आपके उत्तर के लिए धन्यवाद। बिटमैप को विभाजित करने की कोशिश करने से पहले 'बी = बिटमैपफैक्टरी.decodeStream (फिस, नल, विकल्प);' पहले से ही आउटऑफमेमरी एरर देता है। इसके अलावा, आप बिटमैप को 50 छोटे से विभाजित कर रहे हैं, फिर भी आप सभी को स्मृति में एक ही समय में लोड कर रहे हैं, जो एक ही परिणाम देता है। –

+0

यदि आप एंड्रॉइड सेट करते हैं: bigHeap = "true" और फिर उपरोक्त कोड करें? जैसा कि मैंने अपने नोट में कहा था, आपको छवि को टुकड़ों में विभाजित करना होगा क्योंकि अधिकतम रिज़ॉल्यूशन 2048 * 2048 है। बेशक मेरे समाधान में आपको छवि को ऊर्ध्वाधर टुकड़ों में विभाजित करना होगा (चौड़ाई 2048 से अधिक है)। – Ceetn

0

यदि यह संसाधनों से लोड होने पर काम करता है और अब जब आप मैन्युअल रूप से लोड करते हैं तो यह काम नहीं करता है तो मुझे संदेह है कि आपको संसाधन प्रबंधन के साथ कुछ समस्या है, शायद कुछ स्मृति रिसाव।

क्या OutOfMemoryError एप्लिकेशन की शुरुआत में हमेशा होता है या यह बाद में होता है? क्या यह विन्यास परिवर्तन पर है? क्या आप स्थिर फ़ील्ड का उपयोग करते हैं (यदि गलत तरीके से उपयोग किया जाता है तो यह अक्सर एंड्रॉइड एप्लिकेशन में मेमोरी लीक की ओर जाता है, example with explanation यह आपके मामले में फिट बैठता है)?
कुछ प्रोफाइलिंग टूल का उपयोग करने का प्रयास करें (google कुछ उपयोगी खोजने में मदद कर सकते हैं)।

http://developer.android.com/reference/android/graphics/BitmapFactory.html

decodeStream (InputStream is, Rect outPadding, BitmapFactory.Options opts) 

या

decodeFile (String pathName, BitmapFactory.Options opts) 

इस तरह से आप अपने पूरे बिटमैप लोड और स्क्रीन पर दिखा सकते हैं:

+0

'आउटऑफमेमरी एरर' इस लाइन पर 'ड्रायबल डी = Drawable.createFromStream (इनपुट, "छवि") होता है;' जब मैं उरी से लोड हो रहा हूं। मैं किसी भी स्थिर क्षेत्र का उपयोग नहीं कर रहा हूँ। आप पूर्ण श्रेणी [यहां] देख सकते हैं (https://github.com/BenitoBertoli/ZoomImageView/blob/master/src/com/benitobertoli/largeimagezoom/ZoomImageView.java)। मैं 'setImageUri() 'को बुला रहा हूँ। –

+0

यह लिंक कामकाजी कोड (संसाधनों से लोड छवि) के लिए है। जाहिर है आपने समस्याग्रस्त परिवर्तनों को नहीं दबाया (नई शाखा?)। –

+0

कृपया अभी दोबारा जांचें। मैंने एक मेनू जोड़ा "Res से" और "फ़ाइल से"। त्रुटि "फ़ाइल से" का उपयोग कर होती है। आपको 'res_ drawage'' से अपने sdcard में 'huge_image.png' कॉपी करना होगा और अपने कोड में पथ को प्रतिस्थापित करना होगा। –