android – 如何将ListView,ArrayList,ArrayAdapter和notifyDataSetChanged与runnable一起使用,并在主线程中使用消息Handler

我通过其他6个SO问题线程加上一些博客文章也一样

错误消息,一切都无济于事.

我正在将文件目录加载到ArrayList中,然后加载到ListView中
使用ArrayAdapter.代码实际上有效 – 目录显示
并响应单击事件 – 但程序然后以此错误结束,
通常在我显示目录之后,几次或几次然后运行
程序的另一部分,它将loadUrl作为WebView:

适配器的内容已更改但ListView未收到
通知.确保未修改适配器的内容
后台线程,但仅限于UI线程.确保你的
适配器在其内容更改时调用notifyDataSetChanged().

我见过的所有简单示例都没有使用notifyDataSetChanged或a
单独的线程.我想知道我是否需要使用它们以及如何使用它们
正确.我在ArrayAdapter和ArrayList之间感到困惑,只需要在主线程中修改它. ArrayList中的数据仅在请求新目录列表时更改,然后清除并加载新数据.

我的问题是:在这种情况下我需要notifyDataSetChanged吗? [我很好
确定我没有],我如何安排主要之间的各个部分
和后台/可运行的线程?

我通过交替运行directoryList和来使程序崩溃
displayFtpHelp [使用WebView / loadUrl()]部分 – 但我必须运行该对
几次到多次才能得到崩溃.

LogCat不包括程序中执行的最后一个语句,
因此很难确定实际触发器;而是这样的:

android.widget.ListView.layoutChildren(ListView.java:1555)

displayFtpHelp部分将loadUrl()转换为WebView.我想知道这是怎么回事
最终触发错误.似乎该计划实际上并非如此
发生错误时在ArrayList / ArrayAdapter上运行.这好像是
程序结构错误而不是技术或逻辑错误.

我正在做的是,首先定义两个ListViews和ArrayList.一
ListView用于本地设备文件,另一个用于远程Ftp站点.

static ListView filesListLocal, filesListRemote;
static ArrayList<HashMap<String,String>> directoryEntries = new ArrayList<>();

我将显示Ftp目录的代码.本地目录的代码是
基本相同,除了使用file.getAbsolutePath()而不是
ftp.list(),以及对lookForFilesAndDirs()的技术更改,这是
递归本地目录处理.

然后我使用Runnable将Ftp目录读入数组.我是
省略错误检查和其他细节;这是使用Ftp4j
图书馆:

Runnable r = new Runnable() {
public void run() {
    Message msg = mHandler.obtainMessage();
    try {
        FTPFile[] fileAndDirs = ftp.list();
        dirName = ftp.currentDirectory();
    } catch (Various Exceptions e) {
        result = "The operation failed\n" + e.toString();
    }
    Bundle bundle = new Bundle();
    bundle.putString("mickKey", result);
    msg.setData(bundle);
    mHandler.sendMessage(msg);
}
};
 Thread t = new Thread(r);
 t.start();

在消息处理程序中,我将数据从数组复制到ArrayList
 并使用适配器在ListView中显示它.我原来有这个
 Runnable中的代码,然后将其移动到main中的消息处理程序
 线程,当我第一次收到错误消息,但没有帮助.
 我在猜测中放入了notifyDataSetChanged调用,但它也没有
 区别:

dirName = ftp.currentDirectory();
sa.notifyDataSetChanged();
directoryEntries.clear();
lookForFilesAndDirs(fileAndDirs);    // load ArrayList
SimpleAdapter saFtp = new SimpleAdapter(myContext, directoryEntries,
    R.layout.my_two_lines, new String[] {"path", "filename"},
    new int[] {R.id.path, R.id.filename});
filesListRemote.setAdapter(saFtp);
sa.notifyDataSetChanged();

…在这里留下更多细节

public static void lookForFilesAndDirs(FTPFile[] fileAndDirs) {
for (FTPFile fileOrDir : fileAndDirs) {
    String fileOrDirName = fileOrDir.getName();
    int entryType = fileOrDir.getType();
    if (entryType == 1) {               // entry is a directory
        HashMap<String,String> listEntry = new HashMap<>();
        listEntry.put("path", fileOrDirName);
        listEntry.put("filename", null);
        directoryEntries.add(listEntry);
    } else {                             // entry is a file
        HashMap<String,String> listEntry = new HashMap<>();
        listEntry.put("path", dirName);
        listEntry.put("filename", fileOrDirName);
        directoryEntries.add(listEntry);
    }
}

LogCat:

10-14 19:47:29.403 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: onMenuItemClick
10-14 19:47:29.403 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: ftpPrintFilesList
10-14 19:47:30.235 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: handleMessage
10-14 19:47:32.339 3006-3006/com.webs.mdawdy.htmlspyii W/EGL_genymotion: eglSurfaceAttrib not implemented
10-14 19:47:34.627 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: onMenuItemClick
10-14 19:47:34.627 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: displayFtpHelp Begin
10-14 19:47:34.627 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: displayFtpHelp end
10-14 19:47:34.819 3006-3006/com.webs.mdawdy.htmlspyii D/AndroidRuntime: Shutting down VM
10-14 19:47:34.819 3006-3006/com.webs.mdawdy.htmlspyii W/dalvikvm: threadid=1:
    thread exiting with uncaught exception (group=0xa4d2db20)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: FATAL EXCEPTION: main
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: Process: com.webs.mdawdy.htmlspyii, PID: 3006
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:
    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.
    Make sure your adapter calls notifyDataSetChanged() when its content changes.
    [in ListView(2131427419, class android.widget.ListView) with
    Adapter(class android.widget.SimpleAdapter)]
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.widget.ListView.layoutChildren(ListView.java:1555)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.widget.ListView.setSelectionInt(ListView.java:1980)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.widget.AbsListView.resurrectSelection(AbsListView.java:5376)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.widget.AbsListView.onWindowFocusChanged(AbsListView.java:2822)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.view.View.dispatchWindowFocusChanged(View.java:7900)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:968)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:972)
[6 more lines identical to the one above]
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:3133)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:102)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:136)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5001)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at java.lang.reflect.Method.invokeNative(Native Method)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:515)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime:     at dalvik.system.NativeStart.main(Native Method)

最佳答案 一些建议:

>使用单独的目录条目列表来存储支持两个ListView的数据.

> List< HashMap< String,String>> directoryEntriesLocal = new ArrayList<>();
> List< HashMap< String,String>> directoryEntriesRemote = new ArrayList<>();

>将适配器sa和saFtp设置为活动的onCreate()方法中的相应列表视图.在此阶段,directoryEntriesLocal和directoryEntriesRemote都可以为空.
>在主线程的Handler中,只需更改需要更新的目录条目列表的数据,然后调用相应适配器的notifyDataSetChanged().如果您声明了两个处理程序,一个用于更新远程列表而另一个用于本地列表,则会更好.或者你可以在一个Handler中完成这两个任务.这个选择留给你.无需在此实例化和分配新适配器.
>请不要直接使用Handler和Thread类.请改用AsyncTask.它将使您作为Android程序员的生活变得更加轻松.

点赞