Android - 如何在runOnUiThread中将数据传递给Runnable?

时间:2011-10-13 23:50:01

标签: android multithreading user-interface arguments

我需要使用runOnUiThread更新一些UI并在UI线程内部进行更新 现在,UI的数据来自另一个线程,在此处由data表示。

如何将数据传递给Runnable,以便它们可用于更新UI? Android似乎不允许直接使用数据。是否有一种优雅的方式来做到这一点?

public void OnNewSensorData(Data data) {

    runOnUiThread(new Runnable() {
        public void run() {
            //use data
        }
    });
}

我的解决方案是在runnable中创建一个fioeld private Data sensordata,并为其分配数据。这仅适用于原始Data data是最终的。

public void OnNewSensorData(final Data data) {

    runOnUiThread(new Runnable() {
        private Data sensordata = data;
        public void run() {
            //use sensordata which is equal to data
        }
    });
}

6 个答案:

答案 0 :(得分:36)

您发现的问题是

  

Java中的内部类捕获(“关闭”)其中的词法范围   他们是定义的。但它们只捕获声明为“最终”的变量。

如果这很清楚,那么这里有一个很好的讨论细节: Cannot refer to a non-final variable inside an inner class defined in a different method

但你的解决方案看起来不错。此外,如果data是最终版,您可以将代码简化为:

public void OnNewSensorData(final Data data) {
    runOnUiThread(new Runnable() {
        public void run() {
            // use data here
            data.doSomething();
        }
    });
}

答案 1 :(得分:21)

如果您想避免使用中间最终变量(如Dan S所述),您可以使用其他方法实现Runnable来设置数据:

public class MyRunnable implements Runnable {
  private Data data;
  public void setData(Data _data) {
    this.data = _data;
  }

  public void run() {
    // do whatever you want with data
  }
}

然后你可以这样调用这个方法:

public void OnNewSensorData(Data data) {
  MyRunnable runnable = new MyRunnable();
  runnable.setData(data);
  runOnUiThread(runnable);
}

您还可以使MyRunnable的构造函数将Data实例作为参数:

public class MyRunnable implements Runnable {
  private Data data;
  public MyRunnable(Data _data) {
    this.data = _data;
  }

  public void run() {
    ...
  }
}

然后只说runOnUiThread(new MyRunnable(data));

答案 2 :(得分:5)

我有一个类似的问题,我想把信息传递给线程。为了用android系统解决它,我修改了corsiKa的答案:Runnable with a parameter?

您可以在方法中声明一个类,并传递参数,如下所示:

void Foo(String str) {
    class OneShotTask implements Runnable {
        String str;
        OneShotTask(String s) { str = s; }
        public void run() {
            someFunc(str);
        }
    }
    runOnUiThread(new OneShotTask(str));
}

答案 3 :(得分:1)

每当您的计划有新的Data想要展示时,您就需要更新。您在此处列出的第二个代码是实现此目标的标准方法。如果您继续在线程中更新Data,可能会有一些捕获。如果是这种情况,请考虑阻止线程,直到UI完成更新或将数据复制到另一个Data对象。

内部发生的事情是JVM正在复制对Data对象的引用,以便匿名类运行。存储在里面的Data仍然可以更改。如果您的方法需要对Data进行其他更改,请使用另一个变量(对象引用),例如:final Data finalData = data;。您也可以删除行private Data sensordata = data;并直接在run方法中使用数据。

它可能看起来不优雅,但这是Java将对象变量传递给匿名类的方式。 Java语言版本7中有更新的语法,但Android与Java语言版本5和6兼容。

答案 4 :(得分:1)

以下是调用服务回调以更新UI状态字符串(TextView textStatus)的典型情况。该服务可能是线程化的。

该示例结合检查是否需要线程重定向和实际重定向:

   // service callbacks
    public void service_StatusTextChanged(final String s) {
        if( isOnUiThread() ) {
            textStatus.setText(s);
        } else {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    textStatus.setText(s);
                }
            });
        }
    }

    static public boolean isOnUiThread() {
       return Thread.currentThread() == Looper.getMainLooper().getThread();
    }

另见How to check if running on UI thread in Android?

答案 5 :(得分:0)

public static Activity globalContext = null;    
CommonSetting.globalContext = this;// put this in MainACtivity.onCreate()   
public void createToastShort(final String message) {
    runOnUiThread(new Runnable() {
        public void run() {
         Toast.makeText(CommonSetting.globalContext, message, Toast.LENGTH_SHORT).show();
        }
    });
}