编者:李国帅
qq:9611153 微信lgs9611153
时间:2019-02-20
在android编程的时候,经常会遇到连续点击或ListView下拉,激发事件后向服务器请求数据,等返回后进行数据处理的情况。
为了避免这种情况,一般想到的就是计算两次事件的时间间隔,第二次激发事件的时候直接跳过。网上实现这种方法的方式很多,不过万变不离其宗。类似如下:
这种方法并不是完美的方案,弊端有很多:
1,两次事件之间的间隔多少才算合适呢?不同的情景都不一样,使用统一的时间间隔明显不合适。
2,如果事件激发后需要向后台请求数据,那么网络的好坏和服务器响应时间都会成为制约因素。
3、有的解决方法使用全局静态的时间间隔,那么就会造成所有的事件变成的相关事件,如果两个事件无关而被快速激发的时候,就会造成后面的事件无法执行,从而导致业务逻辑问题。
4、对于ListView RecyclerView这样的控件一次下拉可能多次激发”加载更多”事件,如果网络迟延大于事件间隔,有可能导致重复从同一位置请求的事件。
Android studio
故而,我认为判断是否激发第二次事件,应该根据第一次激发事件的结果而判断。
如果是普通的二次激发,可以直接丢弃;如果是允许的二次激发,那么应该根据第一次的事件响应来判断。
比如listview的下拉事件,明显是可以进行快速重复调用加载事件的。
这就需要添加一个判断标记,当响应之后恢复标记表示可以重新开始激发事件。
示意如下:
//是否正在获取数据 private boolean isLoadingData = false; 初始调用 isLoadingData = false; loadData(curPos); 需要的时候调用 loadData(curPos); 调用之前需要先进行判断 private void loadData(int iPos) { if (onGetDataListener != null && !isLoadingData) { isLoadingData = true; onGetDataListener.getData(iPos);//真正调用接口获取网络数据 } else { SxbLog.e("listAdapter","loadData isLoadingData == " + isLoadingData); } } public void afterGetData() {//调用结束后 //设置数据 //列表中信息数量增加,curPos改变 isLoadingData = false; }
下面列举一个曾经使用过的方案。
1、使用简单变量,控制多次调用retrofit调用
private boolean isAdding = false;// 正在添加 private void addData() { if (isAdding) { return; } isAdding = true; //.... JsonDataCheck.printJsonObj(1, in); MyServiceI service = MyApplication.getServiceI(MyServiceI.WAITTIMEOUT_SENCONDS); Call<AddDataOut> model = service.addData(in);//通过retrofit2传统方法获取数据 model.enqueue(new Callback<AddDataOut>() { public void onResponse(Call<AddDataOut> call, Response<AddDataOut> response) { //数据处理 isAdding = false; }
2、使用锁定类改造
public class ServiceOperate { //... //避免多次操作 private static Integer taskRefreshing = 0; public static void setLock(int value) { synchronized (taskRefreshing) { taskRefreshing = value; } } public static boolean isLock() { synchronized (taskRefreshing) { if (taskRefreshing == 1) { return true; } } return false; } }打开页面的时候初始化
protected void onCreate(Bundle savedInstanceState) {
ServiceOperate.setLock(0);//避免以前被锁定
异步查询
private void addData(final int newStatus) { //... if (ServiceOperate.isLock()) {//查询之前判断是否正在查询 return; } ServiceOperate.setLock(1);//锁定 //... MyServiceI service = MyApplication.getServiceI(MyServiceI.WAITTIMEOUT_SENCONDS); Call<AddDataOut> model = service.addData(in); model.enqueue(new Callback<AddDataOut>() { public void onResponse(Call<AddDataOut> call, Response<AddDataOut> response) { //数据处理 ServiceOperate.setLock(0);//查询后解锁 }
3、使用list中的变量,不在外部使用锁定。
可以把锁定放在xlistview的setDataUpdating函数中
@SuppressLint("HandlerLeak") private final Handler mHandler = new Handler(); class ixListListener implements IXListViewListener { @Override public void onRefresh() { mHandler.postDelayed(new Runnable() { @Override public void run() { if (!mListView.getDataUpdating()) { mListView.setDataUpdating(true);//查询的时候设置 refreshContent(); } } }, 50); } @Override public void onLoadMore() { mHandler.postDelayed(new Runnable() { @Override public void run() { if (!mListView.getDataUpdating()) { mListView.setDataUpdating(true); updateList();// 追加数据。 } } }, 50); } }
查询完成后复原
private void updateList() { if (readTag == 2) { refreshListView();//条件不具备复原 return;// 数据添加完毕 } //... JsonDataCheck.printJsonObj(1, in); MyServiceI service = MyApplication.getServiceI(MyServiceI.WAITTIMEOUT_SENCONDS); Call<QueryListOut> model = service.QueryList(in); model.enqueue(new Callback<QueryListOut>() { public void onResponse(Call<QueryListOut> call, Response<QueryListOut> response) { refreshListView();//查询后复原,间接调用mListView.setDataUpdating(false);当然了,这里只是一个简单的没有太多花哨的技术。
