Androidは初めてです。Looper
クラスの機能と使い方を知りたい。Android Looperクラスのドキュメントを読みましたが、完全に理解できません。多くの場所で見ましたが、目的がわかりません。誰かが目的を定義し、Looper
可能であれば簡単な例を挙げて私を助けてくれますか?
Androidは初めてです。Looper
クラスの機能と使い方を知りたい。Android Looperクラスのドキュメントを読みましたが、完全に理解できません。多くの場所で見ましたが、目的がわかりません。誰かが目的を定義し、Looper
可能であれば簡単な例を挙げて私を助けてくれますか?
回答:
ルーパーとは?
ルーパーは、キュー内のメッセージ(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();
}
}
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フレームワークでもほぼ同じメカニズムを見つけることができます。
ルーパーを使用すると、タスクを単一のスレッドで順次実行できます。そしてハンドラーは、実行する必要があるタスクを定義します。これは、この例で説明しようとしている典型的なシナリオです。
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スレッドでメッセージを処理できる暗黙のルーパーがあります。
アンドロイドはLooper
添付するためのラッパーであるMessageQueue
にThread
、それはキューの処理を管理します。Androidのドキュメントでは非常に不可解に見え、Looper
関連するUIアクセスの問題に直面することがよくあります。基本を理解していないと扱いが大変になります。
ここにある記事を説明しLooper
、それとの使用使用方法、ライフサイクルをLooper
ではHandler
ルーパー=スレッド+メッセージキュー
ルーパーとハンドラーの定義:
ルーパーは、スレッドをパイプラインスレッドに変換するクラスであり、ハンドラーは他のスレッドからタスクをプッシュするメカニズムを提供します。
詳細:
したがって、PipeLineスレッドは、ハンドラを介して他のスレッドからより多くのタスクを受け入れることができるスレッドです。
ルーパーはそれを実行し、次のタスクをとり、その後、次のいずれかなどをとり、 -それはループを実装しているため、そのように命名されます。ハンドラーは、他のスレッドから毎回その次のタスクを処理または受け入れるために使用され、ルーパー(スレッドまたはパイプラインスレッド)に渡されるため、ハンドラーと呼ばれます。
例:
ルーパーとハンドラーまたはパイプラインスレッドの非常に完璧な例は、バックグラウンドでネットワークコールごとに新しいスレッドを開始する代わりに、複数の画像をダウンロードするか、1つのスレッドで1つずつサーバー(Http)にアップロードすることです。
ルーパーとハンドラーの詳細とパイプラインスレッドの定義については、こちらをご覧ください。
A ルーパーはありsynchronized
MessageQueue
キューに置かれたプロセスメッセージに使われているが。
Thread
特定のストレージパターンを実装します。
にLooper
つき1 つのみThread
。主な方法としてはprepare()
、loop()
とquit()
。
prepare()
電流をThread
として初期化しますLooper
。prepare()
あるstatic
使用方法ThreadLocal
以下に示すようにクラスが。
public static void prepare(){
...
sThreadLocal.set
(new Looper());
}
prepare()
イベントループを実行する前に明示的に呼び出す必要があります。 loop()
メッセージが特定のスレッドのメッセージキューに到着するのを待つイベントループを実行します。次のメッセージが受信されると、loop()
メソッドはメッセージをターゲットハンドラにディスパッチしますquit()
イベントループをシャットダウンします。ループは終了しませんが、代わりに特別なメッセージをエンキューしますLooper
Thread
いくつかのステップでビアにプログラムできます
伸ばす Thread
Looper.prepare()
としてスレッドを初期化するための呼び出しLooper
Handler
着信メッセージを処理する1つ以上の(s)を作成します
Looper.loop()
ループが指示されるまでメッセージを処理するために呼び出しますquit()
。メソッドの完了後、Java スレッドの寿命が終了しましたrun()
。同じスレッドを再開することはできません。
ルーパーは、通常Thread
のメッセージループに変換します。主な方法は次のLooper
とおりです。
void prepare ()
現在のスレッドをルーパーとして初期化します。これにより、実際にループを開始する前に、このルーパーを参照するハンドラーを作成できます。このメソッドを呼び出した後は必ずloop()を呼び出し、quit()を呼び出して終了してください。
void loop ()
このスレッドでメッセージキューを実行します。必ずquit()を呼び出してループを終了してください。
void quit()
ルーパーを終了します。
メッセージキュー内のメッセージをこれ以上処理せずにloop()メソッドを終了させます。
Janisharによるこのmindorksの記事は、コアコンセプトをわかりやすく説明しています。
Looper
スレッドに関連付けられています。Looper
UIスレッドで必要な場合は、Looper.getMainLooper()
関連付けられたスレッドを返します。
ハンドラーにLooper
関連付ける必要があります。
Looper
、Handler
、およびHandlerThread
非同期プログラミングの問題を解決するためのAndroidの方法です。
を取得したらHandler
、以下のAPIを呼び出すことができます。
post (Runnable r)
Runnable rをメッセージキューに追加します。実行可能オブジェクトは、このハンドラーが接続されているスレッドで実行されます。
boolean sendMessage (Message msg)
現在時刻より前のすべての保留メッセージの後に、メッセージをメッセージキューの最後にプッシュします。このハンドラに接続されているスレッドのhandleMessage(Message)で受信されます。
HandlerThreadは、ルーパーを持つ新しいスレッドを開始するための便利なクラスです。その後、ルーパーを使用してハンドラークラスを作成できます。
一部のシナリオでは、Runnable
UIスレッドでタスクを実行できません。例:ネットワーク操作:ソケットでメッセージを送信し、URLを開き、読み取りによってコンテンツを取得しますInputStream
このような場合にHandlerThread
便利です。メインスレッドの代わりにLooper
オブジェクトを取得HandlerThread
してHandler
on を作成でき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;
}
コード例については、以下の投稿を参照してください。
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をルーパースレッドに渡すと、フィルター効果が適用されて一時的な場所に保存され、画像の一時パスがブロードキャストされます。
ルーパーとは?
ドキュメントから
Looper
のメッセージループを実行するために使用されるクラスthread
。デフォルトでは、スレッドにはメッセージループが関連付けられていません。メッセージを作成prepare()
するには、ループを実行するスレッドを呼び出しloop()
、ループが停止するまでメッセージを処理します。
Looper
はメッセージ処理ループです。MessageQueue
は、リストメッセージを含むを維持します。ルーパーの重要な特徴は、ルーパーが作成されるスレッドに関連付けられていることです。Looper
次のタスクを取り、それを実行し、その後、次のものなどを取る-それはループを実装しているため、そのように命名されます。Handler
誰かがより良い名前を発明できなかったので、これはハンドラと呼ばれています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();
}
}
詳細については、以下の投稿を確認してください