【Android入门】MVVM + LiveData & 乱入的butterKnife

1. MVVM 架构

分工

  • View 层对应 XML 中的静态UI、 Activity 和 Fragment 中设置显示内容的部分,不处理业务逻辑,不操作或处理数据
  • ViewModel 层只处理业务逻辑和操作处理业务数据,不处理UI相关,简单的事件封装成 Command 处理(?),完成 View 与 Model 间的交互
  • Model 层执行数据获取、储存、 监听状态变化等,是数据的实体模型

2. ViewMode 与 LiveData

以下示例代码搬运自android 开发者官方网站

ViewMode

  • 按生命周期存储和管理与ui相关的数据,允许数据在配置更改(如屏幕旋转)中存活
  • 通常情况下,关闭一个 activity 或 fragment 将同时销毁保存在其中的数据,ViewMode实现数据与UI进程同步创建或销毁
  • 不必保证数据为可序列化
  • 可以保持较大体积的数据

LiveData

可观测的数据持有类。它是生命周期感知的,确保 LiveData 只观测并更新处于活动生命周期状态的应用程序组件

MutableLiveData

LiveData 的子类,包含 postValue(T value) 方法和 setValue(T value) 方法,其中 setValue 在主线程中直接调用赋值, postValue 在子线程中将数据传递到主线程

public class MyViewModel extends ViewModel {
    private MutableLiveData> users; //储存并修改数据
    public LiveData> getUsers() {
        if (users == null) {
            users = new MutableLiveData>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, new Observer () {

            @Override
            public void onChanged(@Nullable users entity) {
                // update UI
            }
        });
    }
}

getUsers() 获取 LiveData 对象,设置 observe ,在 onChanged 中更新UI

Transformations

用于修改LiveData观察对象的值或类型,包含 map(LiveData source, Function

 LiveData userLiveData = ...;
 LiveData userName = Transformations.map(userLiveData, user -> {
      return user.firstName + " " + user.lastName
 });

观察userName这个LiveData对象,从userLiveData数据中提取姓名并传递给它的观察者

  • switchMap 方法
 MutableLiveData userIdLiveData = ...;
 LiveData userLiveData = Transformations.switchMap(userIdLiveData, new Function() {
     @Override
     public LiveData apply(final LiveData id) {
         repository.getUserById(id);
     }
 });

 void setUserId(String userId) {
      this.userIdLiveData.setValue(userId);
 }

返回观察者的是一个LiveData对象,可以方便的转换对象类型或值并持续观察

  • 自定义转换
    使用 MediatorLiveData 类正确观察多个 LiveData 的变化并处理这些事件, MediatorLiveData 会将自身的 active/inactive 状态变化正确的传递给它所处理的 LiveData

3. 实例: netsgod列表刷新与加载

View层

包含一个列表的UI类 MyFavoriteFragment 中,实例化 GLRefreshLayout 对象 refreshLayout , 调用 refreshLayout.attachRefresh(this, getRefreshable()); 设置下拉刷新与上拉加载

FeedListBaseFragment 类实现 getRefreshable ,其中维护一个 GLRefreshableLD 类型的 refreshable 如下

    protected GLRefreshableLD getRefreshable() {
        if (refreshable == null) {
            refreshable = buildRefreshable();
        }
        return refreshable;
    }

    @NonNull
    protected GLRefreshableLD buildRefreshable() {
        return new GLRefreshableLD() {
            @Override
            public LiveData refresh() {
                setRefresh(true);
                return LiveDataStream.from(dataSourceViewModel.refresh())
                        .observe(FeedListBaseFragment.this, refreshFeedsObserver)
                        .getLiveData();
            }

            @Override
            public LiveData loadMore() {
                return LiveDataStream.from(dataSourceViewModel.loadMore())
                        .observe(FeedListBaseFragment.this, loadMoreFeedsObserver)
                        .getLiveData();
            }
        };
    }

observer

    protected Observer>> refreshFeedsObserver = new Observer>>() {

        @Override
        public void onChanged(@Nullable GLResult> listGLResult) {
            onHandleResult(listGLResult, true);
            handleResult(listGLResult, true);
            if (listGLResult.getResult().isSuccess()) {
                List refreshData = hookFeedUnActive(listGLResult.getSpecific());
                showUpdateTip(refreshData, feedAdapter.getLastData());
                feedAdapter.replaceAll(dataSourceViewModel.hookIFeedData(refreshData), autoLoadMore.isTouchEnd(), getEmptyTips());
            }
        }
    };

    protected Observer>> loadMoreFeedsObserver = new Observer>>() {

        @Override
        public void onChanged(@Nullable GLResult> listGLResult) {
            onHandleResult(listGLResult, false);
            handleResult(listGLResult, true);
            if (listGLResult.getResult().isSuccess()) {
                feedAdapter.replaceAll(hookFeedUnActive(dataSourceViewModel.getDataSource().getList()), autoLoadMore.isTouchEnd(), getEmptyTips());
            }
        }
    };

// onHandleResult 没实现= =
handleResult 在UI层处理返回结果,设置toast
// showUpdateTip 没实现,显示更新数据数量(?)
Adapter.replaceAll 全局更新列表数据

ViewModel层

View层的类 MyFavoriteFragment 类继承自 FeedListBaseFragment ,在后者中设置

    private FeedViewModel viewModel;
    public MyFavoriteViewModel dataSourceViewModel;

    ……

    dataSourceViewModel = getViewModel(MyFavoriteViewModel.class);
    viewModel = getViewModel(FeedViewModel.class);
    viewModel.attachDataSource(dataSourceViewModel.getDataSource());
    EventbusUtil.register(this);

刷新时调用

        dataSourceViewModel.refreshRepo(); //拿数据
        viewModel.refreshRepo(); //拿数据
        refreshable.refresh(); // 更新UI

Model层

FeedRepo 类拉取本地数据库中用户相关数据,提供网络请求动态等信息的api参数
用户动态数据获取:从网络直接请求固定数量动态

网络请求

调用 GLProtoExecutor.execute 发起网络请求( okhttp 3封装的 GLClient 类, http 请求, json 数据)
回调 onPostExecute 监听请求结果,执行保存到本地数据库之类的操作
observe 监听
回调 onChanged 更新数据
By mingfeng

all in all

执行的操作就是
用户点击 -> View 调用自己的 ViewModel -> 执行 Viewmodel 中的业务逻辑 -> 调用 Model 的数据操作方法,并且以监听的形式交给 LiveData -> LiveData 以回调方式执行 ViewModel 的逻辑, ViewModel 控制 View 刷新
By mingfeng

4. others

butterKnife

以下内容搬运自官方文档

  • 资源绑定
    对一个成员变量使用 @BindView 注解,并传入一个 View ID, ButterKnife 就能找到对应的 View 并在编译时自动转换为对应子类
    @BindView(R.id.feed_list) VideoList feedList;

    @Override 
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.simple_activity);
        ButterKnife.bind(this);
        // TODO Use fields...
    }

