【问题标题】:ScheduledExecutorService not working correctlyScheduledExecutorService 无法正常工作
【发布时间】:2020-11-21 22:22:11
【问题描述】:

我使用 ScheduledExecutorService 每 15 秒从网站获取数据:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.scheduleAtFixedRate(this::refresh, 0, 15, TimeUnit.SECONDS);

这是每 15 秒调用一次的方法:

public void refresh() {
        Site.refreshData();
        System.out.println("Refreshed Data");
        LinearLayout listView = findViewById(R.id.linearLayout);
        System.out.println("Size: " + List.getList().size());
        for (int i = 0; i < List.getList().size(); i++) {
            Entry entry = List.getList().get(i);

            CardView cardView = (CardView) LayoutInflater.from(
                    getApplicationContext()).inflate(R.layout.card, listView, false);

            ImageView checkedView = (ImageView) cardView.getChildAt(0);
            checkedView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(),
                    entry.isChecked() ? R.drawable.check : R.drawable.cross));

            TextView name = (TextView) cardView.getChildAt(1);
            name.setText(entry.getName());

            TextView comment = (TextView) cardView.getChildAt(2);
            comment.setText(entry.getComment());

            listView.addView(cardView, i);
        }
        System.out.println("Added Cardviews");
        listView.invalidate();
        System.out.println("Refreshed LinearLayout");
    }

我添加了多个打印,作为一个穷人调试器,我只到达打印 ArrayList 大小的地步。从那里开始没有任何东西被打印出来,就像执行停止一样。 我确定错误发生在 for 循环内。我通过添加打印进行了检查,它显示了当前的i,它刚刚停止在 4,即使列表大小为 57。

有什么问题?

【问题讨论】:

  • 您的问题是您在工作线程中添加了 GUI 元素。引用 Android 文档:因此,您不得从工作线程操作您的 UI — 您必须从 UI 线程对您的用户界面进行所有操作。
  • @Robert 我怎么能做到这一点?我必须使用 ScheduledExecutorService 刷新我的数据,那么我将如何“返回”到主线程来操作 UI?由于 SES 调用的方法和内部的所有内容都将在 SES-Thread 而不是主线程中运行。请对此提供一些详细的答案。
  • 常见的做法是在工作线程上更新一个数据结构,然后让主线程做更新工作:stackoverflow.com/questions/16483462/…

标签: java android android-cardview android-scrollview scheduledexecutorservice


【解决方案1】:

在构建 GUI 之前,让您的计划页面检索在控制台上运行。

这是一个执行此操作的示例应用程序。每五秒半分钟,我们下载一个页面并将其各个部分转储到控制台。

此演示使用每个 JEP 321: HTTP Client 添加到 Java 11 的 HttpClient 类。此处显示的网页访问代码是从this article 复制的。

提示:始终优雅地关闭您的执行器服务,因为它的线程池可能会在您的应用结束后无限期地继续运行。

package work.basil.example;

import java.io.IOException;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpHeaders;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class PageFetch
{
    public static void main ( String[] args )
    {
        PageFetch app = new PageFetch();
        app.demo();
    }

    private void demo ( )
    {
        Runnable pageFetchRunnable = ( ) -> { this.fetchPage(); };

        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleAtFixedRate( pageFetchRunnable , 1 , 5 , TimeUnit.SECONDS );  // Wait one second, then every five seconds.

        try
        {
            Thread.sleep( Duration.ofSeconds( 30 ).toMillis() );  // Let the executor service do its thing for a half-minute, then shut it down.
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }
        finally
        {
            scheduledExecutorService.shutdown();
        }
    }

    private void fetchPage ( )
    {
        // Example code for using `HttpClient` framework of Java 11 taken from this article:
        // https://mkyong.com/java/java-11-httpclient-examples/

        HttpClient httpClient = HttpClient.newBuilder()
                .version( HttpClient.Version.HTTP_2 )
                .followRedirects( HttpClient.Redirect.NORMAL )
                .connectTimeout( Duration.ofSeconds( 20 ) )
//                .proxy( ProxySelector.of(new InetSocketAddress("proxy.yourcompany.com", 80)))
//                .authenticator( Authenticator.getDefault())
                .build();

        HttpRequest request = HttpRequest.newBuilder()
                .GET()
                .uri( URI.create( "https://httpbin.org/get" ) )
                .setHeader( "User-Agent" , "Java 11 HttpClient Bot" ) // add request header
                .build();

        HttpResponse < String > response = null;
        try
        {
            System.out.println( "\n-----|  Demo  |-------------------------------------------" );
            System.out.println( "INFO - Access attempt at " + Instant.now() );
            response = httpClient.send( request , HttpResponse.BodyHandlers.ofString() );
            // print response headers
            HttpHeaders headers = response.headers();
            headers.map().forEach( ( k , v ) -> System.out.println( k + ":" + v ) );

            // print status code
            System.out.println( response.statusCode() );

            // print response body
            System.out.println( response.body() );
        }
        catch ( IOException e )
        {
            e.printStackTrace();
        }
        catch ( InterruptedException e )
        {
            e.printStackTrace();
        }
    }
}

