Looperの目的とその使用方法は何ですか?


454

Androidは初めてです。Looperクラスの機能と使い方を知りたい。Android Looperクラスのドキュメントを読みましたが、完全に理解できません。多くの場所で見ましたが、目的がわかりません。誰かが目的を定義し、Looper可能であれば簡単な例を挙げて私を助けてくれますか?


7
LooperとSafari Books Onlineでのその使用について、非常に徹底的で明確な説明を見つけました。残念ながら、私はアクセスが限られた時間だけ無料であると疑っています。safaribooksonline.com/library/view/efficient-android-threading/...
ジョーのLapp

1
Androidの記事とリファレンスページでは、現在の記事を把握する前に、以前の記事を理解しておく必要があります。APIガイドのアクティビティとサービスの記事を読んでから、ハンドラとルーパーを読むことをお勧めします。スレッドが何であるかを理解している場合にも役立ちます(Androidスレッドではなく、一般的なスレッド(例:POSIX))。
FutureSci 2016

1
私はこの記事が有用であることが判明:codetheory.in/...
ハーマン

回答:


396

ルーパーとは?

ルーパーは、キュー内のメッセージ(Runnables)を実行するために使用されるクラスです。通常のスレッドにはそのようなキューはありません。たとえば、単純なスレッドにはキューがありません。これは1回実行され、メソッドの実行が終了した後、スレッドは別のMessage(Runnable)を実行しません。

Looperクラスはどこで使用できますか?

誰かが複数のメッセージ(Runnables)を実行したい場合は、スレッド内にキューを作成するルーパークラスを使用する必要があります。たとえば、インターネットからファイルをダウンロードするアプリケーションを作成しているときに、Looperクラスを使用して、ダウンロードするファイルをキューに入れることができます。

使い方?

prepare()ルーパーを用意する方法があります。次にloop()、メソッドを使用して現在のスレッドでメッセージループを作成できます。これで、ループを終了するまで、ルーパーはキュー内の要求を実行する準備ができました。

以下は、ルーパーを準備するためのコードです。

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();
      }
  }

17
AsyncTaskは、すべてのスレッド管理をカプセル化するため、その目的に適し、複雑さが軽減されます。
Fernando Gallego

4
run()およびhandleMessage()メソッドの前に@Overrideアノテーションを付ける必要があります
Andrew Mackenzie

5
ドキュメントには、looper.quitを呼び出す必要があることが示されています。上記のコードでは、Looper.loopは無期限にブロックされます。
AndroidDev 2013

2
ループを終了する方法。上記のコード例のどこにLooper.quit()を含めるか?
Seenu69 2013年

6
ルーパー付きのスレッドには便利なクラスであるHandlerThreadを使うほうがいいと思います。
Nimrod Dayan 2014

287

GUIフレームワークのコンテキストでLooperが何であるかをよりよく理解できます。ルーパーは2つのことをするように作られています。

1)Looperは、run()メソッドが戻ると終了する通常のスレッドをAndroidアプリが実行されるまで継続的に実行される何か変換します。これは、GUIフレームワークで必要です(技術的には、run()メソッドが戻ると終了します。以下で私が何を意味するかを明確にしてください)。

2)Looper は、実行するジョブがキューに入れられるキューを提供します。これは、GUIフレームワークでも必要です。

ご存知のとおり、アプリケーションが起動すると、システムは「メイン」と呼ばれるアプリケーションの実行スレッドを作成します。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アプリが通常のスレッドで実行されるとどうなりますか?「メイン」または「UI」などのスレッドがアプリケーションを起動し、すべてのUIを描画します。したがって、最初の画面がユーザーに表示されます。ならどうしよう?メインスレッドは終了しますか?いいえ、必要ありません。ユーザーが何かをするまで待つべきですよね?しかし、どうすればこの動作を実現できますか?まあ、Object.wait()またはで試すことができますThread.sleep()。たとえば、メインスレッドは最初のジョブを終了して最初の画面を表示し、スリープします。実行する新しいジョブがフェッチされると、それは目覚めます。つまり、中断されます。これまでのところ良好ですが、現時点では、複数のジョブを保持するためのキューのようなデータ構造が必要です。ユーザーが連続して画面をタッチし、タスクの完了に時間がかかる場合を考えてみます。したがって、先入れ先出しで実行されるジョブを保持するためのデータ構造が必要です。また、割り込みを使用して、常に実行されプロセスジョブが到着したときにスレッドを実装することは容易ではなく、複雑でメンテナンス不可能なコードにつながることも想像できるでしょう。そうした目的のために新しいメカニズムを作成したいと思っています。それがルーパーのすべてですルーパークラス公式ドキュメント「デフォルトでは、スレッドにはメッセージループが関連付けられていません」と、ルーパーは「スレッドのメッセージループを実行するために使用される」クラスです。これで、その意味を理解できます。

