文章目录
7.1实现多线程
在程序开发时,对于- -些比较耗时的操作,通常会为其开辟一个 单独的线程来执行,以尽可能减少用户的等待时间。在Android中,默认情况下,所有的操作都在主线程中进行,主线程负责管理与UI相关的事件,而在用户自己创建的子线程中,不能对UI组件进行操作。因此,Android 提供了消息处理传递机制来解决这一问题。
在现实生活中,很多事情都是同时进行的,例如,我们可以一边看书,一 边喝咖啡;而计算机则可以一边播放音乐,一边打印文档。对于这种可以同时进行的任务,可以用线程来表示,每个线程完成一个任务,并与其他线程同时执行,这种机制被称为多线程。下面就来介绍如何创建线程、开启线程、让线程休眠和中断线程。
7.1.1 创建线程
1.通过Thread类的构造方法创建线程
Thread(Runnable runnable)
该构造方法的参数runnable可以通过创建一个Runnable类的对象并重写其run0方法来实现,例如,要创建一个名称为thread的线程,可以使用下面的代码:
Thread thread=new Thread(new Runnable(){
//重写run()方法
@Override
public void run() {
//要执行的操作
}
});
说明:在run0方法中,可以编写要执行的操作的代码,当线程被开启时,run()方法将被执行。
2.通过实现Runnable接口创建线程
public class ClassName extends 0bject implements Runnable
当一个类实现Runnable接口后,还需要实现其run0方法,在run0方法中,可以编写要执行的操作的代码。
例如,要创建一个实现了Runnable 接口的Activity,可以使用下面的代码:
public class MainActivity extends Activity implements Runnable {
@Override
public void onCreate ( Bundle savedInstanceState) {
super.onCreate ( savedInstanceState);
setContentView(R. layout .main);
@override
public void run() {
//要执行的操作
}
7.1.2 开启线程
创建线程对象后,还需要开启线程,线程才能执行。Thread 类提供了start()方 法用于开启线程,其语法格式如下:
start()
例如,存在一个名称为thread的线程,如果想开启该线程,可以使用下面的代码:
thread.start();//开启线程
7.1.3 线程的休眠
线程的休眠就是让线程暂停一段时间后再次执行。 同Java一样,在Android中,也可以使用Thread类的sleep()方法让线程休眠指定的时间。sleep()方 法的语法格式如下:
sleep(long time) 其中参数time用于指定休眠的时间,单位为毫秒。
例如,想要线程休眠1秒钟,可以使用下面的代码:
Thread. sleep(1000);
7.1.4 中断线程
当需要中断指定的线程时,可以使用Thread类提供的iterupt()方法来实现。使用interrupt()方法可以向指定的线程发送一个中断请求,并将该线程标记为中断状态。interrupt()方 法的语法格式如下:
interrupt()
例如,存在一个名称为thread的线程,如果想中断该线程,可以使用下面的代码:
.//省略部分代码
thread. interrupt();
//省略部分代码
public void run() {
while( !Thread. currentThread().isInterrupted()){
...//省略部分代码
}
|)
另外,由于当线程执行wait()、join()或 sleep0方法时,线程的中断状态将被清除并抛出InteruptedException,所以,如果想在线程中执行了waitO、join()或 sleep0方法时中断线程,就需要使用一个boolean型的标记变量来记录线程的中断状态,并通过该标记变量来控制循环的执行与停止。例如,通过名称为islnterrupt 的boolean型变量来标记线程的中断,关键代码如下:
private boolean isInterrupt=false;
//定义标记变量
...//省略部分代码
...//在需要中断线程时,将isInterrupt的值设置为true
public void run() {
while(!isInterrupt){
...//省略部分代码
}
}
7.1.5 案例 1:通过实现Runnable接口来创建线程
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/startBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="启动线程"
android:layout_gravity="center"
/>
<Button
android:id="@+id/stopBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="中断线程"
android:layout_gravity="center"
/>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements Runnable{
private Thread thread;
private int i;
private boolean interruptFlag;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button1 = (Button) findViewById(R.id.startBtn);
Button button2 = (Button) findViewById(R.id.stopBtn);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
i=0;
thread = new Thread(MainActivity.this);
thread.start();
interruptFlag=true;
}
});
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (thread!=null){
thread.interrupt();
interruptFlag=false;
thread=null;
}
Log.i("线程运行提示====》","线程已中断。。。");
}
});
}
@Override
public void run() {
while (interruptFlag){
i++;
Log.i("线程执行",String.valueOf(i));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
protected void onDestroy() {
if (thread!=null){
thread.interrupt();
interruptFlag=true;
thread=null;
}
super.onDestroy();
}
}
7.1.6 案例 2:开启一个新线程播放背景音乐
main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/startBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放"
android:layout_gravity="center"
/>
</LinearLayout>
MainActivity.java
package com.jingyi.aboutthread;
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity{
private Thread thread;
private static MediaPlayer mediaPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button button1 = (Button) findViewById(R.id.startBtn);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((Button)v).setEnabled(false);//设置不可用
thread=new Thread(new Runnable() {
@Override
public void run() {
playSound();
}
});
thread.start();
}
});
}
private void playSound() {
if (mediaPlayer!=null){
mediaPlayer.release();//释放资源
}
mediaPlayer = MediaPlayer.create(MainActivity.this, R.raw.qilixiang);
mediaPlayer.start();//开始播放
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
try{
Thread.sleep(10000);//线程休眠5秒
playSound();//重新播放
}catch (InterruptedException e){
e.printStackTrace();
}
}
});
}
@Override
protected void onDestroy() {
//释放资源
if (thread!=null){
thread=null;
}
if (mediaPlayer!=null){
mediaPlayer.stop();
mediaPlayer.release();
mediaPlayer=null;
}
super.onDestroy();
}
}
7.2 Handler 消息传递机制
直接在新创建的子线程上对UI界面上的内容进行操作会抛出异常。为此,Android 中引入了Handler 消息传递机制,来实现在新创建的线程中操作UI界面。下面将对Handler 消息传递机制进行介绍。
7.2.1 循环者(Looper)
在介绍Looper之前,需要先来了解一下MessageQueue的概念。在Android中, 一个线程对应一个Looper对象,而一个Looper对象又对应一个MessageQueue(消息队列) 。MessageQueue用于存放Message(消息),在MessageQueue中,存放的消息按照FIFO (先进先出)原则执行,由于MessageQueue被封装到Looper里面,所以这里不对
MessageQueue进行过多介绍。
Looper对象用来为一个线程开启一个消息循环,从而操作MessageQueue。默认情况下,Android 中新创建的线程是没有开启消息循环的,但是主线程除外。系统自动为主线程创建Looper 对象,开启消息循环。所以,当在主线程中应用下面的代码创建Handler对象时不会出错,而如果在新创建的非主线程中应用下面的代码创建Handler对象,将产生异常信息。
Handler handler2 = new Handler();
如果想要在非主线程中创建Handler对象,首先需要使用Looper 类的prepare()方法来初始化一个 Looper对象,然后创建该Handler对象,再使用Looper类的loop()方法启动Looper,从消息队列中获取和处理消息。
小DEMO
public class LooperThread extends Thread {
public Handler handler;
@Override
public void run() {
super.run();
Looper.prepare();//初始化Looper对象
handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
Log.i("Looper_MSG", String