Android Clean 架构

https://github.com/android10/Android-CleanArchitecture

这个项目很多的代码,实现了很简单的列表跳转功能。


首先是 Data 层,负责获取数据。


/**
 * 一个简单的实体类
 */
public class User {

    private String id;

    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}
/**
 * 获取用户信息的类
 * 接口可以让我们更专注于功能
 */
public interface UserRepository {

    /**
     * 功能1 获取用户列表
     *
     * @param userListCallback the user list callback
     */
    void getUserList(UserListCallback userListCallback);


    /**
     * 功能2 获取用户信息
     *
     * @param userId       the user id
     * @param userCallback the user callback
     */
    void getUserById(final String userId, UserDetailsCallback userCallback);



//    /**
//     * Callback 是什么?
//     * 最常用 OnClickListener 就是一个 Callback,.onClick 则是回调函数。
//     * 这是暴露给外部的入口。
//     * 帮助我们在某一个时刻(比如当用户信息加载完毕,或者是被点击时)执行某段代码段。
//     * 回调函数需要向外部提供一些参数(比如 User)。
//     */

    /**
     * The interface User list callback.
     */
    interface UserListCallback {

        /**
         * On user list loaded.
         *
         * @param usersCollection the users collection
         */
        void onUserListLoaded(List<User> usersCollection);


        /**
         * On error.
         *
         * @param errorBundle the error bundle
         */
        void onError(ErrorBundle errorBundle);
    }


    /**
     * The interface User details callback.
     */
    interface UserDetailsCallback {

        /**
         * On user loaded.
         *
         * @param user the user
         */
        void onUserLoaded(User user);

        /**
         * On error.
         *
         * @param errorBundle the error bundle
         */
        void onError(ErrorBundle errorBundle);
    }

}
public class UserRepositoryImpl implements UserRepository {

    static private List<User> userCollection = new ArrayList<>();

    static {
        User user = new User();
        user.setId("1");
        user.setName("Li");
        userCollection.add(user);
        user = new User();
        user.setId("2");
        user.setName("Shang");
        userCollection.add(user);
    }

    @Override
    public void getUserList(UserListCallback userListCallback) {
        // 直接返回数据集,正常的实现应该是费时操作。
        userListCallback.onUserListLoaded(userCollection);
    }

    @Override
    public void getUserById(String userId, UserDetailsCallback userCallback) {
       // 如果找到对应用户则返回
        boolean exist = false;
        for (User user : userCollection) {
            if (userId.equals(user.getId())) {
                userCallback.onUserLoaded(user);
                exist = true;
                break;
            }
        }
        // 否则返回一个填充对象
        if (!exist) {
            User user = new User();
            user.setId("-1");
            user.setName("Clean");
            userCallback.onUserLoaded(user);
        }
    }

}


然后是 Domain 层


/**
 * Common interface for an Interactor {@link java.lang.Runnable} declared in the application.
 * This interface represents a execution unit for different use cases (this means any use case
 * in the application should implement this contract).
 * <p>
 * By convention each Interactor implementation will return the result using a Callback that should
 * be executed in the UI thread.
 */
public interface Interactor extends Runnable {
    /**
     * Everything inside this method will be executed asynchronously.
     */
    void run();
}
/**
 * The interface Get user details use case.
 */
public interface GetUserDetailsUseCase extends Interactor {

    /**
     * The interface Callback.
     */
    interface Callback {
        /**
         * On user data loaded.
         *
         * @param user the user
         */
        void onUserDataLoaded(User user);

        /**
         * On error.
         *
         * @param errorBundle the error bundle
         */
        void onError(ErrorBundle errorBundle);
    }


    /**
     * Execute.
     *
     * @param userId   the user id
     * @param callback the callback
     */
    public void execute(String userId, Callback callback);
}

这两个类,注释一堆,看不到实质,看其实现。

/**
 * This class is an implementation of {@link GetUserDetailsUseCase} that represents a use case for
 * retrieving data related to an specific {@link User}.
 */
public class GetUserDetailsUseCaseImpl implements GetUserDetailsUseCase {

    private final UserRepository userRepository;
    private final ThreadExecutor threadExecutor;
    private final PostExecutionThread postExecutionThread;

    private String userId;
    private GetUserDetailsUseCase.Callback callback;

