0
点赞
收藏
分享

微信扫一扫

(七)线程与消息处理

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.valueOf(msg.what));
            }
        };
        Message message = handler.obtainMessage();//获取一个消息
        message.what=0x1;
        handler.sendMessage(message);//发送消息
        Looper.loop();//启动Looper
    }
}

LooperThread thread = new LooperThread();
thread.start();

在这里插入图片描述

Looper类常用的方法

在这里插入图片描述

注意:写在Looper.loop0之后的代码不会被执行,该函数内部是- -个循环,当调用Handler. getLooperO.quit0方法后,loop0方法才会中止,其后面的代码才能运行。

7.2.2 消息处理类(Handler)

消息处理类(Handler) 允许发送和处理Message或Runnable对象到其所在线程的MessageQueue中。Handler主要有以下两个作用。
**1.**将Message或Runnable应用post( )或sendMessage0方法发送到MessageQueue中,在发送时可以指定延迟时间、发送时间及要携带的Bundle数据。当MessageQueue循环到该Message时,调用相应的Handler对象的handlerMessage0方法对其进行处理。
**2.**在子线程中与主线程进行通信,也就是在工作线程中与UI线程进行通信。
说明:在一 个线程中,只能有一个Looper和MessageQueue,但是可以有多个Handler,而且这些Handler可以共享同一个Looper和MessageQueue。

Handler类常用的方法

在这里插入图片描述

7.2.3 消息类(Message)

消息类(Message)被存放在MessageQueue中, 一个MessageQueue中可以包含多个Message对象。每个Message对象可以通过Message.obtain0或Handler.obtainMessage0方法获得。一个Message对象具有如表所示的5个属性。

在这里插入图片描述

说明:使用Message类的属性可以携带int型数据,如果要携带其他类型的数据,可以先将要携带的数据保存到Bundle对象中,然后通过Message类的setData0方法将其添加到Message中。

总之,Message类的使用方法比较简单,在使用时,需注意以下3点:
[V]尽管Message有public的默认构造方法,但是通常情况下,需要使用Message.obtain()或Handler.obtainMessage0方法来从消息池中获得空消息对象,以节省资源。
[V]如果一个Message只需要携带简单的int型信息,应优先使用Message.arg1和Message.arg2 属性来传递信息,这比用Bundle更节省内存。
[V]尽可能使用Message.what 来标识信息,以便用不同方式处理Message.

7.2.4 案例1 :开启新线程获取网络图片并显示到ImageView中

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"
    >

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        />

</LinearLayout>

MainActivity.java

package com.jingyi.picthread;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

public class MainActivity extends AppCompatActivity {
    private ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        imageView = findViewById(R.id.imageView);
        new Thread(new Runnable() {
            public void run() {
                final Bitmap bm=getPic("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fnimg.ws.126.net%2F%3Furl%3Dhttp%3A%2F%2Fdingyue.ws.126.net%2F2021%2F0702%2Fc248167ej00qvkv29000vc000hs00m8c.jpg%26thumbnail%3D650x2147483647%26quality%3D80%26type%3Djpg&refer=http%3A%2F%2Fnimg.ws.126.net&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1651386571&t=632b6b44ba9408c26b013acb3ce6e11d");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                imageView.post(new Runnable() {
                    @Override
                    public void run() {
                        imageView.setImageBitmap(bm);//在组件中显示图片
                    }
                });
            }
        }).start();
    }

    public Bitmap getPic(String path){
        Bitmap bm=null;
        try {
            URL url = new URL(path);
            URLConnection connection=url.openConnection();//获取链接
            connection.connect();//建立链接
            InputStream inputStream = connection.getInputStream();//获取输入流
            bm = BitmapFactory.decodeStream(inputStream);
            inputStream.close();
        }catch (MalformedURLException e){
            e.printStackTrace();
        }catch (IOException e) {
            e.printStackTrace();
        }
        return bm;
    }
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jingyi.aboutthread">
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Application">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
7.2.5 案例 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"
   >

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView"
        android:layout_gravity="center"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/textView"
        android:textSize="30dp"
        android:layout_gravity="center"
        />

