Flux Architecture on Android
Bài đăng này đã không được cập nhật trong 3 năm
Giới thiệu về kiến trúc Flux(Flux Architecture)
Flux Architecture đã được xây dựng và sử dụng bới Facebook. Mục đích ban đầu của họ khi xây dựng Flux Architecture là cho các dứng dụng web client-side và tất nhiên nó không có ý định xây dựng cho các mobile app.Nhưng với những tính năng và sự đơn giản của nó đã được chuyển đổi rất tốt vào trong nhưng Android Project. Bạn có thể xem chi tiết về Flux tại buổi giới thiệu của Facebook tại đay.
Có 2 tính năng chính để bạn có thể hiều về Flux:
-
Luồng dữ liệu luôn luôn là 1 chiều Luồng dữ liệu một chiều là tính năng chính trong Flux Architecture làm cho nó trở lên dễ dàng để học.
-
Ứng dụng là được chia vào 3 phần Chính:
-View: Giao diện ứng dụng. Nó tạo ra các Action để phản hồi với tương tác của người dùng.
-Dispatcher: Là một Hub trung tâm, chịu trách nhiệm gửi toàn bộ các Action tới mỗi Store.
**-Store **: Duy trì trạnng thái cho ứng dựng, phản hồi lại các Action với các state cụ thể, thực thi logic, phát ra Change Event khi việc xử lý đã xong.Những sự kiện này đã được sử dụng bởi View cho việc update giao diện. Cả 3 phần giao tiếp với nhau bằng Actions. Action là đối tượng đơn giản được xác định bởi một Type của Action, và chứa data liên quan tới Action đó.
Flux Android Architecture
Usage
dependencies {
compile 'com.hardsoftstudio:rxflux:0.1.2'
}
RxFlux có chứa dependency RxAndroid và Android support v4 để nhận được những tiện lợi của những class like ArrayMap, Pair, etc... Class chính là RxFlux chịu trách nhiệm cho việc xử lý vòng đời của các Activity để notify và register tới các Store và các View. Việc đầu tiên bạn cần phải làm là bắt đầu RxFlux trong ứng dụng.
public class GitHubApp extends Application {
private RxFlux mRxFlux;
private GitHubActionCreator mActionCreator;
public static Context mContext;
@Override
public void onCreate() {
super.onCreate();
mRxFlux = RxFlux.init(this);
mActionCreator = new GitHubActionCreator(mRxFlux.getDispatcher(), mRxFlux.getSubscriptionManager());
mContext = this.getApplicationContext();
}
public RxFlux getRxFlux() {
return this.mRxFlux;
}
public void setRxFlux(RxFlux rxFlux) {
this.mRxFlux = rxFlux;
}
public GitHubActionCreator getActionCreator() {
return mActionCreator;
}
public void setActionCreator(GitHubActionCreator actionCreator) {
this.mActionCreator = actionCreator;
}
public static GitHubApp get(Context context) {
return (GitHubApp) context.getApplicationContext();
}
public static Context getContext(){
return mContext;
}
}
View
Mỗi Activity của ứng dụng phải implement ** RxViewDispatch **. Interface này định nghĩa những phương thức cần cho mỗi view. RxFlux sẽ notify mỗi Activity implement RxViewDispatch trong khi tạo nó và gọi onRxStoresRegister() . Phương thức này gọi các Store cần và register nó.
@Override
public void onRxStoresRegister() {
mRepositoriesStore = RepositoriesStore.getInstance(GitHubApp.get(this).getRxFlux().getDispatcher());
mRepositoriesStore.register();
mUsersStore = UsersStore.getInstance(GitHubApp.get(this).getRxFlux().getDispatcher());
mUsersStore.register();
mApp = GitHubApp.get(MainActivity.this);
}
*onRxStoreChanged(RxStoreChange change) * sẽ nhận được notify bởi bất ký store nào thay đổi trong app. Do đó bạn phải filter bởi storeId để xác định xem store nào thay đổi.
@Override
public void onRxStoreChanged(RxStoreChange change) {
setLoadingFrame(false);
switch (change.getStoreId()) {
case RepositoriesStore.REPOSITORIES_ID:
switch (change.getRxAction().getType()) {
case Actions.GET_REPOSITORIES:
mRepoAdapter.setRepos(mRepositoriesStore.getRepositories());
break;
}
break;
case UsersStore.USER_STORE_ID:
switch (change.getRxAction().getType()) {
case Actions.GET_USER:
UserDetailDialog dialog = UserDetailDialog.newInstance((String) change.getRxAction().getData().get(Keys.ID));
dialog.show(getSupportFragmentManager(), UserDetailDialog.class.getName());
}
}
}
RxActionCreator
Bước tiếp theo chúng ta tạo ActionsCreator, để làm vậy chúng ta cần tạo một class mới với việc extends từ * RxActionCreator *. Abastract class này xẽ cung cấp cho chúng ta một số function mở rộng để tạo các action và request. Tôi xin đề nghị là các bạn lên tạo một interface để địn nghĩa các actions và các phương thức như bên dưới:
public interface Actions {
String GET_REPOSITORIES = "get_repositories";
String GET_USER = "get_user";
void getRepositories();
void getUserDetail(String userId);
}
Và ActionCreator sẽ như bên dưới:
public class GitHubActionCreator extends RxActionCreator implements Actions {
public GitHubActionCreator(Dispatcher dispatcher, SubscriptionManager manager) {
super(dispatcher, manager);
}
@Override
public void getRepositories() {
final RxAction takeRepositoriesAction = newRxAction(GET_REPOSITORIES);
if (hasRxAction(takeRepositoriesAction)) {
return;
}
addRxAction(takeRepositoriesAction, GitHubAppService.getRxSampleAppApi()
.getRepositories()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(repos -> postRxAction(newRxAction(GET_REPOSITORIES, Keys.REPOSITORY, repos)),
throwable -> postError(takeRepositoriesAction, throwable)));
}
@Override
public void getUserDetail(String userId) {
final RxAction takeUserAction = newRxAction(GET_USER, Keys.ID, userId);
if (hasRxAction(takeUserAction)){
return;
}
addRxAction(takeUserAction, GitHubAppService.getRxSampleAppApi().getUser(userId).
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(user ->{takeUserAction.getData()
.put(Keys.User, user);postRxAction(takeUserAction);},
throwable -> {postError(takeUserAction,throwable);}));
}
}
RxStore
Đây là class đại diện cho Model + Logic của App. Để đơn giản cho việc tạo nó, mỗi Store bạn nên extends từ một abstract class là RxStore.
public class RepositoriesStore extends RxStore implements RepositoriesStoreInterface {
public static final String REPOSITORIES_ID = "Repositories";
private static RepositoriesStore mInstance;
private ArrayList<GitHubRepo> mGitHubRepos;
public RepositoriesStore(Dispatcher dispatcher) {
super(dispatcher);
}
public static synchronized RepositoriesStore getInstance(Dispatcher dispatcher) {
if (mInstance == null) {
mInstance = new RepositoriesStore(dispatcher);
}
return mInstance;
}
@Override
public ArrayList<GitHubRepo> getRepositories() {
return mGitHubRepos == null ? new ArrayList<>() : mGitHubRepos;
}
@Override
public void onRxAction(RxAction action) {
switch (action.getType()) {
case Actions.GET_REPOSITORIES:
this.mGitHubRepos = (ArrayList<GitHubRepo>) action.getData().get(Keys.REPOSITORY);
break;
default:
break;
}
postChange(new RxStoreChange(REPOSITORIES_ID, action));
}
}
*RepositoriesStoreInterface.java *
public interface RepositoriesStoreInterface {
ArrayList<GitHubRepo> getRepositories();
}
Trong Project Sample này tôi sẽ lấy về các thông tin của các Repository public trên github theo API: *https://api.github.com/repositories * Sau đây là một vài class bạn cần phải thực hiện: Models:
GitHubRepo.java
public class GitHubRepo {
private int id;
private String name;
@SerializedName("full_name")
private String fullName;
private GitUser owner;
private String description;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public GitUser getOwner() {
return owner;
}
public void setOwner(GitUser owner) {
this.owner = owner;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
GitUser.java
public class GitUser {
@SerializedName("login")
@Expose
public String login;
@SerializedName("id")
@Expose
public int id;
@SerializedName("avatar_url")
@Expose
public String avatarUrl;
@SerializedName("gravatar_id")
@Expose
public String gravatarId;
@SerializedName("url")
@Expose
public String url;
@SerializedName("html_url")
@Expose
public String htmlUrl;
@SerializedName("followers_url")
@Expose
public String followersUrl;
@SerializedName("following_url")
@Expose
public String followingUrl;
@SerializedName("gists_url")
@Expose
public String gistsUrl;
@SerializedName("starred_url")
@Expose
public String starredUrl;
@SerializedName("subscriptions_url")
@Expose
public String subscriptionsUrl;
@SerializedName("organizations_url")
@Expose
public String organizationsUrl;
@SerializedName("repos_url")
@Expose
public String reposUrl;
@SerializedName("events_url")
@Expose
public String eventsUrl;
@SerializedName("received_events_url")
@Expose
public String receivedEventsUrl;
@SerializedName("type")
@Expose
public String type;
@SerializedName("site_admin")
@Expose
public boolean siteAdmin;
@SerializedName("name")
@Expose
public String name;
@SerializedName("company")
@Expose
public Object company;
@SerializedName("blog")
@Expose
public String blog;
@SerializedName("location")
@Expose
public String location;
@SerializedName("email")
@Expose
public String email;
@SerializedName("public_repos")
@Expose
public int publicRepos;
@SerializedName("public_gists")
@Expose
public int publicGists;
@SerializedName("followers")
@Expose
public int followers;
@SerializedName("following")
@Expose
public int following;
@SerializedName("created_at")
@Expose
public String createdAt;
@SerializedName("updated_at")
@Expose
public String updatedAt;
public String getLogin() {
return login;
}
public int getId() {
return id;
}
..........
GitHubAppApi.java
public interface GitHubAppApi {
String SERVICE_ENDPOINT = AppConfig.BASE_URL;
@GET("/repositories")
Observable<ArrayList<GitHubRepo>> getRepositories();
@GET("/users/{id}")
Observable<GitUser> getUser(@Path("id") String userId);
}
Bên dưới là screenshot sau khi chạy sample:
Tài liệu tham khảo
Để hiểu chi tiết hơn, các bạn nên tham khảo đầy đủ source code trên github theo link sau: https://github.com/lequanghoa/RxFlux-Architecture-On-Android-
All rights reserved