简体   繁体   English

Runnable已成功发布但未运行

[英]Runnable is posted successfully but not run

In an existing Android project I've encountered the following piece of code (where I inserted the debugging litter) 在现有的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();

New to Android development, and having googled around, I understand that this is the way to do stuff without blocking the main (UI) thread, while still setting the image on the UI thread after decoding. Android开发的新手,并且已经开始搜索,我知道这是在不阻塞主(UI)线程的情况下执行操作的方法,同时仍然在解码后在UI线程上设置图像。 (at least according to android-developers) (which I have verified by logging Thread.currentThread().getName() at various places) (至少根据android-developers) (我通过在各个地方记录Thread.currentThread().getName()验证)

Now sometimes the image just doesn't show up, and stdout only says 现在有时图像不会出现,stdout只说

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

with not a trace of the messages from the Runnable. 没有来自Runnable的消息的痕迹。 So appearantly the Runnable doesn't run() , although img.post() returns true . 所以img.post() Runnable不会run() ,尽管img.post()返回true Pulling the ImageView in onCreate() and declaring it final doesn't help. onCreate()拉出ImageView并将其声明为final也无济于事。

I'm clueless. 我很无能为力。 Simply setting the bitmap directly, while blocking the UI thread, does fix things, but I want to get things right. 简单地设置位图,同时阻止UI线程,确实可以解决问题,但我想把事情弄清楚。 Does anybody understand what's going on here? 有谁知道这里发生了什么?

(ps. this was all observed on an Android 1.6 phone and android-3 sdk) (ps。这是在Android 1.6手机和android-3 sdk上观察到的)

If you look at the docs for View.post there's some relevant info: 如果您查看View.post的文档, View.post一些相关信息:

This method can be invoked from outside of the UI thread only when this View is attached to a window. 仅当此视图附加到窗口时,才能从UI线程外部调用此方法。

Since you're doing this in onCreate , it is likely that sometimes your View will not be attached to the window yet. 由于您是在onCreate执行此操作,因此有时您的View可能尚未附加到窗口。 You can verify this by overriding onAttachedToWindow and putting something in the logs and also logging when you post. 您可以通过覆盖onAttachedToWindow并在日志中放置onAttachedToWindow并在发布时进行日志记录来验证这一点。 You'll see that when the post fails, the post call happens before onAttachedToWindow . 您会看到,当帖子失败时,帖子调用会在onAttachedToWindow之前onAttachedToWindow

As the others have mentioned, you can use Activity.runOnUiThread or provide your own handler. 正如其他人所提到的,您可以使用Activity.runOnUiThread或提供自己的处理程序。 However, if you want to do it directly from the View itself, you can simply get the View 's handler: 但是,如果您想直接从View本身执行此操作,则只需获取View的处理程序:

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

This is especially useful if you have a custom view that includes some sort of background loading. 如果您有一个包含某种背景加载的自定义视图,这将特别有用。 There's also the added bonus of not having to create a new separate handler. 还有一个额外的好处,就是不必创建新的单独处理程序。

I extended ImageView class to solve this problem. 我扩展了ImageView类来解决这个问题。 I collect runnables passed to post while view not attached to window and in onAttachedToWindow post collected runnable. 我收集传递给post的runnables而没有附加到window的视图和onAttachedToWindow post收集的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;
    }
}

I think the problem is you are updating the UI (ImageView) with a separate thread, which is not the UI Thread. 我认为问题是你是用一个单独的线程更新UI(ImageView),而不是UI线程。 The UI can only be updated by the UI Thread. UI只能由UI线程更新。

You can solve this by using Handler : 您可以使用Handler解决此问题:

Handler uiHandler;

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

Then replace your: 然后替换你的:

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

with

uiHandler.post(new Runnable() {

to make sure the imageview is updated in the UI Thread. 确保在UI线程中更新imageview。

Handler is a quite confusing concept, I also took hours of research to really understand about this ;) 处理程序是一个相当混乱的概念,我也花了几个小时的研究来真正理解这个;)

I don't see anything obviously wrong with what you have there; 我没有看到你在那里有什么明显的错误; calling View.post() should cause it to run on the UI thread. 调用View.post()应该使它在UI线程上运行。 If your Activity went away (perhaps through a screen rotation), then your ImageView wouldn't be updated, but I would still expect a log entry to say "setting bitmap ...", even if you couldn't see it. 如果您的Activity消失了(可能是通过屏幕旋转),那么您的ImageView将不会更新,但我仍然期望一个日志条目说“设置位图...”,即使您看不到它。

I suggest trying the following and see if it makes a difference: 我建议尝试以下内容,看看它是否有所作为:

1) Use Log.d (the standard Android logger) rather that System.out 1)使用Log.d(标准Android记录器)而不是System.out

2) Pass your Runnable to Activity.runOnUiThread() rather than View.post() 2)将Runnable传递给Activity.runOnUiThread()而不是View.post()

Use the following code, can post your code to MainThread anytime anywhere, but not depends any Context or Activity . 使用以下代码,可以随时随地将代码发布到MainThread,但不取决于任何ContextActivity That can prevent view.getHandler() failure or tedious onAttachedToWindow() stuffs etc. 这可以防止view.getHandler()失败或繁琐的onAttachedToWindow()东西等。

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

I had the same problem, and using view.getHandler() also failed because the handler was not present. 我遇到了同样的问题,并且使用view.getHandler()也失败了,因为处理程序不存在。 runOnUiThread() solved the problem. runOnUiThread()解决了这个问题。 Presumably this does actually do some queueing until the UI is ready. 据推测,这确实会在UI准备好之前进行排队。

The cause for me was calling the icon load task in a base class and the result being returned so quickly that the main class hadnt estabished the view (getView() in fragment). 我的原因是在基类中调用图标加载任务,结果返回得如此之快,以至于主类没有建立视图(片段中的getView())。

I'm a bit suspicious that it might spuriously fail sometime. 我有点怀疑它可能会在某个时候虚假地失败。 But I'm now ready for it! 但我现在准备好了! Thanks guys. 多谢你们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM