Какова цель Looper и как его использовать? - PullRequest
433 голосов
/ 29 сентября 2011

Я новичок в Android. Я хочу знать, что делает класс Looper, а также как его использовать. Я прочитал документацию по классу Android Looper , но не могу ее полностью понять. Я видел это во многих местах, но не мог понять его цель. Может ли кто-нибудь помочь мне, определив цель Looper, а также приведя простой пример, если это возможно?

Ответы [ 11 ]

383 голосов
/ 29 сентября 2011

Что такое Looper?

Looper - это класс, который используется для выполнения сообщений (Runnables) в очереди.Обычные потоки не имеют такой очереди, например, простой поток не имеет никакой очереди.Он выполняется один раз и после завершения выполнения метода поток не запускает другое сообщение (Runnable).

Где мы можем использовать класс Looper?

Если кто-то хочет выполнитьнесколько сообщений (Runnables), тогда он должен использовать класс Looper, который отвечает за создание очереди в потоке.Например, при написании приложения, которое загружает файлы из Интернета, мы можем использовать класс Looper для помещения файлов для загрузки в очередь.

Как это работает?

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

Вот код, с помощью которого вы можетеподготовить Looper.

class LooperThread extends Thread {
      public Handler mHandler;

      @Override
      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              @Override
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
257 голосов
/ 30 декабря 2015

Вы можете лучше понять, что такое Looper в контексте инфраструктуры GUI.Looper предназначен для двух вещей:

1) Looper преобразует обычный поток , который завершается при возврате метода run (), во что-то непрерывное, пока приложение Android не будет запущено, который необходим в графическом интерфейсе (технически он все еще завершается при возврате метода run (). Но позвольте мне пояснить, что я имею в виду ниже).

2) Looper предоставляет очередь , где выполняемые задания ставятся в очередь, что также необходимо в графическом интерфейсе.

Как вы, возможно, знаете, когда приложение запускается, система создает поток выполнения для приложения, называемый «основным»Приложения Android обычно работают в одном потоке по умолчанию «основной поток».Но основной поток не какой-то секрет, специальный поток .Это просто обычный поток, похожий на потоки, которые вы создаете с помощью кода new Thread(), что означает, что он завершается, когда возвращается его метод run ()!Подумайте о приведенном ниже примере.

public class HelloRunnable implements Runnable {
    public void run() {
        System.out.println("Hello from a thread!");
    }

    public static void main(String args[]) {
        (new Thread(new HelloRunnable())).start();
    }
}

Теперь давайте применим этот простой принцип к приложениям для Android.Что произойдет, если приложение Android будет работать в обычном потоке?Поток с именем "main" или "UI" или чем-то еще, запускает ваше приложение и рисует весь UI.Итак, первый экран отображается для пользователей.И что теперь?Основной поток заканчивается?Нет, не должно.Следует подождать, пока пользователи что-то сделают, верно?Но как мы можем достичь этого поведения?Ну, мы можем попробовать с Object.wait() или Thread.sleep().Например, основной поток завершает свою начальную работу для отображения первого экрана и спит.Он пробуждается, что означает прерывание, когда выбирается новая работа.Пока все хорошо, но в данный момент нам нужна структура данных, похожая на очередь, для хранения нескольких заданий.Подумайте о случае, когда пользователь касается экрана последовательно, и выполнение задачи занимает больше времени.Итак, нам нужна структура данных для хранения заданий, которые должны выполняться в порядке «первым пришел - первым вышел».Кроме того, вы можете себе представить, что реализовать постоянно работающий поток и процесс-задание-при поступлении с использованием прерывания непросто, и это приводит к сложному и часто не поддерживаемому коду.Мы бы предпочли создать новый механизм для этой цели, и - это то, что Лупер все о .Официальный документ класса Looper гласит: «С потоками по умолчанию не связан цикл сообщений», а Looper - это класс, «используемый для запуска цикла сообщений для потока».Теперь вы можете понять, что это значит.

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

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        Looper.loop();
        ...
    }
}

и <a href="https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/Looper.java" rel="noreferrer">Looper.loop()</a> бесконечно зацикливают цикл и удаляют сообщение и обрабатывают его по очереди.time:

public static void loop() {
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
}