    /**
     * Constructor of the class.
     *
     * @param userRepository      A {@link UserRepository} as a source for retrieving data.
     * @param threadExecutor      {@link ThreadExecutor} used to execute this use case in a background
     *                            thread.
     * @param postExecutionThread {@link PostExecutionThread} used to post updates when the use case
     *                            has been executed.
     */
    public GetUserDetailsUseCaseImpl(UserRepository userRepository, ThreadExecutor threadExecutor,
                                     PostExecutionThread postExecutionThread) {
        if (userRepository == null || threadExecutor == null || postExecutionThread == null) {
            throw new IllegalArgumentException("Constructor parameters cannot be null!!!");
        }
        this.userRepository = userRepository;
        this.threadExecutor = threadExecutor;
        this.postExecutionThread = postExecutionThread;
    }

    @Override
    public void execute(String userId, Callback callback) {
        if (userId == null || callback == null) {
            throw new IllegalArgumentException("Invalid parameter!!!");
        }
        this.userId = userId;
        this.callback = callback;
        this.threadExecutor.execute(this);
    }

    @Override
    public void run() {
        this.userRepository.getUserById(this.userId, this.repositoryCallback);
    }

    private final UserRepository.UserDetailsCallback repositoryCallback =
            new UserRepository.UserDetailsCallback() {
                @Override
                public void onUserLoaded(User user) {
                    notifyGetUserDetailsSuccessfully(user);
                }

                @Override
                public void onError(ErrorBundle errorBundle) {
                    notifyError(errorBundle);
                }
            };

    private void notifyGetUserDetailsSuccessfully(final User user) {
        this.postExecutionThread.post(new Runnable() {
            @Override
            public void run() {
                callback.onUserDataLoaded(user);
            }
        });
    }

    private void notifyError(final ErrorBundle errorBundle) {
        this.postExecutionThread.post(new Runnable() {
            @Override
            public void run() {
                callback.onError(errorBundle);
            }
        });
    }
}
因为查询数据可能是费时操作,所以要另起工作线程去完成查询工作。

UseCase 的作用就是另一线程查询,查询完成后切换到主线程执行外部传入的 Callback。

这一层就是完成了线程切换的工作。


最后是 Presentation 表现层。

Navigator 是作者把跳转抽象出的一个类,看 Presenter。

/**
 * Interface representing a Presenter in a model view presenter (MVP) pattern.
 */
public interface Presenter {

}
/**
 * {@link Presenter} that controls communication between views and models of the presentation
 * layer.
 */
public class UserDetailsPresenter implements Presenter {

    /**
     * id used to retrieve user details
     */
    private String userId;

    private final UserDetailsView viewDetailsView;
    private final GetUserDetailsUseCase getUserDetailsUseCase;

    public UserDetailsPresenter(UserDetailsView userDetailsView,
                                GetUserDetailsUseCase getUserDetailsUseCase) {
        if (userDetailsView == null || getUserDetailsUseCase == null) {
            throw new IllegalArgumentException("Constructor parameters cannot be null!!!");
        }
        this.viewDetailsView = userDetailsView;
        this.getUserDetailsUseCase = getUserDetailsUseCase;
    }


    /**
     * Initializes the presenter by start retrieving user details.
     */
    public void initialize(String userId) {
        this.userId = userId;
        this.loadUserDetails();
    }

    /**
     * Loads user details.
     */
    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();
    }

    private void showErrorMessage(ErrorBundle errorBundle) {
    }

    private void showUserDetailsInView(User user) {
        this.viewDetailsView.renderUser(user);
    }

    private void getUserDetails() {
        this.getUserDetailsUseCase.execute(this.userId, this.userDetailsCallback);
    }

    private final GetUserDetailsUseCase.Callback userDetailsCallback = new GetUserDetailsUseCase.Callback() {
        @Override
        public void onUserDataLoaded(User user) {
            UserDetailsPresenter.this.showUserDetailsInView(user);
            UserDetailsPresenter.this.hideViewLoading();
        }

        @Override
        public void onError(ErrorBundle errorBundle) {
            UserDetailsPresenter.this.hideViewLoading();
            UserDetailsPresenter.this.showErrorMessage(errorBundle);
            UserDetailsPresenter.this.showViewRetry();
        }
    };
}

   private final UserDetailsView viewDetailsView;
    private final GetUserDetailsUseCase getUserDetailsUseCase;
