动态显示listview的数据,同步机制
来源:互联网 发布:传奇3 网络传输异常 编辑:程序博客网 时间:2024/06/03 17:41
不知道怎么上传动图,下面这张是在动态跳出数据的时候截出来的图。下面记录一下,怎么做出灵活的动态显示listview数据的效果。
这是在动态显示的过程中截的一张效果图,接下来记录一下如何做出动态显示listview数据的效果.
涉及到的知识点包括:
1.AyncTask机制
2.synchronize同步代码块
关于AsyncTask这里不做过多讲述,不是很了解的童鞋建议先去看一下相关的使用方法。
关于synchronize同步代码块的相关知识点(简单描述):
1.每个java对象都有一个实现同步的锁,这些锁称为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
2.锁分为类锁和对象锁:修饰静态方法时为类锁,当A线程访问X类的静态方法时,B线程不能访问X类的任何被synchronize修饰的静态方法(但是可以访问未被synchronize修饰的静态方法),只能等待(处于阻塞状态)A线程执行完该同步方法后才能执行该方法。修饰普通方法或代码块时为对象锁,当A线程访问X类的一个实例对象的静态方法或代码块时,B线程不能访问同一个对象的所有被synchronize修饰的普通方法或代码块(但是可以访问未被synchronize修饰的方法或代码块),只能等待A线程执行完该同步方法或同步代码块后释放内置锁,处于阻塞状态的B线程才可开始访问。
3.类锁和对象锁不是互斥的。当A线程访问X类的同步静态方法时,B线程可以访问X类对象实例的同步方法或代码块。
待会我会说明一下为什么我们会用到synchronize这个知识点,下面我上一段关键的代码片段:
</pre><pre class="java" name="code">public class AsyncAddressBook extends AsyncTask<String, Integer, Object> { private TextView tv_progress; private ProgressBar progressBar; private ContactArrayList contacts; private AddressBookAdapter contactAdapter; private SwipeMenuListView swipeMenuListView; private int action = -1; private Context context; public AsyncAddressBook(TextView tv_progress, ProgressBar progressBar, ContactArrayList contacts, AddressBookAdapter contactAdapter, SwipeMenuListView swipeMenuListView) { this.tv_progress = tv_progress; this.progressBar = progressBar; this.contacts = contacts; this.contactAdapter = contactAdapter; this.swipeMenuListView = swipeMenuListView; } public AsyncAddressBook(Context context,ContactArrayList contacts, AddressBookAdapter contactAdapter, SwipeMenuListView swipeMenuListView) { this.contacts = contacts; this.context=context; this.contactAdapter = contactAdapter; this.swipeMenuListView = swipeMenuListView; } /** * 0展示全部数据 1展示与编辑框匹配的数据 * * @param action */ public void setAction(int action) { this.action = action; } @Override protected void onPreExecute() { super.onPreExecute(); if (progressBar != null && tv_progress != null) { tv_progress.setVisibility(View.VISIBLE); tv_progress.setText("0/0"); progressBar.setVisibility(View.VISIBLE); progressBar.setProgress(0); } } @Override protected Object doInBackground(String... params) { switch (action) { case 0: //查询所有数据 selectAllContacts(); break; case 1: //模糊查询 selectMatchContacts(params[0]); break; } return null; } @Override protected void onPostExecute(Object o) { super.onPostExecute(o); contactAdapter.sort(); contactAdapter.notifyDataSetChanged(); if (progressBar != null && tv_progress != null) { progressBar.setVisibility(View.GONE); tv_progress.setVisibility(View.GONE); } swipeMenuListView.setSelection(0); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); if (progressBar != null && tv_progress != null) { progressBar.setProgress(values[0]); tv_progress.setText(values[0] + "/" + progressBar.getMax()); } contactAdapter.notifyDataSetChanged(); swipeMenuListView.setSelection(values[0]); } /** * 获取数据库所有联系人 */ public void selectAllContacts() { contacts.clear(); SQLiteDatabase sql_read = new DB_Helper(tv_progress.getContext()).getReadableDatabase(); Cursor cursor = sql_read.query(DB_Constants.AddressBook_TableName, DB_Constants.AddressBookColumns, null, null, null, null, null); if (cursor.getCount() > 0) { progressBar.setMax(cursor.getCount()); sql_read.beginTransaction(); while (cursor.moveToNext()) { Contact contact = new Contact(); contact.setContact_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_ID))); contact.setContact_user_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_User_ID))); contact.setNick_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Nick_Name))); contact.setComp_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Comp_Name))); contact.setUser_image(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Image))); contact.setArea(cursor.getString(cursor.getColumnIndex(DB_Constants.Area))); contact.setCreate_time(cursor.getString(cursor.getColumnIndex(DB_Constants.Create_Time))); contact.setUser_role(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Role))); contacts.add(contact); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } publishProgress(contacts.size()); } sql_read.setTransactionSuccessful(); sql_read.endTransaction(); } cursor.close(); sql_read.close(); } /** * 查询包含S的所有联系人 */ public void selectMatchContacts(String s) { contacts.clear(); SQLiteDatabase sql_read = new DB_Helper(context).getReadableDatabase(); Cursor cursor = sql_read.query(DB_Constants.AddressBook_TableName, DB_Constants.AddressBookColumns, DB_Constants.Nick_Name + " like '%" + s + "%' ", null, null, null, null); if (cursor.getCount() > 0) { sql_read.beginTransaction(); while (cursor.moveToNext()) { Contact contact = new Contact(); contact.setContact_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_ID))); contact.setContact_user_id(cursor.getInt(cursor.getColumnIndex(DB_Constants.Contact_User_ID))); contact.setNick_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Nick_Name))); contact.setComp_name(cursor.getString(cursor.getColumnIndex(DB_Constants.Comp_Name))); contact.setUser_image(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Image))); contact.setArea(cursor.getString(cursor.getColumnIndex(DB_Constants.Area))); contact.setCreate_time(cursor.getString(cursor.getColumnIndex(DB_Constants.Create_Time))); contact.setUser_role(cursor.getString(cursor.getColumnIndex(DB_Constants.User_Role))); contacts.add(contact); if (contacts.size() > 0) { publishProgress(contacts.size()); } } sql_read.setTransactionSuccessful(); sql_read.endTransaction(); } cursor.close(); sql_read.close(); }}
说一下在测试过程中遇到的问题及解决办法:
问题:contactAdapter的数据源是contacs,contacts的数据改变(contacts.add(XXX))是在子线程中进行的,而listview数据的刷新(contactAdapter.notifyDataSetChanged())是在主线程中进行的。当异步任务的后台线程(简称A线程)执行完contacts.add(xxx)方法后,立即调用publishProgress(contacts.size());在主线程(简称B线程)更新进度条。这是两个不同的线程,A线程在调用publishProgress(contacts.size())后仍然会往下执行。而B线程现在正在刷新listview的数据,这个时候肯定会调用contacts.get(position)方法,而因为A线程因为继续在执行,所以这个时候很有可能A线程正在调用contacts.add(XXX)方法。此时,两个线程访问的是同一个对象,但是读到的数据却可能会不一致,所以如果上述代码中的contacts假设是一个普通的ArrayList类型,运行的时候偶尔会出现异常奔溃,奔溃原因是“在另一个线程改变了数据源,却没有即时告知adapter在主线程刷新数据”,产生的原因就是我们在adapter刷新数据时,数据源的值又变化了。
解决办法:这个时候可以考虑用同步机制了。这种情况可以用对象锁。分析可得,可能会产生异常奔溃的原因在于contacts这个数据源的add方法和get方法。所以我们用synchronize修饰add方法和get方法,这样当线程A访问add方法时,线程B不能再访问这个对象的get方法,只能等线程A执行完add方法后,线程B才能访问这个对象的get方法。这样就可以避免两个线程访问到的数据源不一致的问题。下面上一段自定义的ArrayList类.
<pre class="java" name="code">public class ContactArrayList extends ArrayList<Contact> { @Override public void add(int index, Contact object) { //对象锁,this指当前对象 synchronized (this) { //被上锁的代码块 super.add(index, object); } } @Override public Contact get(int index) { synchronized (this) { return super.get(index); } }}
现在就可以实现灵活的数据刷新啦!~synchronize要谨慎使用,因为它是阻塞线程的,会消耗性能。
转载请注明出处!
与 君 共 勉
- 动态显示listview的数据,同步机制
- Android 根据Edittext内容动态改变Listview显示的数据
- ListView动态模糊搜索显示ArrayList数据
- ListView的动态更新数据
- ListView的显示和缓存机制
- Android数据的分页 listview显示数据
- 从网络读取数据并动态的显示在ListView中
- 从网络读取数据并动态的显示在ListView中
- 如何动态刷新ListView的显示---notifyDataSetChanged
- 动态设置ListView可以显示的高度
- 记录开发中listview的动态显示
- 在ListView中显示图片及ListView的动态刷新
- Android 根据EditText搜索框ListView动态显示数据
- Android 根据EditText搜索框ListView动态显示数据
- Android 根据EditText搜索框ListView动态显示数据小结
- EditText+ListView实现从动态显示数据库数据
- Android 根据EditText搜索框ListView动态显示数据
- android自定义listview格式动态更新显示数据测试
- bitnami redmine 邮件 配置
- TCP为什么要三次握手,不是两次四次?
- Firefox显示xml文件或者xml类型的返回时遇到的问题
- windows 窗口类类型
- FTP主动模式和被动模式的区别
- 动态显示listview的数据,同步机制
- svg文件在不同浏览器中显示有差异svg
- apache-activemq-5.14.0设置自启动和加入服务管理
- Java 使用 SQLite数据库
- SharedPreferences封装类SPUtils
- Android 动画 - TranslateAnimation位移动画
- 多数据源连接池
- Android 5.X 新特性详解(一)——主题、Palette、阴影、着色和裁剪
- 汇编排序算法代码总结