Итак, в основном, Looper - это класс, созданный для решения проблемы, возникающей в среде GUI.Но такого рода потребности могут возникать и в других ситуациях.На самом деле это довольно известный шаблон для многопоточного приложения, и вы можете узнать о нем больше в " Параллельное программирование в Java " Дуга Ли (особенно, глава 4.1.4 "Рабочие потоки" была бы полезна),Кроме того, вы можете себе представить, что этот тип механизма не уникален в платформе Android, но все графические интерфейсы могут нуждаться в чем-то похожем на это.Вы можете найти почти такой же механизм в Java Swing Framework.

73 голосов
/ 01 октября 2013

Looper позволяет последовательно выполнять задачи в одном потоке.И обработчик определяет те задачи, которые нам нужно выполнить.Это типичный сценарий, который я пытаюсь проиллюстрировать в этом примере:

class SampleLooper extends Thread {
@Override
public void run() {
  try {
    // preparing a looper on current thread     
    // the current thread is being detected implicitly
    Looper.prepare();

    // now, the handler will automatically bind to the
    // Looper that is attached to the current thread
    // You don't need to specify the Looper explicitly
    handler = new Handler();

    // After the following line the thread will start
    // running the message loop and will not normally
    // exit the loop unless a problem happens or you
    // quit() the looper (see below)
    Looper.loop();
  } catch (Throwable t) {
    Log.e(TAG, "halted due to an error", t);
  } 
}
}

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

handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
    }
});

В потоке пользовательского интерфейса у нас есть неявный Looper, который позволяет нам обрабатывать сообщения в потоке пользовательского интерфейса.

31 голосов
/ 18 сентября 2013

Android Looper - это оболочка для прикрепления MessageQueue к Thread, которая управляет обработкой очереди.Это выглядит очень загадочно в документации Android, и часто мы можем столкнуться с Looper проблемами доступа к пользовательскому интерфейсу.Если мы не понимаем основ, с этим становится очень трудно справиться.

Вот статья , в которой объясняется Looper жизненный цикл, как его использовать и как использовать Looper в Handler

enter image description here

Looper = Тема + MessageQueue

13 голосов
/ 16 августа 2016

Определение Looper & Handler:

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

Подробности:

Таким образом, ThreadLine Thread - это поток, который может принимать больше задач из других потоков через обработчик.

Looper назван так, потому что он реализует цикл - берет следующую задачу, выполняет ее, затем берет следующую и так далее.Обработчик называется обработчиком, потому что он используется для обработки или принятия следующей задачи каждый раз из любого другого потока и передачи в Looper (Thread или ThreadLine Thread).

Пример:

Отличным примером Looper and Handler или PipeLine Thread является загрузка более одного изображения или загрузка их на сервер (Http) по одному в одном потоке вместо запуска нового потока для каждого сетевого вызова в фоновом режиме..

Подробнее о Looper и Handler и определении конвейерной резьбы читайте здесь:

Android Guts: введение в Loopers и Handlers

7 голосов
/ 26 мая 2014

A Looper имеет synchronized MessageQueue, который используется для обработки сообщений, помещенных в очередь.

Он реализует Thread определенный шаблон хранения.

Только один Looper за Thread. Ключевые методы включают prepare(), loop() и quit().

prepare() инициализирует текущий Thread как Looper. prepare() - это метод static, который использует класс ThreadLocal, как показано ниже.

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
  1. prepare() должен быть вызван явно перед запуском цикла обработки событий.
  2. loop() запускает цикл обработки событий, который ожидает поступления сообщений в очередь сообщений определенного потока. После получения следующего сообщения метод loop() отправляет сообщение целевому обработчику
  3. quit() отключает цикл обработки событий. Это не завершает цикл, но вместо этого ставит в очередь специальное сообщение

Looper можно запрограммировать в Thread с помощью нескольких шагов

  1. Продлить Thread

  2. Позвоните Looper.prepare(), чтобы инициализировать Thread как Looper

  3. Создать один или несколько Handler для обработки входящих сообщений

  4. Позвоните Looper.loop() для обработки сообщений, пока цикл не будет передан quit().
5 голосов
/ 31 августа 2017

Срок службы Java Тема закончилась после завершения метода run().Та же тема не может быть запущена снова.

Looper превращает обычный Thread в цикл сообщений.Ключевые методы Looper:

void prepare ()

Инициализировать текущий поток как петлитель.Это дает вам возможность создавать обработчики, которые затем ссылаются на этот петлитель, прежде чем фактически запустить цикл.После вызова этого метода обязательно вызовите loop () и завершите его, вызвав quit ().

