বিটম্যাপ অবজেক্টে কোনও চিত্র লোড করার সময় মেমরি সমস্যা থেকে অদ্ভুত


1287

আমার প্রতিটি সারিতে বেশ কয়েকটি চিত্রের বোতামের সাথে একটি তালিকা ভিউ রয়েছে। আপনি তালিকার সারিটিতে ক্লিক করলে এটি একটি নতুন ক্রিয়াকলাপ শুরু করে। ক্যামেরা লেআউটে কোনও সমস্যা থাকায় আমাকে নিজের ট্যাবগুলি তৈরি করতে হয়েছিল। ফলাফলের জন্য যে ক্রিয়াকলাপটি আরম্ভ হয় তা হ'ল একটি মানচিত্র। আমি যদি আমার বাটনে ক্লিক করে চিত্রের পূর্বরূপ চালু করতে (এসডি কার্ডের বাইরে একটি চিত্র লোড করুন) অ্যাপ্লিকেশনটি কার্যকলাপ থেকে ফিরিয়ে listviewনিয়ে আসে ফলাফলের হ্যান্ডেলারটিতে আমার নতুন ক্রিয়াকলাপটি পুনরায় লঞ্চ করতে যা কোনও চিত্র উইজেট ছাড়া আর কিছু নয়।

তালিকা ভিউতে চিত্র পূর্বরূপ কার্সার এবং দিয়ে করা হচ্ছে ListAdapter। এটি একেবারে সহজ করে তোলে তবে আমি কীভাবে পুনরায় আকারযুক্ত চিত্র রাখতে পারি তা নিশ্চিত নই ( srcউড়ানের চিত্রের বোতামের জন্য ছোট আকারের বিট আকার পিক্সেল নয় So তাই আমি কেবল ফোন ক্যামেরা থেকে আসা চিত্রটির আকার পরিবর্তন করেছি ized

সমস্যাটি হ'ল এটি যখন ফিরে যেতে এবং ২ য় ক্রিয়াকলাপটি পুনরায় চালু করার চেষ্টা করে তখন আমি স্মৃতি ত্রুটি থেকে বেরিয়ে আসি।

  • আমি কীভাবে তালিকাটিতে অ্যাডাপ্টারটি সারি সারি সারি তৈরি করতে পারি, যেখানে আমি উড়ে আবার আকার পরিবর্তন করতে পারি ( বিট ওয়াইস )?

এটি অগ্রাধিকারযোগ্য হবে কারণ আমার প্রতিটি সারিতে উইজেট / উপাদানগুলির বৈশিষ্ট্যগুলিতেও কিছু পরিবর্তন আনতে হবে কারণ ফোকাস সমস্যার কারণে আমি টাচ স্ক্রিনের সাথে একটি সারি নির্বাচন করতে অক্ষম। ( আমি বেলন বল ব্যবহার করতে পারেন। )

  • আমি জানি যে আমি ব্যান্ডের আকার পরিবর্তন এবং আমার চিত্রটি সংরক্ষণ করতে পারি, তবে আমি যা করতে চাই তা আসলে তা নয়, তবে এর জন্য কয়েকটি স্যাম্পল কোডটি দুর্দান্ত হবে।

তালিকার দৃশ্যে ছবিটি অক্ষম করার সাথে সাথেই এটি আবার ঠিকঠাক হয়ে গেছে।

এফওয়াইআই: আমি এটি এটিই করছিলাম:

String[] from = new String[] { DBHelper.KEY_BUSINESSNAME,DBHelper.KEY_ADDRESS,DBHelper.KEY_CITY,DBHelper.KEY_GPSLONG,DBHelper.KEY_GPSLAT,DBHelper.KEY_IMAGEFILENAME  + ""};
int[] to = new int[] {R.id.businessname,R.id.address,R.id.city,R.id.gpslong,R.id.gpslat,R.id.imagefilename };
notes = new SimpleCursorAdapter(this, R.layout.notes_row, c, from, to);
setListAdapter(notes);

কই R.id.imagefilenameButtonImage

এখানে আমার লগকেট:

01-25 05:05:49.877: ERROR/dalvikvm-heap(3896): 6291456-byte external allocation too large for this process.
01-25 05:05:49.877: ERROR/(3896): VM wont let us allocate 6291456 bytes
01-25 05:05:49.877: ERROR/AndroidRuntime(3896): Uncaught handler: thread main exiting due to uncaught exception
01-25 05:05:49.917: ERROR/AndroidRuntime(3896): java.lang.OutOfMemoryError: bitmap size exceeds VM budget
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.nativeDecodeStream(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:304)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:149)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.BitmapFactory.decodeFile(BitmapFactory.java:174)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.graphics.drawable.Drawable.createFromPath(Drawable.java:729)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.resolveUri(ImageView.java:484)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ImageView.setImageURI(ImageView.java:281)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.setViewImage(SimpleCursorAdapter.java:183)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.SimpleCursorAdapter.bindView(SimpleCursorAdapter.java:129)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.CursorAdapter.getView(CursorAdapter.java:150)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.obtainView(AbsListView.java:1057)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.makeAndAddView(ListView.java:1616)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.fillSpecific(ListView.java:1177)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.ListView.layoutChildren(ListView.java:1454)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.AbsListView.onLayout(AbsListView.java:937)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1108)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:922)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1119)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.layoutVertical(LinearLayout.java:999)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.LinearLayout.onLayout(LinearLayout.java:920)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.widget.FrameLayout.onLayout(FrameLayout.java:294)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.View.layout(View.java:5611)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.performTraversals(ViewRoot.java:771)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.view.ViewRoot.handleMessage(ViewRoot.java:1103)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Handler.dispatchMessage(Handler.java:88)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.os.Looper.loop(Looper.java:123)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at android.app.ActivityThread.main(ActivityThread.java:3742)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invokeNative(Native Method)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at java.lang.reflect.Method.invoke(Method.java:515)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:497)
01-25 05:05:49.917: ERROR/AndroidRuntime(3896):     at dalvik.system.NativeStart.main(Native Method)
01-25 05:10:01.127: ERROR/AndroidRuntime(3943): ERROR: thread attach failed 

ছবিটি প্রদর্শন করার সময় আমারও একটি নতুন ত্রুটি রয়েছে:

01-25 22:13:18.594: DEBUG/skia(4204): xxxxxxxxxxx jpeg error 20 Improper call to JPEG library in state %d
01-25 22:13:18.604: INFO/System.out(4204): resolveUri failed on bad bitmap uri: 
01-25 22:13:18.694: ERROR/dalvikvm-heap(4204): 6291456-byte external allocation too large for this process.
01-25 22:13:18.694: ERROR/(4204): VM won't let us allocate 6291456 bytes
01-25 22:13:18.694: DEBUG/skia(4204): xxxxxxxxxxxxxxxxxxxx allocPixelRef failed

8
আমি এটি বিটম্যাপ.ডেকোড স্ট্রিম বা ডিকোডফিল এড়িয়ে এবং বিটম্যাপফ্যাক্টরি.ডেকোডফিলডিস্কিটার পদ্ধতি ব্যবহার করে সমাধান করেছি।
19:51

1
আমি কয়েক সপ্তাহ আগে একই ধরণের সমস্যার মুখোমুখি হয়েছিলাম এবং ইমেজগুলি সর্বোত্তম বিন্দু পর্যন্ত স্কেল করে সমাধান করেছি। আমি আমার ব্লগে কোডিংজুঙ্কিজসফরম.ওয়ার্ডপ্রেস.কম / ২০১৪/০//১২/২ এ সম্পূর্ণ পন্থা লিখেছি এবং ওওএম প্রোন কোড বনাম ওওএম প্রুফ কোডের সাথে সম্পূর্ণ নমুনা প্রকল্প আপলোড করেছি: //github.com/shaileender123/ বিটম্যাপ হ্যান্ডলিংডেমো
শৈলেন্দ্র সিং রাজাওয়াত

5
এই প্রশ্নে গৃহীত উত্তর আলোচনা করা হচ্ছে মেটা
রেনে

4
আপনি যখন অ্যান্ড্রয়েড বিকাশকারী গাইডগুলি না পড়েন তখন এটি ঘটে
পেড্রো ভেরেলা

2
খারাপ অ্যান্ড্রয়েড আর্কিটেকচারের কারণে এটি ঘটে। আইওএস এবং ইউডাব্লুপি এটির মতো করে ইমেজগুলিকে নিজেই আকার পরিবর্তন করতে হবে। আমাকে এই জিনিসগুলি নিজেই করতে হবে না। অ্যান্ড্রয়েড বিকাশকারীরা সেই নরকে অভ্যস্ত হয়ে যায় এবং মনে করে যে এটি যেমন করা উচিত তেমনিভাবে কাজ করে।
অ্যাক্সেস অস্বীকার

উত্তর:


650

অ্যান্ড্রয়েড প্রশিক্ষণ বর্গ ", দক্ষতার বিটম্যাপ প্রদর্শিত হচ্ছে ," অফার বোঝার এবং ব্যতিক্রম সঙ্গে তার আচরণ জন্য কিছু মহান তথ্য java.lang.OutOfMemoryError: bitmap size exceeds VM budgetযখন বিটম্যাপ লোড।


বিটম্যাপের মাত্রা এবং প্রকারটি পড়ুন

BitmapFactoryবর্গ বিভিন্ন পাঠোদ্ধারতা পদ্ধতি (উপলব্ধ decodeByteArray(), decodeFile(), decodeResource(), ইত্যাদি) একটি তৈরি করার জন্য Bitmapবিভিন্ন সূত্র থেকে। আপনার চিত্রের ডেটা উত্সের ভিত্তিতে সবচেয়ে উপযুক্ত ডিকোড পদ্ধতি চয়ন করুন। এই পদ্ধতিগুলি নির্মিত বিটম্যাপের জন্য মেমরি বরাদ্দ করার চেষ্টা করে এবং তাই সহজেই তার OutOfMemoryব্যতিক্রম হতে পারে । প্রতিটি ধরণের ডিকোড পদ্ধতির অতিরিক্ত স্বাক্ষর রয়েছে যা আপনাকে BitmapFactory.Optionsক্লাসের মাধ্যমে ডিকোডিং বিকল্পগুলি নির্দিষ্ট করতে দেয় । সেট inJustDecodeBoundsথেকে সম্পত্তি trueযখন এড়াতে মেমরি অ্যালোকেশন ডিকোডিং, ফিরে nullবিটম্যাপ বস্তু কিন্তু সেটিং এর জন্য outWidth, outHeightএবং outMimeType। এই কৌশলটি আপনাকে বিটম্যাপের নির্মাণ (এবং মেমরির বরাদ্দ) পূর্বে চিত্রের ডাইমেনশন এবং ধরণ পড়তে দেয়।

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

java.lang.OutOfMemoryব্যতিক্রমগুলি এড়ানোর জন্য, কোনও বিটম্যাপের ডিকোডিংয়ের আগে মাত্রাগুলি পরীক্ষা করুন, যদি না আপনি উত্সটিকে নির্ভরযোগ্য আকারের চিত্রের ডেটা সরবরাহ করে যা বিশ্বাসযোগ্যভাবে উপলভ্য মেমরির মধ্যে উপযুক্তভাবে ফিট করে fits


মেমোরিতে একটি ছোট আকারের সংস্করণ লোড করুন

এখন চিত্রের মাত্রাগুলি জানা হয়ে গেছে, এগুলি পুরো চিত্রটি মেমরিতে লোড করা উচিত কিনা বা পরিবর্তে কোনও সাব-মডেল সংস্করণ লোড করা উচিত কিনা তা সিদ্ধান্ত নিতে তাদের ব্যবহার করা যেতে পারে। এখানে কয়েকটি বিষয় বিবেচনা করার জন্য রয়েছে:

  • মেমরিতে পূর্ণ চিত্র লোড করার আনুমানিক মেমরি ব্যবহার।
  • আপনার অ্যাপ্লিকেশনটির অন্য কোনও মেমরির প্রয়োজনীয়তা মেনে আপনি এই চিত্রটি লোড করতে কতটা মেমরি প্রতিশ্রুতি রাখতে চান।
  • লক্ষ্যটি ইমেজভিউ বা UI উপাদানটির মাত্রা যা চিত্রটি লোড করতে হবে।
  • বর্তমান ডিভাইসের স্ক্রিনের আকার এবং ঘনত্ব।

