继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Google官方MVP框架源码解读

潇湘沐
关注TA
已关注
手记 104
粉丝 12
获赞 38

最近在看关于MVP框架的封装,于是网上搜了一下,发现了谷歌官方的MVP Demo,发现还是Google的看着顺眼,上图为Demo的框架图。于是就对其分析了一下。Google的MVP版本很多,不过思想都一样,所以就拿当前较火的RxJava那个MVP版本进行分析。

接口类

我们挑其中的一个addedittask模块来看,可以看到里面有个叫AddEditTaskContract的类,这个类是干嘛用的呢? 这就是和普通的MVP结构不同的地方:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

public interface AddEditTaskContract {

    interface View extends BaseView<Presenter> {

        void showEmptyTaskError();

        void showTasksList();

        void setTitle(String title);

        void setDescription(String description);

        boolean isActive();

    }

    interface Presenter extends BasePresenter {

        void saveTask(String title, String description);

        void populateTask();

    }

}

Contract字面意思是契约,这个契约类的作用是把View层和Presenter层的接口包装起来,方便管理,同时也减少了接口文件的数量。BaseView传了个Presenter的泛型进去,我们接下来看看BaseView和BasePresenter:

1

2

3

4

5

public interface BaseView<T> {

    void setPresenter(T presenter);

}

 

1

2

3

4

5

6

7

public interface BasePresenter {

    void subscribe();

    void unsubscribe();

}

BaseView中只有一个方法setPresenter,这个方法的作用是获取Presenter对象,方便在View层使用(如在Activity和Fragment中)。看见BasePresenter中的两个方法,是不是就想到了RxJava里的订阅和解除订阅?没错,在这里还真的是这个意思。

下面,就开始介绍它们的实现类了~

接口的实现

Presenter实现类

首先是实现了AddEditTaskContract.Presenter接口的AddEditTaskPresenter类,先看参数:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

@NonNull

private final TasksDataSource mTasksRepository;

@NonNull

private final AddEditTaskContract.View mAddTaskView;

@NonNull

private final BaseSchedulerProvider   mSchedulerProvider;

@Nullable

private String mTaskId;

@NonNull

private CompositeSubscription mSubscriptions;

其中TasksDataSource是数据源,AddEditTaskContract.View是View对象,BaseSchedulerProvider是订阅提供者(用于线程切换),CompositeSubscription可以理解为RxJava的订阅管理者,在适当的时候解除订阅防止内存泄漏。

然后看构造方法:

1

2

3

4

5

6

7

8

9

10

11

12

public AddEditTaskPresenter(@Nullable String taskId,

                            @NonNull   TasksDataSource tasksRepository,

                            @NonNull   AddEditTaskContract.View addTaskView,

                            @NonNull   BaseSchedulerProvider schedulerProvider) {

    mTaskId = taskId;

    mTasksRepository =   checkNotNull(tasksRepository, "tasksRepository cannot be null!");

    mAddTaskView = checkNotNull(addTaskView, "addTaskView cannot be   null!");

    mSchedulerProvider =   checkNotNull(schedulerProvider, "schedulerProvider cannot be null!");

    mSubscriptions = new CompositeSubscription();

    mAddTaskView.setPresenter(this);

}

构造方法传进来了三个主要的参数,同时初始化了CompositeSubscription。注意看,上面BaseView中提到的setPresenter方法在这里用到了,就是获取当前对象,也就是Presenter对象。至于构造方法中传进来的对象怎么来的,我们等下在View层会详细讲。

剩下的就是接口实现的方法了:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

@Override

public void subscribe() {

    if (mTaskId != null) {

        populateTask();

    }

}

@Override

public void unsubscribe() {

    mSubscriptions.clear();

}

/**

 * 省略部分代码

 */

@Override

public void populateTask() {

    if (mTaskId == null) {

        throw new RuntimeException("populateTask() was called but   task is new.");

    }

    Subscription subscription =   mTasksRepository

            .getTask(mTaskId)

            .subscribeOn(mSchedulerProvider.computation())

              .observeOn(mSchedulerProvider.ui())

            .subscribe(new Observer<Task>() {

                @Override

                public void onCompleted() {

                }

                @Override

                public void onError(Throwable e) {

                    if (mAddTaskView.isActive()) {

                          mAddTaskView.showEmptyTaskError();

                    }

                }

                @Override

                public void onNext(Task task) {

                    if (mAddTaskView.isActive()) {

                          mAddTaskView.setTitle(task.getTitle());

                          mAddTaskView.setDescription(task.getDescription());

                    }

                }

            });

    mSubscriptions.add(subscription);

}

我们主要看subscribe、unsubscribe和populateTask方法。在subscribe方法中进行初始化数据,调用了populateTask方法添加订阅和获取数据。unsubscribe方法中进行解除订阅。在populateTask方法中,返回数据的地方调用了View的接口,将数据传给View层。至此,整个Presenter算是讲完啦。我们接下来看到View层,也就是Activity或者Fragment。

View实现类

先看代码,下面的代码我们只看重要的部分,所以省略了很多,想看完整的可以下载Google官方完整的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