void loop ()

Запустите очередь сообщений в этом потоке.Обязательно вызовите quit (), чтобы завершить цикл.

void quit()

Завершает цикл.

Заставляет метод loop () завершаться без обработки дополнительных сообщений.в очереди сообщений.

В этой статье mindorks автор Janishar хорошо объясняет основные понятия.

enter image description here

Looper связан с темой.Если вам нужно Looper в потоке пользовательского интерфейса, Looper.getMainLooper() вернет связанный поток.

Вам необходимо Looper быть связанным с Обработчиком .

Looper, Handler и HandlerThread - это способ Android для решения проблемасинхронного программирования.

Если у вас есть Handler, вы можете вызывать ниже API.

post (Runnable r)

Вызывает добавление Runnable r в очередь сообщений.Runnable будет выполняться в потоке, к которому подключен этот обработчик.

boolean sendMessage (Message msg)

Помещает сообщение в конец очереди сообщений после всех ожидающих сообщений до текущего времени.Он будет получен в handleMessage (Message) в потоке, прикрепленном к этому обработчику.

HandlerThread - удобный класс для запуска нового потока, в котором есть петлитель.Затем петлитель можно использовать для создания классов обработчиков

. В некоторых случаях вы не можете запускать задачи Runnable в потоке пользовательского интерфейса.Например, сетевые операции: отправьте сообщение в сокет, откройте URL-адрес и получите контент, прочитав InputStream

. В этих случаях HandlerThread полезно.Вы можете получить Looper объект из HandlerThread и создать Handler в HandlerThread вместо основного потока.

Код HandlerThread будет выглядеть следующим образом:

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

См. Ниже сообщение, например, код:

Android: тост в теме

4 голосов
/ 16 ноября 2017

Этот ответ не имеет ничего общего с вопросом, но использование «петлителя» и способа, которым люди создавали обработчик и петлитель во ВСЕХ ответах здесь, - явная плохая практика (хотя некоторые объяснения верны), я должен опубликовать это:

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);

и для полной реализации

3 голосов
/ 20 марта 2013

Лучшим примером является обработка нескольких нажатий или загрузка элементов в Сервисе .

Handler и AsnycTask часто используются для распространения События / Сообщения между UI (поток) и рабочим потоком или для задержки действий.Таким образом, они больше связаны с пользовательским интерфейсом.

A Looper обрабатывает задачи ( Runnables, Futures ) в связанной с потоком очереди в фоновом режиме - даже без взаимодействия с пользователем или отображаемого пользовательского интерфейса (приложение загружает файл в фоновом режиме во время разговора).

1 голос
/ 14 марта 2019

Понимание потоков Looper

Поток Java - это единица выполнения, которая была разработана для выполнения задачи в своем методе run () и завершается после этого: enter image description here

Но в Android есть много случаев использования, когда нам нужно поддерживать активность потока и ждать, например, пользовательских вводов / событий.Пользовательский интерфейс ака Main Thread.

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

Когда приложение запускается, система создает поток выполнения для приложения, называемый «основным».Этот поток очень важен, потому что он отвечает за отправку событий в соответствующие виджеты пользовательского интерфейса, включая события рисования.

enter image description here

Теперь обратите внимание на то, что хотя основной поток является потоком Java, он продолжает прослушивать пользовательские события и рисует кадры со скоростью 60 кадров в секунду на экране, но после каждого цикла он не умирает.как это так?

Ответ: Looper Class : Looper - это класс, который используется для поддержания потока в рабочем состоянии и управления очередью сообщений для выполнения задач вэтот поток.

По умолчанию потоки не имеют связанного с ними цикла сообщений, но вы можете назначить его, вызвав Looper.prepare () в методе run, а затем вызвать Looper.loop ().

Целью Looper является поддержание потока и ожидание следующего цикла ввода Message объекта для выполнения вычислений, которые в противном случае будут уничтожены после первого цикла выполнения.

Если вы хотите глубже понять, как Looper управляет Message очередью объектов, вы можете взглянуть на исходный код Looperclass:

https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/os/Looper.java

Ниже приведен пример того, как вы можете создать Looper Thread и общаться с Activity классом, используя LocalBroadcast

class LooperThread : Thread() {

