الگوی معماری MVVM در اندروید

پیاده سازی معماری MVVM در اندروید – بخش دوم


در مطلب قبلی به شرح الگوی معماری MVVM پرداخته شد و در این مطلب قصد داریم به نحوه پیاده سازی آن بپردازیم، با ما همراه باشید.

الگوی معماری MVVM در اندروید

همانطور که در نمودار مشاهده می کنید، View نه تنها دستورات، بلکه چرخه عمر (Life cycle) خود را هم برای ViewModel ارسال می کند. زیرا این عملیاتی است که باید توسط کاربر آغاز شود و به خاطر اقدامات کاربر وضعیت صفحه نمایش تغییر پیدا می کند. لازم است فراخوانی های مناسبی به VM داشته باشیم.

به عنوان نمونه ممکن است لازم باشد هر بار که کاربر به اکتیویتی برمی گردد اطلاعاتی را دانلود کنید، برای این کار باید متد دانلود داده را برای ()onResume فراخوانی کنید.

ProfileActivity را تغییر می دهیم:

private ProfileViewModel viewModel;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

ActivityProfileBinding binding = DataBindingUtil.setContentView(this, LAYOUT_ACTIVITY);

viewModel = new ProfileViewModel(this);

binding.setViewModel(viewModel);

}

@Override

protected void onResume() {

super.onResume();

viewModel.onResume();

}

و همان متد را در ProfileViewModel تعریف می کنیم:

public void onResume() {

isLoading.set(this.user.get() == null);

userRepo.getUser(this::onUserLoaded);

}

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

همین کار را باید با ساید متدها نیز انجام داد. البته با هر بار ساخت VM تعیین این موضوع دشوار است، لذا باید این منطق را برای کلاس های اصلی پیاده سازی کرد که BindingActivity و ActivityViewModel نام دارند:

public abstract class BindingActivity extends AppCompatActivity {

@Override

protected void onStart() {

super.onStart();

viewModel.onStart();

}

@Override

protected void onActivityResult(int requestCode, int resultCode, Intent data) {

super.onActivityResult(requestCode, resultCode, data);

viewModel.onActivityResult(requestCode, resultCode, data);

}

@Override

protected void onResume() {

super.onResume();

viewModel.onResume();

}

@Override

public void onBackPressed() {

if (!viewModel.onBackKeyPress()) {

super.onBackPressed();

}

}

//….other methods

}

public abstract class ActivityViewModel extends BaseObservable {

public void onStart() {

//Override me!

}

public void onActivityResult(int requestCode, int resultCode, Intent data) {

//Override me!

}

public void onResume() {

//Override me!

}

public void onBackPressed() {

//Override me!

}

//….other methods

}

در این مرحله تنها کار لازم اورراید کردن متدهای مناسب برای واکنش به تغییرات خاص می باشد.

با هر بار ساخت یک اکتیویتی، نیازی به ساخت bindings و VM connection نمی باشد و این منطق را می توان برای کلاس اصلی اما با تغییر متد ()onCreate پیاده سازی کرد. همین کار را هنگام ساخت اکتیویتی برای VM نیز انجام می دهیم و چندین متد انتزاعی برای پارامترهای لازم می افزاییم:

private AppCompatActivity binding;

private ActivityViewModel viewModel;

public abstract ActivityViewModel onCreate();

public abstract @IdRes int getVariable();

public abstract @LayoutRes int getLayoutId();

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

bind();

}

public void bind() {

binding = DataBindingUtil.setContentView(this, getLayoutId());

this.viewModel = viewModel == null ? onCreate() : viewModel;

binding.setVariable(getVariable(), viewModel);

binding.executePendingBindings();

}

تنها کار باقی مانده ساخت یک کلاس اصلی و پایه برای ActivityViewModel می باشد، تنها باید یک کپی از اکتیوتی اضافه کنید و intent بسازید:

public abstract class ActivityViewModel extends BaseObservable {

protected Activity activity;

public ActivityViewModel(Activity activity) {

this.activity = activity;

}

public Activity getActivity() {

return activity;

}

//...lifecycle methods

}

کار با اکتیویتی ها تمام شد و هم اکنون ابزار لازم برای شرح منطق را در اختیار داریم. ViewModel و binding در اکتیوتی اصلی به صورت ضمنی تایپ شده اند و همین امر کار با آنها را پیچیده می کند و هر بار محبور به دریافت نوع آنها هستید، لذا باید کلاس ها را به صورت زیر خلاصه بندی کنیم:

public abstract class BindingActivity<B extends ViewDataBinding, VM extends ActivityViewModel> extends AppCompatActivity {

private B binding;

private VM viewModel;

public B getBinding() {

return binding;

}

}

public abstract class ActivityViewModel<A extends AppCompatActivity>

extends BaseObservable {

protected A activity;

public ActivityViewModel(A activity) {

this.activity = activity;

}

public A getActivity() {

return activity;

}

}

با این اقدامات این کلاس اکتیویتی نتیجه خواهد شد:

public class ProfileActivity

extends BindingActivity<ActivityProfileBinding, ProfileViewModel> {

@Override

public ProfileViewModel onCreate() {

return new ProfileViewModel(this);

}

@Override

public int getVariable() {

return BR.viewModel;

}

@Override

public int getLayoutResources() {

return R.layout.activity_profile;

}

}

()getVariable باید نام متغیر را برگرداند که در تگ data-variable در فایل XML تعیین شده است، ()getLayout هم باید همان xml را برگرداند. شایان ذکر است که ProfileViewModel باید ویژگی های خود را از ActivityViewModel به ارث ببرد.

پیاده سازی این کلاس ها برای فرگمنت ها کمی متفاوت بوده و جزئیات آن در این مقاله مورد بررسی قرار نمی گیرد، زیرا مفهوم کلی برای تمامی آنها مشابه است.

در مطلب بعدی به شرح مثال های برای پیاده سازی الگوی MVVM می پردازیم، با ما همراه باشید.

 

https://stfalcon.com برگرفته از

اینها را هم بخوانید