1.为什么要使用多线程
(a)提高用户体验或者避免ANR
在事件待处理码中需要使用多线程,否则会出现ANR(Application is not responding),或者因为响应时间太慢导致用户体验很差。
(b)异步
应用中有些情况并不一定需要同步阻塞去等待返回结果,可以通过多线程实现异步,例如你的应用中的某个Activity需要从云端获取一些图片,加载图片比较耗时,这时就需要异步加载,加载完成一个图片刷新一个。
2.为什么通过多线程可以提高用户体验,避免ANR
(a)深入了解
Android应用程序的main线程,它负责处理UI的绘制,Android系统为了防止应用程序反应较慢导致无法正常运行做了一个处理,一种情况是当用户输入事件5秒内无法得到相应,那么系统会弹出ANR对话框,由用户决定继续等待还是强制结束应用程序,另一种情况就是BroadcastReciever超过10s没执行完也会弹出。
即使你的程序中的某个事件响应不超过5分钟,人眼可辨别的时间是0.1秒,小于0.1秒基础感觉不出来,超过0.2秒用户就觉得有点卡了,俗称“打嗝”现象,用户体验会很差。
比如有些应用,它要显示很多图片,所以它是异步的,可以在图片加载成功之前显示一个默认的图片,加载成功后刷新一下即可。
(b)事件处理的原则
①所有耗时的操作都放到其他线程去处理。
②Android中的Main线程的事件处理不能太耗时,否则后续事件无法在5秒内得到回应,就会弹出ANR对话框。
(c)Main线程
①Activity的生命周期的方法
②通常on开头的方法是在Main线程中回调的
③onStart()、onCreate()、onResume()执行时间决定了首页的打开时间。
④可以使用SplashScreen,用动态的,让用户知道你的应用没死掉。
3.如何实现多线程之间的通讯
(a)Handler
(b)AsyncTask
1.继承Thread类实现多线程
Thread本质上也是实现了Runnable接口的一个实例,他代表一个线程的实例,并且启动线程的唯一方法就是通过Thread类的start()实例方法。
start()方法是一个native方法,他将启动一个新线程,并执行run()方法,通过自己的类直接extend Thread,并复写run()方法,就可以启动新线程并执行自己的run()方法。
2.实现Runable接口方式实现多线程
如果自己的类已经extends另一个类,就无法直接extends Thread,此时必须实现一个Runnable接口。
同时为了启动MyThread,需要首先实例化一个Thread,并传入自己的已经实现好Runnable接口的目标对象。
3.实现Runable和实现Thread的区别
(a)一个类只能继承一个父类,存在局限;一个类可以实现多个接口
(b)在实现Runnable接口的时候调用Thread(Runnable target)创建进程时,使用同一个Runnable实例,则建立的多线程的实例变量也是共享的。但是通过继承Thread类是不能用一个实例建立多个线程,故而实现Runnable接口适用于资源共享。当然,继承Thread类也能够共享变量,能共享Thread类的static变量
(c)Runnable接口和Thread之间的联系
public class Thread extends Object implements Runnable
可以看出Thread类也是Runnable接口的子类
MainActivity代码:
package bnuz.it.wjy.hellothread;
import androidx.appcompat.app.AppCompatActivity;
import android.mtp.MtpConstants;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void btnOnclick(View view){
// testThread();
// testRunnable();
testSale();
}
public void testThread(){
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt1.start();
mt2.start();
}
public void testRunnable(){
MyRunnable mr1 = new MyRunnable();
MyRunnable mr2 = new MyRunnable();
Thread t1 = new Thread(mr1);
// Thread t2 = new Thread(mr2);
// 多个Thread对象可以共用一个mr
// 线程同步
Thread t2 = new Thread(mr1);
t1.start();
t2.start();
}
public void testSale(){
SaleTicket saleTicket = new SaleTicket();
Thread t1 = new Thread(saleTicket,"A代理");
Thread t2 = new Thread(saleTicket,"B代理");
Thread t3 = new Thread(saleTicket,"C代理");
Thread t4 = new Thread(saleTicket,"D代理");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
布局代码:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Thread"
android:id="@+id/Thread"
android:onClick="btnOnclick"
/>
</RelativeLayout>
MyThread类
package bnuz.it.wjy.hellothread;
import android.util.Log;
public class MyThread extends Thread{
@Override
public void run() {
Log.d("TAG",Thread.currentThread().getName()+".run()");
}
}
MyRunnable类
package bnuz.it.wjy.hellothread;
import android.util.Log;
public class MyRunnable implements Runnable{
@Override
// 必须是实现run()方法
public void run() {
Log.d("TAG",Thread.currentThread().getName()+".run()");
}
}
SaleTicket类
package bnuz.it.wjy.hellothread;
import android.util.Log;
public class SaleTicket implements Runnable{
private int ticket = 20;
@Override
public void run() {
while (true){
if(ticket > 0){
Log.d("TAG",Thread.currentThread().getName()+"卖出了第"+(20-ticket+1)+"张票");
ticket--;
}
else{
break;
}
}
try {
Thread.sleep(200);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}