MVP اندروید برای مبتدیان

جمعه ۲۸ اردیبهشت ۹۷ توسط فاطمه بهاروند

ما یک برنامه ی پخش فیلم داریم که به عنوان یک پروژه ی سرگرمی آخر هفته طراحی شده است و قادر به انجام اعمالی همچون حرکت آهسته، معکوس کردن و بزرگنمایی(zoom) است.

قسمت ویو(view) و لیست برنامه با کمترین میزان توجه بر طراحی و کدنویسی انجام شد اما بعد از افزودن ویژگی های جدید این کم کاری باعث ناراحتی و شرم ما شد. چندین کرش(crash) را در برنامه مشاهده کردیم و تنها راه رهایی از این کرش ها اضافه نمودن try-catch به کد بود.

زمانی که می خواهیم ویدئو لیست بخشی از برنامه ی ما شود می بایست برنامه عاری از کرش بوده و گسترش و پشتیبانی از آن بدون اشکال باشد. من مجبور شدم به یک الگوی طراحی مراجعه کنم که آن الگو Model View Presenter بود.

من هرگز به صورت حرفه ای در اندروید کد ننوشته ام. بنابراین تمام توضیحات زیر را از دیدگاه افراد مبتدی می توان نگریست.

این مقاله پیاده سازی ایده هایی است که از مقاله ی زیر آموخته ام. برای درک بهتر مفاهیم لطفا مقاله ی زیر را مطالعه کنید.

معماری MVP و MVC در اندروید - قسمت اول

 

طرح برای برنامه ای با الگوی MVP

طرح برای برنامه ای با الگوی MVP

 

توصیف MVP

مانند بسیاری از الگوهای معماری، MVP برای انواع و اقسام مختلف آزمایشات مهیا شده است و پیاده سازی آن می تواند مبهم باشد. بنابراین شما می توانید پیاده سازی خود را بر اساس نیاز تغییر دهید تا زمانی که اجرای شما با اهداف زیر مطابقت داشته باشد.

  1. جدا کردن کد View که ما آن را به کاربر نمایش می دهیم مانند list، buttons، labels، textbox وغیره... از منطق کاری است که کاربر در تعامل با آن بوده و ما آن را Presenter می نامیم. داده هایی که ما در View می بینیم باید توسط یک ماژول جداگانه دیگر ارائه شود که آن را به عنوان Model معرفی می کنیم. در ازای آن کدی خوانا، با مفهوم و قابل پشتیبانی داریم.
  2. ارتباط بین View و Model باید از طریق Presenter انجام شود برای مثال View و Model نمی توانند مرجع یکدیگر باشند.
  3. با استفاده از این الگوی طراحی ما باید قادر به اعمال تغییرات در یک کامپوننت (Component) بدون نیاز به تغییر کد در دو کامپوننت(Components) دیگر باشیم. همچنین در این مورد ما خواهان جایگزینی کامل View و Model با یک پیاده سازی دیگریم که باید به درستی قابل انجام باشد. برای مثال شما می توانید با استفاده از Content Provider لیست فیلم ها را دریافت کنید و روز بعد بدون تغییر هیچ قسمتی آن را با دیتابیس sqLite جایگزین کنید.
  4. کدهایی را که می توان با استفاده از automation تست کرد را به حداکثر برسانید.

Android Activity و MVP

چارچوب اندروید شرایط بسیار ساده ای را برای ظهور و گسترش هر ایده ای که در ذهن وجود دارد فراهم آورده است، اما این ساختار کد خیلی زود با تعداد زیادی اسپاگتی کد(منظور از اسپاگتی کد در کدنویسی پیچیدگی و آشفتگی کدهاست به گونه ای که بعد از مدتی درک ساختار برنامه را دچار مشکل می کند) و کلاس ها با ویژگی های مختلف ترکیب شده و به یک کلاس اکتیویتی یا فرگمنت(activity/fragment) غیرقابل کنترل تبدیل می شود.

