美文网首页
android 常见架构设计 java实现

android 常见架构设计 java实现

作者: 蜡笔小强要学习 | 来源:发表于2021-04-07 14:49 被阅读0次

    android常用的架构有MVC、MVP、MVVM,他们出现的目的就是为了解决代码乱和多的问题,方便维护和迭代,提高效率。

    本文gitee地址,如果对您有帮忙点个星星,谢谢~

    image.png

    一、MVC

    Model-View-Controller, 职责如下:

    • Model, 模型层,用于获取和存储数据
    • View,视图层,即xml布局
    • Controller,控制层,负责业务逻辑,一般写在Activity
      MainActivity 对应Controller 控制显示的内容,DataCenter对应的是Model负责数据
      结合太紧密,view和Controller 有耦合,这也是它的缺点。
    //这是controller
    public class MainActivity extends AppCompatActivity {
    
        EditText data1View;
        EditText data2View;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            data1View = findViewById(R.id.data1View);
            data2View = findViewById(R.id.data2View);
    
            String[] data = DataCenter.getData();
            data1View.setText(data[0]);
            data2View.setText(data[1]);
        }
    }
    
    

    model

    /**
     * 描述:model
     */
    public class DataCenter {
        public static String[] getData() {
            return new String[] {"Hello","MVC","MVP"};
        }
        public static void setData() {
        }
    }
    
    

    xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <EditText
            android:id="@+id/data1View"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <EditText
            android:id="@+id/data2View"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    
        <Button
            android:id="@+id/saveButton"
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="save"/>
    
    </LinearLayout>
    

    二、MVP

    Model-View-Presenter,职责如下:

    • Model,模型层,用于获取和存储数据
    • View,视图层,即Activity/Fragment
    • Presenter,控制层,负责业务逻辑
      MVP presenter和view彻底分离,和MVC相比就是把Controller抽出来改成Presenter。
      所以MVP解决了MVC的问题:
      View责任明确,逻辑写在Presenter中,而不是Activity中
    /**
     * view
     */
    public class MvpActivity extends AppCompatActivity {
    
        EditText data1View;
        EditText data2View;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            data1View = findViewById(R.id.data1View);
            data2View = findViewById(R.id.data2View);
            //初始化
            new Presenter(this).load();
        }
    
        public void showData(String data1, String data2) {
           data1View.setText(data1);
           data2View.setText(data2);
    }
    }
    

    Presenter

    /**
     * 描述:Presenter
     */
    public class Presenter {
        MvpActivity mvpActivity;//引用
        //初始化加进来
        Presenter(MvpActivity mvpActivity) {
            this.mvpActivity = mvpActivity;
        }
        /**
         * 取数据+更新
         */
        void load() {
            //取数据
            String[] data = DataCenter.getData();
            //更新
            mvpActivity.showData(data[0],data[2]);
        }
    }
    

    直观的MVP结构就完成了,下面进行优化,改成面向接口.
    好处是有二点:

    • 减少了我没有必要知道的信息
    • 增加拓展性,ex:presenter可以对接多种view
      缺点是:
    • 随着项目的变复杂会引入大量的IView、IPresenter接口,代码变复杂。
    • View和Presenter耦合

    总结:

    Presenter

    /**
     * 描述:Presenter
     */
    public class Presenter {
        IView iView;
    
        //初始化加进来
        Presenter(IView mvpActivity) {
            this.iView = mvpActivity;
        }
        /**
         * 取数据+更新
         */
        void load() {
            //取数据
            String[] data = DataCenter.getData();
            //更新
            iView.showData(data[0],data[2]);
        }
    
        interface IView {
            void showData(String data1, String data2);
        }
    }
    

    view

    public class MvpActivity extends AppCompatActivity implements Presenter.IView {
    
        EditText data1View;
        EditText data2View;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            data1View = findViewById(R.id.data1View);
            data2View = findViewById(R.id.data2View);
            //初始化
            new Presenter(this).load();
        }
    
        @Override
        public void showData(String data1, String data2) {
           data1View.setText(data1);
           data2View.setText(data2);
    }
    }
    

    三、MVVM

    Model-View-ViewModel:

    • Model, 模型层,用于获取和存储数据
    • View, 视图,即Activity/Fragment
    • ViewModel, 视图模型,负责业务逻辑

    所以MVVM的本质就是数据驱动,把解耦做的更彻底,viewModel不再持有view,
    View产生事件,使用ViewModel进行逻辑处理后,通知Model更新数据,Model把更新的数据给ViewModel,ViewModel自动通知View更新界面,而不是主动调用View的方法。

    四、Jetpack MVVM

    Google官方推荐的架构

    实战

    4.1 构建界面

    首先创建一个列表页面 MvvmActivity,并且知道页面所需要的数据是,用户信息列表。

    那么 用户信息列表 如何获取呢?根据上面的架构图,就是ViewModel了,所以我们创建 UserListViewModel 继承自 ViewModel,并且把 用户信息列表 以 LiveData呈现。

    public class UserListViewModel extends ViewModel {
        //用户信息
        private MutableLiveData<List<User>> userListLiveData;
        //进条度的显示
        private MutableLiveData<Boolean> loadingLiveData;
    
        public UserListViewModel() {
            userListLiveData = new MutableLiveData<>();
            loadingLiveData = new MutableLiveData<>();
        }
    
        public LiveData<List<User>> getUserListLiveData() {
            return userListLiveData;
        }
        public LiveData<Boolean> getLoadingLiveData() {
            return loadingLiveData;
        }
        ...
    }
    

    LiveData 是一种可观察的数据存储器。应用中的其他组件可以使用此存储器监控对象的更改,而无需在它们之间创建明确且严格的依赖路径。LiveData 组件还遵循应用组件(如 Activity、Fragment 和 Service)的生命周期状态,并包括清理逻辑以防止对象泄漏和过多的内存消耗。

    将 UserListViewModel 中的字段类型更改为 MutableLiveData<List>。现在,更新数据时,系统会通知 UserListActivity。此外,由于此 LiveData 字段具有生命周期感知能力,因此当不再需要引用时,会自动清理它们。

    另外,注意到暴露的获取LiveData的方法 返回的是LiveData类型,即不可变的,而不是MutableLiveData,好处是避免数据在外部被更改。

    现在,我们修改 UserListActivity 以观察数据并更新界面:

    //MvvmActivity.java
    ...
    //观察ViewModel的数据,且此数据 是 View 直接需要的,不需要再做逻辑处理
    private void observeLivaData() {
        mUserListViewModel.getUserListLiveData().observe(this, new Observer<List<User>>() {
            @Override
            public void onChanged(List<User> users) {
                if (users == null) {
                    Toast.makeText(MvvmActivity.this, "获取user失败!", Toast.LENGTH_SHORT).show();
                    return;
                }
                //刷新列表
                mUserAdapter.setNewInstance(users);
            }
        });
    
        mUserListViewModel.getLoadingLiveData().observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(Boolean aBoolean) {
             //显示/隐藏加载进度条
                mProgressBar.setVisibility(aBoolean? View.VISIBLE:View.GONE);
            }
        });
    }
    

    每次更新用户列表信息数据时,系统都会调用 onChanged() 回调并刷新界面,而不需要 ViewModel主动调用View层方法刷新,这就是 数据驱动 了 —— 数据的更改 驱动 View 自动刷新。

    因为LiveData具有生命周期感知能力,这意味着,除非 Activity 处于活跃状态,否则它不会调用 onChanged() 回调。当调用 Activity 的 onDestroy() 方法时,LiveData 还会自动移除观察者。

    另外,我们也没有添加任何逻辑来处理配置更改(例如,用户旋转设备的屏幕)。

    UserListViewModel 会在配置更改后自动恢复,所以一旦创建新的 Activity,它就会接收相同的 ViewModel 实例,并且会立即使用当前的数据调用回调。鉴于 ViewModel 对象应该比它们更新的相应 View 对象存在的时间更长,因此 ViewModel 实现中不得包含对 View 对象的直接引用,包括Context。

    4.2 获取数据

    现在,我们已使用 LiveData 将 UserListViewModel 连接到MvvmActivity,那么如何获取用户个人信息列表数据呢?

    实现 ViewModel 的第一个想法可能是 使用Retrofit/Okhttp调用接口 来获取数据,然后将该数据设置给 LiveData 对象。这种设计行得通,但如果采用这种设计,随着应用的扩大,应用会变得越来越难以维护。这样会使 UserListViewModel 类承担太多的责任,这就违背了单一职责原则。

    ViewModel 会将数据获取过程委派给一个新的模块,即Repository。

    Repository模块会处理数据操作。它们会提供一个干净的 API,以便应用内其余部分也可以轻松获取该数据。数据更新时,它们知道从何处获取数据以及进行哪些 API 调用。您可以将Repository视为不同数据源(如持久性模型、网络服务和缓存)之间的媒介。

    public class UserRepository {
    
    private static UserRepository mUserRepository;
    public static UserRepository getUserRepository(){
        if (mUserRepository == null) {
            mUserRepository = new UserRepository();
        }
        return mUserRepository;
    }
    
    //(假装)从服务端获取
    public void getUsersFromServer(Callback<List<User>> callback){
        new AsyncTask<Void, Void, List<User>>() {
            @Override
            protected void onPostExecute(List<User> users) {
                callback.onSuccess(users);
                //存本地数据库
                saveUsersToLocal(users);
            }
            @Override
            protected List<User> doInBackground(Void... voids) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //假装从服务端获取的
                List<User> users = new ArrayList<>();
                for (int i = 0; i < 20; i++) {
                    User user = new User("user"+i, i);
                    users.add(user);
                }
                return users;
            }
        }.execute();
    }
    

    虽然Repository模块看起来不必要,但它起着一项重要的作用:它会从应用的其余部分中提取数据源。现在,UserListViewModel 是不知道数据来源的,因此我们可以为ViewModel提供从几个不同的数据源获取数据。

    4.3 连接 ViewModel 与存储区

    我们在UserListViewModel 提供一个方法,用户Activity获取用户信息。此方法就是调用Repository来执行,并且把数据设置到LiveData。

    public class UserListViewModel extends ViewModel {
        //用户信息
        private MutableLiveData<List<User>> userListLiveData;
        //进条度的显示
        private MutableLiveData<Boolean> loadingLiveData;
    
        public UserListViewModel() {
            userListLiveData = new MutableLiveData<>();
            loadingLiveData = new MutableLiveData<>();
        }
    
        /**
         * 获取用户列表信息
         * 假装网络请求 2s后 返回用户信息
         */
        public void getUserInfo() {
    
            loadingLiveData.setValue(true);
    
            UserRepository.getUserRepository().getUsersFromServer(new Callback<List<User>>() {
                @Override
                public void onSuccess(List<User> users) {
                    loadingLiveData.setValue(false);
                    userListLiveData.setValue(users);
                }
    
                @Override
                public void onFailed(String msg) {
                    loadingLiveData.setValue(false);
                    userListLiveData.setValue(null);
                }
            });
        }
    
        //返回LiveData类型
        public LiveData<List<User>> getUserListLiveData() {
            return userListLiveData;
        }
        public LiveData<Boolean> getLoadingLiveData() {
            return loadingLiveData;
        }
    }
    

    在Activity中,就要获取UserListViewModel实例,获取用户信息:

    public class MvvmActivity extends AppCompatActivity {
    private UserListViewModel mUserListViewModel;
    private ProgressBar mProgressBar;
    private RecyclerView mRvUserList;
    private UserAdapter mUserAdapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_list);
    
        initView();
        initViewModel();
        getData();
        observeLivaData();
    }
    private void initView() {...}
    
    private void initViewModel() {
        ViewModelProvider viewModelProvider = new ViewModelProvider(this);
        mUserListViewModel = viewModelProvider.get(UserListViewModel.class);
    }
    
    /**
     * 获取数据,调用ViewModel的方法获取
     */
    private void getData() {
        mUserListViewModel.getUserInfo();
    }
    
    private void observeLivaData() {...}
    

    4.4 缓存数据

    现在UserRepository 有个问题是,它从后端获取数据后,不会将缓存该数据。因此,如果用户在离开页面后再返回,则应用必须重新获取数据,即使数据未发生更改也是如此。这就浪费了宝贵的网络资源,迫使用户等待新的查询完成。所以,我们向 UserRepository 添加了一个新的数据源,本地缓存。缓存实现 可以是 数据库、SharedPreferences等持久化技术。(具体实现就不再写了)

      //从本地数据库获取
        public void getUsersFromLocal(){
        }
    
        //存入本地数据库 (从服务端获取数据后可以调用)
        private void saveUsersToLocal(List<User> users){
        }
    

    至此,Jetpack Mvvm 就介绍完了。

    相关文章

      网友评论

          本文标题:android 常见架构设计 java实现

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