【问题标题】:How to return a string from another thread?如何从另一个线程返回字符串?
【发布时间】:2016-10-20 23:41:01
【问题描述】:

我想从另一个线程返回一个字符串“finalurl”,如下所示:

public String getImageURL(String url) {

    final String finalUrl = url;

    Thread t1 = new Thread(new Runnable() {
        public void run() {
            Document doc = null;
            try {
                doc = Jsoup.connect(finalUrl).get();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Element masthead = doc.select("div.post-image").select("img[src~=(?i)\\.(png|jpe?g)]").first();
            finalUrl = masthead.absUrl("src");
        }
    });

    t1.start();
    return finalUrl;
}

但它不起作用。如何正确制作?

【问题讨论】:

  • 你要明白它是一个单独的执行线程,也就是说它是一个Async调用。它发布请求,其他线程开始获取最终 url。因此,在调用 getImageUrl 的线程到达函数末尾之前,不能保证 Thread t1 已完成执行。
  • 首先查看 developer.android.com 上的 Processes and Thread 指南。关于您的问题,您可以尝试使用AsyncTask
  • 也许你的finalUrl 应该是java.util.concurrent.Future<String>(或FutureTask)类型,所以你可以在它上面调用get 来阻止,直到答案准备好。

标签: java android multithreading return


【解决方案1】:
 return finalUrl;

这段代码永远不会返回你想要的字符串。

您需要通过多种技术之一来修复您的逻辑。对您来说最简单的方法是使用处理程序。像这样:

private static final int URL_MESSAGE = 1;

private final Handler handler = new Handler(Looper.getMainLooper()) {

    @Override
    public void handleMessage(Message message) {
        super.handleMessage(message);
        if (message.arg1 == URL_MESSAGE) {
            final Object obj = message.obj;

            if (obj instanceof String) {
                String url = (String) obj;
            }
        }
    }
};

private final Runnable runnable = new Runnable() {
    public void run() {
        Document doc = null;
        try {
            doc = Jsoup.connect(finalUrl).get();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Element masthead = doc.select("div.post-image").select("img[src~=(?i)\\.(png|jpe?g)]").first();
        String finalUrl = masthead.absUrl("src");

        Message message = new Message();
        message.arg1 = URL_MESSAGE;
        message.obj = finalUrl;
        handler.sendMessage(message);
    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    handler.removeCallbacks(runnable);
}

private void execute() {
    handler.post(runnable);
}

【讨论】:

    【解决方案2】:

    对于这种需要,你应该做的是:

    1. 创建线程池
    2. 将您的任务作为Callable 提交到线程池
    3. 得到结果

    使用Executors 创建您的线程池,例如:

    // Create a thread pool composed of only four threads in this case
    private final ExecutorService executor = Executors.newFixedThreadPool(4);
    

    以Callable 的身份提交您的任务

    final Future<String> future = executor.submit(new Callable<String>() {
        @Override
        public String call() throws Exception {
            Document doc = Jsoup.connect(url).get();
            Element masthead = doc.select("div.post-image")
                .select("img[src~=(?i)\\.(png|jpe?g)]").first();
            return masthead.absUrl("src");
        }
    });
    

    得到结果

    try {
        return future.get();
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
        throw new RuntimeException(e);
    } catch (ExecutionException e) {
        throw new RuntimeException(e);
    }
    

    注意:线程池不能在每次调用时创建,它必须在你的类中创建一次,以便在每次调用时重用它。

    【讨论】:

    • 您的方法运行良好,但以您的方式执行此操作后,“已发布”apk 无法正常运行并给我一个错误(应用程序已停止运行):/
    • 什么是“已发布”的 apk?什么是堆栈跟踪?
    • 还有问题。在调试模式下(我的意思是在 Android Studio 中运行时),应用程序运行良好 - 日志中没有错误。生成签名应用并在 vm (bluestacks)/phone 上安装后,应用根本无法运行。
    • 我很难在没有任何信息的情况下为您提供帮助,因为您可以看到 ExecutorService 在 android developer.android.com/reference/java/util/concurrent/… 上工作
    【解决方案3】:

    您还没有明确说明您要做什么,但我很确定您不希望您的 getImageURL(url) 在获得 URL 之前返回。

    正确的做法是不使用线程。

    public String getImageURL(String url) {
        Document doc = null;
        try {
            doc = Jsoup.connect(finalUrl).get();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        Element masthead = doc.select("div.post-image").select("img[src~=(?i)\\.(png|jpe?g)]").first();
        return masthead.absUrl("src");
    }
    

    如果您认为出于某种原因需要在此处使用线程,那么您应该在另一个问题中再次提问,并更详细地说明您要尝试做什么以及为什么要这样做。


    错误提示:“06-18 21:22:12.701 7816-7816/pl.dariusz.app I/Choreographer:跳过 39 帧!应用程序可能在其主线程上做的工作过多。”

    好的,这个错误并不意味着应用程序在一个线程中做了太多的工作。这意味着 GUI 调用了您的处理程序函数之一,并且在处理程序返回之前经过了太多时间。

    如果您的事件处理函数没有及时返回,它将阻止您的应用程序及时响应用户接下来所做的任何事情。这就是错误消息真正想要告诉您的内容。

    如果您的处理程序仍然必须等待工作完成才能返回,您无法通过在其他线程中执行“工作”来修复它。重要的是时间。

    解决此类问题的方法是:

    • 您的处理程序将 GUI 置于某种状态,告诉用户,我在等什么,

    • 然后您的处理程序将启动一个后台任务,该任务将等待任何内容,(注意:我不是 Android 程序员,但我很确定有比创建一个更好的方法来启动该任务新的,赤裸的Thread 对象。)

    • 然后你的处理程序返回,

    • 您的后台任务在其他线程中运行,执行类似Jsoup.connect(finalUrl).get();

    • 1234563 1234563更新 UI 状态以显示最终发生的一切。

    【讨论】:

    • 我必须在另一个线程中做getImageURL方法,因为调试器在调试时说在同一个线程中完成了太多的操作。
    • @bioreset,“完成了太多操作......”听起来不像来自调试器的实际消息。真正的讯息是什么?你用的是什么调试器?当消息出现时你想做什么?有时,构建代码以使其可调试是个好主意。其他时候,更重要的是构建代码以解决任何问题,然后在编写代码时必须格外小心,不要放入任何错误。
    • 错误提示:“06-18 21:22:12.701 7816-7816/pl.dariusz.app I/Choreographer: 跳过了 39 帧!应用程序可能在其主线程上做了太多工作。 "我正在使用默认的 Android Studio 调试器。
    • 我多次阅读您的整个回复,但我没有找到减少应用程序主线程工作量的解决方案。
    猜你喜欢
    • 2014-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-25
    • 2021-01-14
    • 1970-01-01
    • 2022-12-03
    • 2022-01-10
    相关资源
    最近更新 更多