线程通信模型(1)
UI线程模型
进程通常表现为一个正在运行的应用程序实体,在默认情况下,
每个正在运行的应用程序有且仅有1个进程。
线程(Thread)是计算机进行调度的基本单位,表现为进程中某个单一顺序的控制流,它可以被认为是一个轻量级的进程
(LightWeightProcess);
同一个进程中可以有若干个线程 。
各进程的地址空间与数据资源是相互独立的,而同一个进程里的各个线程则可以共享这些资源。
各进程之间如果需要实现通信,则需要制定特殊的通信机制,而
同一个进程里的各个线程之间可以使用全局变量进行通信。
当应用程序启动时,它的进程则被创建,则执行应用程序的本质则是一个线程,这个线程被称之为主线程(Main Thread);
主线程默认运行在应用程序的默认进程中。
主线程负责所有的用户界面,及与用户操作的响应,即负责UI的处理工作,所以主线程也被称之为UI线程。
基本UI的定位(负责显示用户的界面、响应用户的操作),因此,在UI线程中执行耗时操作时,比如网络访问、读写文件、耗时的计算
与循环等等,就会阻塞UI线程,给使用者带来的感觉就是太卡。
如果Activity的执行时间超过了5喵,或者BroadcastReceiver的执行时间超过了10秒,
就会导致ANR(Application Not Respongding:应用程序无响应);
当执行耗时操作时,应该开启子线程(也叫做工作线程:Worker Thread)来完成对应的任务,因为:
ANR只是UI进程被阻塞时出现的问题,ANDROID系统对子线程的运行时长并没有
提出任何要求或约束;
从本质上个来看,UI进程只应该负责UI相关的任务,通常耗时操作与UI并没有直接关系。
在ANDROID系统中,非UI线程不能访问UI工具包,表现为子线程不允许更新UI,因为系统没有办法约束应用程序的子线程的数量与执行周期,
如果大量子线程同时更新同一个UI控件,那么就有可能会失控。
Android中的UI线程模型主要表现为:
主线程(即UI线程)负责所有关于UI的操作,比如控件的初始化、控件的更新等等;
主线程必须快速完成各种任务,保证用户体验,如果被阻塞的话,则可能就会导致ANR;
耗时操作应该使用子线程完成,但是子线程不允许更新UI。
以下方法适用于在子线程中调用主线程来更新UI:
1、Activity类:
public void runOnUiThread(Runnable action)
2、View类:
public boolean post(Runnable action)
3、View类:
public boolean postDelayed(Runnable action,long delayMillis)
线程通信模型:
Android系统中使用了“消息机制”完成各线程间的通信,即A线程发出消息,B线程
接收消息,则可以实现A、B线程间的通信。
在Activity中的runOnUiThread()方法,及View中的post()方法,也都是基于消息机制实现的线程间的通信。
Message:Message是消息机制中的信息载体,开发人员可以在该对象中封装数据,封装数据的方式有:
setData()方法: 调用该方法可以在Message中封装Bundle类型的数据,而消息的接收方则可以调用getData()
方法获取该Bundle对象;
arg1属性:int类型,用于在Message中封装int型变量;
arg2属性:int类型,作用等同于arg1属性;
what属性:int类型,用于在Message中封装int型变量,表示消息的标识符;
obj属性:Object类型,用于在Message中封装其它任意类型的数据;
Handle对象是消息的发送者与处理者,其常用的方法有:
public final boolean sendMessage(Message msg)
public void handleMessage(Message msg)
实例:
MainActivity:
关于Message:
尽管Message类存在公有的无参数构造方法,开发人员可以使用new Message()语法创建Message对象,但是这种做法不建议使用。
在许多时候,发送消息的频率是非常高的,如果频繁的创建的新的对象,则会造成较大的系统开销。
为了避免Message对象的频繁创建和销毁造成较大的系统开销,系统使用了Message Pool(消息池)来
管理和维护Message对象;
Message Pool是专门用于管理和维护Message对象的池,它的大小是10,即可以存放10个Message对象,当
需要Message对象时,从池中获取即可;
推荐获取Message对象的方式:
调用Message类的obtain()静态方法;
调用Handler的obtainMessage()方法;
Message类中的obtain()系列方法如下:
public static Message obtain()
public static Message obtain(Message oirg)
public static Message obtain(Handler h)
public static Message obtain(Handler h,Runnable callback)
public static Message obtain(Handler h,int what)
public static Message obtain(Handler h,int what,Object obj)
public static Message obtain(Handler h,int what,int arg1,int arg2)
public static Message obtain(Handler h,int what,int arg1,int arg2,Object obj)
Handler类中的obtainMessage()系列方法从本质上依然是调用了Message类中的obtain()系列方法:
public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what,Object obj)
public final Message obtainMessage(int what,int arg1,int arg2)
public final Message obtainMessage(int what,int arg1,int arg2,Object obj)
Message使用原则
尽量使用Message的obtain()或Handler的obtainMessage()系列的方法创建Message对象,
而不使用Message的构造方法;
严格区分Message的what、arg1,arg2属性,尽管都是int类型,但应使用what标识消息类型,并使用常量
表现其意义。
可以视具体情况,将要封装的数据声明为全局变量,在子线程中更新
该变量,而主线程直接获取,就不必在Message中进行封装。
尽量不要在Message中封装大量的数据,且能使用Object属性时则不要使用setData()/getData()方法;
实例:
布局与上面的相同,
关于Handler
Handler发送消息的方法有:
public final boolean sendMessage(Message msg)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg,long uptimeMillis)
public final boolean sendMessageDelayed(Message msg,long delayMillis)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what,long delayMillis)
public final boolean sendEmptyMessageAtTime(int what,long uptimeMillis)
Handler中定义了post()系列方法,可使用Runnable对象作为参数,其本质依然是基于消息机制实现进程间通信:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r),0);
}
Handler中的post()系列方法:
public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r,Object token,long uptimeMillis)
public final boolean postDelayed(Runnable r,long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)
在Activity中的runOnUiThread()方法,及View中的post()方法,从本质上都是调用了Handler中的post()方法实现
线程间通信。
开发人员可以自定义Handler的子类,并重写handleMessage()方法,
以处理消息,或者,自定义类实现Handler.Callback接口,并将该
接口的实现类对象作为public Handler(Callback callback)的参数以创建Handler对象。
private class InnerHandlerCallback implements Handler.Callback{
public boolean handlerMessage(Message msg){
//....
return false;
}
}
Handler handler = new Handler(new InnerHandlerCallback());
实例:
大致代码与上边相同,仅这些地方做了修改
进程通常表现为一个正在运行的应用程序实体,在默认情况下,
每个正在运行的应用程序有且仅有1个进程。
线程(Thread)是计算机进行调度的基本单位,表现为进程中某个单一顺序的控制流,它可以被认为是一个轻量级的进程
(LightWeightProcess);
同一个进程中可以有若干个线程 。
各进程的地址空间与数据资源是相互独立的,而同一个进程里的各个线程则可以共享这些资源。
各进程之间如果需要实现通信,则需要制定特殊的通信机制,而
同一个进程里的各个线程之间可以使用全局变量进行通信。
当应用程序启动时,它的进程则被创建,则执行应用程序的本质则是一个线程,这个线程被称之为主线程(Main Thread);
主线程默认运行在应用程序的默认进程中。
主线程负责所有的用户界面,及与用户操作的响应,即负责UI的处理工作,所以主线程也被称之为UI线程。
基本UI的定位(负责显示用户的界面、响应用户的操作),因此,在UI线程中执行耗时操作时,比如网络访问、读写文件、耗时的计算
与循环等等,就会阻塞UI线程,给使用者带来的感觉就是太卡。
如果Activity的执行时间超过了5喵,或者BroadcastReceiver的执行时间超过了10秒,
就会导致ANR(Application Not Respongding:应用程序无响应);
当执行耗时操作时,应该开启子线程(也叫做工作线程:Worker Thread)来完成对应的任务,因为:
ANR只是UI进程被阻塞时出现的问题,ANDROID系统对子线程的运行时长并没有
提出任何要求或约束;
从本质上个来看,UI进程只应该负责UI相关的任务,通常耗时操作与UI并没有直接关系。
在ANDROID系统中,非UI线程不能访问UI工具包,表现为子线程不允许更新UI,因为系统没有办法约束应用程序的子线程的数量与执行周期,
如果大量子线程同时更新同一个UI控件,那么就有可能会失控。
Android中的UI线程模型主要表现为:
主线程(即UI线程)负责所有关于UI的操作,比如控件的初始化、控件的更新等等;
主线程必须快速完成各种任务,保证用户体验,如果被阻塞的话,则可能就会导致ANR;
耗时操作应该使用子线程完成,但是子线程不允许更新UI。
以下方法适用于在子线程中调用主线程来更新UI:
1、Activity类:
public void runOnUiThread(Runnable action)
2、View类:
public boolean post(Runnable action)
3、View类:
public boolean postDelayed(Runnable action,long delayMillis)
线程通信模型:
Android系统中使用了“消息机制”完成各线程间的通信,即A线程发出消息,B线程
接收消息,则可以实现A、B线程间的通信。
在Activity中的runOnUiThread()方法,及View中的post()方法,也都是基于消息机制实现的线程间的通信。
Message:Message是消息机制中的信息载体,开发人员可以在该对象中封装数据,封装数据的方式有:
setData()方法: 调用该方法可以在Message中封装Bundle类型的数据,而消息的接收方则可以调用getData()
方法获取该Bundle对象;
arg1属性:int类型,用于在Message中封装int型变量;
arg2属性:int类型,作用等同于arg1属性;
what属性:int类型,用于在Message中封装int型变量,表示消息的标识符;
obj属性:Object类型,用于在Message中封装其它任意类型的数据;
Handle对象是消息的发送者与处理者,其常用的方法有:
public final boolean sendMessage(Message msg)
public void handleMessage(Message msg)
实例:
布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="${relativePackage}.${activityClass}" >
<Button
android:id="@+id/update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="104dp"
android:onClick="loadImage"
android:layout_marginTop="58dp"
android:text="更新图片" />
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/update"
android:layout_below="@+id/update"
android:layout_marginTop="28dp"
android:text="Button" />
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/btn"
android:layout_below="@+id/btn"
android:layout_marginLeft="35dp"
android:layout_marginTop="84dp"
android:src="@android:drawable/star_big_on" />
</RelativeLayout>
MainActivity:
package com.example.thread;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageView;
public class MainActivity extends Activity {
private Handler handler;
private ImageView image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image = (ImageView) findViewById(R.id.image);
handler = new InnerHandler();
}
private class InnerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
Bitmap bm = (Bitmap) msg.obj;
image.setImageBitmap(bm);
}
}
public void loadImage(View view){
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
Bitmap bm = loadImageFromNetWork();
Message msg = new Message();
msg.obj = bm;
handler.sendMessage(msg);
};
}.start();
}
private Bitmap loadImageFromNetWork(){
Bitmap bm = null;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bm = BitmapFactory.decodeResource(getResources(), R.drawable.image);
return bm;
}
}
关于Message:
尽管Message类存在公有的无参数构造方法,开发人员可以使用new Message()语法创建Message对象,但是这种做法不建议使用。
在许多时候,发送消息的频率是非常高的,如果频繁的创建的新的对象,则会造成较大的系统开销。
为了避免Message对象的频繁创建和销毁造成较大的系统开销,系统使用了Message Pool(消息池)来
管理和维护Message对象;
Message Pool是专门用于管理和维护Message对象的池,它的大小是10,即可以存放10个Message对象,当
需要Message对象时,从池中获取即可;
推荐获取Message对象的方式:
调用Message类的obtain()静态方法;
调用Handler的obtainMessage()方法;
Message类中的obtain()系列方法如下:
public static Message obtain()
public static Message obtain(Message oirg)
public static Message obtain(Handler h)
public static Message obtain(Handler h,Runnable callback)
public static Message obtain(Handler h,int what)
public static Message obtain(Handler h,int what,Object obj)
public static Message obtain(Handler h,int what,int arg1,int arg2)
public static Message obtain(Handler h,int what,int arg1,int arg2,Object obj)
Handler类中的obtainMessage()系列方法从本质上依然是调用了Message类中的obtain()系列方法:
public final Message obtainMessage()
public final Message obtainMessage(int what)
public final Message obtainMessage(int what,Object obj)
public final Message obtainMessage(int what,int arg1,int arg2)
public final Message obtainMessage(int what,int arg1,int arg2,Object obj)
Message使用原则
尽量使用Message的obtain()或Handler的obtainMessage()系列的方法创建Message对象,
而不使用Message的构造方法;
严格区分Message的what、arg1,arg2属性,尽管都是int类型,但应使用what标识消息类型,并使用常量
表现其意义。
可以视具体情况,将要封装的数据声明为全局变量,在子线程中更新
该变量,而主线程直接获取,就不必在Message中进行封装。
尽量不要在Message中封装大量的数据,且能使用Object属性时则不要使用setData()/getData()方法;
实例:
布局与上面的相同,
public class MainActivity extends Activity {
private Handler handler;
private int MESSAGE_LOAD_IMAGE_COMPLETED = 100;
private ImageView image;
private Bitmap bm;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image = (ImageView) findViewById(R.id.image);
handler = new InnerHandler();
}
private class InnerHandler extends Handler{
@Override
public void handleMessage(Message msg) {
if(msg.what == MESSAGE_LOAD_IMAGE_COMPLETED){
image.setImageBitmap(bm);
}
}
}
public void loadImage(View view){
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
bm = loadImageFromNetWork();
Message msg = Message.obtain();
msg.what = MESSAGE_LOAD_IMAGE_COMPLETED;
handler.sendMessage(msg);
};
}.start();
}
private Bitmap loadImageFromNetWork(){
Bitmap bm = null;
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
bm = BitmapFactory.decodeResource(getResources(), R.drawable.image);
return bm;
}
}
关于Handler
Handler发送消息的方法有:
public final boolean sendMessage(Message msg)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg,long uptimeMillis)
public final boolean sendMessageDelayed(Message msg,long delayMillis)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what,long delayMillis)
public final boolean sendEmptyMessageAtTime(int what,long uptimeMillis)
Handler中定义了post()系列方法,可使用Runnable对象作为参数,其本质依然是基于消息机制实现进程间通信:
public final boolean post(Runnable r){
return sendMessageDelayed(getPostMessage(r),0);
}
Handler中的post()系列方法:
public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r,Object token,long uptimeMillis)
public final boolean postDelayed(Runnable r,long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)
在Activity中的runOnUiThread()方法,及View中的post()方法,从本质上都是调用了Handler中的post()方法实现
线程间通信。
开发人员可以自定义Handler的子类,并重写handleMessage()方法,
以处理消息,或者,自定义类实现Handler.Callback接口,并将该
接口的实现类对象作为public Handler(Callback callback)的参数以创建Handler对象。
private class InnerHandlerCallback implements Handler.Callback{
public boolean handlerMessage(Message msg){
//....
return false;
}
}
Handler handler = new Handler(new InnerHandlerCallback());
实例:
大致代码与上边相同,仅这些地方做了修改
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
image = (ImageView) findViewById(R.id.image);
// handler = new InnerHandler();
handler = new Handler(new InnerHandlerCallback());
}
private class InnerHandlerCallback implements Handler.Callback{
@Override
public boolean handleMessage(Message msg) {
if(msg.what == MESSAGE_LOAD_IMAGE_COMPLETED){
image.setImageBitmap(bm);
}
return false;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理