Android ThreadPoolExecutor 完成后如何调用RecyclerVIew notifyDataSetChanged()?
Android How Do I Call RecyclerVIew notifyDataSetChanged() after ThreadPoolExecutor finishes?
我正在按照此 tutorial 学习 ThreadPoolExecutor。为了演示它的用法,我做了一个简单的 android 项目,它有一个会显示一些字符串的回收视图。最初,Strings(String[] myDataset = new String[10]
) 数组有 10 个空值。我的 threadPoolExecutor 生成一些随机字符串并填充数组。因此,无论何时生成新字符串并将其放入数组中,我都应该调用 notifyDataSetChanged()
以便 recyclerView 更新并显示这些随机字符串。
问题
我不明白如何调用 notifyDataSetChanged()
所以我被压住了。我得到这个例外:
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
因为我知道 AsyncTask,我知道这个错误意味着我不能在后台线程中调用这个方法,但我必须在主线程中调用它 thread/ui(所以在 AsyncTask 中,它看起来像这样:
@Override
protected void onPostExecute(String result) {
weakReference.get().notifyDataSetChanged(); // something like that
}
)。我需要它的 ThreadPoolExecutor 对应物。我做了 google 并找到了 但我不知道该怎么做。
必要的代码段如下:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] myDataset;
private final ThreadPoolExecutor threadPoolExecutor;
private Future future;
private Runnable getRunnable(final int i) {
Runnable runnable = new Runnable() {
@Override
public void run() {
String randomString = MyAdapter.getRandomString(i)+" "+i; // <--create random string
Log.e(TAG, randomString);
myDataset[i] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
}
};
return runnable;
}
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error. Where/How should I call it?
};
threadPoolExecutor.execute(commands[j1]);
}
}
public MyAdapter(String[] myDataset) {
this.myDataset = myDataset; // {null, null, ... ...}
this.threadPoolExecutor = DefaultExecutorSupplier.getInstance().forBackgroundTasks(); // returns new ThreadPoolExecutor( ... parameters... );
// future[i] = threadPoolExecutor.submit(command); future[i].cancel(true); use it like this
doSomeBackgroundWork();
}
// ... the rest of the recyclerview related code
}
有人能帮帮我吗?感谢阅读。
在所有需要从另一个线程(AsyncTask 也使用它)与 UI线程通信的情况下,引擎盖下都有一个 Handler class。
一些可能的选择:
使用Handler,连接到main looper:
Handler handler = new Handler(getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
使用您提到的"runOnUiThread":
runOnUiThread(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
使用UI-View(例如RecyclerView)的"post"方法:
yourRecyclerView.post(new Runnable() {
@Override
public void run() {
notifyDataSetChanged();
}
});
如果有人需要,这是我根据sergiy-tikhonov的回答所做的。
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error
recyclerViewWeakReference.get().post(new Runnable() { // <---- this is the change
@Override
public void run() {
notifyDataSetChanged();
}
});
};
threadPoolExecutor.execute(commands[j1]);
}
}
如您所见,我尝试了第三个选项。首先,我在父片段中创建了一个 WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView)
(或者 activity,如果你正在使用它的话)。然后我将弱引用传递给 MyAdapter
。我使用 weakReference
因为那是你用 AsyncTask 做的,所以我的直觉提醒我这样做。希望对您有所帮助。
我正在按照此 tutorial 学习 ThreadPoolExecutor。为了演示它的用法,我做了一个简单的 android 项目,它有一个会显示一些字符串的回收视图。最初,Strings(String[] myDataset = new String[10]
) 数组有 10 个空值。我的 threadPoolExecutor 生成一些随机字符串并填充数组。因此,无论何时生成新字符串并将其放入数组中,我都应该调用 notifyDataSetChanged()
以便 recyclerView 更新并显示这些随机字符串。
问题
我不明白如何调用 notifyDataSetChanged()
所以我被压住了。我得到这个例外:
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
因为我知道 AsyncTask,我知道这个错误意味着我不能在后台线程中调用这个方法,但我必须在主线程中调用它 thread/ui(所以在 AsyncTask 中,它看起来像这样:
@Override
protected void onPostExecute(String result) {
weakReference.get().notifyDataSetChanged(); // something like that
}
)。我需要它的 ThreadPoolExecutor 对应物。我做了 google 并找到了
必要的代码段如下:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] myDataset;
private final ThreadPoolExecutor threadPoolExecutor;
private Future future;
private Runnable getRunnable(final int i) {
Runnable runnable = new Runnable() {
@Override
public void run() {
String randomString = MyAdapter.getRandomString(i)+" "+i; // <--create random string
Log.e(TAG, randomString);
myDataset[i] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
}
};
return runnable;
}
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error. Where/How should I call it?
};
threadPoolExecutor.execute(commands[j1]);
}
}
public MyAdapter(String[] myDataset) {
this.myDataset = myDataset; // {null, null, ... ...}
this.threadPoolExecutor = DefaultExecutorSupplier.getInstance().forBackgroundTasks(); // returns new ThreadPoolExecutor( ... parameters... );
// future[i] = threadPoolExecutor.submit(command); future[i].cancel(true); use it like this
doSomeBackgroundWork();
}
// ... the rest of the recyclerview related code
}
有人能帮帮我吗?感谢阅读。
在所有需要从另一个线程(AsyncTask 也使用它)与 UI线程通信的情况下,引擎盖下都有一个 Handler class。
一些可能的选择:
使用Handler,连接到main looper:
Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });
使用您提到的"runOnUiThread":
runOnUiThread(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });
使用UI-View(例如RecyclerView)的"post"方法:
yourRecyclerView.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });
如果有人需要,这是我根据sergiy-tikhonov的回答所做的。
public void doSomeBackgroundWork(){
Runnable[] commands = new Runnable[myDataset.length];
for(int i1=0; i1<commands.length; i1++) {
final int j1 = i1;
commands[j1] = () -> {
String randomString = MyAdapter.getRandomString(j1)+" "+j1;
Log.e(TAG, randomString);
myDataset[j1] = randomString;
try { Thread.sleep(3000); }
catch (InterruptedException e) { e.printStackTrace(); }
// notifyDataSetChanged(); // <-------- Error
recyclerViewWeakReference.get().post(new Runnable() { // <---- this is the change
@Override
public void run() {
notifyDataSetChanged();
}
});
};
threadPoolExecutor.execute(commands[j1]);
}
}
如您所见,我尝试了第三个选项。首先,我在父片段中创建了一个 WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView)
(或者 activity,如果你正在使用它的话)。然后我将弱引用传递给 MyAdapter
。我使用 weakReference
因为那是你用 AsyncTask 做的,所以我的直觉提醒我这样做。希望对您有所帮助。