</LinearLayout>

MainActivity.java

package com.jingyi.adthread;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.Random;

public class MainActivity extends AppCompatActivity implements Runnable{
    private ImageView imageView;
    private Handler handler;

    private int[] imageID=new int[]{R.drawable.adaofu,R.drawable.baicaowei,R.drawable.cola,R.drawable.huaxizii};
    private String[] titles=new String[]{"阿道夫","百草味","百事可乐","花西子"};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        imageView=findViewById(R.id.imageView);
        Thread thread = new Thread(this);
        thread.start();
        handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                TextView textView = (TextView) findViewById(R.id.textView);
                if (msg.what==0x1){
                    textView.setText(msg.getData().getString("title"));
                    imageView.setImageResource(imageID[msg.arg1]);
                }
                super.handleMessage(msg);
            }
        };
    }

    @Override
    public void run() {
        int index=0;//图片索引
        while(!Thread.currentThread().isInterrupted()){
            index= new Random().nextInt(imageID.length);//随机一个图片索引
            Message message = handler.obtainMessage();
            message.arg1=index;//保存广告图片的索引值
            Bundle bundle = new Bundle();
            message.what=0x1;//设置消息标识
            bundle.putString("title",titles[index]);//保存广告内容
            message.setData(bundle);
            handler.sendMessage(message);//发送消息
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在这里插入图片描述

在这里插入图片描述

7.3 应用实例

7.3.1 多彩的霓虹灯

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"
    android:id="@+id/linear1"
   >

</LinearLayout>

colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#FFBB86FC</color>
    <color name="purple_500">#FF6200EE</color>
    <color name="purple_700">#FF3700B3</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>

    <color name="color1">#f00</color>
    <color name="color2">#f60</color>
    <color name="color3">#ff0</color>
    <color name="color4">#0f0</color>
    <color name="color5">#0ff</color>
    <color name="color6">#00f</color>
    <color name="color7">#60f</color>
    <color name="color8">#fff</color>
</resources>

MainActivity.java

public class MainActivity extends AppCompatActivity{
    private LinearLayout linearLayout;
    private Handler handler;
    private static  TextView [] tv=new TextView[14];
    private int[] color=new int[]{R.color.color1,R.color.color2,R.color.color3,R.color.color4,R.color.color5,R.color.color6,R.color.color7};
    private int index=0;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        linearLayout=findViewById(R.id.linear1);
        int height = this.getResources().getDisplayMetrics().heightPixels;//获取屏幕高度


        for (int i=0;i<tv.length;i++){
            tv[i]=new TextView(this);//新建文本框对象
            tv[i].setWidth(this.getResources().getDisplayMetrics().widthPixels);
            tv[i].setHeight(height/tv.length);
            linearLayout.addView(tv[i]);//将文本框添加到线性布局中
        }



        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (!Thread.currentThread().isInterrupted()){
                    Message message = handler.obtainMessage();
                    message.what=0x1;
                    handler.sendMessage(message);
                    try {
                        Thread.sleep(new Random().nextInt(1000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        thread.start();
        handler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                int temp=0;
                if (msg.what==0x1){
                    for (int i=0;i<tv.length;i++){
                         temp= new Random().nextInt(color.length);
                         if (temp==index){//同一种颜色不能同时挨着出现
                             temp++;
                             if (temp==color.length){//越界处理
                                 temp=0;
                             }
                         }
                         index=temp;
                         tv[i].setBackgroundColor(getResources().getColor(color[index]));
                    }
                }
                super.handleMessage(msg);
            }
        };
    }

}

Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.jingyi.adthread">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.AppCompat.NoActionBar">
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

在这里插入图片描述

在这里插入图片描述

举报

相关推荐

多线程与高并发学习七

0 条评论