Simple:Flexible Model for MVP and MVVM(附技術分享ppt)


Simple:一個Android Model層架構

項目GitHub地址:Simple

Simple主要實現了MVPMVVM中的M層,是一個Model層框架。其利用了Repository Pattern作為實現方式。Simple可以簡化應用中的Model層編碼,提供清晰的業務層實現思路。

編寫的Simple的目的有以下幾點:

  • 提高Model層代碼的可測性;
  • 將Model層代碼進行分層;
  • 降低對第三方庫的依賴以及第三方庫代碼的侵入性;
  • 提供近乎插件的靈活性和可擴展性;

Simple的特性

  • 低侵入性。框架層未引用任何第三方庫或Android API,全部基於JDK開發,通過抽象進行依賴,低層對高層透明;
  • 可測性。Model的層級清晰,相互之間通過接口或抽象類耦合,耦合性很低,便於單元測試;
  • 靈活性。Simple分為三層,每一層都可以被其他實現替代,下層的改變不會影響上層,下層對上層透明,利用注解庫如Dagger可以輕松切換每一層的實現方式。
  • 運用設計模式設計,符合設計原則。

Simple架構簡圖

Simple架構簡圖

  • Business Service。業務層,通過接口定義功能,View層使用此接口實現業務操作。業務層可以維持多個Repository,用於實現不同的業務需求;
  • Repository。數據倉庫,DataSource的代理,用於控制業務代碼對數據源的訪問(參考代理模式);
  • DataSource。數據源,可以管理多個數據讀取者,實現數據切換(本地,網絡,緩存等)。將獲取數據的請求和請求的實現者進行解耦(參考命令模式
  • DataFetcher。數據讀取者,真正請求數據和數據層打交道的對象。
  • 數據層。數據的存儲位置。

各模塊的依賴關系由上到下。

Simple類圖:

tips:圖看不清楚可右鍵新標簽頁打開或保存到本地再看。

Simple類圖

Simple各層依賴關系:

Simple Depends

Simple在MVP架構中的位置:

MVP

在MVVM中的位置:

MVVM

RepositoryManager:

用於管理業務邏輯代碼所涉及的所有Repository,在創建View(Repository)時注冊,在View銷毀時,取消注冊,並關閉Repository。

使用Simple+Dagger2+DataBinding+RxJava實現MVVM架構:

1.配置
在應用的build.gradle文件中:
引入Simple library

compile project(‘:library’)

添加Dagger2支持:
在project的build.gradle文件下添加classpath 依賴(注:1.4與databinding不兼容):

dependencies {
classpath ‘com.neenbedankt.gradle.plugins:android-apt:1.8’
}

在app的build.gradle文件下,應用注解處理器插件:

apply plugin: ‘com.neenbedankt.android-apt’//注解處理器

app的dependencies下加入Dagger2依賴:

//dagger2
apt ‘com.google.dagger:dagger-compiler:2.0.2’
compile ‘com.google.dagger:dagger:2.0.2’
//producer,可選
compile ‘com.google.dagger:dagger-producers:2.0-beta’
provided ‘javax.annotation:jsr250-api:1.0’

app的android配置下啟用Databinding

dataBinding{
enabled true
}

2.寫布局:activity_repo_search.xml( 參考databinding教程)

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app = "http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">


<data>

<import type="android.view.View" />

<variable
name="repoVm"
type="com.simple.creact.simple.app.presentation.viewmodel.RepoViewModel" />

</data>

<RelativeLayout android:padding="10dp">

<LinearLayout
android:id="@+id/search_container"
android:layout_width="match_parent"
android:layout_height="40dp"
android:orientation="horizontal">


<EditText
android:id="@+id/input_name"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:singleLine="true"
android:hint="輸入git用戶名"
android:textColorHint="#c7c6c6" />


<Button
android:id="@+id/search_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:onClick="search"
android:text="Search" />

</LinearLayout>

<android.support.v7.widget.RecyclerView
android:id="@+id/repos_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{repoVm.isEmpty?View.GONE:View.VISIBLE}"
android:layout_below="@id/search_container">
</android.support.v7.widget.RecyclerView>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="@{repoVm.isEmpty?View.VISIBLE:View.GONE}">


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="沒有相關的倉庫"
android:textColor="#c7c6c6" />

</RelativeLayout>
</RelativeLayout>
</layout>

3.寫ViewModel:

public class RepoViewModel {

//observable objects
public ObservableArrayList<Repo> observableRepoList = new ObservableArrayList<>();
public ObservableBoolean isEmpty = new ObservableBoolean(false);

public void add(Repo repo) {
observableRepoList.add(repo);
isEmpty.set(observableRepoList.isEmpty());
}

public void add(int index, Repo repo) {
observableRepoList.add(index, repo);
isEmpty.set(observableRepoList.isEmpty());
}

public void addAll(List<Repo> repos) {
observableRepoList.clear();
if (repos != null)
observableRepoList.addAll(repos);
isEmpty.set(observableRepoList.isEmpty());
}
}

4.在SearchRepoActivity中綁定ViewModel:

 private RepoViewModel repoViewModel = new RepoViewModel();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_repo_search);
