如何在主线程中将 ListView、ArrayList、ArrayAdapter 和 notifyDataSetChanged 与可运行对象和消息处理程序一起使用
How to use ListView, ArrayList, ArrayAdapter and notifyDataSetChanged together with a runnable, and a message Handler in the main thread
我研究了其他 6 个 SO 问题线程以及一些具有相同问题的博客文章
错误信息,全部无效。
我正在将文件目录加载到 ArrayList
,然后再加载到 ListView
使用 ArrayAdapter
。代码实际工作 - 目录显示
并响应点击事件 - 但程序随后以此错误结束,
通常在我显示目录一次,几次或几次然后 运行
程序的另一部分将 loadUrl 加载到 WebView 中:
适配器的内容已经改变但是ListView没有收到
通知。确保你的适配器的内容没有被修改
后台线程,但仅来自 UI 线程。确保你的
适配器在其内容更改时调用 notifyDataSetChanged()。
None 我见过的简单示例使用 notifyDataSetChanged
或
单独的线程。我想知道我是否需要使用这些以及如何使用它们
正确。我对 ArrayAdapter
和 ArrayList
感到困惑,因为哪些只需要在主线程中修改。 ArrayList
中的数据仅在请求新目录列表时更改,然后清除并加载新数据。
我的问题是:在这种情况下我需要 notifyDataSetChanged 吗? [我很
当然我没有],我如何安排主要部分之间的各个部分
和 background/runnable 线程?
我通过交替 运行ing directoryList 和
显示 Ftp 帮助 [使用 WebView/loadUrl()] 部分 - 但我必须 运行 这对
多次到多次崩溃。
LogCat不包括程序最后执行的语句,
因此很难确定实际的触发因素;相反,它是这样的:
android.widget.ListView.layoutChildren(ListView.java:1555)
显示Ftp帮助部分 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
并使用 Adapter 将其显示在 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<>();
在 activity 的 onCreate()
方法中将适配器 sa
和 saFtp
设置为各自的列表视图。在这个阶段directoryEntriesLocal
和directoryEntriesRemote
都可以为空。
- 在主线程
Handler
中,修改需要更新的目录条目列表的数据,然后调用 notifyDataSetChanged()
对应的适配器。如果声明两个处理程序会更好,一个用于更新远程列表,另一个用于本地列表。或者,您可以在一个 Handler
中完成这两项操作。这个选择留给你。无需在此处实例化和分配新的适配器。
- 请不要直接使用
Handler
和Thread
类。请改用 AsyncTask。它将使您作为 Android 程序员的生活变得更加轻松。
我研究了其他 6 个 SO 问题线程以及一些具有相同问题的博客文章 错误信息,全部无效。
我正在将文件目录加载到 ArrayList
,然后再加载到 ListView
使用 ArrayAdapter
。代码实际工作 - 目录显示
并响应点击事件 - 但程序随后以此错误结束,
通常在我显示目录一次,几次或几次然后 运行
程序的另一部分将 loadUrl 加载到 WebView 中:
适配器的内容已经改变但是ListView没有收到 通知。确保你的适配器的内容没有被修改 后台线程,但仅来自 UI 线程。确保你的 适配器在其内容更改时调用 notifyDataSetChanged()。
None 我见过的简单示例使用 notifyDataSetChanged
或
单独的线程。我想知道我是否需要使用这些以及如何使用它们
正确。我对 ArrayAdapter
和 ArrayList
感到困惑,因为哪些只需要在主线程中修改。 ArrayList
中的数据仅在请求新目录列表时更改,然后清除并加载新数据。
我的问题是:在这种情况下我需要 notifyDataSetChanged 吗? [我很 当然我没有],我如何安排主要部分之间的各个部分 和 background/runnable 线程?
我通过交替 运行ing directoryList 和 显示 Ftp 帮助 [使用 WebView/loadUrl()] 部分 - 但我必须 运行 这对 多次到多次崩溃。
LogCat不包括程序最后执行的语句, 因此很难确定实际的触发因素;相反,它是这样的:
android.widget.ListView.layoutChildren(ListView.java:1555)
显示Ftp帮助部分 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
并使用 Adapter 将其显示在 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<>();
在 activity 的
onCreate()
方法中将适配器sa
和saFtp
设置为各自的列表视图。在这个阶段directoryEntriesLocal
和directoryEntriesRemote
都可以为空。- 在主线程
Handler
中,修改需要更新的目录条目列表的数据,然后调用notifyDataSetChanged()
对应的适配器。如果声明两个处理程序会更好,一个用于更新远程列表,另一个用于本地列表。或者,您可以在一个Handler
中完成这两项操作。这个选择留给你。无需在此处实例化和分配新的适配器。 - 请不要直接使用
Handler
和Thread
类。请改用 AsyncTask。它将使您作为 Android 程序员的生活变得更加轻松。