文章目錄
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
# 基本流程
- 創建Handler,並添加handleMessage方法。
使用自定義的匿名子類的方法創建Handler對象,並重寫handleMessage方法實現消息的處理。
Handler handler = new Handler() {
public void handleMessage (Message msg) {
switch (msg.what) {
case MSG_CURRENT: // TODO
break;
}
}
};
- 創建Thread對象,在Thread對象的run方法中發送消息。
使用自定義的匿名子類的方法創建Thread對象,並重寫run方法實現消息的參數設置和添加到消息隊列中等操作。
Thread backgroundThread = new Thread() {
public void run() {
Message msg = handler.obtainMessage();
msg.what = MSG_CURRENT;
handler.sendMessage(msg);
}
};
- 啓動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();
}
}
}
}