Presenter 控制 View 和 'UseCase',前者负责如何渲染界面,后者是屏蔽了线程差异的数据源。

那么 Presener 的工作就是,View 渲染的逻辑。

initialize 是他的入口。


最后看一个 View

/**
 * Fragment that shows details of a certain user.
 */
public class UserDetailsFragment extends BaseFragment implements UserDetailsView {

    private static final String ARGUMENT_KEY_USER_ID = "org.android10.ARGUMENT_USER_ID";

    private String userId;
    private UserDetailsPresenter userDetailsPresenter;

    private TextView tv_fullname;
    private TextView tv_email;
    private TextView tv_followers;
    private TextView tv_description;
    private RelativeLayout rl_progress;
    private RelativeLayout rl_retry;
    private Button bt_retry;

    public UserDetailsFragment() {
        super();
    }

    public static UserDetailsFragment newInstance(int userId) {
        UserDetailsFragment userDetailsFragment = new UserDetailsFragment();

        Bundle argumentsBundle = new Bundle();
        argumentsBundle.putInt(ARGUMENT_KEY_USER_ID, userId);
        userDetailsFragment.setArguments(argumentsBundle);

        return userDetailsFragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.initialize();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View fragmentView = inflater.inflate(R.layout.fragment_user_details, container, false);

        this.tv_fullname = (TextView) fragmentView.findViewById(R.id.tv_fullname);
        this.tv_email = (TextView) fragmentView.findViewById(R.id.tv_email);
        this.tv_followers = (TextView) fragmentView.findViewById(R.id.tv_followers);
        this.tv_description = (TextView) fragmentView.findViewById(R.id.tv_description);
        this.rl_progress = (RelativeLayout) fragmentView.findViewById(R.id.rl_progress);
        this.rl_retry = (RelativeLayout) fragmentView.findViewById(R.id.rl_retry);
        this.bt_retry = (Button) fragmentView.findViewById(R.id.bt_retry);
        this.bt_retry.setOnClickListener(this.retryOnClickListener);

        return fragmentView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        this.userDetailsPresenter.initialize(this.userId);
    }

    @Override
    void initializePresenter() {
        // All these dependency initialization could have been avoided using a
        // dependency injection framework. But in this case are used this way for
        // LEARNING EXAMPLE PURPOSE.
      ThreadExecutor threadExecutor = JobExecutor.getInstance();
     PostExecutionThread postExecutionThread = UIThread.getInstance();
        UserRepository userRepository = new UserRepositoryImpl();

        GetUserDetailsUseCase getUserDetailsUseCase = new GetUserDetailsUseCaseImpl(userRepository,
                threadExecutor, postExecutionThread);

        this.userDetailsPresenter =
                new UserDetailsPresenter(this, getUserDetailsUseCase);
    }

    @Override
    public void renderUser(User user) {
        if (user != null) {
            this.tv_fullname.setText(user.getName());
        }
    }

    @Override
    public void showLoading() {
        this.rl_progress.setVisibility(View.VISIBLE);
        this.getActivity().setProgressBarIndeterminateVisibility(true);
    }

    @Override
    public void hideLoading() {
        this.rl_progress.setVisibility(View.GONE);
        this.getActivity().setProgressBarIndeterminateVisibility(false);
    }

    @Override
    public void showRetry() {
        this.rl_retry.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideRetry() {
        this.rl_retry.setVisibility(View.GONE);
    }

    @Override
    public void showError(String message) {
        this.showToastMessage(message);
    }

    @Override
    public Context getContext() {
        return getActivity().getApplicationContext();
    }

    /**
     * Initializes fragment's private members.
     */
    private void initialize() {
        this.userId = getArguments().getString(ARGUMENT_KEY_USER_ID);
    }

    /**
     * Loads all users.
     */
    private void loadUserDetails() {
        if (this.userDetailsPresenter != null) {
            this.userDetailsPresenter.initialize(this.userId);
        }
    }

    private final View.OnClickListener retryOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            UserDetailsFragment.this.loadUserDetails();
        }
    };
}

View 中会初始化 Presenter,并在合适的时候启动它。

View 负责实现渲染功能,但具体渲染逻辑是在 Presenter 中实现的。









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值