Когда runnables, опубликованные в postDelayed, действительно выполняются на Android?

Есть фрагмент кода, в котором я использую postDelayed и какой-то другой код, выполняемый в основном потоке. Я запускал его несколько раз и всегда видел следующий вывод:

07-13 14:22:18.511 15376-15376/sample1.com.sample_1 D/MainActivity: я = 0

....

07-13 14:22:18.601 15376-15376/sample1.com.sample_1 D/MainActivity: onResume 07-13 14:22:18.601 15376-15376/sample1.com.sample_1 D/MainActivity: postDelayed

Как я вижу из лога, не имеет значения, что моя задержка составляет 50 мс. Сообщение "postDelayed" набирается примерно через 100 мс (601 - 511 = 90). Похоже, что отложенный исполняемый файл добавлен в конец очереди сообщений моего потока пользовательского интерфейса. Но в любом случае, есть ли гарантия того, когда именно будет напечатан postDelayed? Можно ли ввести его в середине цикла for?

package sample1.com.sample_1;

import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

public class MainActivity extends AppCompatActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                Log.d("MainActivity", "postDelayed");
            }
        }, 10);
        for (int i = 0; i < 10000; i++) {
            Log.d("MainActivity", "i = " + i);
        }
    }

    @Override
    protected void onResume() {
        Log.d("MainActivity", "onResume");
        super.onResume();

    }
}

person Maksim Dmitriev    schedule 13.07.2016    source источник
comment
Android ограничивает работу пользовательского интерфейса в основном потоке, чтобы предотвратить то, что вы хотите сделать.   -  person Harlan    schedule 13.07.2016
comment
@ Харлон, я не уверен, что понял тебя.   -  person Maksim Dmitriev    schedule 13.07.2016


Ответы (3)


Есть журналы и от других сервисов и приложений, поэтому вычисление задержки обработчика на основе вывода журнала будет неверным. Механизм ведения журнала на Android имеет собственную очередь, поэтому ваше сообщение на самом деле может быть задержано из-за других журналов в очереди.

Я бы предложил использовать System.nanoTime() для рассчитать время между задержками обработчика. Насколько я знаю, это дает наиболее точное значение таймера.

Наконец, чтобы ответить на ваш вопрос, нет, вы не можете точно определить время, когда произойдет действие. Существуют тысячи (если не миллионы) различных условий, которые могут задержать действия вашего приложения, особенно если это асинхронное действие.

Редактировать: эта задержка не может гарантировать задержку ровно 50 мс, но может гарантировать задержку «по крайней мере» 50 мс.

person Göktay K.    schedule 13.07.2016
comment
Да, похоже, что postDelayed(r, 10) гарантирует минимум 10 мс. Вот пример с nanoTime gist.github.com/anonymous/70f301a06959f91b96302d6a6adddd6d - person Maksim Dmitriev; 13.07.2016
comment
07–13 17:58:36.391 30902–30902/sample1.com.sample_1 D/MainActivity: timeHandler = 22583545657183 07–13 17:58:36.281 30902–30902/sample1.com.sample_1 D/MainActivity: beforeTimeHandler = 27098iff43 108557959 нс = 10 мс (приблизительно) - person Maksim Dmitriev; 13.07.2016

После отслеживания кода для Handler.postDelay() в конечном итоге он выглядит как MessageQueue.enqueueMessage(). Судя по коду, кажется, что он бесконечно перебирает очередь сообщений, пока не достигнет конца или пока текущее время задачи не превысит наше время задержки, а затем вставит задачу в эту позицию в очереди. Это означает, что очередь перед выполнением вашей задачи занимает очень много времени, вы должны более или менее выполнять свою задачу в течение нескольких миллисекунд, когда вы хотите.

Я предполагаю, что проблема не в ваших операциях, а в том, что Android занят основным потоком, отображающим ваш пользовательский интерфейс и другие внутренние задания, что и задерживает вашу задачу. Предположительно, выполнение всего кода в onCreate, onStart, onResume в вашей активности и классах, которые она расширяет, было поставлено в очередь до вашего времени postDelay + 10 мс.

100 мс — это примерно 6 кадров, поэтому это может быть время, необходимое для отображения пользовательского интерфейса вашей активности, попробуйте вместо этого выполнить postDelay по нажатию кнопки, я считаю, что время будет намного более предсказуемым, чем тогда, когда пользовательский интерфейс и активность не настраиваются или снесен.

person Ali    schedule 13.07.2016
comment
07-13 17:49:45.781 14332-14332/sample1.com.sample_1 D/MainActivity: i = 0 ..... 07-13 17:49:45.871 14332-14332/sample1.com.sample_1 D/MainActivity: postDelayed Похоже, что перенос кода на нажатие кнопки не имеет никакого значения - person Maksim Dmitriev; 13.07.2016

используйте post() на самом деле вызовите sendMessageDelayed(getPostMessage(r), 0), а postDelayed() вызовет sendMessageDelayed(getPostMessage(r), delayMillis), они похожи.

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

person Harlan    schedule 13.07.2016