你的位置:首页 > 操作系统

[操作系统]【轻松一刻】实战项目开发(二) list数据去重 数据追加与缓存


引入开源控件 PullToRefresh 下拉刷新列表

 

每次下拉刷新都会发送请求,从接口返回json信息。

如果前后两次请求返回的数据中有重复的数据 该怎么给list去重 

 

在上一篇中我们重写了实体Data的hashcode和equals方法

/**   * 因为更新时间和unixtime都不是唯一的   * 这里使用唯一标识hashId来得到哈希码   */  @Override  public int hashCode() {    final int prime = 31;    int result = 1;    result = prime * result + ((hashId == null) ? 0 : hashId.hashCode());    return result;  }  /**   * 因为更新时间和unixtime都不是唯一的   * 这里使用唯一标识hashId来比较   */  @Override  public boolean equals(Object obj) {    if (this == obj)      return true;    if (obj == null)      return false;    if (getClass() != obj.getClass())      return false;    Data other = (Data) obj;    if (hashId == null) {      if (other.hashId != null)        return false;    } else if (!hashId.equals(other.hashId))      return false;    return true;  }

因为data数据中只有hashId 是唯一标识,所以我们使用它做比较

 

使用set中不能添加重新元素的特性作为判断条件去重

/**   * 重写Data中的hashCode和equals方法   * 使用set中不能添加重新元素的特性作为判断条件   * 将不重复的data元素依次放入临时的newlist   * 循环完毕后,将原始list清空,addAll(newlist)   * @param list   * @return   */  public static List<Data> removeDuplicateDataInOrder(List<Data> list)  {    HashSet<Data> hashSet = new HashSet<Data>();    List<Data> newlist = new ArrayList<Data>();    for (Iterator iterator = list.iterator(); iterator.hasNext();)    {      Data element = (Data) iterator.next();      if (hashSet.add(element))      {        newlist.add(element);      }    }    list.clear();    list.addAll(newlist);    return list;  }

 

而为了保证数据排列的先后顺序 我们应该在去重复之前做如下操作

 1.如果是下拉刷新 在把数据添加到list的头部 list.addAll(0,list<T>)
 2.如果是上拉加载更多则把数据加载到尾部 list.addAll(list<T>)

 

聚合数据接口 还可以按时间戳返回该时间点前或后的笑话列表

请求示例:http://japi.juhe.cn/joke/content/list.from?key=您申请的KEY&page=2&pagesize=10&sort=asc&time=1418745237

其中参数中的sort param sort desc:指定时间之前发布的,asc:指定时间之后发布的 ,time 表示时间戳

 

所以我们在上拉加载更多时 使用这种请求方式,以当前list中最后一条数据的时间戳为节点 返回该时间点前笑话列表。

故上拉加载更多时,list没有必要再去重复了、