より明確にするために、メインスレッドが変換されるコードを確認してみましょう。それはすべてActivityThreadクラスで発生します。main()メソッドには、通常のメインスレッドを必要なものに変換する以下のコードがあります。

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

そしてLooper.loop()無限方法ループおよび時間で、メッセージ及びプロセス1をデキュー。

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フレームワークで発生する問題に対処するために作成されたクラスです。ただし、このようなニーズは他の状況でも発生する可能性があります。実際、これはマルチスレッドアプリケーションのかなり有名なパターンであり、Doug Leaによる「Javaでの並行プログラミング」で詳細を学ぶことができます(特に、4.1.4章「ワーカースレッド」が役立つでしょう)。また、この種のメカニズムはAndroidフレームワークに固有のものではないことは想像できますが、すべてのGUIフレームワークはこれと多少似ている必要があるかもしれません。Java Swingフレームワークでもほぼ同じメカニズムを見つけることができます。


26
これは、Looperクラスが使用される理由を実際に説明する唯一の回答です。なぜそれがトップアンサーではないのか分かりませんが、3つのより高い評価の答えは何も説明しません。
Andrew Koster 2016年

4
@AK。だから遅すぎたように見えても、この回答を追加しました。私の答えがあなたを助けてくれてうれしいです!:)
김준호2016年

1
@ Hey-men-whatsupはい
'2016

1
これを読む前は、「ルーパー???」のようでした。そして今「そうそう、それを議論しましょう」。おかげで、素晴らしい答え:)
umerk44

簡単な質問。メインスレッドで、UI要素をすべて引き出した後、スリープ状態にすると述べました。しかし、ユーザーが画面上のボタンを操作するとします。そのボタンのクリックがメインキューに入れられていなくても、いくつかのオブジェクトがそれを正しいアクティビティにディスパッチし、そのアクティビティのメインスレッドが起動して実行されます。そのボタンクリックのコールバック内のコード?
CapturedTree

75

ルーパーを使用すると、タスクを単一のスレッドで順次実行できます。そしてハンドラーは、実行する必要があるタスクを定義します。これは、この例で説明しようとしている典型的なシナリオです。

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);
  } 
}
}

これで、他のいくつかのスレッド(たとえばuiスレッド)でハンドラーを使用して、実行するタスクをLooperにポストできます。

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

UIスレッドには、UIスレッドでメッセージを処理できる暗黙のルーパーがあります。


それはUIプロセスをロックしません、それは本当ですか?
gumuruh 2014年

4
「ジョブ」をキューに投稿する方法のサンプルを含めてくれてありがとう
Peter Lillevold

これは、なぜこのクラスを使用するのかを説明していません。
Andrew Koster、2016年

33

アンドロイドはLooper添付するためのラッパーであるMessageQueueThread、それはキューの処理を管理します。Androidのドキュメントでは非常に不可解に見え、Looper関連するUIアクセスの問題に直面することがよくあります。基本を理解していないと扱いが大変になります。

ここにある記事を説明しLooper、それとの使用使用方法、ライフサイクルをLooperではHandler

ここに画像の説明を入力してください

ルーパー=スレッド+メッセージキュー


3
これは、なぜこのクラスを使用するのかを説明していません。
Andrew Koster、2016年

14

ルーパーとハンドラーの定義:

ルーパーは、スレッドをパイプラインスレッドに変換するクラスであり、ハンドラーは他のスレッドからタスクをプッシュするメカニズムを提供します。

詳細:

したがって、PipeLineスレッドは、ハンドラを介して他のスレッドからより多くのタスクを受け入れることができるスレッドです。

ルーパーはそれを実行し、次のタスクをとり、その後、次のいずれかなどをとり、 -それはループを実装しているため、そのように命名されます。ハンドラーは、他のスレッドから毎回その次のタスクを処理または受け入れるために使用され、ルーパー(スレッドまたはパイプラインスレッド)に渡されるため、ハンドラーと呼ばれます。

