網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
一、同步與異步
同步的執(zhí)行任務(wù):在執(zhí)行程序時(shí),如果沒(méi)有收到執(zhí)行結(jié)果,就一直等,不繼續(xù)往下執(zhí)行,直到收到執(zhí)行結(jié)果,才接著往下執(zhí)行。
異步的執(zhí)行任務(wù):在執(zhí)行程序時(shí),如果遇到需要等待的任務(wù),就另外開(kāi)辟一個(gè)子線程去執(zhí)行它,自己繼續(xù)往下執(zhí)行其他程序。子線程有結(jié)果時(shí),會(huì)將結(jié)果發(fā)送給主線程
Android中的多線程
線程:通俗點(diǎn)講就是一個(gè)執(zhí)行過(guò)程。多線程自然就是多個(gè)執(zhí)行過(guò)程而已。
程序中可能有多個(gè)任務(wù),如果只有一個(gè)線程,那么就只能一個(gè)接一個(gè)的執(zhí)行了。類(lèi)似同步執(zhí)行任務(wù)。很明顯會(huì)比較慢,因?yàn)橹挥猩弦粋€(gè)任務(wù)執(zhí)行完,才能進(jìn)行下一個(gè)。
因此,我們需要多線程來(lái)分別執(zhí)行這些任務(wù)。這也就是對(duì)應(yīng)了上面說(shuō)的異步執(zhí)行任務(wù)。
Android中的多線程:主線程與子線程
類(lèi)比到我們Android app程序。App一啟動(dòng),本身就是一個(gè)線程,這個(gè)線程被稱(chēng)為主線程mainThread,負(fù)責(zé)顯示界面,跟用戶(hù)交互。
另外,界面通常被稱(chēng)為UI(User Interface),因此,主線程也被稱(chēng)為UI線程。
兩個(gè)原則:
主線程不能執(zhí)行網(wǎng)絡(luò)請(qǐng)求/文件讀寫(xiě)等耗時(shí)操作
子線程不能執(zhí)行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) {
//做一個(gè)耗時(shí)任務(wù)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String stringFromNet = getStrFromNet();
//子線程中執(zhí)行UI操作會(huì)報(bào)錯(cuò)
tvContent.setText(stringFromNet);
}
});
thread.start();
Toast.makeText(this, "任務(wù)完成!", Toast.LENGTH_SHORT).show();
}
private String getStrFromNet() {
//假裝從網(wǎng)絡(luò)上獲取了一個(gè)字符串
String result = " ";
StringBuilder stringBuilder = new StringBuilder();
//模擬一個(gè)耗時(shí)操作
for (int i = 0; i < 100; i++) {
stringBuilder.append("字符串" + i);
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = stringBuilder.toString();
return result;
}
}
報(bào)錯(cuò):android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
說(shuō)明只有主線程才能進(jìn)行View里的UI操作進(jìn)行交互。
Handler異步通信系統(tǒng)
Handler機(jī)制主要的幾個(gè)角色:Handler、Message、Looper、MessageQueue
異步消息Handler處理機(jī)制
首先在主線程當(dāng)中創(chuàng)建一個(gè)Handler對(duì)象,并重寫(xiě)handleMessage()方法。然后當(dāng)子線程中需要進(jìn)行UI操作時(shí),就創(chuàng)建一個(gè)Message對(duì)象,并通過(guò)Handler將這條消息發(fā)送出去。之后這條消息就會(huì)被添加到MessageQueue隊(duì)列中等待被處理,而Looper則會(huì)一直嘗試從MessageQueue中取出待處理消息,最后分發(fā)回Handler的handleMessage()方法中。由于Handler是在主線程中創(chuàng)建的,所以此時(shí)handleMessage()方法中的代碼也會(huì)在主線程中運(yùn)行,所以可以安心的進(jìn)行UI操作。
這樣一條Message經(jīng)過(guò)這樣一個(gè)流程的輾轉(zhuǎn)調(diào)用后,也就從子線程進(jìn)入到了主線程,從而從不能更新UI到能更新UI。
Looper和MessageQueue會(huì)在UI線程創(chuàng)建時(shí)就存在,如果在子線程中就需要自己寫(xiě)Looper。
會(huì)出現(xiàn)這樣一個(gè)警告,表示內(nèi)存泄漏,leak是泄漏的意思。
只需要寫(xiě)上Looper.myLooper()就不報(bào)警告,表示取主線程自己的Looper。
what用于區(qū)分是誰(shuí)發(fā)的消息
arg1,arg2,obj相當(dāng)于一個(gè)變量,用于標(biāo)識(shí),區(qū)分
sendMessage發(fā)送有消息的
sendEmptyMessage發(fā)送空消息,啥都沒(méi)有,觸發(fā)一下而已
下面是使用Handle機(jī)制進(jìn)行延時(shí)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界面是沒(méi)問(wèn)題的
private Handler mHandler=new Handler(Looper.myLooper()){
@Override
public void handleMessage(@NonNull Message msg) {
//此時(shí)的參數(shù)msg就是我們傳過(guò)去的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) {
//做一個(gè)耗時(shí)任務(wù)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String stringFromNet = getStrFromNet();
//子線程中執(zhí)行UI操作會(huì)報(bào)錯(cuò)
//tvContent.setText(stringFromNet);
Message message=new Message();
message.what=0;
message.obj=stringFromNet;
mHandler.sendMessage(message);
}
});
thread.start();
Toast.makeText(this, "任務(wù)完成!", Toast.LENGTH_SHORT).show();
}
private String getStrFromNet() {
//假裝從網(wǎng)絡(luò)上獲取了一個(gè)字符串
String result = " ";
StringBuilder stringBuilder = new StringBuilder();
//模擬一個(gè)耗時(shí)操作
for (int i = 0; i < 100; i++) {
stringBuilder.append("字符串" + i);
}
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
e.printStackTrace();
}
result = stringBuilder.toString();
return result;
}
}
其實(shí)在執(zhí)行new Thread過(guò)程中就是一個(gè)異步的
比如:
public void start(View view) {
//做一個(gè)耗時(shí)任務(wù)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String stringFromNet = getStrFromNet();
//子線程中執(zhí)行UI操作會(huì)報(bào)錯(cuò)
tvContent.setText(stringFromNet);
}
});
thread.start();
Toast.makeText(this, "任務(wù)完成!", Toast.LENGTH_SHORT).show();
}
主線程會(huì)先執(zhí)行 Thread thread = new Thread(new Runnable() {和thread.start();和 Toast.makeText(this, “任務(wù)完成!”, Toast.LENGTH_SHORT).show();而 @Override public void run() {
String stringFromNet = getStrFromNet();tvContent.setText(stringFromNet);
}會(huì)當(dāng)主線程執(zhí)行到了thread.start();的時(shí)候子線程才會(huì)去執(zhí)行
使用新線程計(jì)算質(zhì)數(shù)
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:
//計(jì)算從2開(kāi)始,到upper的所有質(zhì)數(shù)
for (int i = 2; i <= upper; i++) {
//用i除以從2開(kāi)始,到i的平方根的所有數(shù)
int j = 2;
while (j <= Math.sqrt(i)) {
//如果可以整除,則表明這個(gè)數(shù)不是質(zhì)數(shù)
if (i != 2 && i % j == 0) {
continue outer;
}
j++;
}
nums.add(i);
}
//使用Toast顯示統(tǒng)計(jì)出來(lái)的所在質(zhì)數(shù)
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();
//啟動(dòng)新線程
mCalThread.start();
}
//為按鈕的點(diǎn)擊事件提供事件處理方法
public void cal(View view){
Log.d("====", "cal: +ddasdsadas");
//創(chuàng)建消息
Message msg=new Message();
msg.what=0x123;
Bundle bundle=new Bundle();
bundle.putInt(UPPER_NUM,Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
//向新線程中的Handler發(fā)送消息
mCalThread.mHandler.sendMessage(msg);
}
}
在子線程(或新線程處理Handler消息的方法)
1.調(diào)用Looper的prepare()方法為當(dāng)前線程創(chuàng)建Looper對(duì)象,創(chuàng)建Looper對(duì)象時(shí),它的構(gòu)造器會(huì)創(chuàng)建與之配套的MessageQueue。
2.有了Looper之后,創(chuàng)建Handler子類(lèi)的實(shí)例,重寫(xiě)handlerMessage()方法,該方法負(fù)責(zé)處理來(lái)自其他線程的消息。
3.調(diào)用Looper的loop()方法啟動(dòng)Looper。
原文鏈接:https://blog.csdn.net/ChenYiRan123456/article/details/121909428
相關(guān)推薦
- 2022-07-01 使用python實(shí)現(xiàn)簡(jiǎn)單去水印功能_python
- 2022-05-21 python實(shí)現(xiàn)會(huì)員管理系統(tǒng)_python
- 2022-06-28 ADO.NET防SQL注入與使用參數(shù)增刪改查_(kāi)實(shí)用技巧
- 2022-08-28 詳解Python獲取線程返回值的三種方式_python
- 2022-06-09 ASP.NET?Core使用EF創(chuàng)建模型(索引、備用鍵、繼承、支持字段)_實(shí)用技巧
- 2022-06-13 Docker鏡像的commit操作示例及作用_docker
- 2023-03-26 C#?try?catch代碼塊不起效果的解決方法_C#教程
- 2022-05-29 C++掃盲篇之指針詳解_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支