目录
1. Android中的线程
1.1 什么是UI线程、工作线程?
UI线程
其实就是主线程。在启动一个Android程序时,会启动一个DVM虚拟机
,创建一个新的进程,在这个进程中创建一个线程,这个线程就是主线程
。因为主线程是与UI界面交互的线程,因此也称为UI线程
。
Android框架中这种在单条线程
中进行事件分发及UI交互的机制被称为Android单线程模型
。
单线程模型的两条规则:
- 不允许在UI线程中进行
耗时操作
(影响用户体验); - 不允许在UI线程外操作
界面控件
(界面控件是非线程安全的,只能在UI线程中操作UI控件);
工作线程
其实就是子线程。为了不阻塞UI线程,耗时操作应该在工作线程中进行。
Android系统同时也提供了一些工具,用于在工作线程中更新UI
。
2. Android中更新UI的几种方式
2.1 在工作线程中更新UI
- 使用Activity对象中的runOnUiThread()方法
- 使用View对象及子类对象的post()和postDelayed()方法
代码演示:
public class MainActivity extends AppCompatActivity {
Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.Button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Executors.defaultThreadFactory().newThread(new Runnable() {
@Override
public void run() {
//1.使用Activity对象的runOnUiThread()方法更新UI
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
button.setText("使用Activity对象的runOnUiThread()方法更新UI");
}
});
//2.1使用View对象的post()方法更新UI
button.post(new Runnable() {
@Override
public void run() {
button.setText("使用View对象的post()方法更新UI");
}
});
//2.2使用View对象的postDelayed()方法更新UI
button.postDelayed(new Runnable() {
@Override
public void run() {
button.setText("使用View对象的postDelayed()方法更新UI");
}
},1000);
}
}).start();
}
});
}
}
2.2 使用Handler
2.2.1 什么是Handler
Handler
是用于线程间传递信息
的工具类,用来处理异步消息,是Android消息处理机制的重要角色。
子线程可以通过Handler更新UI
,也可以通过Handler与主线程进行通讯
。
2.2.2 Handler的作用
- Handler可以发送并
立即执行
一个消息 - Handler可以发送并
延迟
或在指定时间
执行一个消息
应用场景:
- 在当前线程中调度任务
- 在其他线程中为当前线程安排任务
2.2.3 Handler的相关类及其方法
知道什么是Handler后,我们还需要了解一下Handler的相关类
,及它们之间的相互作用
,如下表:
类 | 作用 | 备注 |
---|---|---|
Handler(处理者) | 发送与处理Message | ①添加Message 到MessageQueue ;②处理Looper 分派的Message |
Message(消息) | 存储需要操作的通讯信息 | Message 是Handler 接受和处理的消息对象(即线程间通讯的数据单元) |
MessageQueue(消息队列) | 先进先出地管理Message | 一种数据结构,初始化Looper 时会自动创建一个MessageQueue |
Looper(循环器) | 管理MessageQueue ,不断地从MessageQueue 中取出Message ,然后交给Handler 处理 | 每个线程只能有一个Looper ,每个Looper 可以绑定多个线程的Handler (即多个线程中的Handler 可以往一个线程中Looper 管理的MessageQueue 中添加Message ),这样就为多线程通讯提供了可能 |
相关类的常用方法
,如下各表:
Handler(处理者)
常用方法 | 作用 |
---|---|
obtainMessage():Message | 返回一个Message对象 |
obtainMessage(int what, int arg1, int arg2, Object obj):Message | 返回一个Message对象,返回的Message上设置what,obj,arg1和arg2值 |
sendMessage(Message msg):boolean | 将Message传入MessageQueue的最后面 |
sendMessageAtFrontOfQueue(Message msg):boolean | 将Message传入MessageQueue的最前面,以便在消息循环的下一次迭代中处理 |
sendMessageDelayed(Message msg,long delayMillis):boolean | 在一定延迟后,将Message传入MessageQueue的最后 |
sendEmptyMessage(int what):boolean | sendMessage()方法的简约版本,把传入的Message换成what |
post(Runnable r):boolean | sendMessage()方法的简约版本,Runnable对象会被包装成一个Message对象,这个Message对象中的callback属性值也就是Runnable对象 |
hasMessages(int what):boolean | 判断消息队列中是否有指定的消息对象 |
removeCallbacks(Runnable r):void | 在消息队列中,删除待处理的 Runnabler |
removeMessages(int what):void | 在消息队列中,删除指定what的Message |
removeCallbacksAndMessages(Object token):void | 删除所有待处理的发送消息为 token 的 callbacks 和 message;如果 token 为 null,则所有的 callbacks 和 message 都将被删除 |
obtainMessage()
的一系列重载方法,内部其实是调用了Message
的obtain()
方法,并把调用obtainMessage()
方法的Handler
对象传入到obtain()
方法中。
Message(消息):
常用方法 | 作用 |
---|---|
obtain():Message | 创建一个不包含任何初始数据的Message对象 |
obtain(Message orig):Message | 拷贝一个Message对象 |
obtain(Handler h):Message | 为创建的Message对象绑定一个Handler |
obtain(Handler h, Runnable callback):Message | 为创建的Message对象创建一段可执行代码 |
obtain(Handler h, int what, int arg1, int arg2):Message | 与obtain()方法相同,但设置target,what, arg1和arg2成员的值 |
setTarget(Handler target):void | 设置Message对象对应的Handler |
sendToTarget():void | 将Message发送到对应的Handler中 |
2.2.4 Handler的工作流程
当主线程创建时,会自动创建Looper
对象(其他线程需手动创建)。而Looper
对象创建时会自动创建一个MessageQueue
。
在主线程创建一个Handler
,Handler
发送Message
后,MessageQueue
存放Handler
发送过来的Message
。
Looper
用来维护MessageQueue
,不断地从MessageQueue
中取出Message
,然后交给Handler
处理。Looper
是与线程绑定的,每个线程只能有一个Looper
,也只能有一个MessageQueue
(MessageQueue
中能有多个Message
)。
创建Handler
时(每个线程可以有多个Handler
),Handler
会跟线程中的Looper
绑定,由于Looper
也是与线程绑定的,所以Handler
同样与所在线程绑定。当使用Handler
在其他线程发消息时,最终也会回到原线程处理,从而达到所谓的异步效果
。
2.2.5 Handler的应用
创建消息:
//创建主线程Handler
Handler handler = new Handler();
//创建消息对象
Message message = Message.obtain();
//拷贝消息对象
Message message1 = Message.obtain(message);
//创建消息对象,并为为消息对象设置可执行代码
Message message2 = Message.obtain(handler, new Runnable() {
@Override
public void run() {
//do something
}
});
//创建一个与handler对象绑定的消息对象
handler.obtainMessage();
发送消息:
//创建主线程Handler
Handler handler = new Handler();
//发送消息
Message message = Message.obtain();
handler.sendMessage(message);
//基于android.os.SystemClock#uptimeMillis
handler.sendMessageAtTime(message,1000);
handler.sendMessageDelayed(message,1000);
//发送消息到消息队列最前端
handler.sendMessageAtFrontOfQueue(message);
//sendMessage系列方法的简约版本
handler.sendEmptyMessage(1);
Runnable runnable = new Runnable() {
@Override
public void run() {
//do something
}
};
Object token = new Object();
//sendMessage系列方法的简约版本
handler.post(runnable);
//基于android.os.SystemClock#uptimeMillis
handler.postAtTime(runnable,token,1000);
//判断消息队列中是否有指定的消息对象
handler.hasMessages(1);
handler.hasMessages(1,token);
//移除what值为指定值的消息对象
handler.removeMessages(1);
//如果token为null,移除所有what值为指定值的消息对象
handler.removeMessages(1,token);
//移除callback值为指定值的消息对象
handler.removeCallbacks(runnable);
//如果token为null,移除callback值为指定值的消息对象
handler.removeCallbacks(runnable,token);
//移除所有Object值为指定值的消息对象,如果token为null,移除消息队列中所有消息对象
handler.removeCallbacksAndMessages(token);
接收与处理消息:
①handler.post(Runnable r)
的Runnable
对象的处理方式:
Runnable
对象在发送前会被打包成一个Message
对象,把Runnable
对象赋值给Message
对象的包访问权限属性callback
。
在Looper
处理消息时,调用Message
对象对应的Handler
的dispatchMessage(Message msg)
派发消息,对于callback
属性不为空的Message
对象,它的callback
的run()
方法将被直接调用执行,完成消息的处理。
源码:
//1.post方法
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}
//2.getPostMessage()方法
private static Message getPostMessage(Runnable r) {
Message m = Message.obtain();
m.callback = r;
return m;
}
//3.dispatchMessage(Message msg)方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//4.handleCallback(Message message)方法
private static void handleCallback(Message message) {
message.callback.run();
}
方法演示:
Handler handler = new Handler()
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(DemoActivity.this,"Run",Toast.LENGTH_SHORT).show();
}
});
②重写handleMessage(Mesage msg)
方法处理消息:
在Looper
处理消息时,调用Message
对象对应的Handler
的dispatchMessage(Message msg)
派发消息,对于callback
属性为空,mCallback
也为空的Message
对象,直接调用handleMessage(Mesage msg)
方法,由于handleMessage(Mesage msg)
方法是空的,所以需要重写这个方法进行消息处理。
源码:
//1.dispatchMessage(Message msg)方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//2.handleMessage(Message msg)方法
public void handleMessage(Message msg) {
}
方法演示:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if (msg.what == 1){
Toast.makeText(DemoActivity.this,"what is 1",Toast.LENGTH_SHORT).show();
}
}
};
③实现Handler.Callback
接口处理消息:
在Looper
处理消息时,调用Message
对象对应的Handler
的dispatchMessage(Message msg)
派发消息,对于callback
属性为空,mCallback
不为空的Message
对象,调用mCallback
的handleMessage(Mesage msg)
方法,如果返回值为ture则dispatchMessage(Message msg)
运行结束,返回值为false则调用需要重写的handleMessage(Mesage msg)
方法。
源码:
//1.dispatchMessage(Message msg)方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
//2.Callback的handleMessage(Message msg)方法
public interface Callback {
/**
* @param msg A {@link android.os.Message Message} object
* @return True if no further handling is desired
*/
public boolean handleMessage(Message msg);
}
//3.handleMessage(Message msg)方法
public void handleMessage(Message msg) {
}
方法演示:
Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 1){
Toast.makeText(DemoActivity.this,"First handle",Toast.LENGTH_SHORT).show();
}
return false;//false 需要消息进一步处理
}
}){
@Override
public void handleMessage(Message msg) {
if (msg.what == 1){
Toast.makeText(DemoActivity.this,"what is 1",Toast.LENGTH_SHORT).show();
}
}
};
防止内存泄漏:
如下代码所示:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//do something
}
};
Handler
应为静态内部类
才不会导致内存泄漏的发生,为什么呢?
因为一个非静态内部类
默认持有一个外部类的引用,当我们使用Handler
发送一个消息时,Messager
会默认持有一个Handler
的引用(即持有外部类的引用),如果这个Message
对象是延迟执行
的,当它被发送到主线程Looper
的MessageQueue
中,这时候从当前界面退出,主线程的Looper
并未因此关闭,由于Looper
中还有当前界面的引用,这将导致当前界面不能正常销毁,从而导致内存泄漏
。
2.2.6 HandlerThread
- 创建工作线程
Handler
创建工作线程Handler
前必须先为工作线程创建Looper
对象,让我们先了解一下Looper
类
常用方法 | 作用 |
---|---|
prepare():void | 为当前线程创建Looper 对象(内部使用ThreadLocal实现 ) |
loop():void | 线程阻塞到MessageQueue 上,等待Message 执行,后面不会执行其他代码 |
quit():void | 让Looper 立即终止执行 |
quitSafely():void | 等待MessageQueue 中的Message 执行完毕后,让Looper 终止执行 |
myLooper():Looper | 获取当前线程的Looper 对象 |
getThread():Thread | 获取当前Looper 对象绑定的线程 |
getMainLooper():Looper | 获取主线程的Looper 对象 |
isCurrentThread():Thread | 判断当前线程是否是Looper 绑定的线程 |
代码演示:
class Task extends Thread{
Handler handler;
@Override
public void run() {
Looper.prepare();//为当前线程创建Looper对象
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
//process message
}
};
Looper.loop();//线程阻塞到MessageQueue上,等待Message执行,后面不会执行其他代码
}
- HandlerThread的使用
是Thread
的子类,用于方便地实现一个带有Looper的线程。
常用方法 | 作用 |
---|---|
构造函数:HandlerThread(String name) | 指定线程名字的HandlerThread |
构造函数:HandlerThread(String name, int priority) | 指定线程名字,线程优先级的的HandlerThread |
getLooper():Looper | 获取HandlerThread 中的Looper |
getThreadId():int | 获取HandlerThread 中的线程Id |
quit():void | 让HandlerThread 中的Looper 立即终止执行 |
quitSafely():void | 等待MessageQueue 中的Message 执行完毕后,让HandlerThread 中的Looper 终止执行 |
onLooperPrepared():void | 在Looper 的prepare() 和loop() 之间调用,可执行自己想要的操作 |
代码演示:
public class MainActivity2 extends AppCompatActivity {
HandlerThread handlerThread = new HandlerThread("HanlderThread"){
@Override
protected void onLooperPrepared() {
System.out.println("Looper prepared");
}
};
Handler handler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
Button button = (Button) findViewById(R.id.button2);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(handler==null){
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
handler.post(new Runnable() {
@Override
public void run() {
System.out.println("Send from Main"+
" and run on "+Thread.currentThread().getName());
}
});
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
handlerThread.quit();
}
}
2.3 使用AsyncTask
AsyncTask
(即异步任务)是Android为我们提供的一个轻量级的异步任务类
。
AsyncTask
相当于一个多线程编程框架
,介于Thread和Handler之间。通过AsyncTask
我们可以轻松解决多线程之间的通讯问题。
AsyncTask抽象类:
在使用AsyncTask之前,我们需要先定义一个类继承AsyncTask<Params, Progress, Result>
,并指定3个泛型参数:
Params
:传递给异步任务执行时的参数类型Progress
:更新进度的参数类型Result
:返/回结果的参数类型
常用方法 | 作用 |
---|---|
doInBackground(Params… params):Result | 执行在工作线程中需要执行的任务(唯一一个运行在工作线程中的方法) |
cancel(boolean mayInterruptIfRunning):boolean | 指定异步任务执行时是否可以中断 |
execute(Runnable runnable):AsyncTask<Params, Progress, Result> | 方便在工作线程中执行一段代码(是在主线程中调用的) |
get():Result | 返回异步任务执行的结果,属于阻塞方法(在doInBackground执行结束后才返回结果) |
getStatus():AsyncTask.Status | 获取异步任务的状态 |
isCancelled():boolean | 判断异步任务是否被取消 |
计数器小Demo:
public class AsyncTask extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task);
final EditText input = (EditText) findViewById(R.id.TextView);
final Button countBtn = (Button) findViewById(R.id.count);
countBtn.setOnClickListener(new View.OnClickListener() {
TimeCountTask timeCountTask;
@Override
public void onClick(View v) {
String inputText = input.getText().toString();
if(countBtn.getTag()==null){
timeCountTask = new TimeCountTask();
if(!TextUtils.isEmpty(inputText)){
//开始计时
timeCountTask.execute(Long.valueOf(inputText));
}else{
//开始计时
timeCountTask.execute();
}
}else{
timeCountTask.cancel(true);
}
}
});
}
class TimeCountTask extends android.os.AsyncTask<Object, String, String> {
TextView showText;
Button countBtn;
EditText inputEdt;
@Override
protected void onPreExecute() {
showText = (TextView) findViewById(R.id.show);
countBtn = (Button) findViewById(R.id.count);
inputEdt = (EditText) findViewById(R.id.TextView);
inputEdt.setEnabled(false);
countBtn.setTag(0);
countBtn.setText("取消");
}
@Override
protected String doInBackground(Object... params) {
if(params.length==1&¶ms[0] instanceof Long){
long inputTime = (long) params[0];
for(;inputTime>0;inputTime--){
publishProgress("剩余:"+inputTime+"秒");
System.out.println(">>>:"+inputTime);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
return "";
}
}
}else{
AsyncTask.this.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(AsyncTask.this, "参数错误", Toast.LENGTH_LONG).show();
}
});
cancel(true);
}
return null;
}
@Override
protected void onProgressUpdate(String... values) {
showText.setText(values[0]);
}
@Override
protected void onPostExecute(String s) {
showText.setText("计时已完成");
inputEdt.setEnabled(true);
countBtn.setTag(null);
countBtn.setText("计时");
}
@Override
protected void onCancelled(String s) {
showText.setText("计时已取消");
inputEdt.setEnabled(true);
countBtn.setTag(null);
countBtn.setText("计时");
}
}
}
运行结果:
3. 在Service中使用线程
3.1 为什么要在Service中使用线程
在Service
中运行线程可以保证线程所在进程至少具有服务进程级别
的优先级。
避免随着它们所在的进程
变成较低优先级的进程,线程执行任务的完整得不到保证。
Android中进程的分类及其优先级(优先级从上到下逐级递减):
- 前台进程
- 可见进程
- 服务进程
- 后台进程
- 空进程
详细介绍推荐观看:Android的进程优先级
3.2 使用IntentService
IntentService
是一个抽象类。在使用是需要先实现onHandleIntent(Intent intent)
方法,然后在需要使用它的组件中调用startService(Intent intent)
方法发送指令调用它。
当然IntentService也有它的优缺点:
- 优点:
使用方便
,不需要自己管理线程的创建和Service的销毁; - 缺点:只有单个线程,所有任务需要
排队执行
,不适合多数的多任务情况。
计数器小Demo:
public class IntentServiceDemo extends AppCompatActivity {
MyBroadcastReceiver myBroadcastReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_intent_service_demo);
final EditText input1 = (EditText) findViewById(R.id.input1);
final EditText input2 = (EditText) findViewById(R.id.input2);
final Button startBtn1 = (Button) findViewById(R.id.start1);
final Button startBtn2 = (Button) findViewById(R.id.start2);
startBtn1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String inputStr = input1.getText().toString();
if(TextUtils.isEmpty(inputStr)){
Toast.makeText(IntentServiceDemo.this,"请输入",Toast.LENGTH_LONG).show();
}else{
input1.setEnabled(false);
startBtn1.setEnabled(false);
Intent intent = new Intent(IntentServiceDemo.this, TimeCountService.class);
intent.putExtra(TimeCountService.START_TIME, Long.valueOf(inputStr));
intent.putExtra(TimeCountService.TARGET, R.id.show1);
startService(intent);
}
}
});
startBtn2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String inputStr = input2.getText().toString();
if(TextUtils.isEmpty(inputStr)){
Toast.makeText(IntentServiceDemo.this,"请输入",Toast.LENGTH_LONG).show();
}else{
input2.setEnabled(false);
startBtn2.setEnabled(false);
Intent intent = new Intent(IntentServiceDemo.this, TimeCountService.class);
intent.putExtra(TimeCountService.START_TIME, Long.valueOf(inputStr));
intent.putExtra(TimeCountService.TARGET, R.id.show2);
startService(intent);
}
}
});
myBroadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TimeCountService.ACTION_TIME_COUNT);
registerReceiver(myBroadcastReceiver, intentFilter);
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(myBroadcastReceiver);
}
class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if(TimeCountService.ACTION_TIME_COUNT.equals(intent.getAction())){
String result = intent.getStringExtra(TimeCountService.RESULT);
int target = intent.getIntExtra(TimeCountService.TARGET, -1);
if(result!=null&&target!=-1){
TextView show = (TextView) findViewById(target);
show.setText(result);
}
}
}
}
}
Github项目地址:AndroidThreadDemo