//bind view model
activityMainBinding.setRepoVm(repoViewModel);
mDataCallback = new RepoCallback(this);
initViews();
//注入依賴
injectDepens();
}

5.編寫業務接口

public interface RepoService {

/**
* 獲取某個人的全部倉庫
*
* @param owner
* @param callback UICallback
* @return
*/

void getRepos(String owner, DataCallback callback);
}

6.編寫業務實現類

public class RepoServiceImpl implements RepoService {
//使用抽象基類,而不引入具體類,降低耦合
private BaseRepository repository;
//inject concrete Repository
@Inject
public RepoServiceImpl(@Named(QualifierConstants.PROVIDE_REPO_REPOSITORY_DAGGER) BaseRepository repository) {
this.repository = repository;
}

@Override
public void getRepos(String owner, DataCallback repoListCallback) {
//構造請求參數
RequestParameter extraParams = RequestParameter.newActionParameter(QualifierConstants.PROVIDE_REPO_REPOSITORY);
//設置回調
repository.setCallback(repoListCallback);
//請求數據
repository.getData(extraParams, owner);
}
}

7.編寫Repository

public class RepoRepository extends BaseObservableRepository<List<Repo>, List<Repo>> {
//inject concrete datasource
@Inject
public RepoRepository(@Named(QualifierConstants.PROVIDE_REPO_DATA_SOURCE_RX)DataSource dataSource) {
super(dataSource);
}
@Override
public List<Repo> convert(List<Repo> repos) {
return repos;
}
}

這里直接繼承BaseObservableRepository,也可以繼承BaseDaggerRepository 實現另一種策略。

8.編寫DataSource和DataFetcher

//RepoDataSource 
public class RepoDataSource extends BaseObservableDataSource<List<Repo>, List<Repo>> {

@Inject
public RepoDataSource(DataFetcher<List<Repo>> dataFetcher) {
super(dataFetcher);
}

@Override
public List<Repo> convert(List<Repo> repos) {
return repos;
}
//RepoFetcher
public static class RepoFetcher extends GitHubDataFetcher<List<Repo>> {

@Inject
public RepoFetcher(GitHubApi gitHubApi) {
super(gitHubApi);
}

@Override
public List<Repo> fetchDataImpl(@NonNull RequestParameter parameter) throws Exception {
call = gitHubApi.listRepos(parameter.get("user"));
List<Repo> result = call.execute().body();
return result;
}

@Override
public IParameter putValues(@NonNull IParameter<String, String> parameter, @NonNull String... values) {
if (values == null || values.length == 0)
return parameter;
parameter.put("user", values[0]);
return parameter;
}
}
}

9.編寫Module和Component 參考Dagger2教程
RepoComponent:

@ActivityScope
@Component(dependencies = ApplicationComponent.class, modules = RepoModule.class)
public interface RepoComponent {
void inject(SearchRepoActivity searchRepoActivity);

RepoService getRepoService();
}

RepoModule:

@Module(includes = {GitHubApiModule.class})
public class RepoModule {
/**************************************
* DataFetcher
**************************************/

@ActivityScope
@Provides
DataFetcher<List<Repo>> reposDataFetcher(RepoDataSource.RepoFetcher reposFetcher) {
return reposFetcher;
}

/***************************************
* DataSource
***************************************/

@ActivityScope
@Provides
@Named(QualifierConstants.PROVIDE_REPO_DATA_SOURCE_RX)
DataSource repoDataSource(DataFetcher<List<Repo>> dataFetcher) {
return new RepoDataSource(dataFetcher);
}

/**************************************
* Repository
**************************************/

@ActivityScope
@Provides
@Named(QualifierConstants.PROVIDE_REPO_REPOSITORY)
BaseRepository reposRepository(RepoRepository repository) {
return repository;
}

/*************************************
* Service
*************************************/

@ActivityScope
@Provides
RepoService repoService(RepoServiceImpl repoService) {
return repoService;
}


}

RepoModule依賴的GitHubApiModule,這里使用了Retrofit作http請求。