উদাহরণস্বরূপ, 1024x768 পিক্সেল চিত্রটি মেমরিতে লোড করার মতো নয় যদি এটি শেষ পর্যন্ত একটি 128x96 পিক্সেলের থাম্বনেইলে প্রদর্শিত হয় ImageView

ইমেজ subsample করার ডিকোডার বলতে, মেমরি, সেট মধ্যে একটি ক্ষুদ্রতর সংস্করণটি লোড inSampleSizeকরার trueআপনার BitmapFactory.Optionsঅবজেক্ট। উদাহরণস্বরূপ, রেজোলিউশন সহ 2048x1536 inSampleSizeএর 4 টির সাথে ডিকোড করা একটি চিত্র প্রায় 512x384 বিটম্যাপ তৈরি করে। এটিকে মেমোরিতে লোড করা পুরো চিত্রের জন্য 12MB এর পরিবর্তে 0.75MB ব্যবহার করে (এর বিটম্যাপ কনফিগারেশন ধরে নেওয়া ARGB_8888)। একটি লক্ষ্য প্রশস্ততা এবং উচ্চতার উপর ভিত্তি করে দুটি পাওয়ার হিসাবে একটি নমুনার আকারের মান গণনা করার জন্য এখানে একটি পদ্ধতি রয়েছে:

public static int calculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
    // Raw height and width of image
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;

    if (height > reqHeight || width > reqWidth) {

        final int halfHeight = height / 2;
        final int halfWidth = width / 2;

        // Calculate the largest inSampleSize value that is a power of 2 and keeps both
        // height and width larger than the requested height and width.
        while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;
        }
    }

    return inSampleSize;
}

দ্রষ্টব্য : দুটি মানের একটি পাওয়ার গণনা করা হয় কারণ inSampleSizeডকুমেন্টেশন অনুসারে ডিকোডারটি দুটি নিকটতম পাওয়ারকে গোল করে একটি চূড়ান্ত মান ব্যবহার করে ।

এই পদ্ধতিটি ব্যবহার করতে, প্রথমে inJustDecodeBoundsসেট trueদিয়ে ডিকোড করুন, বিকল্পগুলি পাস করুন এবং তারপরে নতুন inSampleSizeমানটি ব্যবহার করে আবার ডিকোড করুন এবং এতে inJustDecodeBoundsসেট করুন false:

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

    // Calculate inSampleSize
    options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

নিম্নলিখিত পদ্ধতির ImageViewকোড হিসাবে দেখানো হয়েছে এই পদ্ধতিটি, ইচ্ছামত বৃহত আকারের একটি বিটম্যাপটি লোড করা সহজ করে তোলে যা 100x100 পিক্সেলের থাম্বনেল প্রদর্শন করে:

mImageView.setImageBitmap(
    decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

অন্যান্য উত্স থেকে বিটম্যাপগুলি ডিকোড করার জন্য অনুরূপ প্রক্রিয়াটি যথাযথ BitmapFactory.decode*পদ্ধতিটি যথাযথভাবে প্রতিস্থাপন করে অনুসরণ করতে পারেন ।


21
এই উত্তরটি আলোচনা হচ্ছে মেটা
রেনে

9
এই উত্তরটি (লিঙ্কটির মাধ্যমে পৌঁছে দেওয়া তথ্য ব্যতীত) কোনও উত্তর হিসাবে কোনও সমাধানের প্রস্তাব দেয় না। লিঙ্কটির গুরুত্বপূর্ণ অংশগুলি প্রশ্নের মধ্যে একীভূত করা উচিত।
ফ্যালানএঞ্জেল

7
এই উত্তরটি যেমন প্রশ্নের মতো এবং অন্যান্য উত্তরগুলি হ'ল সম্প্রদায়ের উইকি, সুতরাং এটি এমন কিছু যা সম্প্রদায় সম্পাদনা করে ঠিক করতে পারে, এমন কিছু যা মডারেটরের হস্তক্ষেপের প্রয়োজন হয় না।
মার্টিজন পিটারস

সামগ্রী এবং কোটলিন সমর্থনের বর্তমান লিঙ্কটি এখানে পাওয়া যাবে: ডেভেলপার.অ্যান্ড্রয়েড
টপিক / পারফরম্যান্স

890

আউটআউফ মেমরি ত্রুটিটি ঠিক করতে, আপনার এমন কিছু করা উচিত:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 8;
Bitmap preview_bitmap = BitmapFactory.decodeStream(is, null, options);

এই inSampleSizeবিকল্পটি মেমরির খরচ কমিয়ে দেয়।

এখানে একটি সম্পূর্ণ পদ্ধতি। প্রথমে এটি সামগ্রীতে ডিকোডিং না করে চিত্রের আকারটি পড়ে reads তারপরে এটি সর্বোত্তম inSampleSizeমানটি সন্ধান করে , এটি 2 এর শক্তি হওয়া উচিত এবং অবশেষে চিত্রটি ডিকোড করা হয়।

// Decodes image and scales it to reduce memory consumption
private Bitmap decodeFile(File f) {
    try {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;
        BitmapFactory.decodeStream(new FileInputStream(f), null, o);

        // The new size we want to scale to
        final int REQUIRED_SIZE=70;

        // Find the correct scale value. It should be the power of 2.
        int scale = 1;
        while(o.outWidth / scale / 2 >= REQUIRED_SIZE && 
              o.outHeight / scale / 2 >= REQUIRED_SIZE) {
            scale *= 2;
        }

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
    } catch (FileNotFoundException e) {}
    return null;
}

31
নোট করুন যে 10 স্যাম্পল সাইজের জন্য সেরা মান নাও হতে পারে, ডকুমেন্টেশনটি 2 এর শক্তি ব্যবহারের পরামর্শ দেয়
মিরকো এন।

70
আমি ক্রিসপিক্সের মতো একই সমস্যার মুখোমুখি, তবে আমি মনে করি না যে এখানে সমাধানটি সত্যিই সমস্যাটি সমাধান করে, বরং এটির পাশ দিয়ে গেছে। নমুনার আকার পরিবর্তন করা ব্যবহৃত মেমরির পরিমাণ হ্রাস করে (চিত্রের মানের জন্য, যা সম্ভবত কোনও চিত্র পূর্বরূপের জন্য ঠিক আছে), তবে একাধিক চিত্রের প্রবাহগুলি যদি যথেষ্ট পরিমাণে চিত্র স্ট্রিমটি ডিকোড করা হয় তবে এটি ব্যতিক্রমটিকে আটকাবে না if সঙ্কেতমুক্ত। যদি আমি এর থেকে আরও ভাল সমাধান পাই তবে (এবং এটির একটিও নাও থাকতে পারে) আমি এখানে একটি উত্তর পোস্ট করব।
ফ্লাইএন 8১

4
জুম বাড়ানোর জন্য আপনার পিক্সেল ঘনত্বের সাথে স্ক্রিনটি মেলে তুলতে কেবল একটি উপযুক্ত আকার প্রয়োজন need
স্টিলথকপ্টার

4
আপনি যে নতুন আকারে স্কেল করতে চান তা হল REQUIRED_SIZE।
ফেডোর

8
এই সমাধানটি আমাকে সহায়তা করেছে তবে চিত্রের গুণমানটি ভয়ানক। আমি ছবিতে কোনও পরামর্শ দেওয়ার জন্য একটি ভিউ ফিল্ডার ব্যবহার করছি?
ব্যবহারকারী1106888

372

আমি ফেডোরের কোডটিতে সামান্য উন্নতি করেছি। এটি মূলত একই কাজ করে, তবে লুপের সময় (আমার মতে) কুরুচি ছাড়াই এবং এটি সর্বদা দু'জনের শক্তি অর্জন করে। আসল সমাধানটি করার জন্য ফেডোরকে কুদোস, তার সন্ধান না পাওয়া পর্যন্ত আমি আটকে ছিলাম এবং তারপরে আমি এটি তৈরি করতে সক্ষম হয়েছি :)

 private Bitmap decodeFile(File f){
    Bitmap b = null;

        //Decode image size
    BitmapFactory.Options o = new BitmapFactory.Options();
    o.inJustDecodeBounds = true;

    FileInputStream fis = new FileInputStream(f);
    BitmapFactory.decodeStream(fis, null, o);
    fis.close();

    int scale = 1;
    if (o.outHeight > IMAGE_MAX_SIZE || o.outWidth > IMAGE_MAX_SIZE) {
        scale = (int)Math.pow(2, (int) Math.ceil(Math.log(IMAGE_MAX_SIZE / 
           (double) Math.max(o.outHeight, o.outWidth)) / Math.log(0.5)));
    }

    //Decode with inSampleSize
    BitmapFactory.Options o2 = new BitmapFactory.Options();
    o2.inSampleSize = scale;
    fis = new FileInputStream(f);
    b = BitmapFactory.decodeStream(fis, null, o2);
    fis.close();

    return b;
}

40
হ্যাঁ আপনি ঠিক বলেছেন যখন এত সুন্দর নয়। আমি এটি সবার কাছে পরিষ্কার করার চেষ্টা করেছি। আপনার কোডের জন্য ধন্যবাদ।
ফেডার

10
@ থমাস ভার্ভেস্ট - এই কোডটিতে একটি বড় সমস্যা রয়েছে। a একটি শক্তিতে 2 বাড়ায় না, এটি ফলাফলের সাথে 2 xors করে। আপনি ম্যাথ.পাউ (2.0, ...) চান। অন্যথায়, এটি ভাল দেখাচ্ছে।
ডগডাব্লু

6
ওহ, এটি খুব ভাল! আমার খারাপ, আমি এটি সঙ্গে সঙ্গে সংশোধন করব, উত্তরের জন্য ধন্যবাদ!
টমাস ভার্ভেস্ট

8
আপনি দুটি নতুন ফাইলআইনপুট স্ট্রিম তৈরি করছেন, প্রতিটি কলের জন্য একটি BitmapFactory.decodeStream()। আপনার কি তাদের প্রত্যেকটির একটি রেফারেন্স সংরক্ষণ করতে হবে না যাতে সেগুলি একটি finallyব্লকে বন্ধ করা যায় ?
matsev

1
@ বাবিবু ডকুমেন্টেশনে উল্লেখ করা হয়নি যে আপনার জন্য প্রবাহটি বন্ধ রয়েছে, তাই আমি ধরে নিই যে এটি এখনও বন্ধ করা উচিত। একটি আকর্ষণীয় এবং সম্পর্কিত, আলোচনা এখানে পাওয়া যাবে । অ্যাড্রিয়ান স্মিথের মন্তব্যটি নোট করুন, যা আমাদের বিতর্কের সাথে সরাসরি সম্পর্কিত।
টমাস ভার্ভেস্ট 12'13

232

আমি আইওএসের অভিজ্ঞতা থেকে এসেছি এবং কোনও চিত্র লোড করা এবং দেখানোর মতো মৌলিক কিছু নিয়ে কোনও সমস্যা আবিষ্কার করতে আমি হতাশ হয়ে পড়েছিলাম। সর্বোপরি, এই সমস্যাটি রয়েছে এমন প্রত্যেকে যুক্তিসঙ্গত আকারের চিত্রগুলি প্রদর্শনের চেষ্টা করছে। যাইহোক, এখানে দুটি পরিবর্তন যা আমার সমস্যাটিকে স্থির করেছে (এবং আমার অ্যাপ্লিকেশনটিকে খুব প্রতিক্রিয়াশীল করেছে)

1) প্রতিবার আপনি যখন যাবেন BitmapFactory.decodeXYZ(), সেটে সেটটি BitmapFactory.Optionsদিয়ে পাস করার বিষয়টি নিশ্চিত করুন (এবং আরও ভালভাবে সেট করার সাথেও )।inPurgeabletrueinInputShareabletrue

2) কখনও ব্যবহার Bitmap.createBitmap(width, height, Config.ARGB_8888)। মানে কখনই না! আমি কখনও কখনও পাস করার পরে মেমরি ত্রুটি বাড়ানোর জিনিসটি পাইনি। এর কোনো পরিমাণ recycle(), System.gc()যাই হোক না কেন সাহায্য করেছিল। এটি সর্বদা ব্যতিক্রম উত্থাপন। অন্য একটি উপায় যা আসলে কাজ করে তা হ'ল আপনার আঁকতে একটি ডামি চিত্র থাকে (বা অন্য বিটম্যাপ যা আপনি উপরের 1 ধাপটি ব্যবহার করে ডিকোড করেছেন) পুনরুদ্ধার করুন যা আপনি চান, তারপরে ফলাফল বিটম্যাপটি পরিচালনা করুন (যেমন এটি একটি ক্যানভাসে পাস করার মতো) আরও মজা জন্য)। সুতরাং, আপনি যদি এর পরিবর্তে ব্যবহার করা উচিত হল: Bitmap.createScaledBitmap(srcBitmap, width, height, false)। কারনের জন্য আপনাকে অবশ্যই পাশব বল ব্যবহার করেন তাহলে অন্তত পাস এ পদ্ধতি তৈরি করেন, তারপর Config.ARGB_4444

দিন না হলেও এটি আপনাকে ঘন্টা বাঁচানোর প্রায় গ্যারান্টিযুক্ত। চিত্র ইত্যাদির স্কেলিং সম্পর্কিত সমস্ত কথাই আসলে কাজ করে না (যদি না আপনি ভুল আকার বা অবনতিযুক্ত চিত্রটি সমাধান হিসাবে বিবেচনা করেন)।


22
BitmapFactory.Options options = new BitmapFactory.Options(); options.inPurgeable = true;এবং Bitmap.createScaledBitmap(srcBitmap, width, height, false);অ্যান্ড্রয়েড .0.০.০ এ আমার স্মৃতি ব্যতিক্রম ছাড়াই আমার সমস্যাটি সমাধান হয়েছে। ধন্যবাদ বন্ধু!
জান-তেরজে সেরেনসেন

5
বিটম্যাপ। ক্রিয়েটসেল্ডবিটম্যাপ () কলটিতে আপনার সম্ভবত পতাকা প্যারামিটার হিসাবে সত্য ব্যবহার করা উচিত। অন্যথায় স্কেলিংয়ের সময় চিত্রটির গুণমান মসৃণ হবে না। এই থ্রেড চেক করুন stackoverflow.com/questions/2895065/...
rOrlig

11
এটি সত্যিই দুর্দান্ত পরামর্শ। এই আশ্চর্যজনক রিঙ্কি ডিংক বাগের জন্য গুগলকে টাস্কে নেওয়ার জন্য আমি আপনাকে একটি অতিরিক্ত +1 দিতে পারতাম বলে আশা করি। মানে ... যদি এটি কোনও ত্রুটি না হয় তবে ডকুমেন্টেশনে সত্যিকার অর্থে কিছু গুরুতর ঝলকানি নিয়ন লক্ষণ থাকা দরকার যা "এটি আপনার প্রসেস ফটো কীভাবে" বলেছে, কারণ আমি 2 বছর ধরে এটির সাথে লড়াই করছি এবং এখনই এই পোস্টটি খুঁজে পেয়েছি। দুর্দান্ত খুঁজে।
ইয়েজগেনি সিমকিন

আপনার চিত্রগুলি স্কেলিং অবশ্যই স্পষ্টভাবে সহায়তা করে তবে এটি একটি গুরুত্বপূর্ণ পদক্ষেপ এবং শেষ পর্যন্ত আমার পক্ষে এই সমস্যাটি কী সমাধান হয়েছিল। আপনার চিত্রগুলি কেবল স্কেলিংয়ের সমস্যাটি হ'ল যদি আপনার অনেকগুলি থাকে বা উত্সের চিত্রগুলি খুব বড় হয় তবে আপনি এখনও একই সমস্যার মধ্যে চলে যেতে পারেন। আপনার কাছে ইফ্রয়িমকে +1 করুন।
ডেভ

10
Lollipop এর হিসাবে, BitmapFactory.Options.inPurgeableএবং BitmapFactory.Options.inInputShareableঅবচিত হয় developer.android.com/reference/android/graphics/...
ডেনিস Kniazhev

93

এটি একটি পরিচিত বাগ , এটি বড় ফাইলগুলির কারণে নয়। যেহেতু অ্যান্ড্রয়েড ড্রয়াবলকে ক্যাচ করে, তাই কয়েকটি চিত্র ব্যবহারের পরে এটি মেমরির বাইরে চলে যায়। তবে অ্যান্ড্রয়েড ডিফল্ট ক্যাশে সিস্টেমটি এড়িয়ে গিয়ে এর জন্য আমি একটি বিকল্প উপায় খুঁজে পেয়েছি।

সমাধান : চিত্রগুলিকে "সম্পদ" ফোল্ডারে সরান এবং বিটম্যাপড্রেবলযোগ্য পেতে নিম্নলিখিত ফাংশনটি ব্যবহার করুন:

public static Drawable getAssetImage(Context context, String filename) throws IOException {
    AssetManager assets = context.getResources().getAssets();
    InputStream buffer = new BufferedInputStream((assets.open("drawable/" + filename + ".png")));
    Bitmap bitmap = BitmapFactory.decodeStream(buffer);
    return new BitmapDrawable(context.getResources(), bitmap);
}

79

আমি এই একই সমস্যাটি পেয়েছিলাম এবং বিটম্যাপফ্যাক্টরি.ডেকোড স্ট্রিম বা ডিকোডফিল ফাংশন এড়িয়ে সমাধান করেছি এবং পরিবর্তে ব্যবহার করেছি BitmapFactory.decodeFileDescriptor

decodeFileDescriptor দেখে মনে হচ্ছে এটি ডিকোড স্ট্রিম / ডিকোডফিলের চেয়ে আলাদা দেশীয় পদ্ধতিকে কল করে।

যাইহোক, কি কাজ এই (নোট যে আমি কিছু অপশন কিছু উপরে ছিল জুড়েছেন, কিন্তু যে কি পার্থক্য তৈরি না কি সমালোচনামূলক হয় আহবান। ছিল BitmapFactory.decodeFileDescriptor পরিবর্তে decodeStream বা decodeFile ):