运行时:

-----|  Demo  |-------------------------------------------
INFO - Access attempt at 2020-11-20T21:54:37.905896Z
:status:[200]
access-control-allow-credentials:[true]
access-control-allow-origin:[*]
content-length:[242]
content-type:[application/json]
date:[Fri, 20 Nov 2020 21:54:38 GMT]
server:[gunicorn/19.9.0]
200
{
  "args": {}, 
  "headers": {
    "Host": "httpbin.org", 
    "User-Agent": "Java 11 HttpClient Bot", 
    "X-Amzn-Trace-Id": "Root=1-5fb83b1e-7a6acb893aec6fb310984adb"
  }, 
  "origin": "76.22.40.96", 
  "url": "https://httpbin.org/get"
}



-----|  Demo  |-------------------------------------------
INFO - Access attempt at 2020-11-20T21:54:42.907678Z
:status:[200]
access-control-allow-credentials:[true]
access-control-allow-origin:[*]
content-length:[242]
content-type:[application/json]
date:[Fri, 20 Nov 2020 21:54:43 GMT]
server:[gunicorn/19.9.0]
200
{
  "args": {}, 
  "headers": {
    "Host": "httpbin.org", 
    "User-Agent": "Java 11 HttpClient Bot", 
    "X-Amzn-Trace-Id": "Root=1-5fb83b23-3dbb566f5d58a8a367e0e528"
  }, 
  "origin": "76.22.40.96", 
  "url": "https://httpbin.org/get"
}

… and so on for a half-minute.

【讨论】:

  • 感谢您的回答。您能否提供更多与 Android 相关的答案?因为使用您的方法会导致“应用程序没有响应”错误。我还覆盖了关闭 ScheduledExecutorService 的 onStop() 方法,感谢您的提示!
  • @M0e (a) 不,抱歉,我不懂 Android 编程。 (b) 并且您的 Android 应用程序不会响应,因为我的代码示例使主线程处于休眠状态。我这样做是因为在一个普通的 Java 应用程序中,我们需要在应用程序执行结束和退出之前留出时间让后台线程完成它们的工作。但是在 Android/Swing/JavaFX 应用程序中,您永远不应该让主 UI 线程休眠,因为应用程序变得对用户没有响应,似乎已经崩溃了。
【解决方案2】:

经过更多搜索,我找到了一种完全符合我要求的方法:

runOnUiThread(Runnable runnable)

//scheduler.scheduleAtFixedRate(this::refresh, 0, 15, TimeUnit.SECONDS); ->

public void refresh() {
        Site.refreshData();
        runOnUiThread(() -> {
            ((TextView) findViewById(R.id.queueView)).setText("Total Entries: " + List.getList().size());
            LinearLayout listView = findViewById(R.id.linearLayout);
            listView.removeAllViews();
            for (Entry entry : List.getList()) {

                CardView cardView = (CardView) LayoutInflater.from(
                        getApplicationContext()).inflate(R.layout.card, listView, false);

                ImageView checkedView = (ImageView) cardView.getChildAt(0);
                checkedView.setImageDrawable(ContextCompat.getDrawable(getApplicationContext(),
                        entry.isChecked() ? R.drawable.check : R.drawable.cross));

                TextView name = (TextView) cardView.getChildAt(1);
                name.setText(entry.getName());

                TextView comment = (TextView) cardView.getChildAt(2);
                comment.setText(entry.getComment());

                listView.addView(cardView);
            }
            Toast.makeText(getApplicationContext(), "Refreshed data and UI.", Toast.LENGTH_SHORT).show();
        });
    }

【讨论】:

    猜你喜欢
    • 2016-12-01
    • 1970-01-01
    • 2016-09-01
    • 2012-07-11
    • 2018-04-08
    • 2017-04-20
    • 2018-10-02
    • 2016-09-04
    • 2010-10-06
    相关资源
    最近更新 更多