日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Android同步異步任務與多線程及Handler消息處理機制基礎詳細講解_Android

作者:發飆的蝸牛YR ? 更新時間: 2022-12-23 編程語言

同步與異步

同步的執行任務:在執行程序時,如果沒有收到執行結果,就一直等,不繼續往下執行,直到收到執行結果,才接著往下執行。

異步的執行任務:在執行程序時,如果遇到需要等待的任務,就另外開辟一個子線程去執行它,自己繼續往下執行其他程序。子線程有結果時,會將結果發送給主線程

Android中的多線程

線程:通俗點講就是一個執行過程。多線程自然就是多個執行過程而已。

程序中可能有多個任務,如果只有一個線程,那么就只能一個接一個的執行了。類似同步執行任務。很明顯會比較慢,因為只有上一個任務執行完,才能進行下一個。

因此,我們需要多線程來分別執行這些任務。這也就是對應了上面說的異步執行任務。

Android中的多線程與主線程與子線程

類比到我們Android app程序。App一啟動,本身就是一個線程,這個線程被稱為主線程mainThread,負責顯示界面,跟用戶交互。

另外,界面通常被稱為UI(User Interface),因此,主線程也被稱為UI線程。

兩個原則:

主線程不能執行網絡請求/文件讀寫等耗時操作

子線程不能執行UI刷新

package com.example.androidnetwork;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity2 extends AppCompatActivity {
    private TextView tvContent;
    private String strFromNet;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tvContent = findViewById(R.id.tv_content);
    }
    public void start(View view) {
        //做一個耗時任務
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String stringFromNet = getStrFromNet();
                //子線程中執行UI操作會報錯
                tvContent.setText(stringFromNet);
            }
        });
        thread.start();
        Toast.makeText(this, "任務完成!", Toast.LENGTH_SHORT).show();
    }
    private String getStrFromNet() {
        //假裝從網絡上獲取了一個字符串
        String result = " ";
        StringBuilder stringBuilder = new StringBuilder();
        //模擬一個耗時操作
        for (int i = 0; i < 100; i++) {
            stringBuilder.append("字符串" + i);
        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        result = stringBuilder.toString();
        return result;
    }
}

報錯:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

說明只有主線程才能進行View里的UI操作進行交互。

Handler異步通信系統

Handler機制主要的幾個角色:Handler、Message、Looper、MessageQueue

異步消息Handler處理機制

首先在主線程當中創建一個Handler對象,并重寫handleMessage()方法。然后當子線程中需要進行UI操作時,就創建一個Message對象,并通過Handler將這條消息發送出去。之后這條消息就會被添加到MessageQueue隊列中等待被處理,而Looper則會一直嘗試從MessageQueue中取出待處理消息,最后分發回Handler的handleMessage()方法中。由于Handler是在主線程中創建的,所以此時handleMessage()方法中的代碼也會在主線程中運行,所以可以安心的進行UI操作。

這樣一條Message經過這樣一個流程的輾轉調用后,也就從子線程進入到了主線程,從而從不能更新UI到能更新UI。

Looper和MessageQueue會在UI線程創建時就存在,如果在子線程中就需要自己寫Looper。

會出現這樣一個警告,表示內存泄漏,leak是泄漏的意思。