در حال حاضر Activity اندروید قابلیت های View خود را از طریق مواردی مانند ()setContentView در ()OnCreate نمایش می دهد. به طور همزمان lifecyle activity اندروید نظیر ()OnCreate() ،OnResume و غیره... را که با منطق کاری Presenter همراه است حفظ می کند.

یک نمونه ی ساده ی دیگر برای نمایش چگونگی ترکیب آن ها

  1. ثبت listener ها برای تعامل کاربر با UI برنامه و View
  2. انجام مقدمات در پاسخ به تعاملات کاربر با UI برنامه و منطق کاری

اکثر شما احتمالا با این موضوع موافق باشید که اشکال زدایی یک activity code با چندین خط کد، نگاشت کامپوننت های UI و در عین حال داشتن چند خط کد از منطق کاری در برنامه سبب ناراحتی و عذاب بزرگی می شود.

در حال حاضر بر اساس اصل مسئولیت واحد(Single Responsibility Principle) ما بایدactivity را در نقش یک View یا Presenter در نظر بگیریم. من تصمیم گرفتم تا از activity به عنوان یک Presenter استفاده کنم چرا که activity دارای محتواست. این امر شرایطی را فراهم می آورد تا activity یک انتخاب بدیهی برای حفظ مسئولیت منطق کاری باشد. از تمام این مسائل که بگذریم می خواهیم View را به عنوان یک ماژول که تنها قابلیت بارگذاری عناصر UI را دارد در نظر بگیریم. برای دریافت اطلاعات بیشتر در مورد این موضوع و علت این امر که چرا activity ها از عناصر UI نیستند این مقاله را ملاحظه کنید.

پیاده سازی MVP در برنامه ی من

کد کامل این برنامه در github قابل دسترسی است. لطفا به android- video-listing-mvp مراجعه کنید.

من از طریق کد برنامه ی خود تلاش خواهم کرد تا چگونگی بررسی هرکدام از موارد اهداف MVP را برای شما شرح دهم.

Model

این بخش از برنامه آسانترین قسمت برای پیاده سازی بود. ما از ContentProvider که توسط اندروید فراهم شده است به منظور دریافت لیست ویدئوها استفاده نموده ایم. از آنجایی که ContentProvider توسط اندروید ارائه شده است از محدوده ی موارد قابل بررسی خارج است، بنابراین ما به جز چند خط کد برای راه اندازی contentprovider و query آن برای دریافت لیست فیلم ها به مورد دیگر نیازی نداریم. شما می توانید برای اطلاعات بیشتر به VideoListManagerImpl.java مراجعه کنید. LoaderManger.LoaderCallbacks به جهت دریافت خودکار به روزرسانی های(update) لیست فیلم ها استفاده شده است.

در صورتی که ما در آینده قصد جایگزینی آن با دیتابیس SQLite را داشته باشیم(فرمت فایل های .mov بازگشت پذیر نیستند)، از آنجایی که فرمت MVP ساخت یافته است این جایگزینی را می توان با حداقل اختلاف انجام داد.

بیاید چگونگی جا به جایی را در قالب یک نمونه ببینیم

تعامل ContentProvider با Model  توسط یک کلاس مدیر(Manager Class) جداگانه انجام می شود. این کلاس مدیر(Manager Class) طبق قرارداد VideoListManager را دنبال می کند.

برخی می توانند استدلال کنند که کلاس مدیر(Manager Class) متعلق به Model است اما به نظر من کلاس مدیر بخشی از Presenter به حساب می آید، لیکن تا زمانی که کد به طور کامل جدا شده باشد با کسانی که بر این عقیده اند کاملا مخالف نیستم.

public interface VideoListManager {

    interface VideoListManagerListener {
        void onVideoListUpdate(VideoListInfo videoListInfo);
    }

    void getVideosWithNewSorting(int sortType);
    void registerListener(VideoListManagerListener videoListManagerListener);

    void unRegisterListener();

}

جایگزینی ContentProvider با sqLite تا زمانی که Manager Class ، واسط یا Interface بالا را (طبق قرارداد من) اجرا کند و همچنین Presenter را با لیستی از ویدئوها در فرمت مورد نیاز Interface به روز کند کار نسبتا آسانی خواهد بود و به تغییر هیچ کدی در هیچ جا حتی در کد من نیازی نیست.

View

ما فیلم ها را با استفاده از ListView و ExpandableListView در یک tablayout نمایش می دهیم. ما از رابط ViewMvp که View اصلی را برایPresenter بر می گرداند استفاده کردیم و از این طریق برای ارتباط بین presenter و View به منظور تعاملات کاربر بهره بردیم.

/*
  All Mvp Views must implement this interface
  It is a dumb View whose role is to present the view to the user
  and return the view to the presenter
  also handling the logic for communication between the presenter
 */

public interface ViewMvp {
    /*
    Get the root android view which is used by android mvp to present the view to the user
    this rootview is then used by the presenter
    to modify the root view or its child views
     */
    View getRootView();
    
    /*
    This method aggravates all the information about the mvp view
    which can be used by presenter 
    by using the saved states in scenarios of android life cycle events
     */
    Bundle getViewState();
}

واسط ViewMvp که در بالا توضیح داده شد یک واسط عمومی است و به عنوان 4 ویو(view) که در نسخه ی نمایشی به هم پیوسته اند به قابلیت های بیشتری از قبیل بازگشت به ListView و... نیاز دارد، اما از آن ها با عنوان زیر مجموعه ی ViewMvp یاد کرده ایم برای کسب درک بهتر پیاده سازی VideoListFragmentView را ملاحظه کنید.

برخلاف دیگر مقالات MVP شما میتوانید کد اعمال شده در یک برنامه ی کاربردی واقعی با پیچیدگی هایی نظیر ارائه ی انیمیشن در حین حرکت(swipes)، ویژگی هایی مانند جست و جو، مرتب سازی و ایجاد ارتباط بین فرگمنت ها و activity را در این مقاله ببینید.

این رویکرد ساختن یک View دارای UI که هیچ منطقی در آن نیست به نمایش گذاشته و از طریق به کارگیری یک Interface پیاده سازی شده است، همچنین با استفاده از موارد زیر نیز به ما کمک می کند.

  • استفاده ی مجدد از UI
  • تست واحد از زمانی که ما یک رابط را اجرا می کنیم
  • هرگونه تغییر در UI پیش بینی می شود خطای دید کمتری داشته باشد.

Adapters: از آنجایی که ما از ListView استفاده می کنیم باید آن را با استفاده از Adapter اجرا کنیم. ما از الگوی ViewHolder pattern مستند شده استفاده می کنیم که برای نمایش عناصر لیست در ListView به خوبی کار می کند. پیاده سازی Adapter را در اینجا بررسی کنید.

  @Override
    public void bindVideoList(VideoListInfo videoListInfo)
    {
        mVideoListAdapter.bindVideoList(videoListInfo);
        mVideoListAdapter.notifyDataSetChanged();
    }

    @Override
    public View getRootView() {
        return mFragemntVideoListView;
    }

bindVideoList: تمام View هایی که برای به روز رسانی لیست فیلم، اضافه کردن فیلم، حذف، جست و جو و مرتب سازی استفاده می شوند bindVideoList را اجرا می کنند.

Presenter

برنامه نویس هسته ی اصلی برنامه است و View و Model را از ابتدا پیاده سازی می کند، همچنین چارچوب ارتباطی را برای مدیریت تعامل با کاربر و تغییرات داده مدل ایجاد می کند. منطق جست و جو، مرتب سازی، به اشتراک گذاری فیلم، حذف، تغییرنام و آنچه که کاربر در زمان انتخاب فیلم با آن مواجه می شود، تمام منطق کار در این حیطه قرار می گیرد. فهرستی از ویژگی های Presenter بدین شرح است:

  • تعامل با سیستم عامل
  • رسیدگی به ورودی/درخواست کابر و ایجاد نتیجه
  • بررسی ارتباط بین View و Model
  • حفظ وضعیت برنامه
  • نگهداری و حفظ منطق کاری برنامه

