web-dev-qa-db-ja.com

Looperの目的は何ですか?またその使い方は?

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

431
Khawar Raza

ルーパーとは何ですか?

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

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

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

どのように動作しますか?

Looperを準備するためのprepare()メソッドがあります。それから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();
      }
  }
383
Dharmendra

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

より明確にするために、メインスレッドが変換されているコードを確認しましょう。それはすべて 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);
        ...
    }
}

だから、基本的にルーパーはGUIフレームワークで発生する問題に対処するために作られたクラスです。しかし、この種のニーズは他の状況でも起こる可能性があります。実際、これはマルチスレッドアプリケーションではかなり有名なパターンであり、Doug Leaによる " Concurrent Programming in Java "でもっと詳しく学ぶことができます(特に、4.1.4章 "Worker Threads"が役に立つでしょう)。 。また、この種のメカニズムはAndroidフレームワークに固有のものではないと想像することができますが、すべてのGUIフレームワークはこれといくらか似ている必要があるかもしれません。 Java Swingフレームワークにもほぼ同じメカニズムがあります。

253
김준호

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

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

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

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

73
user2834274

AndroidのLooperは、MessageQueueThreadに添付するラッパーで、Queue処理を管理します。 Androidのドキュメントでは非常に難解に見えますが、何度もLooper関連のUIアクセス問題に直面する可能性があります。基本を理解していないと、処理が非常に困難になります。

これはLooperのライフサイクル、使い方、そしてLooperHandlerの使い方を説明した 記事 です。

enter image description here

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

31
user542954

Looper&Handlerの定義:

ルーパーは、スレッドをパイプラインスレッドハンドラに変換するクラスです。他のスレッドからタスクをプッシュします。

詳細:

そのため、PipeLine Threadは、Handlerを通じて他のスレッドからより多くのタスクを受け入れることができるスレッドです。

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

例:

Looper and HandlerまたはPipeLine Threadの非常に完璧な例は、バックグラウンドで各ネットワーク呼び出しに対して新しいThreadを開始するのではなく、1つのスレッドで複数の画像をダウンロードするか、サーバー(Http)に1つずつアップロードすることです。

LooperとHandler、およびPipeline Threadの定義については、こちらを参照してください。

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

13
ahmadalibaloch

A Looper には、キューに置かれたメッセージを処理するためのsynchronizedMessageQueueがあります。

これはThread Specific Storage Patternを実装しています。

LooperごとにThreadは1つだけです。重要なメソッドには、prepare()loop()、およびquit()があります。

prepare()は現在のThreadLooperとして初期化します。 prepare()は、以下に示すようにstaticクラスを使用するThreadLocalメソッドです。

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

LooperはいくつかのステップでThreadにプログラムすることができます

  1. Threadを拡張する

  2. ThreadをLooperとして初期化するためにLooper.prepare()を呼び出します

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

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

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

Looper は通常のThreadをメッセージループに変換します。 Looperの主なメソッドは以下のとおりです。

void prepare ()

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

void loop ()

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

void quit()

ルーパーを終了します。

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

Janisharによるこの mindorksの記事 は、基本的な概念をNiceの方法で説明しています。

enter image description here

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

ハンドラ に関連付けるにはLooperが必要です。

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

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

post (Runnable r)

Runnable rをメッセージキューに追加します。ランナブルは、このハンドラがアタッチされているスレッド上で実行されます。

boolean sendMessage (Message msg)

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

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

いくつかのシナリオでは、UI Threadで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:スレッドで乾杯

5
Ravindra babu

この回答は質問とは関係ありませんが、ここでの回答の中でlooperを使用して人々がハンドラーとlooperを作成する方法は明らかに悪い習慣です(ただし、説明は正しい場合もあります)。

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

そして 完全な実装の場合

4
TacB0sS

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

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

Looperは、バックグラウンドでスレッド関連のキューでタスク(Runnables、Futures)を処理します - ユーザーの操作や表示されたUIがない場合でも(呼び出し中にバックグラウンドでファイルをダウンロードします).

3
Thorsten

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

Javaスレッドは、run()メソッドでタスクを実行し、その後終了するように設計された実行単位です。 enter image description here

しかしAndroidでは、Threadを有効にしてユーザー入力やイベントを待つ必要があるユースケースが多数あります。 UIスレッド、別名Main Thread

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

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

enter image description here

ここで注意しなければならないのは、メインスレッドはJavaスレッドですが、それでもユーザーイベントを監視し、画面に60 fpsのフレームを描画し続け、それでも各サイクル後に終了しないことです。それはどうですか?

答えはルーパークラスです :ルーパーは、スレッドを有効に保ち、そのスレッドでタスクを実行するためのメッセージキューを管理するために使用されるクラスです。

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

Looperの目的はThreadを生きたままにしておき、入力Messageオブジェクトの次のサイクルが計算を実行するのを待つことです。そうしなければ最初の実行サイクルの後に破壊されるでしょう。

LooperがどのようにMessageオブジェクトキューを管理するのかをもっと深く掘り下げたいのなら、Looperclassのソースコードを見てください。

https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/Java/Android/os/Looper.Java

Activityを使用してLooper Threadを作成し、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をLooperスレッドに渡すと、フィルタ効果が適用されて一時的な場所に保存され、次に画像の一時パスがブロードキャストされます。

1
Hitesh Sahu

ルーパーとは何ですか?

DOCSから

Looper

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

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

使い方?

enter image description here

ルーパーを作成する

スレッドは、実行後にLooper.prepare()を呼び出すことによって、Looperおよび MessageQueue を取得します。 Looper.prepare()は、呼び出し元のスレッドを識別し、Looperオブジェクトと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();
      } 
  }

詳細については、下記の記事をチェックしてください

0
Nilesh Rathod