Runnable已成功发布但未运行

时间:2010-11-03 02:25:49

标签: java android multithreading runnable

在现有的Android项目中,我遇到了以下代码(我插入了调试垃圾)

ImageView img = null;

public void onCreate(...) {

    img = (ImageView)findViewById(R.id.image);

    new Thread() {
        public void run() {
            final Bitmap bmp = BitmapFactory.decodeFile("/sdcard/someImage.jpg");
            System.out.println("bitmap: "+bmp.toString()+" img: "+img.toString());
            if ( !img.post(new Runnable() {
                public void run() {
                    System.out.println("setting bitmap...");
                    img.setImageBitmap(bmp);
                    System.out.println("bitmap set.");
                }
            }) ) System.out.println("Runnable won't run!");
            System.out.println("runnable posted");
        }
    }.start();

Android开发的新手,并且已经google了一下,我明白这是在不阻塞主(UI)线程的情况下执行操作的方法,同时仍然在解码后在UI线程上设置图像。 (at least according to android-developers)(我已通过在各个地方记录Thread.currentThread().getName()进行了验证)

现在有时图像没有出现,stdout只说

I/System.out( 8066): bitmap: android.graphics.Bitmap@432f3ee8 img: android.widget.ImageView@4339d698
I/System.out( 8066): runnable posted

没有来自Runnable的消息的痕迹。因此,尽管run()返回img.post(),但Runnable并不true。在onCreate()中拉取ImageView并将其声明为final也无济于事。

我很无能为力。简单地设置位图,同时阻止UI线程,确实可以解决问题,但我想把事情弄清楚。有人知道这里发生了什么吗?

(ps。这是在Android 1.6手机和android-3 sdk上观察到的)

6 个答案:

答案 0 :(得分:56)

如果您查看View.post的文档,则会提供一些相关信息:

  

此方法只能在UI线程外部调用   此视图附加到窗口。

由于您是在onCreate中执行此操作,因此有时您的View可能尚未附加到窗口。您可以通过覆盖onAttachedToWindow并在日志中放置内容并在发布时进行日志记录来验证这一点。您会看到,当帖子失败时,帖子调用会在onAttachedToWindow之前发生。

正如其他人所提到的,您可以使用Activity.runOnUiThread或提供自己的处理程序。但是,如果您想直接从View本身执行此操作,则只需获取View的处理程序:

view.getHandler().post(...);

如果您的自定义视图包含某种背景加载,这将非常有用。还有一个额外的好处就是不必创建新的单独处理程序。

答案 1 :(得分:10)

我扩展了ImageView课来解决这个问题。我收集传递给帖子的runnables,而视图没有附加到窗口和onAttachedToWindow帖子收集的runnable。

public class ImageView extends android.widget.ImageView
{
    List<Runnable> postQueue = new ArrayList<Runnable>();
    boolean attached;

    public ImageView(Context context)
    {
        super(context);
    }

    public ImageView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
    }

    public ImageView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onAttachedToWindow()
    {
        super.onAttachedToWindow();

        attached = true;

        for (Iterator<Runnable> posts = postQueue.iterator(); posts.hasNext();)
        {
            super.post(posts.next());
            posts.remove();
        }
    }

    @Override
    protected void onDetachedFromWindow()
    {
        attached = false;
        super.onDetachedFromWindow();
    }

    @Override
    public boolean post(Runnable action)
    {
        if (attached) return super.post(action);
        else postQueue.add(action);
        return true;
    }
}

答案 2 :(得分:7)

我认为问题是你是用一个单独的线程更新UI(ImageView),而不是UI线程。 UI只能由UI线程更新。

您可以使用Handler

解决此问题
Handler uiHandler;

public void onCreate(){
    ...
    uiHandler = new Handler(); // This makes the handler attached to UI Thread
    ...
}

然后替换你的:

if ( !img.post(new Runnable() {

uiHandler.post(new Runnable() {

确保在UI线程中更新imageview。

Handler是一个相当令人困惑的概念,我还花了几个小时的研究来真正理解这个;)

答案 3 :(得分:6)

我没有看到你在那里有什么明显的错误;调用View.post()应该使它在UI线程上运行。如果您的Activity消失了(可能是通过屏幕旋转),那么您的ImageView将不会更新,但我仍然希望日志条目说“设置位图...”,即使您看不到它。 / p>

我建议尝试以下操作,看看它是否有所作为:

1)使用Log.d(标准Android记录器)而不是System.out

2)将Runnable传递给Activity.runOnUiThread()而不是View.post()

答案 4 :(得分:3)

使用以下代码,可以随时随地将代码发布到MainThread,但不取决于任何ContextActivity。这可以防止view.getHandler()失败或繁琐的onAttachedToWindow()内容等。

    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //TODO
        }
    });

答案 5 :(得分:1)

我遇到了同样的问题,并且使用view.getHandler()也失败了,因为处理程序不存在。 runOnUiThread()解决了这个问题。据推测,在UI准备好之前,这确实会排队等待。

我的原因是在基类中调用图标加载任务并返回结果 很快,主类没有建立视图(片段中的getView())。

我有点怀疑它可能会在某个时候虚假地失败。 但我现在准备好了!谢谢你们。

相关问题