private void showImage(String path)   {

    Log.i("showImage","loading:"+path);
    BitmapFactory.Options bfOptions=new BitmapFactory.Options();
    bfOptions.inDither=false;                     //Disable Dithering mode
    bfOptions.inPurgeable=true;                   //Tell to gc that whether it needs free memory, the Bitmap can be cleared
    bfOptions.inInputShareable=true;              //Which kind of reference will be used to recover the Bitmap data after being clear, when it will be used in the future
    bfOptions.inTempStorage=new byte[32 * 1024]; 

    File file=new File(path);
    FileInputStream fs=null;
    try {
        fs = new FileInputStream(file);
    } catch (FileNotFoundException e) {
        //TODO do something intelligent
        e.printStackTrace();
    }

    try {
        if(fs!=null) bm=BitmapFactory.decodeFileDescriptor(fs.getFD(), null, bfOptions);
    } catch (IOException e) {
        //TODO do something intelligent
        e.printStackTrace();
    } finally{ 
        if(fs!=null) {
            try {
                fs.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    //bm=BitmapFactory.decodeFile(path, bfOptions); This one causes error: java.lang.OutOfMemoryError: bitmap size exceeds VM budget

    im.setImageBitmap(bm);
    //bm.recycle();
    bm=null;                        
}

আমি মনে করি ডিকোড স্ট্রিম / ডিকোডফাইলে ব্যবহৃত নেটিভ ফাংশনে সমস্যা আছে। আমি নিশ্চিত করেছি যে ডিকোডফিলডিসিপেক্টর ব্যবহার করার সময় একটি ভিন্ন দেশীয় পদ্ধতি বলা হয়। এছাড়াও আমি যা পড়েছি তা হ'ল "যে চিত্রগুলি (বিটম্যাপস) একটি স্ট্যান্ডার্ড জাভা উপায়ে বরাদ্দ করা হয়নি তবে নেটিভ কলগুলির মাধ্যমে; বরাদ্দগুলি ভার্চুয়াল হিপের বাইরে করা হয়, তবে এর বিপরীতে গণনা করা হয়! "


1
মেমোরির বাইরে একই ফলাফল, আসলে আপনি যে পদ্ধতিটি ব্যবহার করছেন তা কোনও বিষয় নয় যা মেমরি থেকে বেরিয়ে আসে এমন ডেটা পড়ার জন্য আপনি যে পরিমাণ বাইট ধরে রেখেছেন তার উপর নির্ভর করে।
পীযূষমিশ্রা

72

আমি মনে করি OutOfMemoryErrorএটিকে এড়ানোর সর্বোত্তম উপায় হ'ল এটির মুখোমুখি হওয়া এবং এটি বোঝা।

আমি ইচ্ছাকৃতভাবে কারণ তৈরি করতে এবং মেমরির ব্যবহার নিরীক্ষণের জন্য একটি অ্যাপ্লিকেশন তৈরি করেছি OutOfMemoryError

আমি এই অ্যাপ্লিকেশনটির সাথে অনেকগুলি পরীক্ষা-নিরীক্ষা করার পরে, আমি নিম্নলিখিত সিদ্ধান্তগুলি পেয়েছি:

আমি হানি কম্বের আগে এসডিকে সংস্করণগুলি নিয়ে কথা বলব।

  1. বিটম্যাপ দেশীয় স্তূপে সংরক্ষণ করা হয় তবে এটি স্বয়ংক্রিয়ভাবে সংগ্রহ করা আবর্জনা পাবেন, রিসাইকালে কল করা অকারণ।

  2. যদি {ভিএম হিপ আকার} + {বরাদ্দ নেটিভ হিপ মেমরি}> = the ডিভাইসের জন্য ভিএম হিপ আকার সীমা}, এবং আপনি বিটম্যাপ তৈরি করার চেষ্টা করছেন, OOM নিক্ষেপ করা হবে।

    বিজ্ঞপ্তি: ভিএম হ্যাপ সাইজটি ভিএম অলোকটেড মেমোরির পরিবর্তে গণনা করা হয়।

  3. ভিএম হিপ আকার বড় হওয়ার পরে কখনই সঙ্কুচিত হবে না, এমনকি যদি বরাদ্দ করা ভিএম মেমরি সঙ্কুচিত হয়।

  4. সুতরাং বিটম্যাপের জন্য উপলভ্য মেমরির সংরক্ষণ করতে ভিএম হিপ আকারটি আরও বড় হতে বাড়াতে আপনাকে যথাসম্ভব পিক ভিএম মেমরিটি রাখতে হবে।

  5. ম্যানুয়ালি কল করুন System.gc () অর্থহীন, সিস্টেম গাদা আকার বাড়ানোর চেষ্টা করার আগে প্রথমে এটি কল করবে।

  6. নেটিভ হিপ সাইজ কখনই খুব সঙ্কুচিত হবে না, তবে এটি ওওমের জন্য গণনা করা হয়নি, সুতরাং এটি নিয়ে চিন্তা করার দরকার নেই।

তারপরে, আসুন মধু ঝুঁটি থেকে এসডিকে শুরু সম্পর্কে কথা বলা যাক।

  1. বিটম্যাপটি ভিএম হিপে সংরক্ষণ করা হয়, নেটিভ মেমরি OOM এর জন্য গণনা করা হয় না।

  2. OOM এর শর্তটি অনেক সহজ: {ভিএম হিপ আকার}> = the ডিভাইসের জন্য ভিএম হিপ আকারের সীমা}

  3. সুতরাং একই গাদা আকারের সীমা সহ বিটম্যাপ তৈরি করতে আপনার কাছে আরও উপলভ্য মেমরি রয়েছে, OOM নিক্ষেপ হওয়ার সম্ভাবনা কম।

আবর্জনা সংগ্রহ এবং মেমরি ফুটো সম্পর্কে আমার পর্যবেক্ষণগুলির এখানে কিছু।

আপনি এটি অ্যাপটিতে দেখতে পারেন can যদি কোনও ক্রিয়াকলাপ কোনও অ্যাসিঙ্কটাস্ক কার্যকর করে যা ক্রিয়াকলাপ ধ্বংস হওয়ার পরেও চলছিল, অ্যাসিঙ্কটাস্ক শেষ না হওয়া অবধি ক্রিয়াকলাপ সংগ্রহ করা হবে না।

এটি কারণ AsyncTask একটি বেনামী অভ্যন্তর শ্রেণীর উদাহরণ, এটি ক্রিয়াকলাপের একটি রেফারেন্স ধারণ করে।

পটভূমির থ্রেডে আইও ক্রিয়াকলাপটি যদি টাস্কটি ব্লক করা থাকে তবে AsyncTask.cancel (সত্য) কল করা কার্যকর করা বন্ধ করবে না।

কলব্যাকগুলি বেনামে অভ্যন্তরীণ ক্লাসগুলিও তাই আপনার প্রকল্পে যদি কোনও স্থির উদাহরণ তাদের ধরে রাখে এবং সেগুলি প্রকাশ না করে, মেমরি ফাঁস হয়ে যায়।

যদি আপনি কোনও পুনরাবৃত্তি বা বিলম্বিত কার্য নির্ধারণ করে থাকেন, উদাহরণস্বরূপ একটি টাইমার, এবং আপনি বাতিল () এবং পূজ () অনপস () এ কল করেন না, মেমরি ফাঁস হবে।


অ্যাসিঙ্কটাস্ক অগত্যা "বেনামী অন্তর্নিহিত শ্রেণীর উদাহরণ" হওয়া উচিত নয় এবং এটি কলব্যাক্সের ক্ষেত্রেও ঘটে। আপনি এটির নিজস্ব ফাইলে একটি নতুন পাবলিক ক্লাস তৈরি করতে পারেন যা অ্যাসিঙ্কটাস্ক বা এমনকি private static classএকই শ্রেণীর মধ্যে প্রসারিত । তারা ক্রিয়াকলাপের কোনও রেফারেন্স রাখবে না (যদি আপনি তাদের অবশ্যই একটি না দেন)
সাইমন ফোর্সবার্গ

65

আমি OOM ব্যতিক্রম এবং ইদানীং ক্যাশে করা সম্পর্কে প্রচুর প্রশ্ন দেখেছি। বিকাশকারী গাইড এ সম্পর্কে সত্যিই একটি ভাল নিবন্ধ আছে, কিন্তু কিছু এটি একটি উপযুক্ত উপায়ে বাস্তবায়নে ব্যর্থ হয়।

এ কারণে আমি একটি উদাহরণ অ্যাপ্লিকেশন লিখেছি যা একটি অ্যান্ড্রয়েড পরিবেশে ক্যাশিং প্রদর্শন করে। এই বাস্তবায়ন এখনও একটি OOM অর্জন করতে পারেনি।

উত্স কোডের একটি লিঙ্কের জন্য এই উত্তরটির শেষে দেখুন।

প্রয়োজনীয়তা:

  • অ্যান্ড্রয়েড এপিআই ২.১ বা উচ্চতর (আমি এপিআই ১.6 এ কোনও অ্যাপ্লিকেশনের জন্য উপলব্ধ মেমরিটি পেতে কেবল পরিচালনা করতে পারিনি - এটিই কেবলমাত্র কোডের টুকরো যা এপিআই ১.6 এ কাজ করে না)
  • অ্যান্ড্রয়েড সমর্থন প্যাকেজ

স্ক্রিনশট

বৈশিষ্ট্য:

  • সিঙ্গেলটন ব্যবহার করে ওরিয়েন্টেশন পরিবর্তন হলে ক্যাশে পুনরুদ্ধার করে
  • ব্যবহার করুন এক অষ্টম ক্যাশে নির্ধারিত আবেদন মেমরি (যদি আপনি চান সংশোধন করুন)
  • বড় বিটম্যাপগুলি পরিমাপ করা হয় (আপনি সর্বাধিক পিক্সেল যা আপনি অনুমতি দিতে চান তা নির্ধারণ করতে পারেন)
  • বিটম্যাপগুলি ডাউনলোড করার আগে একটি ইন্টারনেট সংযোগ উপলব্ধ রয়েছে তা নিয়ন্ত্রণ করে
  • নিশ্চিত করে তোলে যে আপনি সারি প্রতি কেবল একটি কাজ ইনস্ট্যান্ট করছেন
  • আপনি যদিListView দূরে উড়ে যাচ্ছেন তবে এটি কেবল বিটম্যাপগুলি এর মধ্যে ডাউনলোড করবে না

এটি অন্তর্ভুক্ত করে না:

  • ডিস্ক ক্যাচিং যাইহোক এটি কার্যকর করা সহজ হওয়া উচিত - কেবল একটি ভিন্ন টাস্কের দিকে নির্দেশ করুন যা ডিস্ক থেকে বিটম্যাপগুলি ধরে

কোডের উদাহরণ:

যে চিত্রগুলি ডাউনলোড হচ্ছে সেগুলি হ'ল ফ্লিকারের চিত্রগুলি (75x75)। তবে আপনি যে চিত্রের ইউআরএল প্রসেস করতে চান তা রাখুন এবং অ্যাপ্লিকেশন এটি সর্বাধিক ছাড়িয়ে গেলে এটি কমিয়ে দেবে। এই অ্যাপ্লিকেশনটিতে ইউআরএলগুলি কেবল একটি Stringঅ্যারেতে থাকে।

LruCacheএকটি ভালো উপায় বিটম্যাপ সাথে মোকাবিলা করতে হবে। যাইহোক, এই অ্যাপ্লিকেশনটিতে আমি LruCacheআরও ক্যাশে ক্লাসের মধ্যে একটি উদাহরণ স্থাপন করেছি যা অ্যাপ্লিকেশনটি আরও সম্ভাব্য করে তোলার জন্য created

Cache.java- এর সমালোচনামূলক জিনিস ( loadBitmap()পদ্ধতিটি সবচেয়ে গুরুত্বপূর্ণ):

public Cache(int size, int maxWidth, int maxHeight) {
    // Into the constructor you add the maximum pixels
    // that you want to allow in order to not scale images.
    mMaxWidth = maxWidth;
    mMaxHeight = maxHeight;

    mBitmapCache = new LruCache<String, Bitmap>(size) {
        protected int sizeOf(String key, Bitmap b) {
            // Assuming that one pixel contains four bytes.
            return b.getHeight() * b.getWidth() * 4;
        }
    };

    mCurrentTasks = new ArrayList<String>();    
}

/**
 * Gets a bitmap from cache. 
 * If it is not in cache, this method will:
 * 
 * 1: check if the bitmap url is currently being processed in the
 * BitmapLoaderTask and cancel if it is already in a task (a control to see
 * if it's inside the currentTasks list).
 * 
 * 2: check if an internet connection is available and continue if so.
 * 
 * 3: download the bitmap, scale the bitmap if necessary and put it into
 * the memory cache.
 * 
 * 4: Remove the bitmap url from the currentTasks list.
 * 
 * 5: Notify the ListAdapter.
 * 
 * @param mainActivity - Reference to activity object, in order to
 * call notifyDataSetChanged() on the ListAdapter.
 * @param imageKey - The bitmap url (will be the key).
 * @param imageView - The ImageView that should get an
 * available bitmap or a placeholder image.
 * @param isScrolling - If set to true, we skip executing more tasks since
 * the user probably has flinged away the view.
 */
public void loadBitmap(MainActivity mainActivity, 
        String imageKey, ImageView imageView,
        boolean isScrolling) {
    final Bitmap bitmap = getBitmapFromCache(imageKey); 

    if (bitmap != null) {
        imageView.setImageBitmap(bitmap);
    } else {
        imageView.setImageResource(R.drawable.ic_launcher);
        if (!isScrolling && !mCurrentTasks.contains(imageKey) && 
                mainActivity.internetIsAvailable()) {
            BitmapLoaderTask task = new BitmapLoaderTask(imageKey,
                    mainActivity.getAdapter());
            task.execute();
        }
    } 
}

আপনি ডিস্ক ক্যাচিং বাস্তবায়ন না করে ক্যাশে.জাভা ফাইলে কোনও কিছু সম্পাদনা করার দরকার নেই।

মেইনএ্যাকটিভিটি.জভা এর সমালোচনা উপাদান:

public void onScrollStateChanged(AbsListView view, int scrollState) {
    if (view.getId() == android.R.id.list) {
        // Set scrolling to true only if the user has flinged the       
        // ListView away, hence we skip downloading a series
        // of unnecessary bitmaps that the user probably
        // just want to skip anyways. If we scroll slowly it
        // will still download bitmaps - that means
        // that the application won't wait for the user
        // to lift its finger off the screen in order to
        // download.
        if (scrollState == SCROLL_STATE_FLING) {
            mIsScrolling = true;
        } else {
            mIsScrolling = false;
            mListAdapter.notifyDataSetChanged();
        }
    } 
}

// Inside ListAdapter...
@Override
public View getView(final int position, View convertView, ViewGroup parent) {           
    View row = convertView;
    final ViewHolder holder;

    if (row == null) {
        LayoutInflater inflater = getLayoutInflater();
        row = inflater.inflate(R.layout.main_listview_row, parent, false);  
        holder = new ViewHolder(row);
        row.setTag(holder);
    } else {
        holder = (ViewHolder) row.getTag();
    }   

    final Row rowObject = getItem(position);

    // Look at the loadBitmap() method description...
    holder.mTextView.setText(rowObject.mText);      
    mCache.loadBitmap(MainActivity.this,
            rowObject.mBitmapUrl, holder.mImageView,
            mIsScrolling);  

    return row;
}

getView()খুব প্রায়ই ডাকা হয়। সাধারণত যদি আমরা কোনও চেক প্রয়োগ না করে থাকে তবে আমাদের প্রতি সারি প্রতি অসীম পরিমাণ থ্রেড শুরু হবে না তা নিশ্চিত করে সেখানে চিত্রগুলি ডাউনলোড করা ভাল ধারণা নয়। ক্যাশে.জভা rowObject.mBitmapUrlইতিমধ্যে কোনও কার্যক্রমে রয়েছে কিনা তা পরীক্ষা করে এবং এটি যদি হয় তবে এটি অন্যটি শুরু করবে না। অতএব, আমরা সম্ভবত AsyncTaskপুল থেকে কাজের সারি সীমাবদ্ধতা অতিক্রম করব না ।

ডাউনলোড করুন:

আপনি উত্স কোডটি https://www.roidbox.com/s/pvr9zyl811tfeem/ListViewImageCache.zip থেকে ডাউনলোড করতে পারেন ।


শেষ কথা:

আমি এখন কয়েক সপ্তাহের জন্য এটি পরীক্ষা করেছি, আমি এখনও একটিও ওওএম ব্যতিক্রম পাইনি। আমি এটি এমুলেটরটিতে, আমার নেক্সাস ওনে এবং আমার নেক্সাস এস-তে পরীক্ষা করেছি I আমি এমন চিত্রের url পরীক্ষা করেছি যা এইচডি মানের মধ্যে থাকা চিত্রগুলি রয়েছে। একমাত্র বাধা হ'ল এটি ডাউনলোড করতে আরও সময় নেয়।

কেবলমাত্র একটি সম্ভাব্য দৃশ্য আছে যেখানে আমি ধারণা করতে পারি যে OOM প্রদর্শিত হবে এবং তা হ'ল আমরা যদি অনেকগুলি সত্যই বড় চিত্র ডাউনলোড করি এবং সেগুলি মাপার আগে এবং ক্যাশে রাখার আগে একসাথে আরও মেমরি নিয়ে যায় এবং একটি OOM সৃষ্টি করে। তবে এটি যাইহোক একটি আদর্শ পরিস্থিতিও নয় এবং সম্ভবত এটি আরও কার্যকর উপায়ে সমাধান করা সম্ভব হবে না।

মন্তব্য ত্রুটি রিপোর্ট! :-)


