美文网首页
Android-MVP使用beta

Android-MVP使用beta

作者: 码农老谭 | 来源:发表于2019-09-30 17:09 被阅读0次

    MVP与MVC之间的区别

    MVC
    • View:对应于布局文件
    • Model:业务逻辑和实体模型
    • Controllor:对应于Activity
    MVP
    • View 对应于Activity,负责View的绘制以及与用户交互
    • Model 依然是业务逻辑和实体模型
    • Presenter 负责完成View于Model间的交互
    MVP模式的作用

    MVP的好处

    • 分离了视图逻辑和业务逻辑,降低了耦合

    • Activity只处理生命周期的任务,代码变得更加简洁

    • 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性

    • Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试

    • 把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM

    其中最重要的有三点:

    Activity 代码变得更加简洁

    相信很多人阅读代码的时候,都是从Activity开始的,对着一个1000+行代码的Activity,看了都觉得难受。

    使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。

    方便进行单元测试

    一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,测试代码已经删掉了!双需要重新再写了……

    MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest。

    避免 Activity 的内存泄露

    Android APP 发生OOM的最大原因就是出现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak))。

    Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而,Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引用的对象,被引用着的对象因为还可能会被调用,所以不能回收。

    Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。

    采用传统的MV模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM。

    采用MVP模式,只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak。

    MVC与MVP最明显的区别就是,MVC中是允许Model和View进行交互的,而MVP中很明显,Model与View之间的交互由Presenter完成。
    还有一点就是Presenter与View之间的交互是通过接口的

    实例代码说明
    • HomeActivity.class
    public class HomeActivity extends AppCompatActivity {
    
        private HomePresenter mAddEditTaskPresenter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.addtask_act);
    
            // Set up the toolbar.
            Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(toolbar);
    
            HomeFragment addEditTaskFragment = (HomeFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.contentFrame);
    
            if (addEditTaskFragment == null) {
                addEditTaskFragment = HomeFragment.newInstance();
                ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
                        addEditTaskFragment, R.id.contentFrame);
            }
    //        // Create the presenter
            mAddEditTaskPresenter = new HomePresenter(addEditTaskFragment);
    
            FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab_edit_task_done);
            fab.setImageResource(R.drawable.ic_logs);
            fab.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Snackbar.make(v, "跳转到LOG页面", Snackbar.LENGTH_LONG).show();
                    startActivity(new Intent(HomeActivity.this, LogActivity.class));
                }
            });
    
        }
    }
    
    • HomeContract.class
    public interface HomeContract {
    
        interface View {
            void showToast(String msg);
        }
    
        interface Presenter{
            void showToast(String msg);
        }
    }
    
    • HomePresenter.class
    public class HomePresenter implements HomeContract.Presenter {
    
    
        @NonNull
        private final HomeContract.View mAddTaskView;
    
        /**
         * Creates a presenter for the add/edit view.
         *
         * @param addTaskView the add/edit view
         */
        public HomePresenter(@NonNull HomeContract.View addTaskView) {
            mAddTaskView = checkNotNull(addTaskView);
            mAddTaskView.setPresenter(this);
        }
    
        @Override
        public void share(String msg) {
            mAddTaskView.showToast(msg);
        }
    }
    
    • HomeFragment.class
    public class HomeFragment extends Fragment implements HomeContract.View {
    
        private HomeContract.Presenter mPresenter;
        @BindView(R.id.button4)
        public Button button4;
    
        public static HomeFragment newInstance() {
            return new HomeFragment();
        }
    
        public HomeFragment() {
    
        }
    
        @Nullable
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View root = inflater.inflate(R.layout.addtask_frag, container, false);
            unbinder = ButterKnife.bind(this, root);
            return root;
        }
    
    
        @Override
        public void setPresenter(@NonNull HomeContract.Presenter presenter) {
            mPresenter = checkNotNull(presenter);
        }
    
        @Override
        public void onResume() {
            super.onResume();
        }
    
        @Override
        public void onDestroyView() {
            super.onDestroyView();
            unbinder.unbind();
        }
    
        @Override
        public void showToast(@NonNull String msg) {
            Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
        }
    
        @OnClick({R.id.button4})
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.button4:
                    mPresenter.showTost("MVP模式显示Tost");
                    break;
            }
        }
    }
    
    • addtask_act.xml
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
    
            <android.support.design.widget.AppBarLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
                <android.support.v7.widget.Toolbar
                    android:id="@+id/toolbar"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:background="?attr/colorPrimary"
                    android:minHeight="?attr/actionBarSize"
                    android:theme="@style/Toolbar"
                    app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
            </android.support.design.widget.AppBarLayout>
    
            <android.support.design.widget.CoordinatorLayout
                android:id="@+id/coordinatorLayout"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
                <FrameLayout
                    android:id="@+id/contentFrame"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" />
    
                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/fab_edit_task_done"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="@dimen/fab_margin"
                    app:srcCompat="@android:drawable/ic_dialog_email"
                    app:fabSize="normal"
                    app:layout_anchor="@id/contentFrame"
                    app:layout_anchorGravity="bottom|right|end" />
            </android.support.design.widget.CoordinatorLayout>
    
        </LinearLayout>
    
    </android.support.v4.widget.DrawerLayout>
    
    • addtask_frag.xml
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        
            <Button
                android:id="@+id/button4"
                android:layout_width="0dp"
                android:layout_height="60dp"
                android:layout_weight="1"
                android:text="屏幕" />
    
    </LinearLayout>
    

    相关文章

      网友评论

          本文标题:Android-MVP使用beta

          本文链接:https://www.haomeiwen.com/subject/bqlhpctx.html