Runnable已成功发布但未运行

mvd*_*vds 24 java multithreading android 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();
Run Code Online (Sandbox Code Playgroud)

Android开发的新手,并且已经开始搜索,我知道这是在不阻塞主(UI)线程的情况下执行操作的方法,同时仍然在解码后在UI线程上设置图像.(至少根据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
Run Code Online (Sandbox Code Playgroud)

没有来自Runnable的消息的痕迹.所以看起来Runnable没有run(),尽管img.post()返回true.拉入ImageView onCreate()并声明它final并没有帮助.

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

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

kab*_*uko 56

如果您查看文档中View.post有相关信息:

仅当此视图附加到窗口时,才能从UI线程外部调用此方法.

既然你正在这样做onCreate,很可能有时你View的窗户还没有附在窗户上.您可以通过覆盖onAttachedToWindow并在日志中放置内容并在发布时进行日志记录来验证这一点.您会看到,当帖子失败时,之前的帖子就会发生onAttachedToWindow.

正如其他人所提到的,您可以使用Activity.runOnUiThread或提供自己的处理程序.但是,如果你想直接从它View自己做,你可以简单地得到View的处理程序:

view.getHandler().post(...);
Run Code Online (Sandbox Code Playgroud)

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

  • 我只是简单地调查了这一点,但看起来当视图没有附加到窗口时它也没有处理程序. (10认同)

Nik*_*Nik 10

我扩展了ImageView课程来解决这个问题.我收集传递给post的runnables,而没有附加到窗口的视图和onAttachedToWindowpost收集的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;
    }
}
Run Code Online (Sandbox Code Playgroud)


xan*_*ndy 7

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

您可以使用Handler解决此问题:

Handler uiHandler;

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

然后替换你的:

if ( !img.post(new Runnable() {
Run Code Online (Sandbox Code Playgroud)

uiHandler.post(new Runnable() {
Run Code Online (Sandbox Code Playgroud)

确保在UI线程中更新imageview.

处理程序是一个相当混乱的概念,我也花了几个小时的研究来真正理解这个;)


Sha*_*zon 6

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

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

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

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