public class AddEditTaskFragment extends Fragment

        implements AddEditTaskContract.View {

    private AddEditTaskContract.Presenter mPresenter;

    /*...*/

    @Override

    public void onResume() {

        super.onResume();

        mPresenter.subscribe();

    }

    @Override

    public void onPause() {

        super.onPause();

        mPresenter.unsubscribe();

    }

    @Override

    public void setPresenter(@NonNull   AddEditTaskContract.Presenter presenter) {

        mPresenter = checkNotNull(presenter);

    }

    /*...*/

    @Override

    public void showEmptyTaskError() {

        Snackbar.make(mTitle,   getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();

    }

    @Override

    public void showTasksList() {

          getActivity().setResult(Activity.RESULT_OK);

        getActivity().finish();

    }

    @Override

    public void setTitle(String title) {

        mTitle.setText(title);

    }

    @Override

    public void setDescription(String description) {

        mDescription.setText(description);

    }

    @Override

    public boolean isActive() {

        return isAdded();

    }

}

首先,AddEditTaskFragment实现了AddEditTaskContract.View接口。然后在onResume和onPause这两个关系生命周期的地方进行了和Presenter的绑定(防止内存泄漏),在setPresenter方法中获取Presenter实例。再看下面一堆的View接口中定义的方法,就是得到数据后用来进行界面操作了啦~

Activity

上面说到AddEditTaskPresenter的构造方法中的参数怎么来的,因为是RxJava的MVP版本,就顺带说一下吧。因为整个工程是多Activity+多Fragment的结构,其实是在Activity中创建了Fragment对象,然后添加到布局中的。我们看一下AddEditTaskActivity的代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

public class AddEditTaskActivity extends AppCompatActivity {

    public static final int REQUEST_ADD_TASK = 1;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.addtask_act);

        /*...*/

        AddEditTaskFragment   addEditTaskFragment =

                (AddEditTaskFragment)   getSupportFragmentManager().findFragmentById(R.id.contentFrame);

        String taskId = null;

        if (addEditTaskFragment == null) {

            addEditTaskFragment =   AddEditTaskFragment.newInstance();

            if (getIntent().hasExtra(AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID))   {

                taskId =   getIntent().getStringExtra(

                          AddEditTaskFragment.ARGUMENT_EDIT_TASK_ID);

                  actionBar.setTitle(R.string.edit_task);

            } else {

                actionBar.setTitle(R.string.add_task);

            }

              ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),

                    addEditTaskFragment,   R.id.contentFrame);

        }

        // Create the presenter

        new AddEditTaskPresenter(

                taskId,

                  Injection.provideTasksRepository(getApplicationContext()),

                addEditTaskFragment,

                  Injection.provideSchedulerProvider());

    }

    /*...*/

}

创建了AddEditTaskFragment对象,然后添加到布局中。最后new了一个AddEditTaskPresenter,这才是重点。到这里,不禁有一个问题,AddEditTaskPresenter构造方法的第二个和第四个参数是哪来的?我们再看Injection类就知道了:

1

2

3

4

5

6

7

8

9

10

11

12

public class Injection {

    public static TasksRepository provideTasksRepository(@NonNull Context context) {

        checkNotNull(context);

        return   TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),

                  TasksLocalDataSource.getInstance(context,   provideSchedulerProvider()));

    }

    public static BaseSchedulerProvider provideSchedulerProvider() {

        return SchedulerProvider.getInstance();

    }

}

文章都快看完了,都说MVP,Model层在哪里呢?看上面代码中的provideTasksRepository方法,其实这就相当于Model层,获取到数据后传给Presenter,只不过用了一个数据源的名字,功能都一样。provideSchedulerProvider方法是获取一个RxJava的线程调度器:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

public class SchedulerProvider implements BaseSchedulerProvider {

    @Nullable

    private static SchedulerProvider INSTANCE;

    // Prevent direct instantiation.

    private SchedulerProvider() {

    }

    public static synchronized SchedulerProvider getInstance() {

        if (INSTANCE == null) {

            INSTANCE = new SchedulerProvider();

        }

        return INSTANCE;

    }

    @Override

    @NonNull

    public Scheduler computation() {

        return Schedulers.computation();

    }

    @Override

    @NonNull

    public Scheduler io() {

        return Schedulers.io();

    }

    @Override

    @NonNull

    public Scheduler ui() {

        return AndroidSchedulers.mainThread();

    }

}

这个类用了懒汉式获取单例(看源码还是有收获的^_^),实现了BaseSchedulerProvider接口:

1

2

3

4

5

6

7

8

9

10

11

public interface BaseSchedulerProvider {

    @NonNull

    Scheduler computation();

    @NonNull

    Scheduler io();

    @NonNull

    Scheduler ui();

}

一共三个线程,计算线程、IO线程、UI线程,完成了对RxJava线程管理的封装使用。

总结

通过这次解读,收益良多。目前来说,MVP确实是个好框架,而且RxJava便捷的线程切换对于在Model层中进行IO操作和Presenter层的UI操作的支持,两者简直是天生一对。

对于Google官方MVP框架的源码解读到这就结束啦,下一篇将对MVP进行封装一下,让这个框架更加实用,同时简化操作,少写代码。敬请期待~

原文链接:http://www.apkbus.com/blog-705730-62887.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP