Android 第三章 第二節 多線程(handler)




1. 線程的作用

Android用戶界面是與用戶交互的接口,對於用戶的操作,Android迅速響應用戶輸入(200ms內)是一個重要目標。

如果Activity中的應用程序在5s之內未做出響應,
可能會出現"應用程序無響應,是否關閉?"的對話框。

對於這類耗時比較多的工作,一般是使用多線程的方法來解決的
網絡操作必須放到子線程(規定)
子線程不允許修改UI (視圖,主線程)

ANR提示:    Application Not Responding    應用程序無響應
一種錯誤的提示框

Android應用中的主線程(UI線程):

Android應用剛啓動時,
會在當前應用所對應的進程中啓動一個主線程(也叫UI線程);	
該UI線程處理與UI相關的事件,如:用戶的按鍵事件,
把相關的事件分派到對應的組件進行處理等    (模塊化)。

對於UI線程中比較耗時的工作,開啓一個子線程來處理這些工作:

首先創建一個Thread對象,然後調用start( )方法啓動新的子線程

# 兩種方式實現創建線程

# 方法一:繼承Thread類
new MyThread().start();  //在主線程啓動子線
----------------------------------------------------
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e("number",Thread.currentThread().getName());
        new MyThread().start();  //在主線程啓動子線程

    }//------------------------------------------------------------------onCreate
    //-------------------------------------------------耗時操作,計算斐波那契數列第n項的數值
    private long fib(int n){
        if (n == 1){
            return 1;
        }else if (n == 2){
            return 1;
        }else {
            return fib(n - 2) + fib(n -1);
        }
    }
    //----------------------------------------------------繼承Thread類實現多線程
    private class MyThread extends Thread{
        @Override
        public void run() {   //這是子線程,要想被使需要在主線程啓動。
            Log.e("name",fib(10)+"");
            Log.e("number",Thread.currentThread().getName());
        }
    }
    //-----------------------------------------------------實現Ruannable接口實現多線程

# 方法二:實現Ruinable接口
new Thread(new MyRunnable()).start();//在主線程啓動子線
--------------------------
代碼塊替換上面的MyThread塊即可
    private class MyRunnable implements Runnable{
        @Override
        public void run() {
            Log.e("1111111111111111111111",Thread.currentThread().getName());
            Message myMessage=new Message();//創建消息對象
            myMessage.what=100;//區分消息的來源/對象,參數是整數。必填
            myMessage.obj=Thread.currentThread().getName();//攜帶發送的數據,數據寫入obj屬性
            mainHandler.sendMessage(myMessage);
        }
    }

在Android中,只有UI線程(即主線程)纔可以更新主UI界面,而其子線程不能更新UI視圖。

對於這類既需要異步執行,又需要更新UI界面的問題,Android提供了多種解決方案:

1.		使用多線程實現:Thread+Handler
http://www.cnblogs.com/playing/archive/2011/03/24/1993583.html

2.		使用AsyncTask實現。(異步任務)

# 打印當前線程名稱

Thread.currentThread().getName()
舉例:        Log.e("number",Thread.currentThread().getName());

在主線程中用結果爲	main
在子線程中用結果爲	Thread-2,數字依次增加



2. 線程的實現 ( Handler )

Handler:接受子線程發送的數據,並用此數據配合主線程更新UI。

Handler定義在主線程中(UI線程中);
Handler充當主線程和子線程之間交互的中介:
Handler在新啓動的子線程中發送消息;
Handler在主線程中獲取並處理子線程所發送的消息。

# 流程簡圖

在這裏插入圖片描述

	1. 主線程創建子線程並啓動,
	2. 子線程開始工作,發送消息到handler(Handler在主線程中創建的)
	3. handler把消息放到主線程的消息隊列
	4. 主線程接受消息隊列中的信息並處理
	5. Looper實現了循環,實現了多個線程的處理與請求

# 重要的類

• UI線程:創建UI線程時,初始化一個Looper對象以及與其關聯的MessageQueue;

• Handler:發送與處理信息,在當前線程中有一個Looper	對象;

• Message:Handler接收與處理的消息對象;

• MessageQueue:消息隊列,管理Message;

• Looper:管理MessageQueue, 取出Message分發給對應	的Handler處理,
	每個線程有且只有一個Looper。	

# Handler的使用方法

使用Handler實現主線程與子線程的通信主要使用如下三個類:

1. Message類:發送帶有附加參數的消息,
其處理方法由handlerMessage()方法處理。

2. Handler:MQ上添加消息和處理消息,通知MQ它要執行一個任務(sendMessage),
並在loop到自己的時候執行該任務(handleMessage),整個過程是異步的。

3. Looper:循環工作的線程。
	Message Queue

# 注意:

1. 主線程不能執行耗時操作
2. 子線程不能修改UI
3. post 用法		在主線程中使用,線程名稱爲mainActivity
	mainHandler.post(new MyRunnable);,可以直接啓動線程對象
4. 主線程與子線程是同時運行的

在這裏插入圖片描述

細信息請參考:
http://developer.android.com/reference/android/os/Handler.html#pubmethods

# 基本流程

  1. 創建Handler,並添加handleMessage方法。
    使用自定義的匿名子類的方法創建Handler對象,並重寫handleMessage方法實現消息的處理。
Handler handler = new Handler() {
	public void handleMessage (Message msg) {
		switch (msg.what) {
			case MSG_CURRENT: // TODO
			break;
		}
	}
};
  1. 創建Thread對象,在Thread對象的run方法中發送消息。
    使用自定義的匿名子類的方法創建Thread對象,並重寫run方法實現消息的參數設置和添加到消息隊列中等操作。
Thread backgroundThread = new Thread() {
	public void run() {	
		Message msg = handler.obtainMessage();
		msg.what = MSG_CURRENT;
		handler.sendMessage(msg);
	}
};
  1. 啓動Thread對象:
backgroundThread.start();
--------------------------------------------------------------
Message對象的常用方法和屬性參考:
http://developer.android.com/reference/android/os/Message.html
繼承Thread類實現子線程數據返回到主線程並改變主線程界面



package com.example.classcode_handler;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {
    private Button button1;
    private EditText edittext1;
    //線程中創建Handler匿名子類對象,子類定義簡化了,正常寫法也沒有問題----------------------------步驟1
    private Handler mainHandler=new Handler(){
        @Override
        //處理Message,必須重寫(子類重寫)------------------------------------------------------步驟3
        public void handleMessage(@NonNull Message msg) {
            switch(msg.what){
                case 100:
                    edittext1.setText(msg.obj+"");
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);



        //--------------------------------------------實現按鈕點擊效果(主線程)
        button1=findViewById(R.id.button1);
        edittext1=findViewById(R.id.edittext1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new MyThread().start();  //在主線程啓動子線程
            }
        });
    }//------------------------------------------------------------------onCreate
    //-------------------------------------------------耗時操作,計算斐波那契數列第n項的數值
    private long fib(int n){
        if (n == 1){
            return 1;
        }else if (n == 2){
            return 1;
        }else {
            return fib(n - 2) + fib(n -1);
        }
    }//耗時操作
    //----------------------------------------------------繼承Thread類實現多線程
    private class MyThread extends Thread{
        @Override
        public void run() {   //這是子線程,要想被使需要在主線程啓動。
            //消息包裝一下(Message),用handler的senfMessage()方法傳入主線程
            Message myMessage=new Message();//創建消息對象
            myMessage.what=100;//區分消息的來源/對象,參數是整數。必填
            myMessage.obj=fib(25);//攜帶發送的數據,數據寫入obj屬性
            mainHandler.sendMessage(myMessage);//進入消息隊列-----------------------------------步驟2
        }
    }
    //-----------------------------------------------------實現Ruannable接口實現多線程
}//-------------------------------------------------------------------MainActivity

在這裏插入圖片描述





Xueli-Homework-5

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/aim"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    xmlns:android="http://schemas.android.com/apk/res/android" >

</LinearLayout>









package com.example.homework05;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.LinearLayout;

public class MainActivity extends AppCompatActivity {
    private int number;
    private LinearLayout aim;
    //主線程創建handle匿名子類對象對象
    private Handler mainHandler = new Handler() {
        @Override
        //處理Message
        public void handleMessage(@NonNull Message msg) {

            switch (msg.what) {
                case 1:
                    aim.setBackgroundColor(getResources().getColor(android.R.color.holo_blue_light));
                    Log.e("blue______number==", msg.obj+"");
                    break;
                case 2:
                    aim.setBackgroundColor(getResources().getColor(android.R.color.holo_green_light));
                    Log.e("green______number==", msg.obj+"");
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        aim = findViewById(R.id.aim);
        number = 0;
            new MyThread().start();

    }


    //--------------------------------------------------------------------------------------------------
    private class MyThread extends Thread {
        @Override
        public void run() {
            if (number<100&&number % 2 == 1) {
                Message message = new Message();//message對象
                message.what = 1;//區分消息對象
                message.obj=number;
                mainHandler.sendMessageDelayed(message, 2000 * number);//發送消息,進入主線程消息隊列
                ++number;
                new MyThread().start();
            } else if (number<100&&number % 2 == 0) {
                Message message = new Message();//message對象
                message.what = 2;//區分消息對象
                message.obj=number;
                mainHandler.sendMessageDelayed(message, 2000 * number);
                ++number;
                new MyThread().start();
            }
        }
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章