@Module
public class GitHubApiModule {
public static final String API_URL = "https://api.github.com";
private String baseUrl = API_URL;

public GitHubApiModule(String baseUrl) {
this.baseUrl = baseUrl;
}

public GitHubApiModule() {
}

/***************************
* API
***************************/


@ActivityScope
@Provides
@Named(QualifierConstants.PROVIDE_GIT_HUB_API)
Retrofit retrofit() {
return new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}

@ActivityScope
@Provides
GitHubApi gitHubApi(@Named(QualifierConstants.PROVIDE_GIT_HUB_API) Retrofit retrofit) {
return retrofit.create(GitHubApi.class);
}


}

10.SearchRepoActivity中使用RepoService

//使用Dagger注入RepoService,引用接口以降低耦合
@Inject
RepoService repoService;
//請求倉庫數據
repoService.getRepos(name.toString(), mDataCallback);

10.RepoDataCallback

    /**
* DataCallback實現
*/

private static class RepoCallback extends DataCallbackAdapter<List<Repo>> {
//使用WeakReference
private WeakReference<SearchRepoActivity> searchRepoActivityWeak;

public RepoCallback(SearchRepoActivity searchRepoActivity) {
searchRepoActivityWeak = new WeakReference(searchRepoActivity);
}

@Override
public void postUIError(Throwable e) {
//處理異常
SearchRepoActivity searchRepoActivity = searchRepoActivityWeak.get();
if (searchRepoActivity != null)
searchRepoActivity.showError();
}

@Override
public void postUISuccess(List<Repo> repoList) {
//處理返回的數據
SearchRepoActivity searchRepoActivity = searchRepoActivityWeak.get();
if (searchRepoActivity != null) {
searchRepoActivity.showSuccess(repoList);
}
}

}

示例工程說明:

包結構:

這里寫圖片描述

biz:組織與業務實現有關的代碼,如業務接口,業務實現,和Repository,可以在該包下細分多個業務模塊。
data:組織與數據有關的模塊,如di(依賴注入),bean,datasource,datafetcher等。
presentation:組織UI相關的代碼,如Activity,Fragment,ViewModel等
util:工具類

相關類說明

biz->BaseDaggerRepository:

  • 依賴於google guava的ListenableFuture 實現異步或同步請求,如果你想使用guava,可以繼承這個Repository實現自己特定業務場景的Repository

biz ->BaseObservableRepository

  • 依賴RxJava的Observable 實現異步或同步請求

data ->datasource->BaseObservableDataSource

  • 依賴於RxJava的DataSource,返回Observable

data->datasource->BaseDaggerDataSource

  • 依賴於google guava 實現的DataSource,返回ListenableFuture

data->datasource->BaseDaggerMultiDataSource

  • 依賴於Guava的可以配置多個DataFetcher的DataSource基類。使用它可以實現以下效果:
    這里寫圖片描述
    利用Dagger2可以配置DataFetcher非常容易,只用改變Module中的Porvide配置即可。

當然,你可以通過繼承BaseDataSourceBaseRepository 實現自定義的DataSource或者Repository基類

注:在具體業務場景的Repository或者DataSource中,應避免導入第三方API,例如:

  • RepoDataSource extends BaseObservableDataSource<List<Repo>, List<Repo>>
    除自定義類型和JDK類型外未引入RxJava或者Guava的API,這樣可以最大程度減少第三方庫的侵入性,在需要更換實現時,這些具體類可以不用改變,只用你改變父類的實現即可。

  • 可參考RepoDaggerDataSource,RepoRepository,RepoDaggerDataSource 等類的實現,編寫你的低侵入式類。

interface DataCallback<C>

Repository中有個方法名為setCallback(DataCallback<C> callback) 異步請求時,View層在在調業務方法時可以傳入DataCallback,在異步處理結束后,Repository會通過此回調將數據或者異常返給相應View.

UIDataCallback<C> implements DataCallback<C>

實現了將非UI thread的結果post到UI thread,並且使用了裝飾模式,可以將一個非UIDataCallback<C> 類型包裝為UIDataCallback<C> 類型,在編寫自己的DataCallback時,可以直接繼承DataCallbackAdapter<C>(繼承自UIDataCallback<C>),重寫postUISuccess,postUIError 等方法,在這些方法中進行UI相關的操作。

參考鏈接:

技術分享ppt:
百度網盤:https://pan.baidu.com/s/1pL7qgbh
Dagger2(Google):
Video:https://www.youtube.com/watch?v=oK_XtfXPkqw
Guid:http://google.github.io/dagger/
Github:https://github.com/google/dagger
More:http://fernandocejas.com/2015/04/11/tasting-dagger-2-on-android/

DataBinding:
https://realm.io/news/data-binding-android-boyar-mount/
https://www.zybuluo.com/shark0017/note/256112
http://developer.android.com/intl/zh-cn/tools/data-binding/guide.html


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com