RunOnUiThread не может получить прямой доступ к Message

Я отправляю сообщение обработчику, который связан с его собственным потоком. В методе handleMessage я пытаюсь обновить пользовательский интерфейс содержимым сообщения, используя runOnUiThread. Это прекрасно работает, если взять параметр obj сообщения из handleMessage и назначить его новой конечной переменной. Но если я не использую это назначение и беру msg.obj непосредственно в runnable, переменная obj имеет значение null, даже если ссылка msg имеет тот же идентификатор при проверке ссылки msg, переданной в handleMessage перед вызовом runOnUiThread.

Почему это происходит?

Это работает:

bt01.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         Message msg = new Message();
         msg.obj = new Data("Hi im the message");
         mHandler.sendMessage(msg);
     }
 });

class LooperThread extends Thread {
     public void run() {
        Looper.prepare();
        mHandler = new Handler() {
            public void handleMessage(final Message msg) {
                final Object messageString = msg.obj;
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv01.setText(((Data)messageString).getMessage());
                    }
                });
            }
        };
        Looper.loop();
    }
}

Это не работает:

public void handleMessage(final Message msg) {                    
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        tv01.setText(((Data)msg.obj).getMessage());
                    }
                });
            }

person Logarith    schedule 13.05.2018    source источник
comment
во-первых, используйте HandlerThread - не нужно изобретать колесо, во-вторых, см.   -  person pskink    schedule 13.05.2018
comment
Умный разработчик Android использует для этой цели BroadcastReceiver, RxJava или интерфейсные обратные вызовы.   -  person Khemraj Sharma    schedule 13.05.2018
comment
@Хемрадж BroadcastReceiver ? это как убить муху из пушки/кувалды   -  person pskink    schedule 13.05.2018
comment
Скажите мне ваше требование, я дам вам лучший подход для этого.   -  person Khemraj Sharma    schedule 13.05.2018
comment
Это просто для изучения того, как работают Handler, Looper и т.д. Я просто хочу понять, почему obj имеет значение null, если он не назначен непосредственно конечной переменной.   -  person Logarith    schedule 13.05.2018
comment
потому что Message используются повторно - они попадают в какой-то кеш/пул для повторного использования в будущем   -  person pskink    schedule 13.05.2018
comment
использование HandlerThread имеет такое же поведение при переопределении Handler.Callback handleMessage   -  person Logarith    schedule 13.05.2018
comment
вы видели код, который я разместил? есть два Handler и один Handler.Callback (конечно, вы также можете использовать два пользовательских Handler и переопределить их метод handleMessage), и именно так вы должны обрабатывать свои сообщения, а не использовать какую-то плавающую конечную переменную   -  person pskink    schedule 13.05.2018
comment
попробовал ваше решение сейчас с двумя пользовательскими обработчиками, потому что вызов одного и того же обратного вызова с разным поведением для обработчиков с конструкцией if/else не выглядит хорошим кодом. Это сработало, но мне пришлось сделать copyFrom(message) в первом обработчике, чтобы отправить сообщение обработчику пользовательского интерфейса. (получила ошибку, это сообщение уже используется) Похоже, runOnUiThread() кажется сломанным из-за объединения при использовании его с данными сообщения. Не совсем очевидно.   -  person Logarith    schedule 13.05.2018
comment
"[...] because calling the same callback with different behaviour for handlers with if/else construct doesn't look like good code" я так не думаю, найдите любой Handler в исходном коде Android, и вы увидите, что почти любой Handler обрабатывает несколько действий в зависимости от поля Message#what   -  person pskink    schedule 14.05.2018
comment
вместо получения пустого Message и вызова copyFrom просто используйте Message.obtain(message), как здесь   -  person pskink    schedule 14.05.2018
comment
да, вы, безусловно, правы, что обработчик может обрабатывать несколько действий в зависимости от Message#what. Но в вашем примере if/else зависит от того, на каком обработчике он вызывается. Я думаю, что оба типа сообщений должны иметь смысл для пользовательского интерфейса и workerThread. Представьте, что произойдет, если вы отправите sendEmptyMessage(0) в uiHandler. Это было бы неприятной ошибкой, и раньше это не было очевидно.   -  person Logarith    schedule 14.05.2018
comment
Хорошо, его детали реализации, кстати, вы всегда можете использовать Message#getTarget, чтобы определить, где обрабатывать ваш Message   -  person pskink    schedule 14.05.2018


Ответы (1)


Я предполагаю, что операционная система повторно использует эти Message объекты, а не создает новые. К тому времени, когда ваш Runnable начнет работать в потоке пользовательского интерфейса, Message будет возвращен в пул, а его поля будут установлены на null. Существование obtainMessage() подтверждает эту гипотезу.

person Thomas    schedule 13.05.2018
comment
Я просмотрел исходный код Message, он использует пул при вызове get(), а цикл вызывает recycle(), который устанавливает значение obj в null после отправки сообщения. - person Logarith; 13.05.2018