例:

ルーパーとハンドラーまたはパイプラインスレッドの非常に完璧な例は、バックグラウンドでネットワークコールごとに新しいスレッドを開始する代わりに、複数の画像をダウンロードするか、1つのスレッドで1つずつサーバー(Http)にアップロードすることです。

ルーパーとハンドラーの詳細とパイプラインスレッドの定義については、こちらをご覧ください。

Android Guts:ルーパーとハンドラーの紹介


7

A ルーパーはありsynchronized MessageQueueキューに置かれたプロセスメッセージに使われているが。

Thread特定のストレージパターンを実装します。

Looperつき1 つのみThread。主な方法としてはprepare()loop()quit()

prepare()電流をThreadとして初期化しますLooperprepare()あるstatic使用方法ThreadLocal以下に示すようにクラスが。

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
  1. prepare() イベントループを実行する前に明示的に呼び出す必要があります。
  2. loop()メッセージが特定のスレッドのメッセージキューに到着するのを待つイベントループを実行します。次のメッセージが受信されると、loop()メソッドはメッセージをターゲットハンドラにディスパッチします
  3. quit()イベントループをシャットダウンします。ループは終了しませんが、代わりに特別なメッセージをエンキューします

LooperThreadいくつかのステップでビアにプログラムできます

  1. 伸ばす Thread

  2. Looper.prepare()としてスレッドを初期化するための呼び出しLooper

  3. Handler着信メッセージを処理する1つ以上の(s)を作成します

  4. Looper.loop()ループが指示されるまでメッセージを処理するために呼び出しますquit()

5

メソッドの完了後、Java スレッドの寿命が終了しましたrun()。同じスレッドを再開することはできません。

ルーパーは、通常Threadのメッセージループに変換します。主な方法は次のLooperとおりです。

void prepare ()

現在のスレッドをルーパーとして初期化します。これにより、実際にループを開始する前に、このルーパーを参照するハンドラーを作成できます。このメソッドを呼び出した後は必ずloop()を呼び出し、quit()を呼び出して終了してください。

void loop ()

このスレッドでメッセージキューを実行します。必ずquit()を呼び出してループを終了してください。

void quit()

ルーパーを終了します。

メッセージキュー内のメッセージをこれ以上処理せずにloop()メソッドを終了させます。

Janisharによるこのmindorksの記事は、コアコンセプトをわかりやすく説明しています。

ここに画像の説明を入力してください

Looperスレッドに関連付けられています。LooperUIスレッドで必要な場合は、Looper.getMainLooper()関連付けられたスレッドを返します。

ハンドラーにLooper関連付ける必要があります。

LooperHandler、およびHandlerThread非同期プログラミングの問題を解決するためのAndroidの方法です。

を取得したらHandler、以下のAPIを呼び出すことができます。

post (Runnable r)

Runnable rをメッセージキューに追加します。実行可能オブジェクトは、このハンドラーが接続されているスレッドで実行されます。

boolean sendMessage (Message msg)

現在時刻より前のすべての保留メッセージの後に、メッセージをメッセージキューの最後にプッシュします。このハンドラに接続されているスレッドのhandleMessage(Message)で受信されます。

HandlerThreadは、ルーパーを持つ新しいスレッドを開始するための便利なクラスです。その後、ルーパーを使用してハンドラークラスを作成できます。

一部のシナリオでは、RunnableUIスレッドでタスクを実行できません。例:ネットワーク操作:ソケットでメッセージを送信し、URLを開き、読み取りによってコンテンツを取得しますInputStream

このような場合にHandlerThread便利です。メインスレッドの代わりにLooperオブジェクトを取得HandlerThreadしてHandleron を作成でき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:スレッドでのトースト


5

ルーパースレッドについて

Javaスレッドは、run()メソッドでタスクを実行し、その後終了するように設計された実行単位です。 ここに画像の説明を入力してください

しかし、Androidでは、スレッドを存続させ、ユーザー入力/イベントなどを待機する必要がある多くのユースケースがあります。UIスレッド別名Main Thread

Androidのメインスレッドは、アプリの起動時にJVMによって最初に開始されるJavaスレッドであり、ユーザーが閉じることを選択するか、未処理の例外が発生するまで実行され続けます。

アプリケーションが起動すると、システムは「メイン」と呼ばれるアプリケーションの実行スレッドを作成します。このスレッドは、描画イベントを含む適切なユーザーインターフェイスウィジェットへのイベントのディスパッチを担当するため、非常に重要です。

