همیشه گفتم و میگم که برنامه نویسی صرفا یادگیری چند دستور و تابع و کلاس و ... و استفاده از اونا نیست، برنامه نویسی مفاهیمی داره که باید یک برنامه نویس از اونها آگاهی داشته باشه، مهم نیست با چه زبانی برنامه نویسی می کنیم، اگه مفاهیم برنامه نویسی رو بلد باشیم خیلی راحتتر می تونیم با مشکلات روبرو بشیم و حتی به زبان های دیگر سوئیچ کنیم. چرا یک بار دیگه دارم این حرف ها رو میزنم؟ دیروز در استک آورفلو با سوالی روبرو شدم که مشکلش مفهومی بود و ربطی به زبان برنامه نویسی و سیستمی که براش کد میزد نداشت.

سوال این بود که چرا مدیاپلری که برای اندروید نوشته شده بود آهنگ مشخص شده را اصلا پخش نمی کرد. کد نوشته شده هم بصورت زیر بود:

public class SoundPlayer extends MediaPlayer{

    BookInterface ownerActivity;

    public SoundPlayer(BookInterface act){
        ownerActivity = act;
    }

    public void playSound(Context context, int resId){
        Log.d("Debug", "playSound is called");
        MediaPlayer mp = new MediaPlayer();
        try {
            mp = MediaPlayer.create(context, resId);
            mp.start();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Log.d("Debug","Exception" + e);
            e.printStackTrace();
        }

        mp.setOnCompletionListener(new OnCompletionListener() {

            @Override
            public void onCompletion(MediaPlayer mp) {
                Log.d("Debug", "playSound.onCompletion is called");
                mp.stop();
                mp.release();
                ownerActivity.eventHandler();
            }
        });     
    }
}

برای فراخوانی هم از کد زیر استفاده میشه:

SoundPlayer soundPlayer = new SoundPlayer(BookInterface.this);
soundPlayer.playSound(this, R.raw.vroom);

چیزی که اینجا قصد دارم بیان کنم اصلا ربطی به اندروید و جاوا نداره بلکه یک مفهموم برنامه نویسی ساده است، مفهوم محدوده دستیابی (Scope) در برنامه نویسی، اگر به سطر ۱۱ کد توجه داشته باشید شیء `mp` که از نوع کلاس `MediaPlayer` می باشد ساخته شده است، واین همان کلاسی است که قرار است آهنگ را پخش کند. حال نکته اصلی اینجاست که چرا این شیء درون تابع تعریف شده و این که تعریف این شیء درون تابع چه پیامدی در پی خواهد داشت؟

اگه به مفهموم محدوده دستیابی آگاهی داشته باشید جواب سوال راحت است. مدت زمان پایداری متغییر هایی که درون هر محدوده دستیابی (Scope) تعریف می شوند تا زمان پایداری آن محدوده دستیابی است، یعنی به زبان ساده تر طول عمر متغییر هایی که درون یک تابع تعریف می شود تا زمان اتمام اجرای آن تابع خواهند بود. پس بنابه مفهوم گفته شده پس از اجرای تابع `playSound` متغییر تعریف شده ی `mp` نیز از میان می رود و دیگر آهنگی هرگز پخش نمی شود. طول عمر هر متغییر وابسته به محدوده دستیابی ای دارد که متغییر درون آن تعریف میشود، آگاهی داشتن از محدوده دستیابی ها و زمان بقای هر محدوده یک نکته اساسی در برنامه نویسی است. ولی درصورتی که اگر متغییر `mp` درون کلاس تعریف میشد مشکل حل میشد، زیرا در این صورت طول عمر متغییر برابر طول عمر کلاس `SoundPlayer` خواهد بود و زمان کافی برای پخش آهنگ قبل از پاک شدن از حافظه خواهد داشت.

کد صحیح هم بفرم زیر است، همانطور که می بینید متغییر `mp` درون کلاس تعریف شده نه درون تابع.

public class SoundPlayer extends MediaPlayer{

    BookInterface ownerActivity;
    private MediaPlayer mp;

    public SoundPlayer(BookInterface act){
        ownerActivity = act;
    }

    public void playSound(Context context, int resId){
        Log.d("Debug", "playSound is called");
        try {
            mp = MediaPlayer.create(context, resId);

            mp.setOnCompletionListener(new OnCompletionListener() {

              @Override
              public void onCompletion(MediaPlayer mp) {
                  Log.d("Debug", "playSound.onCompletion is called");
                  mp.stop();
                  mp.release();
                  ownerActivity.eventHandler();
              }
            });

            mp.start();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Log.d("Debug","Exception" + e);
            e.printStackTrace();
        }     
    }
}