前言
在上一篇我讲了Agera的基本概念和原理,但是有人估计会说你这讲的是什么呀,看的我好懵逼呀,talk is cheap,show me your code!我接下来就结合demo来讲一下Agera的用法。。
功能分析
接下来要写的demo是一个下拉刷新改变数据的demo。这个功能很常见吧,在很多的应用中都有这个功能吧,下拉之后向网络发送请求,然后改变数据。接下来利用这个demo来更好演示Agera的用法。
利用回调
我们很常见的思路就是回调吧,当数据加载成功或者失败后回调给页面。因此接下来就用这种思路完成demo。首先分析下流程:
- 下拉
- 获取数据
- 更新页面
因此需要监听下拉这个事件的触发,因此就需要一个observable同时实现OnRefreshListener,就命名为OnRefreshObservable,接着获取数据的对象需要监听这个下拉事件,又要通知界面更新,因此就需要实现observable和updatable的责任,同时又要提供数据,也就也需要实现Supplier的责任。那么为什么需要回调呢?因为往往获取数据是一个耗时的任务,我们会新开一个线程用于处理,那么当他失败或者成功的时候使用接口回调进行处理,就命名为UsernamesRepository。最后就是页面更新了,页面需要监听UsernamesRepository,也就需要实现updatable的责任。流程分析了,就下来就该写代码了
首先先描述一下界面:
#MainActivity
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
#activity_main
<?xml version="1.0" encoding="utf-8"?><LinearLayout 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" android:orientation="vertical"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:minHeight="?attr/actionBarSize" app:popupTheme="@style/ThemeOverlay.AppCompat.Light" app:theme="@style/Toolbar" /> <fragment android:id="@+id/fragment" android:name="com.example.lizheng.myapplication.MainFragment" android:layout_width="match_parent" android:layout_height="match_parent" /></LinearLayout>
#main_frag
<?xml version="1.0" encoding="utf-8"?><android.support.v4.widget.SwipeRefreshLayout android:id="@+id/refresh_layout" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <ListView android:id="@+id/list" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin"/></android.support.v4.widget.SwipeRefreshLayout>
布局很简单,fragment由一个下拉刷新控件内嵌套一个listview组成。mainActivity则是由一个toolbar和这个fragment组成。布局写好了,接下来就得按着流程来实现功能了。
首先就是需要让该OnRefreshListener实现Observable功能
public class OnRefreshObservable extends BaseObservable
implements SwipeRefreshLayout.OnRefreshListener {
//当下拉刷新触发时同时通知监听者更新,这样就让OnRefreshListener同时实现了observable的功能
@Override
public void onRefresh() {
dispatchUpdate();
}
}
接下来就应该实现获取数据的功能:
#UsernamesFetcher
//具体获取姓名数据
public class UsernamesFetcher {
//模拟数据数量为4
public static int NUMBER_OF_USERS = 4;
//模拟获取数据,采取新线程异步加载,利用接口回调
public void getUsernames(final UsernamesCallback callback) {
if (NUMBER_OF_USERS < 0) {
callback.setError();
return;
}
new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000); //模拟网络耗时
// Create a fake list of usernames
Log.i("MainActivity","current "+Thread.currentThread().getName()+" "+currentThread().getId());
String name1 = "Joe";
String name2 = "Amanda";
final List<String> usernames = new ArrayList<String>();
Random random = new Random();
for (int i = 0; i < NUMBER_OF_USERS; i++) {
int number = random.nextInt(50);
if (System.currentTimeMillis() % 2 == 0) {
usernames.add(name1 + number);
} else {
usernames.add(name2 + number);
}
}
callback.setUsernames(usernames.toArray(new String[usernames.size()]));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
//回调接口,用于区分数据获取成功还是失败
public interface UsernamesCallback {
void setError();
void setUsernames(String[] usernames);
}
}```
上面这个类是具体的获取数据的类,里面封装了获取数据的函数和相关回调接口。之后便是最为关键的UsernamesRepository:
//该类需要实现observable,updatable和supplier功能
public class UsernamesRepository extends BaseObservable
implements Supplier<String[]>, Updatable, UsernamesFetcher.UsernamesCallback {
private String[] usernames;//姓名数据列表
private boolean lastRefreshError;//用于判定是否更新成功
private final UsernamesFetcher usernamesFetcher;
public UsernamesRepository(UsernamesFetcher usernamesFetcher) {
super();
this.usernamesFetcher = usernamesFetcher;
}
/**
* 获取最新的数据,继承自Supplier
*/
@NonNull
@Override
public String[] get() {
return usernames;
}
/**
* 返回值表示最近更新是否成功
*/
public boolean isError() {
return lastRefreshError;
}
/**
* 继承自updatable,更新姓名数据
*/
@Override
public void update() {
usernamesFetcher.getUsernames(this);
}
/**
* 当数据获取失败时,回调接口调用
*/
@Override
public void setError() {
lastRefreshError = true;
dispatchUpdate(); //通知UsernamesRepositoryuser的监听者更新
}
/**
* 获取数据成功时调用
*/
@Override
public void setUsernames(String[] usernames) {
this.usernames = usernames;
lastRefreshError = false;
dispatchUpdate();
}
/**
* 当从0个监听者到1个监听者时调用。
*/
@Override
protected void observableActivated() {
update();
}
}```
之后就是MainFragment:
public class MainFragment extends Fragment implements Updatable {
private OnRefreshObservable refreshObservable;
private UsernamesRepository usernamesRepository;
private ListAdapter listAdapter;
private ListView listView;
private SwipeRefreshLayout swipeRefreshLayout;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.main_frag, container, false);
listView = (ListView) root.findViewById(R.id.list);
//设置下拉刷新相关设置并且将监听器绑定到view上,这样,refreshObservable就设置好了
refreshObservable = new OnRefreshObservable();
swipeRefreshLayout = (SwipeRefreshLayout) root.findViewById(R.id.refresh_layout);
swipeRefreshLayout.setColorSchemeColors(
ContextCompat.getColor(getActivity(), R.color.colorPrimary),
ContextCompat.getColor(getActivity(), R.color.colorAccent),
ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark));
swipeRefreshLayout.setOnRefreshListener(refreshObservable);
// 初始化UsernamesRepository
usernamesRepository = new UsernamesRepository(new UsernamesFetcher());
return root;
}
@Override
public void onResume() {
super.onResume();
//使repository监听refreshObservable
refreshObservable.addUpdatable(usernamesRepository);
//mainFragment监听repository
usernamesRepository.addUpdatable(this);
// 确保每次进入应用时显示刷新动画
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(true);
}
});
}
@Override
public void onPause() {
super.onPause();
//避免内存泄漏,移除观察者
refreshObservable.removeUpdatable(usernamesRepository);
usernamesRepository.removeUpdatable(this);
}
@Override
public void update() {
//更新完数据后取消动画
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
swipeRefreshLayout.setRefreshing(false);
}
});
// 检查数据获取错误
if (usernamesRepository.isError()) {
Toast.makeText(getContext(), getResources().getString(R.string.error),
Toast.LENGTH_LONG).show();
} else {
// 更新页面数据
listAdapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_list_item_1, usernamesRepository.get());
listView.setAdapter(listAdapter);
}
}
}
MainActivity则是简单的几行代码:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
既然代码写好了,接下来就结合代码分析下事件的流程,但是要想知道代码如何运行,就要先知道Agera的事件传递机制:
- observable首次添加updatable->addUpdatable(Updatable)->workerhandler(handler.obtainMessage(WorkerHandler.MSG_FIRST_ADDED, this).sendToTarget();)->workerHandler.callFirstUpdatableAdded()->observable.observableActivated().
- 当observable发出事件->调用dispatchUpdate()->workerhandler发送信息(handler.obtainMessage(MSG_UPDATE, this).sendToTarget();)->workerhandler调用sendUpdate()->如果是当前线程和添加updatable时所在的线程是同一线程,监听者直接开始update,否则workhandler发送消息(handler.obtainMessage(WorkerHandler.MSG_CALL_UPDATABLE, updatable).sendToTarget();),然后updatable更新。
- 当observable移除最后一个updatable->removeUpdatable(Updatable)->workerhandler发送信息(handler.obtainMessage(MSG_LAST_REMOVED, this).sendToTarget();)->workerHandler调用callLastUpdatableRemoved()->observable.observableDeactivated()
上面讲了Agera的几个事件的传递流程,接下来就结合demo分析代码的执行流程了:
- 进入应用 onResume()->为refreshObservable添加监听者usernamesRepository->为usernamesRepository添加监听者MainFragment->(中间省略了一些Agera的传递)usernamesRepository.observableActivated->usernamesRepository.update(重写了observableActivated(),调用了update())->swipeRefreshLayout.setRefreshing(true)(进入应用时显示刷新动画)->MainFragment.update().这样当进入界面的时候就更新完毕了。
- 下拉刷新触发OnRefreshObservable.onRefresh()->(重写了onRefresh方法)dispatchUpdate()(继承自BaseObservable)->(省略了Agera的传递机制)UsernamesRepository.update()->usernamesFetcher.getUsernames()获取数据->如果获取成功UsernamesRepository调用setUsernames,否则调用serError->dispatchUpdate()(继承自BaseObservable)->MainFragment开始updata()。这样一次下拉刷新就完成了数据的更新。
- 退出应用或者使其成为后台应用一定会发生onpause()->refreshObservable.removeUpdatable(usernamesRepository)为refreshObservable移除监听者->refreshObservable.observableDeactivated()->usernamesRepository.removeUpdatable(this)为usernamesRepository移除监听者->usernamesRepository.observableDeactivated().这样但不需要下拉刷新的时候就移除观察者使其不能再刷新数据,同时这也是为了避免内存泄漏。让observable添加和删除updatable与fragment等的生命周期相对应。
利用CompiledRepository
首先你得知道Repository是什么吧
public interface Repository<T> extends Observable, Supplier<T> {}
所以我们知道repository实现了被观察者的作用同时也能提供数据。那么CompiledRepository
final class CompiledRepository extends BaseObservable implements Repository, Updatable, Runnable{
...
}
那么如何使用它呢?
public class MainFragment extends Fragment implements Updatable {
private OnRefreshObservable refreshObservable;
private Repository<Result<List<String>>> usernamesRepository;
private ListAdapter listAdapter;
private ListView listView;
private SwipeRefreshLayout swipeRefreshLayout;
private ExecutorService networkExecutor;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.main_frag, container, false);
listView = (ListView) root.findViewById(R.id.list);
refreshObservable = new OnRefreshObservable();
swipeRefreshLayout = (SwipeRefreshLayout) root.findViewById(R.id.refresh_layout);
swipeRefreshLayout.setColorSchemeColors(
ContextCompat.getColor(getActivity(), R.color.colorPrimary),
ContextCompat.getColor(getActivity(), R.color.colorAccent),
ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark));
swipeRefreshLayout.setOnRefreshListener(refreshObservable);
setUpRepository();
return root;
}
private void setUpRepository() {
//后台执行
networkExecutor = newSingleThreadExecutor();
//设置repository,关键代码
usernamesRepository = Repositories
.repositoryWithInitialValue(Result.<List<String>>absent())
.observe(refreshObservable)
.onUpdatesPerLoop()
.goTo(networkExecutor)
.thenGetFrom(new UsernamesSupplier())
.compile();
}
@Override
public void onResume() {
super.onResume();
usernamesRepository.addUpdatable(this);
swipeRefreshLayout.post(new Runnable() {
@Override
public void run() {
update();
}
});
}
@Override
public void onPause() {
super.onPause();
usernamesRepository.removeUpdatable(this);
}
@Override
public void update() {
//进行获取数据的判定
//如果拿到的是最初的数据就开始刷新
if (usernamesRepository.get().isAbsent()) {
swipeRefreshLayout.setRefreshing(true);
//如果数据获取失败就提示
} else if (usernamesRepository.get().failed()) {
Toast.makeText(getContext(), getResources().getString(R.string.error),
Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
} else {
listAdapter = new ArrayAdapter<String>(getContext(),
android.R.layout.simple_list_item_1, usernamesRepository.get().get());
listView.setAdapter(listAdapter);
swipeRefreshLayout.setRefreshing(false);
}
}
}```
大部分代码和之前类似。。而这一句代码便是CompiledRepository的使用方法
usernamesRepository = Repositories
.repositoryWithInitialValue(Result.<List<String>>absent())
.observe(refreshObservable)
.onUpdatesPerLoop()
.goTo(networkExecutor)
.thenGetFrom(new UsernamesSupplier())
.compile();
感觉没有看过源代码也能理解这句话,果然使用很简单
.repositoryWithInitialValue(Result.<List<String>>absent())是创建一个Repository并为其赋初值;
.observe(refreshObservable)表示监听refreshObservable;
.onUpdatesPerLoop()表示更新的频率
.goTo(networkExecutor)表示将指令在后台执行
.thenGetFrom(new UsernamesSupplier())表示从UsernamesSupplier中获取数据
.compile得到repository实例。但是为什么要这么写呢?因为这种repository是有state的,你得按照状态一步一步来
public interface RepositoryCompilerStates {
interface REventSource<TVal, TStart> {
@NonNull
RFrequency<TVal, TStart> observe(@NonNull Observable... observables);
}
interface RFrequency<TVal, TStart> extends REventSource<TVal, TStart> {
@NonNull
RFlow<TVal, TStart, ?> onUpdatesPer(int millis);
@NonNull
RFlow<TVal, TStart, ?> onUpdatesPerLoop();
}
interface RFlow<TVal, TPre, TSelf extends RFlow<TVal, TPre, TSelf>>
extends RSyncFlow<TVal, TPre, TSelf> {
@NonNull
@Override
<TCur> RFlow<TVal, TCur, ?> getFrom(@NonNull Supplier<TCur> supplier);
@NonNull
@Override
<TCur> RTermination<TVal, Throwable, RFlow<TVal, TCur, ?>> attemptGetFrom(
@NonNull Supplier<Result<TCur>> attemptSupplier);
@NonNull
@Override
<TAdd, TCur> RFlow<TVal, TCur, ?> mergeIn(@NonNull Supplier<TAdd> supplier,
@NonNull Merger<? super TPre, ? super TAdd, TCur> merger);
@NonNull
@Override
<TAdd, TCur> RTermination<TVal, Throwable, RFlow<TVal, TCur, ?>> attemptMergeIn(
@NonNull Supplier<TAdd> supplier,
@NonNull Merger<? super TPre, ? super TAdd, Result<TCur>> attemptMerger);
@NonNull
@Override
<TCur> RFlow<TVal, TCur, ?> transform(@NonNull Function<? super TPre, TCur> function);
@NonNull
@Override
<TCur> RTermination<TVal, Throwable, RFlow<TVal, TCur, ?>> attemptTransform(
@NonNull Function<? super TPre, Result<TCur>> attemptFunction);
@NonNull
TSelf goTo(@NonNull Executor executor);
@NonNull
RSyncFlow<TVal, TPre, ?> goLazy();
}
interface RSyncFlow<TVal, TPre, TSelf extends RSyncFlow<TVal, TPre, TSelf>> {
@NonNull
<TCur> RSyncFlow<TVal, TCur, ?> getFrom(@NonNull Supplier<TCur> supplier);
@NonNull
<TCur>
RTermination<TVal, Throwable, ? extends RSyncFlow<TVal, TCur, ?>> attemptGetFrom(
@NonNull Supplier<Result<TCur>> attemptSupplier);
@NonNull
<TAdd, TCur> RSyncFlow<TVal, TCur, ?> mergeIn(@NonNull Supplier<TAdd> supplier,
@NonNull Merger<? super TPre, ? super TAdd, TCur> merger);
@NonNull
<TAdd, TCur>
RTermination<TVal, Throwable, ? extends RSyncFlow<TVal, TCur, ?>> attemptMergeIn(
@NonNull Supplier<TAdd> supplier,
@NonNull Merger<? super TPre, ? super TAdd, Result<TCur>> attemptMerger);
@NonNull
<TCur> RSyncFlow<TVal, TCur, ?> transform(@NonNull Function<? super TPre, TCur> function);
@NonNull
<TCur> RTermination<TVal, Throwable, ? extends RSyncFlow<TVal, TCur, ?>> attemptTransform(
@NonNull Function<? super TPre, Result<TCur>> attemptFunction);
@NonNull
RTermination<TVal, TPre, TSelf> check(@NonNull Predicate<? super TPre> predicate);
@NonNull
<TCase> RTermination<TVal, TCase, TSelf> check(
@NonNull Function<? super TPre, TCase> caseFunction,
@NonNull Predicate<? super TCase> casePredicate);
@NonNull
TSelf sendTo(@NonNull Receiver<? super TPre> receiver);
@NonNull
<TAdd> TSelf bindWith(@NonNull Supplier<TAdd> secondValueSupplier,
@NonNull Binder<? super TPre, ? super TAdd> binder);
@NonNull
RConfig<TVal> thenSkip();
@NonNull
RConfig<TVal> thenGetFrom(@NonNull Supplier<? extends TVal> supplier);
@NonNull
RTermination<TVal, Throwable, RConfig<TVal>> thenAttemptGetFrom(
@NonNull Supplier<? extends Result<? extends TVal>> attemptSupplier);
@NonNull
<TAdd> RConfig<TVal> thenMergeIn(@NonNull Supplier<TAdd> supplier,
@NonNull Merger<? super TPre, ? super TAdd, ? extends TVal> merger);
@NonNull
<TAdd> RTermination<TVal, Throwable, RConfig<TVal>> thenAttemptMergeIn(
@NonNull Supplier<TAdd> supplier,
@NonNull Merger<? super TPre, ? super TAdd,
? extends Result<? extends TVal>> attemptMerger);
/**
* Perform the {@link #transform} directive and use the output value as the new value of the
* compiled repository, with notification if necessary.
*/
@NonNull
RConfig<TVal> thenTransform(
@NonNull Function<? super TPre, ? extends TVal> function);
@NonNull
RTermination<TVal, Throwable, RConfig<TVal>> thenAttemptTransform(
@NonNull Function<? super TPre, ? extends Result<? extends TVal>> attemptFunction);
}
interface RTermination<TVal, TTerm, TRet> {
@NonNull
TRet orSkip();
@NonNull
TRet orEnd(@NonNull Function<? super TTerm, ? extends TVal> valueFunction);
}
interface RConfig<TVal> {
@NonNull
RConfig<TVal> notifyIf(@NonNull Merger<? super TVal, ? super TVal, Boolean> checker);
@NonNull
RConfig<TVal> onDeactivation(@RepositoryConfig int deactivationConfig);
@NonNull
RConfig<TVal> onConcurrentUpdate(@RepositoryConfig int concurrentUpdateConfig);
@NonNull
Repository<TVal> compile();
@NonNull
<TVal2> RFrequency<TVal2, TVal> compileIntoRepositoryWithInitialValue(@NonNull TVal2 value);
}
}
我们可以看到这个状态接口里面定义了很多状态,里面的方法真的很多,你会发现我们在使用的使用所调用的方法都包含在里面。而且每个方法返回的不是repository,而是一些状态,这就意味着我们在构建CompiledRepository时的流程是一步步往前推进的,这就说明过程是不可逆的。
接下来就介绍集中关键的状态,其它的状态基本是继承于它们:
REventSource:这个是初始状态,Repositories.repositoryWithInitialValue()这个方法的返回的结果就是REventSource,表明事件源的开始。
RFrequency:表示事件源发送的频率。
RFlow:表示数据处理流,这里定义的方法都是和数据处理相关的,比如getFrom(),mergeIn()等等。但是要注意函数的返回值,如果是RFlow说明处理还在继续,如果是RTermination说明处理已经结束。
RTermination:表示最后数据处理后的终止操作。
RConfig:其余各种配置,比如onDeactivation():repository去活化会执行什么操作之类。
这样定义状态之后,我们能很清楚的知道进行到哪一步流程,也就更好的使用CompliedRepository(用自动补全太爽了)。
初始化(Repositories.repositoryWithInitialValue(…))->
设置监听事件(observe())->
规定事件发送的频率(onUpdatesPerLoop()等)->
处理数据流(各种处理函数)->
结束数据流->
配置一些属性(onDeactivation()等)->
complie()。
这样整个处理流程就完整了。但是你可能会说,这些都只是接口,它怎么实现的呢?那么我们就得关注repositoryWithInitialValue()这个函数:
public static <T> REventSource<T, T> repositoryWithInitialValue(@NonNull final T initialValue) {
return RepositoryCompiler.repositoryWithInitialValue(initialValue);
}
static <TVal> RepositoryCompilerStates.REventSource<TVal, TVal> repositoryWithInitialValue(
@NonNull final TVal initialValue) {
。。。
return compiler.start(initialValue);
}
private RepositoryCompiler start(@NonNull final Object initialValue) {
。。。
return this;
}
也就是repositoryWithInitialValue(…)返回了一个RepositoryCompiler,而状态接口的所有使用方法都在其内部实现了。。那compile()返回了什么呢?
public Repository compile() {
Repository repository = compileRepositoryAndReset();
recycle(this);
return repository;
}
private Repository compileRepositoryAndReset() {
checkExpect(CONFIG);
Repository repository = compiledRepository(initialValue, eventSources, frequency, directives,
notifyChecker, concurrentUpdateConfig, deactivationConfig);
。。。
return repository;
}```
RepositoryCompiler通过compile()生成CompiledRepository。这样CompiledRepository的生成流程就完成了。是不是感觉用法很简单呀,整个流程是很清晰的,你只需要按照思路一步步往下些就好了。
最后附上UsernamesSupplierde代码:
public class UsernamesSupplier implements Supplier<Result<List<String>>> {
public static int NUMBER_OF_USERS = 4;
private static final String TAG = UsernamesSupplier.class.getSimpleName();
private List<String> getUsernames() {
//模拟延时
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Log.e(TAG, e.toString());
return null;
}
if (NUMBER_OF_USERS < 0) {
return null;
}
String name1 = "Joe";
String name2 = "Amanda";
final List<String> usernames = new ArrayList<String>();
Random random = new Random();
for (int i = 0; i < NUMBER_OF_USERS; i++) {
int number = random.nextInt(50);
if (System.currentTimeMillis() % 2 == 0) {
usernames.add(name1 + number);
} else {
usernames.add(name2 + number);
}
}
return usernames;
}
@NonNull
@Override
public Result<List<String>> get() {
List<String> usernames = getUsernames();
if (usernames == null) {
return Result.failure();
} else {
return Result.success(getUsernames());
}
}
}
这样通过两种方法实现下拉刷新数据就完成了
小结
上面写的有点乱,第一种是使用基本的observable和updatable来实现;第二种是使用CompliedRepository来实现。这篇文章主要介绍了一下用法,具体的内部逻辑小伙伴还是要自己看源码哒!!!
写得有误的地方还请小伙伴们多多指出!!!