Presenter شامل تمام موارد به غیر از جزئیات پیاده سازی View و همچنین جزئیات مکانیزم ذخیره سازی داده در Model می شود.

Presenter تنها به قراردادی که ما با استفاده از رابط ها اعمال می کنیم بستگی دارد.

در زیر یک مثال از نحوه کدنویسی View و Model درمتد ()onCreate ارائه شده است.

VideoListingViewImpl mVideoListingViewImpl;
VideoListManagerImpl mVideoListManagerImpl;


protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mVideoListingViewImpl = new VideoListingViewImpl(this, null); //initializing the view

        setContentView(mVideoListingViewImpl.getRootView()); //setting the setContentView from the rootView returned

        mViewPager = mVideoListingViewImpl.getViewPager();

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
        mSortingType = settings.getInt(SORT_TYPE_PREFERENCE_KEY, 3);
        mVideoListManagerImpl = new VideoListManagerImpl(this, mSortingType);// initialising the model
        mVideoListManagerImpl.registerListener(this); // registering the listerner to get video list update events
    }

همانطور که در بالا ذکر شد Activity و Fragment نقش Presenter را در برمی گیرند. نقش Presenter بیشتر مدیریت تعامل با مدل از طریق Manager class است.

به پیاده سازی Presenter از طریق این لینک مراجعه کنید.

دیگر ویژگی های فوق العاده ی برنامه

  1. این برنامه از Fragment ها برای نمایش tab ها به منظور نمایش انواع مختلف View از لیست فیلم ها استفاده می کند. ما برای چگونگی نمایش ارتباط بین fragment و activity با استفاده از Interface و fragment attach به پیاده سازی در onAttach همچنین فراخوانی Interface برقراری ارتباط با Activity از روش توصیه شده در مقالات توسعه اندروید استفاده کردیم.
  2. این برنامه دارای قابلیت scrollup Parallax tab است.
  3. قابلیت هایی نظیر تغییرنام، حذف و اشتراک گذاری فیلم ها
  4. مکانیزم مرتب سازی کارآمد و آسان است
  5. پیاده سازی برای دریافت Load Thumbnail Bitmaps و استفاده از LRU and Disk Caching

پیشنهاد میکنم که git repository من را برای جزئیات کامل پیاده سازی لیست فیلم های اندروید در MVP مشاهده کنید. این پیاده سازی یک نمونه کد ساده، مرتب، کارآمد و بسیار ساده و گسترش پذیر از عملکرد لیست ویدئو در اندروید است.

 


کلیدواژه: اندروید android MVP معماری MVP در اندروید

منابع: android.jlelse.eu

دیدگاه ها:
۱ سال قبل
reply
سلام چرا در مدل mvp از presenter استفاده می کنیم مگر نمیشه مستقیم ویو با مدل ارتباط برقرار کنه ؟ دیگه چه نیازی به پرزنتر به عنوان واسطه داریم
۱ سال قبل در پاسخ به kamal
reply
سلام
در MVC ویو و مدل با هم در ارتباطند و ویو به وسیله ی خود مدل از تغییرات آگاه میشه اما در MVP به این صورت نیست، مدل و ویو اطلاعی از هم ندارند و این وظیفه ی presenter که اطلاعات رو از مدل گرفته و ویو رو به روزرسانی کنه، به همین دلیل وجود presenter الزامی میشه.
۱ سال قبل
reply
اینترفیس کجای mvp استفاده میشه؟
آیا منظور اینترفیس اینجا یعنی برای هرکاری که خواستیم انجام دهیم یکی تابع در اینترفیس تعریف کنیم و این تابع را در کلاس های مدل و ویو و پرزنتر برای هرکدوم override کنیم؟
ارسال دیدگاه:
برای ارسال دیگاه باید به سیستم وارد شوید و یا ثبت نام کنید. ثبت نام چند لحظه بیشتر زمان شما را نمیگیرد.