【问题标题】:Use arrayList values updated in thread outside it使用在其外部线程中更新的 arrayList 值
【发布时间】:2020-07-07 22:49:12
【问题描述】:

基本上,我在线程中从网站获取一些链接并将其添加到 ArrayList。 但是,当我检查 ArrayList 的大小时,它返回 0。无论如何,是否可以让其中的新值反映线程之外。

代码如下:

lsiter.setOnItemClickListener(new AdapterView.OnItemClickListener() {

        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
            final ArrayList<String>LinkAr = new ArrayList<String>();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Document doc = null;
                    try {
                        doc = Jsoup.connect(message).get();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }

                    //Title
                    Element TitleSpan = doc.select("span.detail-info-right-title-font").first();
                    String TitleString = TitleSpan.text();

                    //Chaps Names
                    Elements Chaps = doc.getElementsByClass("title3");

                    for(Element item: Chaps){
                        LinkAr.add(item.text());
                    }
                }
            }).start();
            
                int pos = parent.getPositionForView(view);
                String ClickedVal = (String) parent.getItemAtPosition(position);
                Toast.makeText(ChapterPage.this, LinkAr.size() + "@ " + ClickedVal, Toast.LENGTH_SHORT).show();
        }
    });

【问题讨论】:

  • 您启动了一个线程来构建列表,但您没有等待它完成。如果你在启动线程后立即需要它的结果,那么首先创建线程是没有意义的,逻辑是顺序的。
  • 我需要创建线程,因为在android中所有网络进程都必须在线程上运行
  • 然后你必须等待它完成。将线程分配给变量并使用 thread.join()
  • 你能举一个这段代码的例子吗,我还是线程新手
  • 如果你有一个线程:Thread t=new Thread(...); t.start();t.join();

标签: java android multithreading


【解决方案1】:

警告:我不了解 Android。我在这里一般说的是Java。

警告:正如其他人评论的那样,您的应用在后台线程上执行特定工作负载可能有意义,也可能没有意义。我将忽略这个问题,并重点回答如何在后台线程上生成数据。由读者决定在他们的特定应用中这样做是否合适。


在 Java 中,我们不再需要手动管理线程。 Executors 框架 (Oracle tutorial) 内置于 Java 5 中,可让应用程序开发人员从复杂的线程工作中解脱出来。

ExecutorService 作为全局变量

建立由线程池支持的执行器服务。在您的应用程序中的某处保留对该执行程序服务的引用。这个执行器服务会被重复使用。

要配置ExecutorService 对象,请使用Executors 类中的工厂方法。

ExecutorService executorService = Executors.newCachedThreadPool() ;

onItemClick 代码中的代码

在您的onItemClick 方法中,将要完成的工作定义为CallableCallable 返回一个值。在您的情况下,您希望返回 URL 的 ListSet。后台线程无需访问您的onItemClick 代码中的列表。只需让后台线程生成一个新列表或集合,然后将该列表/集合返回给调用代码。更清洁的分离。

显然您正在使用String 对象来表示您的网址。但是我们有一个类:URL。让我们生成一个URL 对象列表,指定为List &lt; URL &gt;。我们期望我们的Callable 对象最终生成并返回List &lt; URL &gt;。如果我们的任务出现问题,我们希望 Callable 返回一个空列表而不是抛出异常(如果这适合您的业务逻辑)。

Callable < List < URL > > urlFetch =
        ( ) -> {
            List < URL > urls = List.of(); // Default to empty list.
            //  … do the work to fetch your URLs and build a list.
            return urls;
        };

将该任务提交给执行器服务。然后,执行器服务将该任务分配给其后备池中的一个线程。 Wo 不关心线程分配是如何完成的;可能是启动了一个新线程,或者可能重用了一个现有的空闲线程,这不是我们的问题。

将我们的任务提交给执行器服务后,我们会返回一个Future 对象。这个Future 是我们处理任务的句柄。我们可以向Future 询问正在完成的工作的状态。

Future < List < URL > > futureUrls = executorService.submit( urlFetch );
// … do other work while you wait. 

稍后,您可以随时查看我们任务的状态。 Future 可能会报告任务已完成或已取消。否则,我们可以假设任务不完整,并且仍在继续。

如果Future 报告为完整,我们可以检索其结果。这将是一个List&lt; URL &gt; 对象。

// … check to see if the URL fetch work has been done yet.
if ( futureUrls.isDone() )
{
    List < URL > urls = futureUrls.get();
    // … Use your list of URLs. 
} else if ( futureUrls.isCancelled() )
{
    // … deal with cancellation. 
} // else neither done nor cancelled, so still being worked. Check back again later.

上面的语法不完整。我们必须捕获某些异常。让我们添加 try-catch。

// … check to see if the URL fetch work has been done yet.
if ( futureUrls.isDone() )
{
    try
    {
        List < URL > urls = futureUrls.get();
        // …Use your URL collection…
    }
    catch ( InterruptedException e )
    {
        e.printStackTrace();
    }
    catch ( ExecutionException e )
    {
        e.printStackTrace();
    }
} else if ( futureUrls.isCancelled() )
{
    // … deal with cancellation.
} // else neither done nor cancelled, so still being worked. Check back again later.

等待任务完成

如果您希望调用线程无限期地等待直到任务完成,请调用Future::get。但请避免在主用户界面线程中执行此操作,因为您的应用对用户无响应,似乎已冻结/崩溃。

如果您希望调用线程短暂等待任务完成,请调用Future::get,同时传递等待时间。当等待时间到期而我们的任务仍未完成时,调用线程将继续进行其他工作。同样,不要在 UI 线程中等待太久,因为 UI 会冻结一段时间。

不要弄乱用户界面

重要提示:从不从后台线程访问用户界面。坏事可能会发生,也可能不会发生。

清理

最后,请务必在您的应用结束之前优雅地关闭您的执行程序服务。否则它的后备线程池可能会无限期地继续下去。

【讨论】:

  • 哇,谢谢。这是一个很好的解释和有用的答案。肯定会使用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-02
  • 2013-05-05
  • 1970-01-01
相关资源
最近更新 更多