简体   繁体   English

Android 如何在 ThreadPoolExecutor 完成后调用 RecyclerVIew notifyDataSetChanged()?

[英]Android How Do I Call RecyclerVIew notifyDataSetChanged() after ThreadPoolExecutor finishes?

I am learning ThreadPoolExecutor by following this tutorial .我正在按照本教程学习 ThreadPoolExecutor。 To demonstrate its usage, I made a simple android project, it has a recyclerview that will show some Strings.为了演示它的用法,我做了一个简单的 android 项目,它有一个 recyclerview 可以显示一些字符串。 Initially, the array of Strings( String[] myDataset = new String[10] ) has 10 nulls.最初,字符串数组 ( String[] myDataset = new String[10] ) 有 10 个空值。 My threadPoolExecutor generates some random strings and fills up the array.我的 threadPoolExecutor 生成一些随机字符串并填充数组。 So whenever a new String is generated and placed inside the array, I should call notifyDataSetChanged() so that the recyclerView will update and show those random Strings.因此,每当生成一个新字符串并将其放置在数组中时,我都应该调用notifyDataSetChanged()以便 recyclerView 将更新并显示这些随机字符串。

the problem问题

I don't understand how to call notifyDataSetChanged() and so I am pinned down.我不明白如何调用notifyDataSetChanged() ,所以我被固定住了。 I got this exception:我得到了这个例外:

Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Since I know AsyncTask, I understand that this error means I cannot call this method in background thread but I have to call it in main thread/ui thread ( so in AsyncTask, it would look like this:因为我知道 AsyncTask,所以我知道这个错误意味着我不能在后台线程中调用这个方法,但我必须在主线程/ui 线程中调用它(所以在 AsyncTask 中,它看起来像这样:

@Override
    protected void onPostExecute(String result) {
      weakReference.get().notifyDataSetChanged(); // something like that
    }

). )。 I need it's ThreadPoolExecutor counterpart.我需要它的 ThreadPoolExecutor 对应物。 I did google and found this but I am not sure how to do this.我做了谷歌并找到了这个,但我不知道该怎么做。

The necessary code segment is given below:必要的代码段如下:

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

}

Could anyone help me?有人可以帮我吗? Thank you for reading.感谢您的阅读。

There is a Handler class under the hood in all cases where you need to communicate to UIThread from the another Thread (AsyncTask use it as well).在所有需要从另一个线程(AsyncTask 也使用它)与 UIThread 通信的情况下,引擎盖下都有一个处理程序 class

Some of possible choices:一些可能的选择:

  1. Use Handler, connected to main looper:使用 Handler,连接到主循环器:

    Handler handler = new Handler(getMainLooper()); handler.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });

  2. Use "runOnUiThread" that you've mentioned:使用您提到的“runOnUiThread”:

    runOnUiThread(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });

  3. Use the "post" method of your UI-View (RecyclerView, for example):使用 UI-View 的“post”方法(例如 RecyclerView):

    yourRecyclerView.post(new Runnable() { @Override public void run() { notifyDataSetChanged(); } });

In case anyone needs, here is what I did based on sergiy-tikhonov 's answer.如果有人需要,这是我根据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]);
        }
    }

So as you can see, I tried the third option.如您所见,我尝试了第三个选项。 First I created a WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView) in the parent fragment (or activity if you are using that).首先,我在父片段(或活动,如果你正在使用它)中创建了一个WeakReference<RecyclerView> recyclerViewWeakReference = new WeakReference<RecyclerView>(myRecyclerView) ) 。 Then I passed the weak reference into MyAdapter .然后我将弱引用传递给MyAdapter I used weakReference because that is what you do with AsyncTask,so my instincts alerted me to do so.我使用weakReference ,因为这就是你对AsyncTask 所做的,所以我的直觉提醒我这样做。 I hope this is helpful.我希望这是有帮助的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何更新片段中的 recyclerview 列表? (notifyDataSetChanged 不起作用) - How do I update the recyclerview list in the fragment? (notifyDataSetChanged not working) notifydatasetchanged后recyclerview不更新 - recyclerview not updating after notifydatasetchanged 如何从另一个 class 中的 AsyncTask 调用 notifyDataSetChanged() - How do I call notifyDataSetChanged() from a AsyncTask that is in another class 调用notifyDataSetChanged后,Android ListView不会更新 - Android ListView not updating after a call to notifyDataSetChanged 即使我调用 notifyDataSetChanged(),RecyclerView 也不显示项目 - RecyclerView don't show items even if I call notifyDataSetChanged() 如何在另一个线程上的回调内的ArrayAdapter中notifyDataSetChanged? -安卓 - How do I notifyDataSetChanged in ArrayAdapter inside a callback on a different thread? - Android Firebase Cloud Function onWrite完成后,如何触发Android客户端回调? - How do I get Android client side callback to fire after Firebase Cloud Function onWrite finishes? 如何在运行方法完成后清理线程? - How do I cleanup a Thread after its run method finishes? 线程完成后,如何使此For循环继续? - How Do I Make this For Loop continue after the Thread finishes? 删除项目后如何使用Android-notifyDataSetChanged()? - how to use Android - notifyDataSetChanged() after delete an item?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM