網站首頁 編程語言 正文
同步與異步
同步的執行任務:在執行程序時,如果沒有收到執行結果,就一直等,不繼續往下執行,直到收到執行結果,才接著往下執行。
異步的執行任務:在執行程序時,如果遇到需要等待的任務,就另外開辟一個子線程去執行它,自己繼續往下執行其他程序。子線程有結果時,會將結果發送給主線程
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
相關推薦
- 2022-12-09 CALL命令無法在PowerShell中使用的原因及解決方法_DOS/BAT
- 2022-10-18 linux下shell腳本備份文件的方法實現_linux shell
- 2022-05-08 C++類中隱藏的幾個默認函數你知道嗎_C 語言
- 2022-05-13 Python Anaconda安裝sasl,thrift,thrift-sasl 并連接PyHive
- 2022-08-22 Android中關于Binder常見面試問題小結_Android
- 2022-10-24 C語言進度條的實現原理詳解_C 語言
- 2022-09-14 jquery實現計算器小功能_jquery
- 2022-10-05 ASP.NET?Core在Task中使用IServiceProvider的問題解析_實用技巧
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支