    // sendMessage success result on UI
    private fun sendServerResult(result: String) {
        val resultIntent = Intent(ServerService.ACTION)
        resultIntent.putExtra(ServerService.RESULT_CODE, Activity.RESULT_OK)
        resultIntent.putExtra(ServerService.RESULT_VALUE, result)
        LocalBroadcastManager.getInstance(AppController.getAppController()).sendBroadcast(resultIntent)
    }

    override fun run() {
        val looperIsNotPreparedInCurrentThread = Looper.myLooper() == null

        // Prepare Looper if not already prepared
        if (looperIsNotPreparedInCurrentThread) {
            Looper.prepare()
        }

        // Create a handler to handle messaged from Activity
        handler = Handler(Handler.Callback { message ->
            // Messages sent to Looper thread will be visible here
            Log.e(TAG, "Received Message" + message.data.toString())

            //message from Activity
            val result = message.data.getString(MainActivity.BUNDLE_KEY)

            // Send Result Back to activity
            sendServerResult(result)
            true
        })

        // Keep on looping till new messages arrive
        if (looperIsNotPreparedInCurrentThread) {
            Looper.loop()
        }
    }

    //Create and send a new  message to looper
    fun sendMessage(messageToSend: String) {
        //Create and post a new message to handler
        handler!!.sendMessage(createMessage(messageToSend))
    }


    // Bundle Data in message object
    private fun createMessage(messageToSend: String): Message {
        val message = Message()
        val bundle = Bundle()
        bundle.putString(MainActivity.BUNDLE_KEY, messageToSend)
        message.data = bundle
        return message
    }

    companion object {
        var handler: Handler? = null // in Android Handler should be static or leaks might occur
        private val TAG = javaClass.simpleName

    }
}

Использование :

 class MainActivity : AppCompatActivity() {

    private var looperThread: LooperThread? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start looper thread
        startLooperThread()

        // Send messages to Looper Thread
        sendMessage.setOnClickListener {

            // send random messages to looper thread
            val messageToSend = "" + Math.random()

            // post message
            looperThread!!.sendMessage(messageToSend)

        }   
    }

    override fun onResume() {
        super.onResume()

        //Register to Server Service callback
        val filterServer = IntentFilter(ServerService.ACTION)
        LocalBroadcastManager.getInstance(this).registerReceiver(serverReceiver, filterServer)

    }

    override fun onPause() {
        super.onPause()

        //Stop Server service callbacks
     LocalBroadcastManager.getInstance(this).unregisterReceiver(serverReceiver)
    }


    // Define the callback for what to do when data is received
    private val serverReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val resultCode = intent.getIntExtra(ServerService.RESULT_CODE, Activity.RESULT_CANCELED)
            if (resultCode == Activity.RESULT_OK) {
                val resultValue = intent.getStringExtra(ServerService.RESULT_VALUE)
                Log.e(MainActivity.TAG, "Server result : $resultValue")

                serverOutput.text =
                        (serverOutput.text.toString()
                                + "\n"
                                + "Received : " + resultValue)

                serverScrollView.post( { serverScrollView.fullScroll(View.FOCUS_DOWN) })
            }
        }
    }

    private fun startLooperThread() {

        // create and start a new LooperThread
        looperThread = LooperThread()
        looperThread!!.name = "Main Looper Thread"
        looperThread!!.start()

    }

    companion object {
        val BUNDLE_KEY = "handlerMsgBundle"
        private val TAG = javaClass.simpleName
    }
}

Можем ли мы вместо этого использовать Async task или Intent Services?

  • Асинхронные задачи предназначены для выполнения коротких операций в фоновом режиме и обеспечения прогресса и результатов в потоке пользовательского интерфейса.Асинхронные задачи имеют ограничения, например, вы не можете создавать более 128 Асинхронных задач и ThreadPoolExecutor позволят только до 5 асинхронных задач .

  • IntentServices также предназначены для выполнения фоновых задач на более длительный срок, и вы можете использовать LocalBroadcast для связи с Activity.Но сервисы разрушаются после выполнения задачи.Если вы хотите, чтобы он работал в течение длительного времени, вам нужно делать такие хеки, как while(true){...}.

Другие значимые случаи использования для Looper Thread:

  • Используется для двухсторонней связи с сокетом, когда сервер продолжает прослушивать клиентский сокет и записывать подтверждение

  • Обработка растрового изображения в фоновом режиме.Передайте URL-адрес изображения в поток Looper, и он применит эффекты фильтра и сохранит его в определенном месте, а затем передаст временный путь изображения.

...