ListView从另一个线程更新数据

时间:2014-05-23 11:44:28

标签: java android multithreading

我有活动,我在listView中查看数据。但我必须从另一个线程填充数据。正如您在以下代码中看到的那样。在每次数据更改时,我都在调用处理器消息,触发notifyDataSetChanged()。当我更新每秒少于1次更改的数据时,它可以正常工作,但是当我更快地更改数据时它会抛出IllegalStateException。有没有机会抓住这个例外?

有没有更明确的解决方案如何从另一个线程更新数据?

完整代码链接:https://www.dropbox.com/s/nxyac1xd88g2y71/TestListThreadFill.zip

的活动:

public class MainActivity extends Activity {
    private ListView list;
    private TestAdapter adapter;
    public static MainActivity magic;

    /**
     * Called when the activity is first created.
     */
    @Override public void onCreate( Bundle savedInstanceState) {
        magic = this;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        list = (ListView)findViewById( R.id.list);

        adapter = new TestAdapter( getApplicationContext());
        new FillThread().execute((Object) null);

        Data.getInstance().addHandler(this, new Handler(){
            @Override public void dispatchMessage(Message msg) {
                update();
                super.dispatchMessage(msg);
            }
        });

    }

    public void update(){
        adapter.notifyDataSetChanged();
    }

    @Override protected void onResume() {
        list.setAdapter( adapter);
        super.onResume();
    }
}

Data continer:

public class Data extends Vector<DataItem> {
    private static Data instance = null;
    private HashMap< Object, Handler> handlerList;

    public Data() {
        handlerList = new HashMap< Object, Handler>();
    }

    public static Data getInstance(){
        if( instance == null)
            instance = new Data();
        return instance;
    }

    @Override public synchronized boolean add( DataItem object) {
        boolean result = super.add(object);

        for( Map.Entry< Object, Handler> handler : handlerList.entrySet())
        {
            handler.getValue().sendEmptyMessage( 0);
        }

        return result;
    }

    public synchronized void addHandler( Object object, Handler handler){
        handlerList.put( object, handler);
    }

    public synchronized void addHandler( Handler handler){
        addHandler( handler, handler);
    }

    public synchronized void removeHandler( Object object){
        handlerList.remove( object);
    }
}

数据填充线程:

public class FillThread extends AsyncTask{
    @Override protected Object doInBackground(Object... params) {
        while( true) {
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Data.getInstance().add( new DataItem( "L", "TOP", "BOTTOM"));
        }
    }
}

logcat的:

05-23 13:20:23.900  25260-25260/com.example.TestListThreadFill E/AndroidRuntime﹕ FATAL EXCEPTION: main
    java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. [in ListView(2131034115, class android.widget.ListView) with Adapter(class com.example.TestListThreadFill.TestAdapter)]
            at android.widget.ListView.layoutChildren(ListView.java:1549)
            at android.widget.AbsListView.onLayout(AbsListView.java:2170)
            at android.view.View.layout(View.java:13846)
            at android.view.ViewGroup.layout(ViewGroup.java:4466)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1420)
            at android.view.View.layout(View.java:13846)
            at android.view.ViewGroup.layout(ViewGroup.java:4466)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:13846)
            at android.view.ViewGroup.layout(ViewGroup.java:4466)
            at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1649)
            at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1507)
            at android.widget.LinearLayout.onLayout(LinearLayout.java:1420)
            at android.view.View.layout(View.java:13846)
            at android.view.ViewGroup.layout(ViewGroup.java:4466)
            at android.widget.FrameLayout.onLayout(FrameLayout.java:448)
            at android.view.View.layout(View.java:13846)
            at android.view.ViewGroup.layout(ViewGroup.java:4466)
            at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2149)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1907)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1127)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4606)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:747)
            at android.view.Choreographer.doCallbacks(Choreographer.java:567)
            at android.view.Choreographer.doFrame(Choreographer.java:536)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:733)
            at android.os.Handler.handleCallback(Handler.java:615)
            at android.os.Handler.dispatchMessage(Handler.java:92)
            at android.os.Looper.loop(Looper.java:153)
            at android.app.ActivityThread.main(ActivityThread.java:4987)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:821)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:584)
            at dalvik.system.NativeStart.main(Native Method)
05-23 13:20:30.923  25260-25265/com.example.TestListThreadFill D/jdwp﹕ processIncoming
05-23 13:20:30.923  25260-25265/com.example.TestListThreadFill D/jdwp﹕ handlePacket : cmd=0x1, cmdSet=0xC7, len=0x13, id=0x4000016E, flags=0x0, dataLen=0x8
05-23 13:20:31.421  25260-25265/com.example.TestListThreadFill D/jdwp﹕ processIncoming
05-23 13:20:31.421  25260-25265/com.example.TestListThreadFill D/jdwp﹕ handlePacket : cmd=0x1, cmdSet=0xC7, len=0x17, id=0x4000016F, flags=0x0, dataLen=0xC
05-23 13:20:31.429  25260-25265/com.example.TestListThreadFill D/jdwp﹕ processIncoming
05-23 13:20:31.429  25260-25265/com.example.TestListThreadFill D/jdwp﹕ handlePacket : cmd=0x1, cmdSet=0xC7, len=0x13, id=0x40000170, flags=0x0, dataLen=0x8
05-23 13:20:31.429  25260-25265/com.example.TestListThreadFill D/jdwp﹕ processIncoming
05-23 13:20:31.429  25260-25265/com.example.TestListThreadFill D/jdwp﹕ handlePacket : cmd=0x1, cmdSet=0xC7, len=0x13, id=0x40000171, flags=0x0, dataLen=0x8

1 个答案:

答案 0 :(得分:0)

很明显错误信息:
Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.

您可以尝试这样的事情:

public class FillThread extends AsyncTask<Void, Void, DataItem> {
    @Override
    protected DataItem doInBackground(Void... params) {
        return new DataItem();
    }
    @Override
    protected void onPostExecute(DataItem dataItem) {
        // call an update method that adds the dataItem and notifies the adapter
    }
}