天天看點

動态顯示listview的資料,同步機制

不知道怎麼上傳動圖,下面這張是在動态跳出資料的時候截出來的圖。下面記錄一下,怎麼做出靈活的動态顯示listview資料的效果。

動态顯示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要謹慎使用,因為它是阻塞線程的,會消耗性能。

轉載請注明出處!

                                                                                                                                                                                                           與    君    共   勉   

繼續閱讀