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, и, погуглив вокруг, я понимаю, что это способ сделать вещи, не блокируя основной поток (UI), все еще устанавливая изображение в потоке UI после декодирования. (по крайней мере, по мнению android-разработчиков) (что я проверил, зарегистрировав 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. Так appearantly выполнимое не run(), хотя img.post() возвращает true. Потянув ImageView в onCreate() и признании его final не помогает.

я невежда. Просто установка растрового изображения напрямую, блокируя поток пользовательского интерфейса, исправляет вещи,но я хочу все исправить. Делает кто-нибудь понимает, что здесь происходит?

(ps. все это наблюдалось на телефоне Android 1.6 и Android-3 sdk)

6 ответов


если вы посмотрите на документы для View.post есть некоторая соответствующая информация:

этот метод может быть вызван из вне потока пользовательского интерфейса, только когда этот вид прикреплен к окну.

так как вы делаете это в onCreate вполне вероятно, что иногда View еще не будет прикреплен к окну. Вы можете проверить это, переопределив onAttachedToWindow и положить что-то в журналы, а также войти, когда вы должность. Вы увидите, что когда сообщение терпит неудачу, вызов post происходит до onAttachedToWindow.

как уже упоминалось, вы можете использовать Activity.runOnUiThread или предоставить свой собственный обработчик. Однако, если вы хотите сделать это непосредственно с , вы можете просто получить С:

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

это особенно полезно, если у вас есть пользовательский вид, который включает какую-то фоновую загрузку. Также есть дополнительный бонус-не нужно создавать новый отдельный обработчик.


Я ImageView класс для решения этой проблемы. Я собираю runnables, переданные в post, В то время как вид не прикреплен к окну и в 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;
    }
}

Я думаю, проблема в том, что вы обновляете UI (ImageView) с отдельным потоком, который не является потоком UI. Пользовательский интерфейс может быть обновлен только потоком пользовательского интерфейса.

вы можете решить эту проблему с помощью проводник:

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() {

чтобы убедиться, что imageview обновляется в потоке пользовательского интерфейса.

Handler-довольно запутанная концепция, я также взял часы исследований, чтобы действительно поймите об этом;)


Я не вижу ничего явно неправильного в том, что у вас есть; вызов View.post () должен заставить его работать в потоке пользовательского интерфейса. Если ваша активность ушла (возможно, через поворот экрана), то ваш ImageView не будет обновлен, но я все равно ожидаю, что запись в журнале скажет "настройка растрового изображения ...- даже если ты не видел.

Я предлагаю попробовать следующее и посмотреть, имеет ли это значение:

1) Используйте Журнал.d (стандартный регистратор Android) скорее, что Система.вон!--1-->

2) передайте свой Runnable в Activity.runOnUiThread () вместо View.post ()


используйте следующий код, можете опубликовать свой код в MainThread в любое время в любом месте, но не зависит от любого Context или Activity. Это может предотвратить view.getHandler() провал или утомительной onAttachedToWindow() питания etc.

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

У меня была такая же проблема, и с помощью зрения.getHandler() также не удалось, потому что обработчик не присутствовал. runOnUiThread () решил проблему. Предположительно, это реально сделать несколько очередей, пока UI готов.

причиной для меня был вызов задачи загрузки значка в базовом классе и возвращаемый результат так быстро, что основной класс не установил представление (getView () во фрагменте).

Я немного подозреваю, что он может случайно потерпеть неудачу. Но я теперь готов к этому! Спасибо, ребята.