热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

[置顶]动态显示listview的数据,同步机制

不知道怎么上传动图,下面这张是在动态跳出数据的时候截出来的图。下面记录一下,怎么做出灵活的动态显示listview数据的效果。这是在动态显示的过程中截的一张效果图,接下来记录一下如何做出动态显示lis


不知道怎么上传动图,下面这张是在动态跳出数据的时候截出来的图。下面记录一下,怎么做出灵活的动态显示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这个知识点,下面我上一段关键的代码片段:

public class AsyncAddressBook extends AsyncTask {
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类.

public class ContactArrayList extends ArrayList {
@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要谨慎使用,因为它是阻塞线程的,会消耗性能。

转载请注明出处!

                                                                                                                                                            

                                                                                                                                                                                                           与    君    共   勉   


推荐阅读
author-avatar
记忆里的Angle
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有