【问题标题】:How to delay spawned thread when a Runnable run on UI thread isn't an option?当不能在 UI 线程上运行 Runnable 时,如何延迟生成的线程?
【发布时间】:2015-04-17 15:19:08
【问题描述】:

我在上一个问题中遇到问题,我不确定我的代码是否存在内存泄漏。一些答案与它在 UI 线程上运行并因此阻塞有关。是的,它在 UI 线程上运行,不会产生新线程。

所以为了解决这个问题,我使用Thread 而不是Handler 在 UI 之外生成一个新线程。现在的问题是我无法像在 UI 线程中运行的那样延迟它。

这是我之前的问题,我的原始 UI 线程代码是:Is this Runnable safe from memory leak?,以下是生成新线程的更新代码:

package com.example.helloworld;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.widget.TextView;
import android.util.Log;
import java.lang.ref.WeakReference;

public class HelloWorldActivity extends Activity
{

    private static TextView txtview;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        txtview = (TextView) findViewById(R.id.mainview);

        Thread t = new Thread(new WeakRunnable(txtview));
        t.start();
    }

    private static final class WeakRunnable implements Runnable {
        private final WeakReference<TextView> mtextview;

        protected WeakRunnable(TextView textview){
            mtextview = new WeakReference<TextView>(textview);
        }

            @Override
            public void run() {
                TextView textview = mtextview.get();
                if (textview != null) {

                    try {
                        Thread.sleep(1500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    int test = 5*5;
                    txtview.setText("Hola Mundo"+test);
                }
                Log.d("com.example.helloworld", "" + Thread.currentThread().getName()); // Outputs "Thread-<num>" if not running on UI thread
            }
    }           

}

它只是设置视图文本并附加5*5的结果。

我一启动应用程序,它就会自行退出,我不明白为什么。有些东西告诉我我以错误的方式延迟它或使用runOnUiThread 错误。即使将txtview.setText("Hola Mundo"+test); 更改为runOnUiThread( txtview.setText("Hola Mundo"+test) ); 也不会编译给出错误:'void' type not allowed here

简而言之:计算(在这种情况下为5*5)应在单独的线程上完成以避免阻塞主(UI)线程,并且文本应设置在从单独线程中获取计算项的 UI 上.您自己的一个简单示例也可以。

更新 我已经发布了我自己实现AsyncTask 的问题的答案。

【问题讨论】:

  • 使用 javax.swing.Timer 或 java.util.Timer 是一种很好的方法
  • 为什么不使用 AsyncTask?
  • @Axxiss 对。我使用AsyncTask 用更新的代码回答了我自己的问题。
  • @ControlAltDel 在 Android 中使用 java.util.Timer 几乎没有充分的理由,你绝对不应该使用 javax.swing.Timer 或任何其他 Swing 类。

标签: java android multithreading android-runonuithread


【解决方案1】:

使用Handler类和Views中的postDelayed方法

改变,

    Thread t = new Thread(new WeakRunnable(txtview));
    t.start();

到:

    txtview.postDelayed(new WeakRunnable(txtview), 1500);

然后在您的可运行文件中删除以下 sleep 子句,因为它不再需要了

                try {
                    Thread.sleep(1500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

【讨论】:

  • 这将使用 UI 线程并阻塞直到它完成,它不使用生成的线程来执行 5*5 计算。
  • 您需要在 UI 线程上更改文本,不能在后台线程上完成。请参阅我的编辑,我还说要删除线程睡眠。现在应该可以按您需要的方式工作了。
  • 我就是这么做的。问题是计算是在 UI 线程上运行的,如果它比5*5 更复杂,很可能会挂起 UI。这就是为什么我需要在另一个线程上进行计算,同时在 UI 线程上设置视图文本(显然)。我刚刚与Thread.currentThread().getName() 确认过。
  • 我明白了。在这种情况下,您应该使用AsyncTask : http://stackoverflow.com/a/9671602/794088
【解决方案2】:

你可以使用条件直到它被满足而不是使用thread.sleep();

 Condition.wait(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                return textview != null;
            }
        });

【讨论】:

    【解决方案3】:

    正如@Axxiss 所说,这种情况更适合AsyncTask。我更新了我的代码以使用 AsyncTaskWeakReference 以避免内存泄漏:

    package com.example.helloworld;
    
    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.AsyncTask;
    import android.widget.TextView;
    import android.util.Log;
    import java.lang.ref.WeakReference;
    
    public class HelloWorldActivity extends Activity
    {
    
        private static TextView txtview;
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            txtview = (TextView) findViewById(R.id.mainview);
    
            SimpleTask objSimpleTask=new SimpleTask();
            objSimpleTask.execute();
        }
    
        private static class SimpleTask extends AsyncTask<Void,Void,String> {
            private WeakReference<TextView> mtextview = new WeakReference<TextView>(txtview);
    
            @Override
            protected String doInBackground(Void... params) {
                Log.d("com.example.helloworld", "" + Thread.currentThread().getName());
                try{
                    Thread.sleep(1500);
                } catch(InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
                return "Hola Mundo";
            }
            @Override
            protected void onPostExecute(String result) {
                TextView mtxtview = mtextview.get();
                mtxtview.setText(result);
            }
    
    
        }           
    
    }
    

    【讨论】:

    • AsyncTask 对于如此简单的事情来说太过分了。也许如果您的代码所做的不仅仅是乘以 5*5...
    • @KevinKrumwiede 那么让我们将其作为这些情况的参考。这只是一个测试原型,我将适应资源密集型计算。我在 StackOverflow 上发布时简化了我的代码。
    • 很公平。根据经验,Handler#postDelayed(...) 用于在逻辑上受可在 UI 线程上运行的活动生命周期约束的任何事物,AsyncTask 用于受无法在 UI 线程上运行的活动生命周期约束的任何事物,以及 @ 987654330@ 用于不受单个活动生命周期限制的任何内容。
    • @KevinKrumwiede 谢谢。这是有价值的信息,我会在有疑问时使用。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多