if(flag == PULL_DOWN_REFRESH_FLAG){          // 将新刷新的数据 添加到数据集头部          mCurrentListItems.addAll(0,result.getResult());          // 去除list中重复的数据          mCurrentListItems = UtilsHelper.removeDuplicateDataInOrder(mCurrentListItems);        }else if(flag == PULL_UP_REFRESH_FLAG){          // 将新加载的数据 添加到数据集尾部          // 因为是以最后一条数据的时间戳向前查询,故不存在重复,无需list去重          mCurrentListItems.addAll(result.getResult());          pullUpPageNumber++;        }

 

如何得到本次请求之后更新了多少条数据呢,很简单只要保存上次的list,并与最新的list的size做对比即可。

使用handler处理,并在主线程更新UI

if(mLastListItems != null){  int count = mCurrentListItems.size() - mLastListItems.size();    android.os.Message msg = new android.os.Message();  msg.what = UPDATE_DATA_COUNT_MESSAGE;  msg.arg1 = count;  mHandler.sendMessage(msg);}

 

然后通知adapter数据集发生改变,并调用listview (listview是PullToRefreshListView的实例) 的onRefreshComplete()方法

// 通知程序数据集已经改变,如果不做通知,那么将不会刷新mListItems的集合mAdapter.notifyDataSetChanged(); mListView.onRefreshComplete();

 

假设我们处于没有网络的环境,那就有必要搞一个缓存了,以便离线查看,最好是缓存上次我们退出应用时显示的list数据。

没有使用sqlite,暂时使用文件,缓存到sdcard的一个目录中(/storage/emulated/0/qingsongyike/cache)

public static void saveJsonTextInLocalFile(String jsonData){    boolean sdCardExist = Environment.getExternalStorageState()         .equals(android.os.Environment.MEDIA_MOUNTED); //判断sd卡是否存在     if(sdCardExist){      Log.d("UtilsHelper", "sdcard exist.");      File root = Environment.getExternalStorageDirectory();      String path = root.getPath()+File.separator+"qingsongyike"+File.separator+"cache";      Log.d("UtilsHelper", "dir path = "+ path);      File file = new File(path);      if(!file.exists()){        file.mkdirs();        Log.d("UtilsHelper", "cache dir had create finished.");      }            File cacheFile = new File(path+File.separator+"cache.txt");      if(!cacheFile.exists()){        try {          cacheFile.createNewFile();          Log.d("UtilsHelper", "file path = "+ path);          Log.d("UtilsHelper", "cache file is created.");        } catch (IOException e) {          e.printStackTrace();        }      }      try {        synchronized (obj) {          FileOutputStream fos = new FileOutputStream(cacheFile,false);          OutputStreamWriter writer = new OutputStreamWriter(fos);          writer.write(jsonData);          writer.flush();          writer.close();          fos.close();        }        Log.d("UtilsHelper", "cache data write finished.");        //fos.write(JsonData.getBytes());        //fos.flush();      } catch (FileNotFoundException e) {        e.printStackTrace();      } catch (IOException e) {        e.printStackTrace();      }    }else {      Log.d("UtilsHelper", "sdcard is not exist. Create cache file failed");    }  }

 

那我们需要将list数据重新组装成对象,并使用GSON的API函数将bean转换成json字符串

开一个子线程去做IO操作

class CacheThread implements Runnable{    @Override    public void run() {      // 执行缓存操作 将刷新后最新的数据重组成对象 并转换成Json字符      // 不够50条 则都缓存       // 超过50条 则只缓存前50条       // 避免缓存文件较大 读取速度缓慢      String jsonData = null;      List<Data> list = new ArrayList<Data>();      list.addAll(mCurrentListItems);      if(list.size() > 0 && list.size() < 50){        jsonData = UtilsHelper.beanConvertToJson(new Message(0, "Success", list));      }else if(list.size() >= 50){        // 将list中50条之后的都移除        Log.d("CacheThread", "list size is more than 50 !!");        for (int i = list.size()-1; i >= 50; i--) {          list.remove(i);        }        Log.d("CacheThread", "list remove finish. size now is 50 !!");        jsonData = UtilsHelper.beanConvertToJson(new Message(0, "Success", list));      }else {        Log.d("CacheThread", "list size is 0 !!");      }            // 将Json字符保存到本地 以便没有网络时离线浏览      if(jsonData != null){        UtilsHelper.saveJsonTextInLocalFile(jsonData);      }else {        Log.d("CacheThread", "jsonData is null !!");      }    }      }

这里将只截取最多50条笑话信息,以避免数据量大造成的读写缓慢的问题。

 

其中将bean转换成json字符串的函数如下

// bean转换json  public static String beanConvertToJson(Object obj){    Gson gson = new Gson();    String json = gson.toJson(obj);    Log.d("UtilsHelper", "json = " + json);    return json;  }

 

不要忘了在AndroidiManfest.

<!-- 在SDCard中创建与删除文件权限 --><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><!-- 往SDCard写入数据权限 --><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><!-- 从SDCard读取数据权限 --><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

 

只要有更新数据 我们就执行一次本地缓存的操作

 

 

打开文件 内容已经写进去了

 

未完待续。。。