اذهب إلى المحتوى

السؤال

نشر

السلام عليكم
 لدي في مشروعي وضعت فيه SoundPool من أجل تشغيل بعض التأثيرات الصوتية، وكان كل شيء جاهز، ولكن اردت أن اضيف بعض الأشياء الى مشروعي وهذه  الأشياء تتطلب أن يكون minSDK (اصغر اصدار اندرويد مدعوم) هو SDK 21 ، وكانت لدي هي SDK 16 وعندما قمت بتغييرها في ملف gradle الى 21 حدثت معي مشكلة في SoundPool.
الكود التالي هو كود SoundPool الذي استعمله في مشروعي:

        SoundPool soundPool;//لا بد ان يكون في مكان عام
        int soundClick;//لا بد ان يكون في مكان عام
        
//انشاء اداة soundP
//ool (عادتا ما يكون داخل onCreate)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
            AudioAttributes audioAttributes = new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .build(); 
            
            soundPool = new SoundPool.Builder().setMaxStreams(3)
            .setAudioAttributes(audioAttributes).build(); 
        } else { 
            soundPool = new SoundPool(3 , AudioManager.STREAM_MUSIC, 0);                                               
        }

//تحميل الاداة بالملف الصوتي الذي نريد تشغيله
soundClick = soundPool.load(this, R.raw.click , 1); 
//R.raw.click هو الملف الصوتي في مجلد raw
/*
المشكلة حدثت في هذا السطر
فبعد تتبعي للكود واستعمال try catch وجدت ان الكود يعمل بدون مشاكل الا هذا السطر
ورسالة الخطاء التي تظهر هي
File res/raw/click.wav from drawable resource ID #0x7f080000
/*
    
اما بالنسبة لكود تشغيل الملف الصوتي فهو:
   soundPoolplay(soundClick, 1, 1, 1, 0, 1);
//وهذا الكود عادتا ما يكون داخل حدث النقر على زر
//وهذا الكود يعمل بدون مشاكل ولا يوجد مشكلة الا في الكود الذي تم توضيحه سابقا 

بحثت في النت ووجدت مجموعه من الحلول جربت الكثير منها ولكن بدون فائدة.

ملاحظات:
1) عندما اقوم بتغيير minSDK الى SDK اصغر من 21 مثلا 20 او 19 يعمل الكود بدون مشاكل ، ولا تحدث المشاكل الا اذا وضعته 21 او اكبر.

2) جربت طريقة اخرى لتحميل الملف الصوتي للاداة، اي في الجزء الذي حدثت فيه المشكلة، هذه الطريقة هي:
soundClick.load(getAssets().openFd("move.wav"),0);
// هذه الطريقة تختلف عن السابقة انه يتم وضع الملف الصوتي في المجلد assets
// ولكن بدون فائدة وظهرت رسالة الخطاء هذه
//This file can not be opened as a file descriptor; it is probably compressed

Recommended Posts

  • 0
نشر

SoundPool soundPool;
        int soundClick;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 
            AudioAttributes audioAttributes = new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
            .build(); 
            
            soundPool = new SoundPool.Builder().setMaxStreams(3)
            .setAudioAttributes(audioAttributes).build(); 
        } else { 
            soundPool = new SoundPool(3 , AudioManager.STREAM_MUSIC, 0);                                               
        }

soundClick = soundPool.load(this, R.raw.click , 1); 

   soundPoolplay(soundClick, 1, 1, 1, 0, 1);

  • 1
نشر

المشكلة بسبب الطريقة التي يتعامل بها نظام أندرويد مع ملفات الموارد في الإصدار 21 (SDK 21) والإصدارات الأحدث.

ففي الإصدار 21، قدم نظام أندرويد نظامًا جديدًا للموارد يُسمى Vector Drawables يسمح بتخزين وتحميل الموارد بكفاءة أكبر، وأدخل التغيير أيضًا بعض مشكلات التوافق مع واجهات برمجة التطبيقات القديمة، بما في ذلك فئة SoundPool.

فرسالة الخطأ File res/raw/click.wav from drawable resource ID #0x7f080000 تعني أن النظام يحاول تحميل ملف click.wav كمورد قابل للرسم drawable resource بدلاً من مورد خام raw resource،  وذلك لأن معرّف مورد R.raw.click يتم تفسيره على أنه معرّف مورد قابل للرسم، وهو ليس ما نريده.

عليك استخدام ميثود getResources().openRawResourceFd() بدلاً من soundPool.load(this, R.raw.click, 1)، ويعود الميثود بكائن FileDescriptor يمكن استخدامه لتحميل ملف المورد الخام.

soundClick = soundPool.load(getResources().openRawResourceFd(R.raw.click), 1);

واستخدام فئة AssetFileDescriptor لتحميل ملف المورد الخام، حيث توفر تحميل الملفات من دليل الأصول assets directory:

AssetFileDescriptor afd = getResources().openRawResourceFd(R.raw.click);
soundClick = soundPool.load(afd.getFileDescriptor(), 0, afd.getLength(), 1);

ووضع ملف click.wav في assets directory بدلاً من دليل res/raw، للسماح بتحميل الملف باستخدام فئة AssetManager.

AssetManager assetManager = getAssets();
soundClick = soundPool.load(assetManager.openFd("click.wav"), 0);

 

  • 0
نشر

حياك الله أخي مصطفى، شُكراً على تفاعلك.
جربت الطريقة التي وضحتها ولكن لا تزال تظهر نفس المشكلة.
اعتذر عن الازعاج، ولكن بسبب ضعف الانترنت عندنا، وعدم توفر الشبكة في كل الأماكن لدي اضطر لوضع السؤال ورفعه بدلاً من البحث والتنقل بين عدة مواقع والتجربة للوصول للحل، ولكن الحمد لله بعد عدة محاولات للبحث عندما توفرت لي الشبكة وجدت الحل، بعد تجربة عدد من الحلول التي وجدتها.
خلاصة المشكلة:
انه يتم ضغط الملفات المرفقة مع حزمة apk لتوفير مساحة التخزين كما فهمت من هذا النص:
There is a limitations on opening compressed files in the assets folder. This is because uncompressed files can be directly memory mapped into the processes virtual address space, therefore avoiding needing the same amount of memory again for decompression.
وهذا يسبب مشكلة في قرائتها بالطرق السابقة، ولهذا لا بد من استعمال الأكواد التالية، وكما أسلفت في السؤال فالمشكلة في دالة load() وبالتخصيص في الوسيط الخاص بتحديد الملف الصوتي، فالاكواد التي وضعتها في السؤال لا مشكلة فيها الا في المكان المحدد وحلها كالاتي:

 


AssetFileDescriptor afd = null;
        try {
            //انشاء ملف في الكاش
            File cache = new File(getCacheDir(), "click.wav");
            //التأكد من الملف غير موجود في الكاش، لكي لا يتم انشاءه في كل مرة احتجنا اليه
            if (!cache.exists()) {
                //نسخ ملف الصوت من ملف assets الى الملف الذي تم انشاءه في الكاش        
                //copyInputStreamToFile(getAssets().open("click.wav"), cache);
                //نسخ ملف الصوت من ملف raw الى الملف الذي تم انشاءه في الكاش     
                copyInputStreamToFile(getResources().openRawResource(R.raw.click), cache);
            }
            //قراءة الملف الصوتي من الكاش
            afd = new AssetFileDescriptor(ParcelFileDescriptor.open(cache, ParcelFileDescriptor.MODE_READ_ONLY), 0, AssetFileDescriptor.UNKNOWN_LENGTH);
        } catch (Exception e) {
            new AlertDialog.Builder(this).setMessage(e.getMessage()).show();
            
        }

        //تحميل SoundPool بالملف الصوتي
        sound1 = soundPool.load(afd , 0);
}
// دالة عملها نسخ الملف من مجلد raw او مجلد assets الى المسار المحدد في الوسيط file
    private void copyInputStreamToFile(InputStream in, File file) {
        BufferedOutputStream bfos = null;

        try {
            bfos = new BufferedOutputStream(new FileOutputStream(file));
            byte[] buf = new byte[4096];

            int len;
            while ((len = in.read(buf)) != -1) {
                bfos.write(buf, 0, len);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (bfos != null) {
                    bfos.close();
                }
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }}

 

 

ملاحظات:
1) وجدت الحل في الاجابة على سؤال مشابه لسؤالي على موقع stackoverflow:
https://stackoverflow.com/questions/6186866/java-io-filenotfoundexception-this-file-can-not-be-opened-as-a-file-descriptor

2) قمت بشرح الكود على شكل تعليقات ليسهل فهمه لمن يطلع عليه

2) هذا الحل ينطبق على كل انواع الملفات الموجودة في raw او assets سواء اكانت صوتيه او صور او ملفات نصية ...

انضم إلى النقاش

يمكنك أن تنشر الآن وتسجل لاحقًا. إذا كان لديك حساب، فسجل الدخول الآن لتنشر باسم حسابك.

زائر
أجب على هذا السؤال...

×   لقد أضفت محتوى بخط أو تنسيق مختلف.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   جرى استعادة المحتوى السابق..   امسح المحرر

×   You cannot paste images directly. Upload or insert images from URL.

  • إعلانات

  • تابعنا على



×
×
  • أضف...