ここに画像の説明を入力してください

メインスレッドはJavaスレッドですが、ユーザーイベントをリッスンし続け、画面上に60 fpsのフレームを描画しますが、サイクルごとに消えることはありません。どうですか?

答えはルーパークラスです。ルーパーは、スレッドを存続させ、メッセージキューを管理してそのスレッドでタスクを実行するために使用されるクラスです。

デフォルトでは、スレッドにはメッセージループが関連付けられていませんが、runメソッドでLooper.prepare()を呼び出してループを割り当て、次にLooper.loop()を呼び出すことができます。

ルーパーの目的は、スレッドを存続させ、入力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
    }
}

代わりに非同期タスクまたはインテントサービスを使用できますか?

  • 非同期タスクは、バックグラウンドで短い操作を実行し、UIスレッドで進捗と結果を提供するように設計されています。非同期タスクには128個を超える非同期タスクを作成できないなどの制限ThreadPoolExecutorがあり、最大5つの非同期タスクしか許可されません。

  • IntentServicesまた、バックグラウンドタスクをもう少し長く実行するように設計されており、LocalBroadcastとの通信に使用できますActivity。ただし、サービスはタスクの実行後に破棄されます。長時間実行したい場合は、次のようにする必要があります while(true){...}

ルーパースレッドの他の意味のある使用例:

  • サーバーがクライアントソケットをリッスンし、確認応答を書き戻す、双方向のソケット通信に使用されます

  • バックグラウンドでのビットマップ処理。画像のURLをルーパースレッドに渡すと、フィルター効果が適用されて一時的な場所に保存され、画像の一時パスがブロードキャストされます。


4

この回答は質問とは関係ありませんが、ルーパーの使用と、ここでのすべての回答でのハンドラーとルーパーの作成方法は明らかに悪い習慣です(ただし、一部の説明は正しいです)。これを投稿する必要があります。

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

そして、のための完全な実装


3

サービスで複数のダウンまたはアップロードアイテムを処理することは、より良い例です。

Handlerまた、UI(スレッド)とワーカースレッド間でイベント/メッセージAsnycTaskを伝達するため、またはアクションを遅延させるためによく使用されます。したがって、UIとの関連性が高くなります。

Looperハンドルタスク(ランナブル、先物、バックグラウンドでのスレッド関連のキューにある) -でも、ユーザーの操作なしで、またはUIを表示(アプリのダウンロード、通話中にバックグラウンドでファイル)。


1

ルーパーとは?

ドキュメントから

Looper

Looperのメッセージループを実行するために使用されるクラスthread。デフォルトでは、スレッドにはメッセージループが関連付けられていません。メッセージを作成prepare()するには、ループを実行するスレッドを呼び出しloop()、ループが停止するまでメッセージを処理します。

  • A Looperはメッセージ処理ループです。
  • ルーパーの重要な特徴は、ルーパーが作成されるスレッドに関連付けられていることです。
  • LooperクラスMessageQueueは、リストメッセージを含むを維持します。ルーパーの重要な特徴は、ルーパーが作成されるスレッドに関連付けられていることです。
  • Looper次のタスクを取り、それを実行し、その後、次のものなどを取る-それはループを実装しているため、そのように命名されます。Handler誰かがより良い名前を発明できなかったので、これはハンドラと呼ばれています
  • Android Looperは、Androidユーザーインターフェイス内のJavaクラスであり、Handlerクラスとともに、ボタンのクリック、画面の再描画、方向の切り替えなどのUIイベントを処理します。

使い方?

ここに画像の説明を入力してください

ルーパーを作成する

スレッドが取得LooperしてMessageQueue呼び出すことで、Looper.prepare()その実行後。Looper.prepare()呼び出しスレッドを識別し、ルーパーとMessageQueueオブジェクトを作成し、スレッドを関連付けます

サンプルコード

class MyLooperThread extends Thread {

      public Handler mHandler; 

      public void run() { 

          // preparing a looper on current thread  
          Looper.prepare();

          mHandler = new Handler() { 
              public void handleMessage(Message msg) { 
                 // process incoming messages here
                 // this will run in non-ui/background thread
              } 
          }; 

          Looper.loop();
      } 
  }

詳細については、以下の投稿を確認してください


優れた、私は力学を理解しました。ありがとう
Serg Burlaka
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.