这种绑定方式可应用于多个需要使用 findViewById 的地方,比如在 adapter 的内部类中使用

static class ViewHolder {
        @BindView(R.id.title)
        TextView name;
        @BindView(R.id.job_title) TextView jobTitle;

        public ViewHolder(View view) {
            ButterKnife.bind(this, view);
        }
    }
  • View 列表
    一次将多个相同类型的 view 绑定到列表中
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
List nameViews;

apply 函数一次在列表中所有 view 上执行相同的动作

ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);

或设置property属性:ButterKnife.apply(nameViews, View.ALPHA, 0.0f);

Action/Setter 接口一次在列表中所有 view 上指定相同的动作或设置,其中 Setter 可以传入设置参数的值

static final ButterKnife.Action DISABLE = new ButterKnife.Action() {
    @Override public void apply(View view, int index) {
        view.setEnabled(false);
    }
};
static final ButterKnife.Setter ENABLED = new ButterKnife.Setter() {
    @Override public void set(View view, Boolean value, int index) {
        view.setEnabled(value);
    }
};
  • 监视器绑定
    对一个方法使用 @OnClick 等监视器注解,并传入一个 View ID, ButterKnife 就能找到对应的 View 并绑定对应监视器
@OnClick(R.id.submit)
public void submit(VIEW) {
    // TODO
}

上面代码中的VIEW 可以是 view 或置空或特定的类型,如 button

同样的,支持同时为多个 view 绑定相同行为的监视器

自定义 view 中的监视器不需要指定 id

public class FancyButton extends Button {
    @OnClick
    public void onClick() {
        // TODO do something!
    }
}
  • 重置绑定
    fragment 在 onCreateView 中使用绑定后,需要在 onCreateView 将 view 设为 null 。 ButterKnife 的 Unbinder 实例可以简单完成
public class FancyFragment extends Fragment {
    private Unbinder unbinder;

    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fancy_fragment, container, false);
        unbinder = ButterKnife.bind(this, view);
        // TODO Use fields...
        return view;
    }

    @Override public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }
}


  • 可选绑定
    使用 @Nullable 注解 @bind 避免在目标 view 没有找到时抛出异常

注解库中 @Nullable 用于注解可能为空的变量、参数或返回值
反之, @NonNull 用于注解不可为空的变量、参数或返回值

你可能感兴趣的:(Android)