43

আমি ছবিটি নেওয়ার জন্য এবং ফ্লাইতে এটিকে পুনরায় আকার দেওয়ার জন্য নিম্নলিখিতগুলি করেছি। আশাকরি এটা সাহায্য করবে

Bitmap bm;
bm = Bitmap.createScaledBitmap(BitmapFactory.decodeFile(filepath), 100, 100, true);
mPicture = new ImageView(context);
mPicture.setImageBitmap(bm);    

26
এই পদ্ধতির বিটম্যাপ স্কেল করে। তবে এটি আউটঅফমিউরি সমস্যাটি সমাধান করে না কারণ যেভাবেই পুরো বিটম্যাপটি ডিকোড করা হচ্ছে।
ফেডোর

5
আমি আমার পুরানো কোডটি দেখতে পারি কিনা তা আমি দেখতে পাব তবে আমি মনে করি এটি আমার স্মৃতি সমস্যার সমাধান করে নি। আমার পুরানো কোডটি ডাবল চেক করবে।
Chrispix

2
কমপক্ষে এই উদাহরণে, দেখে মনে হচ্ছে আপনি সম্পূর্ণ বিটম্যাপের রেফারেন্স রাখছেন না, এভাবে মেমরির সঞ্চয় হয়।
NoBugs

আমার জন্য এটি মেমরির সমস্যাটি সমাধান করেছিল, তবে চিত্রগুলির গুণমানকে হ্রাস করেছে।
পামেলা সিল্লাহ

35

দেখে মনে হচ্ছে এটি অনেক দীর্ঘ চলমান সমস্যা, যার সাথে অনেকগুলি পৃথক পৃথক ব্যাখ্যা রয়েছে। আমি এখানে দুটি সবচেয়ে সাধারণ উপস্থাপিত উত্তরগুলির পরামর্শ নিয়েছি, তবে এর মধ্যে একটিও আমার ভিএম এর সমস্যার সমাধান করেনি দাবি করে যে এটি প্রক্রিয়াটির ডিকোডিং অংশটি সম্পাদন করতে বাইটগুলি বহন করতে পারে না । কিছু খননের পরে আমি জানতে পারি যে এখানে আসল সমস্যাটি ডিকোডিং প্রক্রিয়াটি নেটিভ হিপ থেকে দূরে নিয়ে যাওয়া ।

এখানে দেখুন: বিটম্যাপফ্যাক্টরি ওওএম আমাকে বাদাম চালাচ্ছে

এটি আমাকে অন্য আলোচনার সূচনায় নিয়ে যায় যেখানে আমি এই সমস্যার আরও কয়েকটি সমাধান পেয়েছি। একটি হল System.gc();আপনার চিত্র প্রদর্শিত হওয়ার পরে ম্যানুয়ালি কল করা। কিন্তু এটি আসলে আপনার অ্যাপ্লিকেশন নেটিভ হিপ হ্রাস করার প্রয়াসে আরও মেমরি ব্যবহার করে। ২.০ (ডোনট) এর মুক্তির আরও ভাল সমাধান হ'ল বিটম্যাপফ্যাক্টরি বিকল্পটি "ইনপুজারেবল" ব্যবহার করা। সুতরাং আমি কেবল o2.inPurgeable=true;ঠিক পরে যুক্তo2.inSampleSize=scale;

এখানে এই বিষয়ে আরও: মেমরির হিপটির সীমাটি কি কেবল 6 এম?

এখন, এই সমস্ত কথা বলার পরেও আমি জাভা এবং অ্যান্ড্রয়েডের সাথেও সম্পূর্ণ অবিরাম। সুতরাং আপনি যদি মনে করেন যে এই সমস্যাটি সমাধানের এটি একটি ভয়ঙ্কর উপায়, আপনি সম্ভবত সঠিক are ;-) তবে এটি আমার জন্য বিস্ময়কর কাজ করেছে এবং আমি এখন গাদা ক্যাশে থেকে ভিএম চালানো অসম্ভব বলে মনে করেছি। আমি কেবলমাত্র ত্রুটিটি খুঁজে পেতে পারি তা হল আপনি নিজের ক্যাশেড টানা চিত্রটি ট্র্যাশ করছেন। যার অর্থ আপনি যদি সেই চিত্রটিতে ডানদিকে যান তবে আপনি প্রতিবার এটিকে আবার অঙ্কন করছেন। আমার অ্যাপ্লিকেশনটি কীভাবে কাজ করে সে ক্ষেত্রে এটি আসলেই সমস্যা নয়। আপনার মাইলেজ পরিবর্তিত হতে পারে.


আমার জন্য প্রযোজ্য স্থির OOM।
আর্টেম রাশাকোভস্কিই

35

দুর্ভাগ্যক্রমে যদি উপরেরগুলির মধ্যে কেউ কাজ না করে, তবে এটি আপনার ম্যানিফেস্ট ফাইলটিতে যুক্ত করুন। অ্যাপ্লিকেশন ট্যাগের ভিতরে

 <application
         android:largeHeap="true"

1
আপনি কি ব্যাখ্যা করতে পারেন এটি আসলে কি করে? কেবল লোকদের এটিকে যুক্ত করতে বললে কোনও লাভ হয় না।
স্টিলথ রাব্বি

1
এটি একটি খুব খারাপ সমাধান। মূলত আপনি সমস্যাটি সমাধান করার চেষ্টা করছেন না। পরিবর্তে অ্যান্ড্রয়েড সিস্টেমকে আপনার আবেদনের জন্য আরও বেশি গাদা জায়গা বরাদ্দ করতে বলুন। এটি আপনার অ্যাপ্লিকেশনটিতে প্রচুর পরিমাণে ব্যাটারি শক্তি গ্রহণ করে যেমন আপনার অ্যাপটিতে খুব খারাপ প্রভাব ফেলবে কারণ মেমরি পরিষ্কার করার জন্য জিসিকে বড় হ্যাপ স্পেসের মধ্য দিয়ে চলতে হবে এবং আপনার অ্যাপ্লিকেশনটির কার্যকারিতাও ধীর হবে।
প্রকাশ

2
তাহলে অ্যান্ড্রয়েড কেন আমাদের এই অ্যান্ড্রয়েড যুক্ত করতে দিচ্ছে: লার্জহীপ = আমাদের ম্যানিফেস্টে "সত্য"? এখন আপনি অ্যান্ড্রয়েডকে চ্যালেঞ্জ করছেন।
হিমাংশু মরি


28

আমার কাছে আরও কার্যকর সমাধান রয়েছে যা কোনও ধরণের স্কেলিংয়ের প্রয়োজন হয় না। কেবলমাত্র একবার আপনার বিটম্যাপটি ডিকোড করুন এবং তারপরে এটির নামের সাথে মানচিত্রে ক্যাশে করুন। তারপরে নামটির বিটম্যাপটি কেবল পুনরুদ্ধার করুন এবং এটি চিত্রমুখে সেট করুন। আর কিছু করার দরকার নেই।

এটি কাজ করবে কারণ ডিকোডড বিটম্যাপের আসল বাইনারি ডেটা ডালভিক ভিএম হিপের মধ্যে সংরক্ষণ করা হয়নি। এটি বাহ্যিকভাবে সংরক্ষণ করা হয়। সুতরাং প্রতিবার আপনি যখন বিটম্যাপটি ডিকোড করেন তখন এটি ভিএম হিপের বাইরে মেমরি বরাদ্দ করে যা কখনই জিসি দ্বারা পুনরুদ্ধার করা হয় না

এটির আরও প্রশংসা করতে আপনাকে সাহায্য করার জন্য, কল্পনা করুন যে আপনি আপনার ছবিটি অঙ্কনযোগ্য ফোল্ডারে রেখেছেন। আপনি কেবল একটি গেটআরসোর্সগুলি ()। GetDrwable (আর.ড্রেব্যাবল।) করে চিত্রটি পান। এটি আপনার চিত্রটি প্রতিবারই ডিকোড করবে না তবে আপনি যতবার কল করবেন ততক্ষণে ইতিমধ্যে ডিকোড করা দৃষ্টান্তটি পুনরায় ব্যবহার করবেন। সুতরাং সংক্ষেপে এটি ক্যাশে করা হয়।

এখন যেহেতু আপনার চিত্রটি কোথাও কোনও ফাইলে রয়েছে (বা এমনকি বাহ্যিক সার্ভার থেকেও আসতে পারে), ডিকোডড বিটম্যাপের উদাহরণটি যেখানে প্রয়োজন সেখানে পুনরায় ব্যবহার করা আপনার নিজের দায়িত্ব।

আশাকরি এটা সাহায্য করবে.


4
"এবং তারপরে মানচিত্রে এটিকে তার নামের বিপরীতে ক্যাশে করুন।" আপনি কিভাবে আপনার চিত্রগুলি ক্যাশে করবেন?
ভিনসেন্ট

3
আপনি কি আসলে এটি চেষ্টা করেছেন? পিক্সেল ডেটা আসলে ডালভিকের স্তূপের মধ্যে সংরক্ষণ করা না হলেও দেশীয় মেমরির আকারটি ভিএমকে জানানো হয় এবং এটি উপলব্ধ মেমরির তুলনায় গণনা করা হয়।
এরিকআর

3
@ ভিনসেন্ট আমার মনে হয় এগুলি মানচিত্রে সংরক্ষণ করা খুব কঠিন নয়। আমি হ্যাশম্যাপ <কেই, বিটম্যাপ> মানচিত্রের মতো কিছু প্রস্তাব করব, যেখানে কী উত্সটির একটি স্ট্রিং বা আপনার পক্ষে অর্থপূর্ণ এমন কোনও কিছু হতে পারে। ধরে নেওয়া যাক আপনি কেইওয়াই হিসাবে কোনও পথ ধরেন, আপনি এটিকে ম্যাপ.পুট (পাথ, বিটম্যাপ) হিসাবে সংরক্ষণ করুন এবং এটিকে ম্যাপ.জেটের মাধ্যমে গ্রহণ করুন (পথ)
রাফায়েল টি

3
আপনি সম্ভবত হ্যাশম্যাপ <স্ট্রিং, সফট রেফারেন্স <বিটম্যাপ>> ব্যবহার করতে চান আপনি যদি কোনও চিত্র ক্যাশে প্রয়োগ করছেন তবে অন্যথায় আপনি যেভাবেই মেমরি হারিয়ে ফেলতে পারেন - আমি এটাও মনে করি না যে "এটি ভিএম হিপের বাইরে মেমরি বরাদ্দ করে যা কখনও জিসি দ্বারা পুনরুদ্ধার করা হয় না is "সত্য, স্মৃতিটি পুনরুদ্ধার করা হয়েছে কারণ আমি বুঝতে পেরেছি যে কেবল বিলম্ব হতে পারে, যা বিটম্যাপ.সাইকেল () এর পক্ষে, স্মৃতিটিকে দ্রুত দাবি করার ইঙ্গিত হিসাবে ...
ডরি

28

আমি একই পদ্ধতিটি নিম্নলিখিত পদ্ধতিতে সমাধান করেছি।

Bitmap b = null;
Drawable d;
ImageView i = new ImageView(mContext);
try {
    b = Bitmap.createBitmap(320,424,Bitmap.Config.RGB_565);
    b.eraseColor(0xFFFFFFFF);
    Rect r = new Rect(0, 0,320 , 424);
    Canvas c = new Canvas(b);
    Paint p = new Paint();
    p.setColor(0xFFC0C0C0);
    c.drawRect(r, p);
    d = mContext.getResources().getDrawable(mImageIds[position]);
    d.setBounds(r);
    d.draw(c);

    /*   
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inTempStorage = new byte[128*1024];
        b = BitmapFactory.decodeStream(mContext.getResources().openRawResource(mImageIds[position]), null, o2);
        o2.inSampleSize=16;
        o2.inPurgeable = true;
    */
} catch (Exception e) {

}
i.setImageBitmap(b);

যে ঠিক আছে কিন্তু আমি onCreate মধ্যে ড্র বৃত্ত এবং কার্যকলাপ কলের জন্য 4-5 বার একাধিক বিটম্যাপ ব্যবহার করছি তাই কিভাবে পরিষ্কার বিটম্যাপ প্রয়োজন এবং কিভাবে দ্বিতীয় সময় জন্য বিটম্যাপ সরিয়ে দিয়ে আবার তৈরি করতে যখন কার্যকলাপ 0nCreate ..
ckpatel

27

এখানে দুটি বিষয় আছে....


এটি অ্যান্ড্রয়েড
২.৩+

27

এটি আমার পক্ষে কাজ করেছিল!

public Bitmap readAssetsBitmap(String filename) throws IOException {
    try {
        BitmapFactory.Options options = new BitmapFactory.Options(); 
        options.inPurgeable = true;
        Bitmap bitmap = BitmapFactory.decodeStream(assets.open(filename), null, options);
        if(bitmap == null) {
            throw new IOException("File cannot be opened: It's value is null");
        } else {
            return bitmap;
        }
    } catch (IOException e) {
        throw new IOException("File cannot be opened: " + e.getMessage());
    }
}

20

উপরের কোনও উত্তর আমার পক্ষে কাজ করেনি, তবে আমি একটি ভয়াবহ কুরুচিপূর্ণ কাজের সমাধান নিয়ে এসেছি যা সমস্যার সমাধান করেছে। আমি একটি উত্স হিসাবে আমার প্রকল্পে খুব ছোট, 1x1 পিক্সেল চিত্র যুক্ত করেছি এবং আবর্জনা সংগ্রহের ডাক দেওয়ার আগে এটিকে আমার চিত্রভিউতে লোড করেছি। আমি মনে করি এটি হতে পারে যে চিত্রভিউ বিটম্যাপটি প্রকাশ করছে না, তাই জিসি এটিকে কখনও তুলেনি। এটি কুৎসিত, তবে এটি আপাতত কাজ করছে বলে মনে হচ্ছে।

if (bitmap != null)
{
  bitmap.recycle();
  bitmap = null;
}
if (imageView != null)
{
  imageView.setImageResource(R.drawable.tiny); // This is my 1x1 png.
}
System.gc();

imageView.setImageBitmap(...); // Do whatever you need to do to load the image you want.

চিত্র মতামত দেখে মনে হচ্ছে সত্যই নিজে থেকে বিটম্যাপটি পুনর্ব্যবহার করবেন না। আমাকে সাহায্য করেছে, ধন্যবাদ
দিমিত্রি জায়েটসেভ

@ মাইক আপনি ইমেজলোডারের সম্পূর্ণ কোড যুক্ত করতে পারেন বা অন্যথায় আপনি আমাকে বিটম্যাপ চিত্রগুলি লোড করার লিঙ্কটি দিতে পারেন। আমি যদি বিটম্যাপে পুনর্ব্যবহার করি তবে আমার সমস্ত তালিকাভিউ প্রদর্শিত হবে তবে সমস্ত আইটেমটি ফাঁকা প্রদর্শিত হবে
TNR

@ মাইক আপনি কি বলতে পারবেন আমি আবর্জনা সংগ্রহ করার ক্ষেত্রে কল করার আগে চিত্রভিউ = নালার করা ভাল কিনা?
Youddh

@ টিএনআর আমি মনে করি আপনি এখানে যা অনুপস্থিত তা হ'ল উপরের কোডটিতে bitmapপূর্বের প্রদর্শিত চিত্রটি আপনাকে পুনর্ব্যবহার করতে হবে, এর সাথে কোনও উল্লেখ মুছে ফেলতে হবে, imageViewএকটি ছোট প্রতিস্থাপন সেট করে এটিও ভুলে যেতে হবে gc(); এবং সর্বোপরি: উপরের কোডটিতে আপনার নতুন চিত্রটি এতে bitmapপ্রদর্শন করুন এবং এটি প্রদর্শন করুন ...
TWiStErRob

এটা ভুল. পুনরায় সাইকেল চালানোর আগে বিটম্যাপের আগে আপনার চিত্র দেখার সামগ্রীটি অবশ্যই সাফ করা উচিত (এটি বাস্তবে প্রদর্শিত এবং ব্যবহৃত হওয়ার পরিবর্তে)।
FindOut_Quran

20

এখানে দুর্দান্ত উত্তর, তবে আমি এই সমস্যার সমাধানের জন্য একটি সম্পূর্ণ ব্যবহারযোগ্য ক্লাস চেয়েছিলাম .. সুতরাং আমি একটি করেছি।

এখানে আমার বিটম্যাপহেল্পার ক্লাস যা আউট অফ মেমরিরির প্রুফ :-)

import java.io.File;
import java.io.FileInputStream;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

public class BitmapHelper
{

    //decodes image and scales it to reduce memory consumption
    public static Bitmap decodeFile(File bitmapFile, int requiredWidth, int requiredHeight, boolean quickAndDirty)
    {
        try
        {
            //Decode image size
            BitmapFactory.Options bitmapSizeOptions = new BitmapFactory.Options();
            bitmapSizeOptions.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapSizeOptions);

            // load image using inSampleSize adapted to required image size
            BitmapFactory.Options bitmapDecodeOptions = new BitmapFactory.Options();
            bitmapDecodeOptions.inTempStorage = new byte[16 * 1024];
            bitmapDecodeOptions.inSampleSize = computeInSampleSize(bitmapSizeOptions, requiredWidth, requiredHeight, false);
            bitmapDecodeOptions.inPurgeable = true;
            bitmapDecodeOptions.inDither = !quickAndDirty;
            bitmapDecodeOptions.inPreferredConfig = quickAndDirty ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888;

            Bitmap decodedBitmap = BitmapFactory.decodeStream(new FileInputStream(bitmapFile), null, bitmapDecodeOptions);

            // scale bitmap to mathc required size (and keep aspect ratio)

            float srcWidth = (float) bitmapDecodeOptions.outWidth;
            float srcHeight = (float) bitmapDecodeOptions.outHeight;

            float dstWidth = (float) requiredWidth;
            float dstHeight = (float) requiredHeight;

            float srcAspectRatio = srcWidth / srcHeight;
            float dstAspectRatio = dstWidth / dstHeight;

            // recycleDecodedBitmap is used to know if we must recycle intermediary 'decodedBitmap'
            // (DO NOT recycle it right away: wait for end of bitmap manipulation process to avoid
            // java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@416ee7d8
            // I do not excatly understand why, but this way it's OK

            boolean recycleDecodedBitmap = false;

            Bitmap scaledBitmap = decodedBitmap;
            if (srcAspectRatio < dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) dstWidth, (int) (srcHeight * (dstWidth / srcWidth)));
                // will recycle recycleDecodedBitmap
                recycleDecodedBitmap = true;
            }
            else if (srcAspectRatio > dstAspectRatio)
            {
                scaledBitmap = getScaledBitmap(decodedBitmap, (int) (srcWidth * (dstHeight / srcHeight)), (int) dstHeight);
                recycleDecodedBitmap = true;
            }

            // crop image to match required image size

            int scaledBitmapWidth = scaledBitmap.getWidth();
            int scaledBitmapHeight = scaledBitmap.getHeight();

            Bitmap croppedBitmap = scaledBitmap;

            if (scaledBitmapWidth > requiredWidth)
            {
                int xOffset = (scaledBitmapWidth - requiredWidth) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, xOffset, 0, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }
            else if (scaledBitmapHeight > requiredHeight)
            {
                int yOffset = (scaledBitmapHeight - requiredHeight) / 2;
                croppedBitmap = Bitmap.createBitmap(scaledBitmap, 0, yOffset, requiredWidth, requiredHeight);
                scaledBitmap.recycle();
            }

            if (recycleDecodedBitmap)
            {
                decodedBitmap.recycle();
            }
            decodedBitmap = null;

            scaledBitmap = null;
            return croppedBitmap;
        }
        catch (Exception ex)
        {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * compute powerOf2 or exact scale to be used as {@link BitmapFactory.Options#inSampleSize} value (for subSampling)
     * 
     * @param requiredWidth
     * @param requiredHeight
     * @param powerOf2
     *            weither we want a power of 2 sclae or not
     * @return
     */
    public static int computeInSampleSize(BitmapFactory.Options options, int dstWidth, int dstHeight, boolean powerOf2)
    {
        int inSampleSize = 1;

        // Raw height and width of image
        final int srcHeight = options.outHeight;
        final int srcWidth = options.outWidth;

        if (powerOf2)
        {
            //Find the correct scale value. It should be the power of 2.

            int tmpWidth = srcWidth, tmpHeight = srcHeight;
            while (true)
            {
                if (tmpWidth / 2 < dstWidth || tmpHeight / 2 < dstHeight)
                    break;
                tmpWidth /= 2;
                tmpHeight /= 2;
                inSampleSize *= 2;
            }
        }
        else
        {
            // Calculate ratios of height and width to requested height and width
            final int heightRatio = Math.round((float) srcHeight / (float) dstHeight);
            final int widthRatio = Math.round((float) srcWidth / (float) dstWidth);

            // Choose the smallest ratio as inSampleSize value, this will guarantee
            // a final image with both dimensions larger than or equal to the
            // requested height and width.
            inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
        }

        return inSampleSize;
    }

    public static Bitmap drawableToBitmap(Drawable drawable)
    {
        if (drawable instanceof BitmapDrawable)
        {
            return ((BitmapDrawable) drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }

    public static Bitmap getScaledBitmap(Bitmap bitmap, int newWidth, int newHeight)
    {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;

        // CREATE A MATRIX FOR THE MANIPULATION
        Matrix matrix = new Matrix();
        // RESIZE THE BIT MAP
        matrix.postScale(scaleWidth, scaleHeight);

        // RECREATE THE NEW BITMAP
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        return resizedBitmap;
    }

}

যে কেউ এটি ব্যবহার করে: আমি কেবল একটি বাগ ঠিক করেছি: "# স্কেলডবিটম্যাপহাইট = স্কেলড বিটম্যাপ.জেটভিডথ ();" ভুল (স্পষ্টতই। আমি এটিকে "int স্কেলডবিটম্যাপটাইট = স্কেলড বিটম্যাপ.জেটহাইট ();) দিয়ে প্রতিস্থাপন করেছি"
পাস্কাল

19

এটি আমার পক্ষে কাজ করে।

Bitmap myBitmap;

BitmapFactory.Options options = new BitmapFactory.Options(); 
options.InPurgeable = true;
options.OutHeight = 50;
options.OutWidth = 50;
options.InSampleSize = 4;

File imgFile = new File(filepath);
myBitmap = BitmapFactory.DecodeFile(imgFile.AbsolutePath, options);

এবং এটি সি # মনোড্রয়েডে রয়েছে। আপনি সহজেই চিত্রটির পথ পরিবর্তন করতে পারেন। কী গুরুত্বপূর্ণ এখানে অপশন সেট করা আছে।


16

সম্প্রদায়ের সাথে চিত্রগুলি লোড এবং প্রসেসিংয়ের জন্য আমার ইউটিলিটি ক্লাসটি ভাগ করে নেওয়ার উপযুক্ত স্থানের মতো এটি মনে হচ্ছে, আপনি এটি ব্যবহার করতে এবং অবাধে এটি সংশোধন করতে আপনাকে স্বাগত।

package com.emil;

import java.io.IOException;
import java.io.InputStream;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * A class to load and process images of various sizes from input streams and file paths.
 * 
 * @author Emil http://stackoverflow.com/users/220710/emil
 *
 */
public class ImageProcessing {

    public static Bitmap getBitmap(InputStream stream, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Bitmap getBitmap(String imgPath, int sampleSize, Bitmap.Config bitmapConfig) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForSampling(sampleSize, bitmapConfig);
        Bitmap bm = BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return bm;
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    public static Dimensions getDimensions(InputStream stream) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeStream(stream,null,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using stream.");
        }
    }

    public static Dimensions getDimensions(String imgPath) throws IOException{
        BitmapFactory.Options options=ImageProcessing.getOptionsForDimensions();
        BitmapFactory.decodeFile(imgPath,options);
        if(ImageProcessing.checkDecode(options)){
            return new ImageProcessing.Dimensions(options.outWidth,options.outHeight);
        }else{
            throw new IOException("Image decoding failed, using file path.");
        }
    }

    private static boolean checkDecode(BitmapFactory.Options options){
        // Did decode work?
        if( options.outWidth<0 || options.outHeight<0 ){
            return false;
        }else{
            return true;
        }
    }

    /**
     * Creates a Bitmap that is of the minimum dimensions necessary
     * @param bm
     * @param min
     * @return
     */
    public static Bitmap createMinimalBitmap(Bitmap bm, ImageProcessing.Minimize min){
        int newWidth, newHeight;
        switch(min.type){
        case WIDTH:
            if(bm.getWidth()>min.minWidth){
                newWidth=min.minWidth;
                newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case HEIGHT:
            if(bm.getHeight()>min.minHeight){
                newHeight=min.minHeight;
                newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
            }else{
                // No resize
                newWidth=bm.getWidth();
                newHeight=bm.getHeight();
            }
            break;
        case BOTH: // minimize to the maximum dimension
        case MAX:
            if(bm.getHeight()>bm.getWidth()){
                // Height needs to minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minHeight;
                if(bm.getHeight()>min.minDim){
                    newHeight=min.minDim;
                    newWidth=ImageProcessing.getScaledWidth(newHeight, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }else{
                // Width needs to be minimized
                min.minDim=min.minDim!=null ? min.minDim : min.minWidth;
                if(bm.getWidth()>min.minDim){
                    newWidth=min.minDim;
                    newHeight=ImageProcessing.getScaledHeight(newWidth, bm);
                }else{
                    // No resize
                    newWidth=bm.getWidth();
                    newHeight=bm.getHeight();
                }
            }
            break;
        default:
            // No resize
            newWidth=bm.getWidth();
            newHeight=bm.getHeight();
        }
        return Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
    }

    public static int getScaledWidth(int height, Bitmap bm){
        return (int)(((double)bm.getWidth()/bm.getHeight())*height);
    }

    public static int getScaledHeight(int width, Bitmap bm){
        return (int)(((double)bm.getHeight()/bm.getWidth())*width);
    }

    /**
     * Get the proper sample size to meet minimization restraints
     * @param dim
     * @param min
     * @param multipleOf2 for fastest processing it is recommended that the sample size be a multiple of 2
     * @return
     */
    public static int getSampleSize(ImageProcessing.Dimensions dim, ImageProcessing.Minimize min, boolean multipleOf2){
        switch(min.type){
        case WIDTH:
            return ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
        case HEIGHT:
            return ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
        case BOTH:
            int widthMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.width, min.minWidth, multipleOf2);
            int heightMaxSampleSize=ImageProcessing.getMaxSampleSize(dim.height, min.minHeight, multipleOf2);
            // Return the smaller of the two
            if(widthMaxSampleSize<heightMaxSampleSize){
                return widthMaxSampleSize;
            }else{
                return heightMaxSampleSize;
            }
        case MAX:
            // Find the larger dimension and go bases on that
            if(dim.width>dim.height){
                return ImageProcessing.getMaxSampleSize(dim.width, min.minDim, multipleOf2);
            }else{
                return ImageProcessing.getMaxSampleSize(dim.height, min.minDim, multipleOf2);
            }
        }
        return 1;
    }

    public static int getMaxSampleSize(int dim, int min, boolean multipleOf2){
        int add=multipleOf2 ? 2 : 1;
        int size=0;
        while(min<(dim/(size+add))){
            size+=add;
        }
        size = size==0 ? 1 : size;
        return size;        
    }

    public static class Dimensions {
        int width;
        int height;

        public Dimensions(int width, int height) {
            super();
            this.width = width;
            this.height = height;
        }

        @Override
        public String toString() {
            return width+" x "+height;
        }
    }

    public static class Minimize {
        public enum Type {
            WIDTH,HEIGHT,BOTH,MAX
        }
        Integer minWidth;
        Integer minHeight;
        Integer minDim;
        Type type;

        public Minimize(int min, Type type) {
            super();
            this.type = type;
            switch(type){
            case WIDTH:
                this.minWidth=min;
                break;
            case HEIGHT:
                this.minHeight=min;
                break;
            case BOTH:
                this.minWidth=min;
                this.minHeight=min;
                break;
            case MAX:
                this.minDim=min;
                break;
            }
        }

        public Minimize(int minWidth, int minHeight) {
            super();
            this.type=Type.BOTH;
            this.minWidth = minWidth;
            this.minHeight = minHeight;
        }

    }

    /**
     * Estimates size of Bitmap in bytes depending on dimensions and Bitmap.Config
     * @param width
     * @param height
     * @param config
     * @return
     */
    public static long estimateBitmapBytes(int width, int height, Bitmap.Config config){
        long pixels=width*height;
        switch(config){
        case ALPHA_8: // 1 byte per pixel
            return pixels;
        case ARGB_4444: // 2 bytes per pixel, but depreciated
            return pixels*2;
        case ARGB_8888: // 4 bytes per pixel
            return pixels*4;
        case RGB_565: // 2 bytes per pixel
            return pixels*2;
        default:
            return pixels;
        }
    }

    private static BitmapFactory.Options getOptionsForDimensions(){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds=true;
        return options;
    }

    private static BitmapFactory.Options getOptionsForSampling(int sampleSize, Bitmap.Config bitmapConfig){
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = sampleSize;
        options.inScaled = false;
        options.inPreferredConfig = bitmapConfig;
        return options;
    }
}

16

আমার একটি অ্যাপ্লিকেশনটিতে আমার কাছে থেকে ছবি তোলা দরকার Camera/Gallery। যদি ক্যামেরা থেকে ব্যবহারকারী ক্লিক চিত্র (2 এমপি, 5 এমপি বা 8 এমপি হতে পারে), চিত্রের আকার kBগুলি থেকে আলাদা হয় MB। যদি কোডের উপরের চিত্রের আকার কম (বা 1-2MB অবধি) কম কাজ করে তবে আমার যদি 4MB বা 5MB এর চেয়ে বেশি আকারের চিত্র থাকে তবে OOMফ্রেমে আসে :(

তারপরে আমি এই সমস্যাটি সমাধান করার জন্য কাজ করেছি এবং অবশেষে ফেডারের (নীচে এত ভাল সমাধান দেওয়ার জন্য ফেডারের সমস্ত ক্রেডিট) কোডের উন্নতি করেছি :)

private Bitmap decodeFile(String fPath) {
    // Decode image size
    BitmapFactory.Options opts = new BitmapFactory.Options();
    /*
     * If set to true, the decoder will return null (no bitmap), but the
     * out... fields will still be set, allowing the caller to query the
     * bitmap without having to allocate the memory for its pixels.
     */
    opts.inJustDecodeBounds = true;
    opts.inDither = false; // Disable Dithering mode
    opts.inPurgeable = true; // Tell to gc that whether it needs free
                                // memory, the Bitmap can be cleared
    opts.inInputShareable = true; // Which kind of reference will be used to
                                    // recover the Bitmap data after being
                                    // clear, when it will be used in the
                                    // future

    BitmapFactory.decodeFile(fPath, opts);

    // The new size we want to scale to
    final int REQUIRED_SIZE = 70;

    // Find the correct scale value. 
    int scale = 1;

    if (opts.outHeight > REQUIRED_SIZE || opts.outWidth > REQUIRED_SIZE) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) opts.outHeight
                / (float) REQUIRED_SIZE);
        final int widthRatio = Math.round((float) opts.outWidth
                / (float) REQUIRED_SIZE);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        scale = heightRatio < widthRatio ? heightRatio : widthRatio;//
    }

    // Decode bitmap with inSampleSize set
    opts.inJustDecodeBounds = false;

    opts.inSampleSize = scale;

    Bitmap bm = BitmapFactory.decodeFile(fPath, opts).copy(
            Bitmap.Config.RGB_565, false);

    return bm;

}

আমি আশা করি এটি একই সমস্যার মুখোমুখি বন্ধুদের সহায়তা করবে!

আরও জন্য দয়া করে এটি দেখুন


14

আমি কয়েক মিনিট আগে এই সমস্যাটি নিয়ে ছুটে এসেছি। আমার লিস্টভিউ অ্যাডাপ্টার পরিচালনা করতে আরও ভাল কাজ করে এটি সমাধান করেছি। আমি ভেবেছিলাম যে এটি আমি ব্যবহার করছি শত শত 50x50px চিত্রগুলির সাথে এটি একটি সমস্যা ছিল, দেখা যাচ্ছে যে প্রতিবার সারি দেখানো হচ্ছে তখন আমি আমার কাস্টম ভিউতে উত্সাহিত করার চেষ্টা করছিলাম। কেবল সারিটি স্ফীত হয়েছে কিনা তা পরীক্ষা করেই আমি এই ত্রুটিটি দূর করেছি এবং আমি কয়েকশ বিটম্যাপ ব্যবহার করছি। এটি আসলে কোনও স্পিনারের জন্য, তবে বেস অ্যাডাপ্টার একটি তালিকাভিউয়ের জন্য একই কাজ করে। এই সাধারণ ফিক্সটি অ্যাডাপ্টারের কার্যকারিতাও ব্যাপকভাবে উন্নত করেছে।

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    if(convertView == null){
        LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView = inflater.inflate(R.layout.spinner_row, null);
    }
...

3
আমি এর জন্য আপনাকে যথেষ্ট ধন্যবাদ দিতে পারি না! এটি দেখার আগে আমি ভুল সমস্যার পিছনে ছুটছিলাম। আপনার জন্য প্রশ্ন যদিও: প্রতিটি বা আমার তালিকার সারিগুলির একটি স্বতন্ত্র নাম এবং ফটো রয়েছে তাই সারিগুলির প্রতিটি মান ধরে রাখতে আমাকে একটি রূপান্তর ভিউ অ্যারে ব্যবহার করতে হয়েছিল। আমি দেখতে পেলাম না যে কীভাবে একটি একক ভেরিয়েবল ব্যবহার করা আপনাকে এটি করতে দেয়। আমি কিছু অনুপস্থিত করছি?
পিটএইচ

13

আমি পুরো দিনগুলি এই সমাধানগুলির পরীক্ষার জন্য ব্যয় করেছি এবং একমাত্র জিনিস যা আমার জন্য কাজ করেছিল তা হল চিত্রটি পাওয়ার জন্য এবং ম্যানুয়ালি জিসি কল করার জন্য উপরের পন্থাগুলি, যা আমি জানি যে এটি প্রয়োজনীয় হওয়ার কথা নয়, তবে এটি কেবল কাজ করেছিল যা কাজ করেছিল যখন আমি আমার অ্যাপ্লিকেশনগুলিকে ক্রিয়াকলাপগুলির মধ্যে ভারী লোড টেস্টিং স্যুইচিংয়ের অধীনে রাখি। আমার অ্যাপ্লিকেশনটির তালিকায় থাকা থাম্বনেইল চিত্রগুলির একটি তালিকা রয়েছে (ক্রিয়াকলাপি এটিকে বলুন) এবং যখন আপনি এই চিত্রগুলির একটিতে ক্লিক করেন তখন এটি আপনাকে অন্য ক্রিয়াকলাপে নিয়ে যায় (ক্রিয়াকলাপ বি বলতে দেয়) যা সেই আইটেমটির জন্য একটি মূল চিত্র দেখায়। যখন আমি দুটি ক্রিয়াকলাপের মধ্যে পিছনে পিছনে স্যুইচ করতাম, অবশেষে আমি OOM ত্রুটিটি পেয়ে যাব এবং অ্যাপটি বন্ধ করতে বাধ্য করবে।

আমি যখন তালিকার অর্ধেক পথটি নামতাম তখন এটি ক্র্যাশ হয়ে যায়।

এখন যখন আমি বি ক্রিয়াকলাপে নিম্নলিখিতগুলি প্রয়োগ করি, তখন আমি কোনও তালিকা ছাড়াই পুরো তালিকাভিউতে যেতে পারি এবং যাচ্ছি এবং যাব এবং চালিয়ে যেতে পারি ... এবং এর প্রচুর দ্রুত।

@Override
public void onDestroy()
{   
    Cleanup();
    super.onDestroy();
}

private void Cleanup()
{    
    bitmap.recycle();
    System.gc();
    Runtime.getRuntime().gc();  
}

আপনার সমাধান ভালবাসেন! আমি এই বাগটি সমাধানের কয়েক ঘন্টাও ব্যয় করেছি, তাই বিরক্তিকর! সম্পাদনা: দুঃখের বিষয়, আমি যখন ল্যান্ডস্কেপ মোডে আমার স্ক্রিনের ওরিয়েন্টেশন পরিবর্তন করি তখনও সমস্যাটি বিদ্যমান ...
জারিয়ালন

এটি অবশেষে আমার সাথে সহায়তা করেছিল: - বিটম্যাপফ্যাক্টরি.অপশন বিকল্পগুলি = নতুন বিটম্যাপফ্যাক্টরি.অপশন (); অপশনস.আইএনপুজারেবল = সত্য; বিকল্পগুলি.আইনস্যাম্পল সাইজ = 2;
ব্যবহারকারীর 3833732

13

এই সমস্যাটি কেবল অ্যান্ড্রয়েড এমুলেটরগুলিতেই ঘটে। আমি এমুলেটরটিতেও এই সমস্যার মুখোমুখি হয়েছি কিন্তু যখন আমি কোনও ডিভাইস চেক করেছিলাম তখন এটি ঠিকঠাক কাজ করেছিল।

সুতরাং দয়া করে একটি ডিভাইস চেক করুন। এটি ডিভাইসে চালিত হতে পারে।


12

আমার 2 সেন্ট: আমি বিটম্যাপ দিয়ে আমার OOM ত্রুটিগুলি এর দ্বারা সমাধান করেছি:

ক) 2 এর গুণক দ্বারা আমার চিত্রগুলি স্কেলিং

খ) ব্যবহার করে পিকাসো একটি ListView জন্য আমার কাস্টম অ্যাডাপ্টার মধ্যে গ্রন্থাগার, ভালো getView মধ্যে একটি এক কলের মাধ্যমে:Picasso.with(context).load(R.id.myImage).into(R.id.myImageView);


আপনি পিকাসোর উল্লেখ করেছেন বলে আমি আনন্দিত, কারণ এটি চিত্রগুলি লোড করা আরও সহজ করে তোলে। বিশেষত দূরবর্তী স্থান থেকে সংরক্ষণ করা।
ক্রিসপিক্স

12

এসডিকার্ড থেকে নির্বাচন করে প্রতিটি চিত্রের জন্য এই কোডগুলি ব্যবহার করুন বা বিটম্যাপ অবজেক্টকে রূপান্তর করতে ড্রয়যোগ্য।

Resources res = getResources();
WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
Display display = window.getDefaultDisplay();
@SuppressWarnings("deprecation")
int width = display.getWidth();
@SuppressWarnings("deprecation")
int height = display.getHeight();
try {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    bitmap = Bitmap.createScaledBitmap(BitmapFactory
        .decodeFile(ImageData_Path.get(img_pos).getPath()),
        width, height, true);
} catch (OutOfMemoryError e) {
    if (bitmap != null) {
        bitmap.recycle();
        bitmap = null;
        System.gc();
    }
    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inPreferredConfig = Config.RGB_565;
    options.inSampleSize = 1;
    options.inPurgeable = true;
    bitmapBitmap.createScaledBitmap(BitmapFactory.decodeFile(ImageData_Path.get(img_pos)
        .getPath().toString(), options), width, height,true);
}
return bitmap;

আপনার চিত্রের ডেটা ইমেজডেডিটা_প্যাথ.জেট (img_pos) .getPath () ব্যবহার করুন


12

সাধারণত অ্যান্ড্রয়েড ডিভাইস হিপ সাইজটি কেবলমাত্র 16 এমবি (ডিভাইস / ওএস পোস্টের হিপ আকারগুলি থেকে পৃথক হয় ), আপনি যদি চিত্রগুলি লোড করছেন এবং এটি 16 এমবি এর আকার অতিক্রম করে, এটি লোডিংয়ের জন্য বিটম্যাপ ব্যবহার না করে মেমরির ব্যতিক্রম বাদ দেয় এসডি কার্ড বা সংস্থান থেকে বা এমনকি নেটওয়ার্ক থেকে চিত্রগুলি আইটেমারি ইউরি ব্যবহার করার চেষ্টা করে , বিটম্যাপ লোড করার জন্য আরও মেমরির প্রয়োজন হয়, বা যদি আপনার বিটম্যাপটি দিয়ে কাজ করা হয় তবে আপনি বিটম্যাপটি নালায় সেট করতে পারেন।


1
যদি setImageURI এখনো ব্যতিক্রম পেয়ে তারপর এই পড়ুন stackoverflow.com/questions/15377186/...
মহেশ

11

এখানে সমস্ত সমাধানের জন্য একটি IMAGE_MAX_SIZE সেট করা দরকার। এটি আরও শক্তিশালী হার্ডওয়্যারযুক্ত ডিভাইসগুলিকে সীমাবদ্ধ করে এবং যদি চিত্রের আকার খুব কম হয় তবে এটি এইচডি স্ক্রিনে দেখতে কুৎসিত দেখায়।

আমি এমন একটি সমাধান নিয়ে এসেছি যা আমার স্যামসুং গ্যালাক্সি এস 3 এবং আরও শক্তিশালী ডিভাইস সহ আরও বেশ কয়েকটি ডিভাইসের সাথে কাজ করে, যখন আরও শক্তিশালী ডিভাইস ব্যবহৃত হয় তখন আরও ভাল মানের মানের সহ।

এর সূচনাটি হ'ল কোনও নির্দিষ্ট ডিভাইসে অ্যাপ্লিকেশনটির জন্য বরাদ্দকৃত সর্বাধিক মেমরির গণনা করা, তারপরে এই মেমরিটি অতিক্রম না করে স্কেল সর্বনিম্ন সম্ভব হতে সেট করুন। কোডটি এখানে:

public static Bitmap decodeFile(File f)
{
    Bitmap b = null;
    try
    {
        // Decode image size
        BitmapFactory.Options o = new BitmapFactory.Options();
        o.inJustDecodeBounds = true;

        FileInputStream fis = new FileInputStream(f);
        try
        {
            BitmapFactory.decodeStream(fis, null, o);
        }
        finally
        {
            fis.close();
        }

        // In Samsung Galaxy S3, typically max memory is 64mb
        // Camera max resolution is 3264 x 2448, times 4 to get Bitmap memory of 30.5mb for one bitmap
        // If we use scale of 2, resolution will be halved, 1632 x 1224 and x 4 to get Bitmap memory of 7.62mb
        // We try use 25% memory which equals to 16mb maximum for one bitmap
        long maxMemory = Runtime.getRuntime().maxMemory();
        int maxMemoryForImage = (int) (maxMemory / 100 * 25);

        // Refer to
        // http://developer.android.com/training/displaying-bitmaps/cache-bitmap.html
        // A full screen GridView filled with images on a device with
        // 800x480 resolution would use around 1.5MB (800*480*4 bytes)
        // When bitmap option's inSampleSize doubled, pixel height and
        // weight both reduce in half
        int scale = 1;
        while ((o.outWidth / scale) * (o.outHeight / scale) * 4 > maxMemoryForImage)
        scale *= 2;

        // Decode with inSampleSize
        BitmapFactory.Options o2 = new BitmapFactory.Options();
        o2.inSampleSize = scale;
        fis = new FileInputStream(f);
        try
        {
            b = BitmapFactory.decodeStream(fis, null, o2);
        }
        finally
        {
            fis.close();
        }
    }
    catch (IOException e)
    {
    }
    return b;
}

আমি এই বিটম্যাপটি ব্যবহার করে সর্বাধিক বরাদ্দকৃত মেমরির 25% ব্যবহার করেছি, আপনাকে এটি আপনার প্রয়োজনের সাথে সামঞ্জস্য করতে হবে এবং নিশ্চিত করতে হবে যে এই বিটম্যাপটি পরিষ্কার হয়েছে এবং আপনি যখন এটি ব্যবহার শেষ করেছেন তখন স্মৃতিতে থাকবেন না। সাধারণত আমি এই কোডটি ইমেজ রোটেশন (উত্স এবং গন্তব্য বিটম্যাপ) সম্পাদন করতে ব্যবহার করি যাতে আমার অ্যাপ্লিকেশনটিকে একই সাথে মেমরিতে 2 বিটম্যাপ লোড করা প্রয়োজন এবং চিত্র ঘূর্ণন সম্পাদন করার সময় 25% স্মৃতি থেকে বেরিয়ে না গিয়ে আমাকে একটি ভাল বাফার দেয়।

আশা করছি এটা ওখানে কাওকে সাহায্য করবে..


11

এগুলি OutofMemoryExceptionকল করেও পুরোপুরি সমাধান করা যায় System.gc()না ইত্যাদি।

ক্রিয়াকলাপ জীবন চক্র উল্লেখ করে

ক্রিয়াকলাপের রাজ্যগুলি প্রতিটি প্রক্রিয়াটির মেমরির ব্যবহার এবং প্রতিটি প্রক্রিয়ার অগ্রাধিকারের ওএসের দ্বারা নির্ধারিত হয়।

আপনি ব্যবহৃত প্রতিটি বিটম্যাপ চিত্রের আকার এবং রেজোলিউশন বিবেচনা করতে পারেন। আমি আকার হ্রাস করার প্রস্তাব, কম রেজোলিউশনের পুনরায় নমুনা, গ্যালারীগুলির নকশাকে উল্লেখ করুন (একটি ছোট ছবি পিএনজি এবং একটি আসল চিত্র))