private Handler mHandler=new Handler(Looper.myLooper()){

只需要寫上Looper.myLooper()就不報警告,表示取主線程自己的Looper。

what用于區分是誰發的消息

arg1,arg2,obj相當于一個變量,用于標識,區分

sendMessage發送有消息的

sendEmptyMessage發送空消息,啥都沒有,觸發一下而已

下面是使用Handle機制進行延時6s后將信息顯示到文本框上

package com.example.androidnetwork;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity2 extends AppCompatActivity {
    private TextView tvContent;
    private String strFromNet;
    //Handler處在主線程里,本身處理UI界面是沒問題的
    private Handler mHandler=new Handler(Looper.myLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            //此時的參數msg就是我們傳過去的message
            if(msg.what==0){
                String strData=(String)msg.obj;
                tvContent.setText(strData);
                Toast.makeText(MainActivity2.this, "主線程收到消息!", Toast.LENGTH_SHORT).show();
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        tvContent = findViewById(R.id.tv_content);
    }
    public void start(View view) {
        //做一個耗時任務
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String stringFromNet = getStrFromNet();
                //子線程中執行UI操作會報錯
                //tvContent.setText(stringFromNet);
                Message message=new Message();
                message.what=0;
                message.obj=stringFromNet;
                mHandler.sendMessage(message);
            }
        });
        thread.start();
        Toast.makeText(this, "任務完成!", Toast.LENGTH_SHORT).show();
    }
    private String getStrFromNet() {
        //假裝從網絡上獲取了一個字符串
        String result = " ";
        StringBuilder stringBuilder = new StringBuilder();
        //模擬一個耗時操作
        for (int i = 0; i < 100; i++) {
            stringBuilder.append("字符串" + i);
        }
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        result = stringBuilder.toString();
        return result;
    }
}

其實在執行new Thread過程中就是一個異步的

比如:

public void start(View view) {
        //做一個耗時任務
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                String stringFromNet = getStrFromNet();
                //子線程中執行UI操作會報錯
                tvContent.setText(stringFromNet);
            }
        });
        thread.start();
        Toast.makeText(this, "任務完成!", Toast.LENGTH_SHORT).show();
    }

主線程會先執行 Thread thread = new Thread(new Runnable() {和thread.start();Toast.makeText(this, “任務完成!”, Toast.LENGTH_SHORT).show();而 @Override public void run() {

String stringFromNet = getStrFromNet();tvContent.setText(stringFromNet);}

會當主線程執行到了thread.start();的時候子線程才會去執行

使用新線程計算質數

package com.example.androidnetwork;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivity2 extends AppCompatActivity {
    public final static String UPPER_NUM = "upper";
    private EditText etNum;
    private CalThread mCalThread;
    class CalThread extends Thread {
        private Handler mHandler;
        @Override
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                //定義處理消息的方法
                @Override
                public void handleMessage(@NonNull Message msg) {
                    if (msg.what == 0x123) {
                        int upper = msg.getData().getInt(UPPER_NUM);
                        List<Integer> nums = new ArrayList<>();
                        outer:
                        //計算從2開始,到upper的所有質數
                        for (int i = 2; i <= upper; i++) {
                            //用i除以從2開始,到i的平方根的所有數
                            int j = 2;
                            while (j <= Math.sqrt(i)) {
                                //如果可以整除,則表明這個數不是質數
                                if (i != 2 && i % j == 0) {
                                    continue outer;
                                }
                                j++;
                            }
                            nums.add(i);
                        }
                        //使用Toast顯示統計出來的所在質數
                        Toast.makeText(MainActivity2.this, nums.toString(), Toast.LENGTH_SHORT).show();
                    }
                }
            };
            Looper.loop();
        }
    }
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        etNum=findViewById(R.id.et_num);
        mCalThread=new CalThread();
        //啟動新線程
        mCalThread.start();
    }
    //為按鈕的點擊事件提供事件處理方法
    public void cal(View view){
        Log.d("====", "cal: +ddasdsadas");
        //創建消息
        Message msg=new Message();
        msg.what=0x123;
        Bundle bundle=new Bundle();
        bundle.putInt(UPPER_NUM,Integer.parseInt(etNum.getText().toString()));
        msg.setData(bundle);
        //向新線程中的Handler發送消息
        mCalThread.mHandler.sendMessage(msg);
    }
}

在子線程(或新線程處理Handler消息的方法)

1.調用Looper的prepare()方法為當前線程創建Looper對象,創建Looper對象時,它的構造器會創建與之配套的MessageQueue。

2.有了Looper之后,創建Handler子類的實例,重寫handlerMessage()方法,該方法負責處理來自其他線程的消息。

3.調用Looper的loop()方法啟動Looper。

原文鏈接:https://blog.csdn.net/ChenYiRan123456/article/details/121909428

欄目分類
最近更新