Androidスレッド、Handler


緒論


昨年のオペレーティングシステムの授業でプロセスとスレッドを学びました.非常に理論化と抽象化されていて、JavaScriptで符号化する場合、スレッドについてのことはあまりないので(明らかですが、よくわかりませんが)、スキップしましたが、Android OSでは、このスレッドの処理も実際には重要なので、少し時間をかけて調べてみたいと思います.
プロセスとスレッドを簡単に理解する必要があります.プロセスは、プログラムを起動するときに実行をプロセスと呼び、複数のタスクを同時に実行するためにスレッドを実行します.

通常、プロセス内のスレッドは線のように実行されます.多くの人がそう形容している.それ以外にも,マルチプロセッシングとマルチスレッド共有リソースによる差異や処理方式などのコンピュータ工学的な差異は,深く検討すれば非常に深刻であるが,1つのプログラムが実行されると1つのプロセスが生成され,その中で複数のスレッドが生成され,同時に複数のタスクが処理されることを知ればよい.

Androidスレッド


アンドロイドosではスレッドが2種類に分かれています.1つはメインスレッド(UIスレッド)とワークスレッド(Workerスレッド)です.メインスレッドは、その名の通りメインタスクを行うスレッドであり、UIタスクを実行できるスレッドである.次に、残りのすべての他のスレッドをワークスレッドと呼びます.
なぜスレッドを2つに分けるのですか?次のコードを見てください.
val firstButton = findViewById<Button>(R.id.first_button)
val text = findViewById<TextView>(R.id.text)

var i = 0
firstButton.setOnClickListener{
	for(i in 1..100000000L){
        text.text = "${i}번째 text"
   }
}
レイアウトにボタンを入力し、ボタンを押すとiが1つ増え、その値をボタンに入れてみます.しかし、このようにエンコードされたアンドロイドシミュレータを設定してボタンを押すと、アプリケーションはすぐに消えてしまいます.
理由は何ですか.プライマリ・スレッドは1本の線のように長いため、1つまたは2つの順序で要求を処理し、そこでfor文を計算し、何の操作もできません.
しかし、アンドロイドシステムを開発する際には、これらの作業を行う必要があることが発生します.以上のコード程度ではないが,UIタスクを同時に実行する必要があることが起こる.そのため、Androidはワークスレッドというものを生成し、単独で演算する必要があります.
すなわち、プライマリ・スレッドではなく、時間がかかる重複タスクによっては、作業スレッドを個別に生成する必要があります.
class MainActivity : AppCompatActivity() {
    private lateinit var myText: TextView
    private lateinit var startButton: Button

    private lateinit var myThread: MyThread

    private val TAG: String = "로그"
    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d(TAG, "MainActivity - onCreate: ");

        startButton = findViewById(R.id.start_button)
        myText = findViewById(R.id.my_text)

        myThread = MyThread()

        startButton.setOnClickListener{
            Log.d(TAG, "MainActivity - onCreate: 스타트 버튼 클릭");
            myThread.start()
            myText.text="world hello"
        }
    }

    inner class MyThread: Thread(){
        override fun run() {
            for(i in 1..100000000L){
                Log.d(TAG, "MyThread - run: i : $i ");
                SystemClock.sleep(1000)
            }
        }
    }
}
このようにしてワークスレッドを作成してfor文を切り替え、textを指定することで、プライマリスレッドは干渉を受けずにtext値を変更できます.ただし、myText.text="world hello"myThreadメソッドにrun部分を入れると、アプリケーションはすぐに離れます.UI演算はメインスレッドのみで行うことが約束されているからである.
UIに関する操作は、メインスレッド(UIスレッド)でのみ行えます.この概念を熟知しなければならない.
では、UI関連のタスクはメインスレッド上でしか実行できないのはなぜでしょうか.ボタンが1つある場合、ボタン内のtextの値はメインスレッドで指定してもよいし、他の操作スレッドで指定してもよい.
まず、UIスレッドでは、あるボタンのテキスト値をボタン1と呼ぶ.このような競合が発生すると、アプリケーションに問題が発生するのは当然です.そのため、AndroidではUI関連のタスクはメインスレッドでのみ実行できます.(これにより、ユーザは、1つのスレッド内でUIを順番に処理することができる.)
それ以外にjavascriptは単一スレッドに基づいており、setTimeoutのような演算が発生すると、自動的にこのタスクをバックグラウンドに配置し、イベントループによって処理されますが、Android、kotlin、javaでは、開発者が自らスレッドを作成し、処理する必要があります.これらの仕事がどのように行われているかを理解してみましょう.

マニピュレータ


まず、これまで学んだことの一部を明確にする必要があります.一つはスレッドであり,これはコンピュータエンジニアリング,開発分野の共通内容であるが,UIスレッドとワークスレッドの区分,および現在学習しているHandlerはAndroid独自の概念である.Handlerは、ユーザが作業スレッドでUIを使用するのを助けることができる.
class MainActivity : AppCompatActivity() {
    private lateinit var myText: TextView
    private lateinit var startButton: Button
    private lateinit var stopButton: Button

