一、项目功能说明
APP获取好友列表后将数据展示在一个ListView中,点击Item会打开一个新页面展示好友详细信息。
二、项目结构
示例将代码分为四层,对应到MVP模式中:
1)Presentation
Presenters和UI被划分到这一层,但Presenters在这里只是负责将Domain逻辑处理后的数据进行组装并调度UI显示,没有业务处理逻辑。这样将数据逻辑处理划分给Domain可以让Presenter更关注于UI显示的调度,从而避免Present逻辑的冗余。这也是我选择这个工程作为示例的原因。
2)Domain
这里对Data中的数据进行逻辑处理,为Present提供业务逻辑和数据支持。
3)Data
数据仓库。例如,当通过id获取用户数据时,首先会检测用户信息是否已经存储在本地,否则的话就会从服务器获取后在本地缓存。根据上篇博客提到的设计原则,外圆代码逻辑无需关心用户数据是从存储介质、内存还是服务器获取,只需拿到Domain处理好的最终数据进行展示和交互即可。
三、代码详解
通过获取用户详细信息这个功能分析各个层级之间的调度及数据传递方向和方式。
data 层
data 模块,把要提供的数据封装好,只向domain层提供UserRepository 接口
/**
* Copyright (C) 2014 android10.org. All rights reserved.
* @author Fernando Cejas (the android10 coder)
*/
package com.guo.data;
import java.util.List;
import com.guo.bean.User;
/**
* Interface that represents a Repository for getting {@link User} related data.
*/
public interface UserRepository {
/**
* Callback used to be notified when either a user list has been loaded or an error happened.
*/
interface UserListCallback {
void onUserListLoaded(List usersCollection);
void onError(ErrorBundle errorBundle);
}
/**
* Callback used to be notified when either a user has been loaded or an error happened.
*/
interface UserDetailsCallback {
void onUserLoaded(User user);
void onError(ErrorBundle errorBundle);
}
/**
* Get a collection of {@link User}.
*
* @param userListCallback A {@link UserListCallback} used for notifying clients.
*/
void getUserList(UserListCallback userListCallback);
/**
* Get an {@link User} by id.
*
* @param userId The user id used to retrieve user data.
* @param userCallback A {@link UserDetailsCallback} used for notifying clients.
*/
void getUserById(final int userId, UserDetailsCallback userCallback);
}
domain层
domain层主要负责数据回调和加载数据的业务处理,向上层提供接口,如GetUserDetailUserCase
GetUserDetailUserCase的代码如下:
package com.guo.domain;
import com.guo.bean.User;
import com.guo.data.ErrorBundle;
public interface GetUserDetailUserCase extends Runnable{
interface Callback{
void onUserDetailLoad(User user);
void onError(ErrorBundle error);
}
void execute(int userId,Callback callback);
}
//GetUserDetailUserCase 的实现GetUserDetailUserCaseImp 如下所示:
package com.guo.domain;
import com.guo.bean.User;
import com.guo.data.ErrorBundle;
import com.guo.data.UserRepository;
import com.guo.data.UserRepository.UserDetailsCallback;
public class GetUserDetailUserCaseImp implements GetUserDetailUserCase {
private UserRepository userRepository;
private ThreadExecutor executor;
private PostExecutionThread postThread;
private int userId;
//回调主界面
private Callback callBack;
public GetUserDetailUserCaseImp(UserRepository userRepository,
ThreadExecutor executor, PostExecutionThread postThread) {
this.executor = executor;
this.userRepository = userRepository;
this.postThread = postThread;
}
@Override
public void run() {
// TODO Auto-generated method stub
// executor.execute(this);
userRepository.getUserById(userId, udCallback);
}
@Override
public void execute(int userId, Callback callback) {
// TODO Auto-generated method stub
this.userId = userId;
this.callBack=callback;
executor.execute(this);
}
//拉取数据后,回调返回数据
private UserRepository.UserDetailsCallback udCallback = new UserDetailsCallback() {
@Override
public void onUserLoaded(User user) {
// TODO Auto-generated method stub
getDataSuccess(user);
}
@Override
public void onError(ErrorBundle errorBundle) {
// TODO Auto-generated method stub
getDataFail(errorBundle);
}
};
public void getDataSuccess(final User user) {
postThread.post(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
callBack.onUserDetailLoad(user);
}
});
}
public void getDataFail(final ErrorBundle errorBundle) {
postThread.post(new Runnable(){
@Override
public void run() {
// TODO Auto-generated method stub
callBack.onError(errorBundle);
}
});
}
}
PostExecutionThread 是将回调得到的数据进行处理的线程,一般为主线程(本例中为UIThread,主线程)
ThreadExecutor 是进行数据请求的线程的线程池
例如,从网络中拉取数据,ThreadExecutor 启动一个线程去拉取数据,PostExecutionThread将拉取到的数据更新到主界面
Presentation层
Presenters在这里只是负责将Domain逻辑处理后的数据进行组装并调度UI显示,如GetUserDetailPresent
public class GetUserDetailPresent {
private GetUserDetailUserCase userDetailCase;
private int userId;
private final UserDetailsView viewDetailsView;
public GetUserDetailPresent(UserDetailsView viewDetailsView,
GetUserDetailUserCase userDetailCase) {
this.userDetailCase = userDetailCase;
this.viewDetailsView = viewDetailsView;
}
private void getUserDetails() {
this.userDetailCase.execute(this.userId, this.userDetailsCallback);
}
private GetUserDetailUserCase.Callback userDetailsCallback = new Callback() {
@Override
public void onUserDetailLoad(User user) {
// TODO Auto-generated method stub
GetUserDetailPresent.this.showUser(user);
GetUserDetailPresent.this.hideViewLoading();
}
@Override
public void onError(ErrorBundle error) {
// TODO Auto-generated method stub
GetUserDetailPresent.this.hideViewLoading();
GetUserDetailPresent.this.showError(error);
GetUserDetailPresent.this.showViewRetry();
}
};
public void showUser(User user) {
viewDetailsView.renderUser(user);
}
public void showError(ErrorBundle error) {
viewDetailsView.showError(error.getError());
}
private void loadUserDetails() {
this.hideViewRetry();
this.showViewLoading();
this.getUserDetails();
}
private void showViewLoading() {
this.viewDetailsView.showLoading();
}
private void hideViewLoading() {
this.viewDetailsView.hideLoading();
}
private void showViewRetry() {
this.viewDetailsView.showRetry();
}
private void hideViewRetry() {
this.viewDetailsView.hideRetry();
}
public void initialize(int userId) {
this.userId = userId;
this.loadUserDetails();
}
}
view层
view层则就是界面的组装,如UserDetailActivity
package com.example.mvpmodeltest;
import com.guo.bean.User;
import com.guo.data.GetData;
import com.guo.data.JobExecutor;
import com.guo.data.UserRepository;
import com.guo.domain.GetUserDetailUserCase;
import com.guo.domain.GetUserDetailUserCaseImp;
import com.guo.domain.PostExecutionThread;
import com.guo.domain.ThreadExecutor;
import com.guo.presentation.GetUserDetailPresent;
import com.guo.presentation.UIThread;
import com.guo.presentation.UserDetailsView;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.widget.TextView;
public class UserDetailActivity extends Activity implements UserDetailsView {
private TextView tv_email;
private TextView tv_followers;
private TextView tv_description;
private GetUserDetailPresent detailPresent;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.view_user_details);
this.tv_email = (TextView) findViewById(R.id.tv_email);
this.tv_followers = (TextView) findViewById(R.id.tv_followers);
this.tv_description = (TextView) findViewById(R.id.tv_description);
initializePresenter();
Intent intent=this.getIntent();
int userId=intent.getIntExtra("userId",-1);
detailPresent.initialize(userId);
}
public void initializePresenter() {
ThreadExecutor threadExecutor = JobExecutor.getInstance();
PostExecutionThread postExecutionThread = UIThread.getInstance();
UserRepository userRepository = new GetData();
GetUserDetailUserCase detailUserCase = new GetUserDetailUserCaseImp(
userRepository, threadExecutor, postExecutionThread);
detailPresent = new GetUserDetailPresent(this, detailUserCase);
}
@Override
public void showLoading() {
// TODO Auto-generated method stub
}
@Override
public void hideLoading() {
// TODO Auto-generated method stub
}
@Override
public void showRetry() {
// TODO Auto-generated method stub
}
@Override
public void hideRetry() {
// TODO Auto-generated method stub
}
@Override
public void showError(String message) {
// TODO Auto-generated method stub
}
@Override
public Context getContext() {
// TODO Auto-generated method stub
return null;
}
@Override
public void renderUser(User user) {
// TODO Auto-generated method stub
System.out.println(user);
tv_email.setText(user.getEmail());
tv_followers.setText(user.getFollowers()+"");
tv_description.setText(user.getDescription());
}
}