11

এই কোডটি অঙ্কনযোগ্য থেকে বৃহত বিটম্যাপ লোড করতে সহায়তা করবে

public class BitmapUtilsTask extends AsyncTask<Object, Void, Bitmap> {

    Context context;

    public BitmapUtilsTask(Context context) {
        this.context = context;
    }

    /**
     * Loads a bitmap from the specified url.
     * 
     * @param url The location of the bitmap asset
     * @return The bitmap, or null if it could not be loaded
     * @throws IOException
     * @throws MalformedURLException
     */
    public Bitmap getBitmap() throws MalformedURLException, IOException {       

        // Get the source image's dimensions
        int desiredWidth = 1000;
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;

        BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        int srcWidth = options.outWidth;
        int srcHeight = options.outHeight;

        // Only scale if the source is big enough. This code is just trying
        // to fit a image into a certain width.
        if (desiredWidth > srcWidth)
            desiredWidth = srcWidth;

        // Calculate the correct inSampleSize/scale value. This helps reduce
        // memory use. It should be a power of 2
        int inSampleSize = 1;
        while (srcWidth / 2 > desiredWidth) {
            srcWidth /= 2;
            srcHeight /= 2;
            inSampleSize *= 2;
        }
        // Decode with inSampleSize
        options.inJustDecodeBounds = false;
        options.inDither = false;
        options.inSampleSize = inSampleSize;
        options.inScaled = false;
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        options.inPurgeable = true;
        Bitmap sampledSrcBitmap;

        sampledSrcBitmap =  BitmapFactory.decodeResource(context.getResources(), R.drawable.green_background , options);

        return sampledSrcBitmap;
    }

    /**
     * The system calls this to perform work in a worker thread and delivers
     * it the parameters given to AsyncTask.execute()
     */
    @Override
    protected Bitmap doInBackground(Object... item) {
        try { 
          return getBitmap();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

ভাল লাগছে, Async টাস্কের পরিবর্তে কোনও লোডার ব্যবহার করা ভাল কি হবে?
ক্রিসপিক্স

কীভাবে Bitmap.Config.ARGB_565? উচ্চ মানের সমালোচনা না হলে।
হামজেহ সোবহ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.