    private lateinit var myThread: MyThread
    private lateinit var myHandler: MyHandler

    private var isRunning: Boolean = false

    private val TAG: String = "로그"
    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d(TAG, "MainActivity - onCreate: ");

        startButton = findViewById(R.id.start_button)
        stopButton = findViewById(R.id.stop_button)
        myText = findViewById(R.id.my_text)


        isRunning = true

        myThread = MyThread()
        myHandler = MyHandler()

        startButton.setOnClickListener{
            Log.d(TAG, "MainActivity - onCreate: 스타트 버튼 클릭");
            myThread.start()
            isRunning = true
        }

        stopButton.setOnClickListener {
            isRunning = false
        }
    }

    inner class MyThread: Thread(){
        override fun run() {
            while(isRunning){
                SystemClock.sleep(1000)
                myHandler.sendEmptyMessage(0)
            }
        }
    }

    inner class MyHandler: Handler(){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            Log.d(TAG, "MyHandler - handleMessage: msg : $msg ");
            var now = System.currentTimeMillis()
            myText.text = "now : $now"
        }
    }
}
コードを少しずつ外す
    private var isRunning: Boolean = false
    startButton.setOnClickListener{
    	Log.d(TAG, "MainActivity - onCreate: 스타트 버튼 클릭");
   	 	myThread.start()
    	isRunning = true
    }

    stopButton.setOnClickListener {
		isRunning = false
    }
スレッドをisRunningに変換するかどうかは、上記のコードによって決定される.その後、startButtonおよびstopButtonのクリックリスナーに登録して変更できます.startButtonをクリックしてスレッドを実行し、stopButtonをクリックしてスレッドの実行を停止します.
inner class MyThread: Thread(){
        override fun run() {
            while(isRunning){
                SystemClock.sleep(1000)
                myHandler.sendEmptyMessage(0)
            }
        }
    }
上のコードはisRunningであり、trueであれば、startButtonをクリックして現在の時間を計算し、1秒待ってからmyHandlerにメッセージを送信します.現在、このsendEmptyMessageはスレッド内でのみHandlerを起動する役割を果たしている.
 inner class MyHandler: Handler(){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            Log.d(TAG, "MyHandler - handleMessage: msg : $msg ");
            var now = System.currentTimeMillis()
            myText.text = "now : $now"
        }
    }
そして皆が望むHandlerHandlerはスレッドからメッセージを受信し、UIを使用します.コンストラクションが完了したら、シミュレータでボタンを押すと、text1秒ごとに変更が表示されます.すなわち、ワークスレッドではHandlerによりUI操作が行われる.
これに基づいて,プロセッサを計算値ではなくUIのみを使用する操作に分割することを提案する.
    inner class MyThread: Thread(){
        override fun run() {
            while(isRunning){
                var now = System.currentTimeMillis()
                Log.d(TAG, "MyThread - run: now : $now ");
                SystemClock.sleep(1000)
                var msg = Message()
                msg.what = 0
                msg.obj = now
                myHandler.sendMessage(msg)
            }
        }
    }

    inner class MyHandler: Handler(){
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            myText.text = "now : ${msg.obj}"
        }
    }
前述したように、スレッドとHandlerコードを変更すればよい.nowオブジェクトを使用してMessage変数を生成し、msgおよび.whatを定義します..objHandlerに送信したいデータ、すなわち計算されたobjが送信され、nowは通常、what構文whenがどのように処理されるかを区別するために使用される.
ただし、objをもう一度押すと、スレッドのライフサイクルに関係するアプリケーションのハングアップが決定されます.

次に議論する情報キューとルパーの概念も写真の中で、まず、テーマは
ねじの作成→ねじの実行→ねじの終了(消去)
手順に従って、スタートボタンを押した瞬間startButtonオブジェクトmyThreadが作成されたため、手順をスキップした.したがって、再度ボタンを押すと、startメソッドを実行する矛盾が生じる.だから
     startButton.setOnClickListener{
            myThread = MyThread()
            Log.d(TAG, "MainActivity - onCreate: 스타트 버튼 클릭");
            myThread.start()
        }
必要に応じてリスナーでスレッドの生成を続行すると、アプリケーションを閉じずに複数のスレッドが生成されます.

n/a.結論


Androidでは、重要なメインスレッド、ワークスレッド、Handler、Loperのコンセプトを熟知するために、一生懸命Googleプログラミングをしていましたが、理解しにくい点が多かったのでYouTubeでよく説明している動画を見て、Handlerのベースから学びました.オペレーティングシステムの授業を受ける時、多くの理論が学べない地方があって、このような実務的な地方が適用することを発見して、専門知識を学ぶ時間に対しても少しの意義を見つけることができます.しかし、Handlerが発表されるたびに、すでにstartになっている言葉が出てきます.やはりAndroid分野も非常に速い分野です.