安卓多线程

一.前言

Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,所以你单纯的new一个Thread并且start()是不行的,因为这违背了Android的单线程模型。
Android只会存在两种线程:UI主线程(UI thread)和工作线程(work thread).
之所以使用多线程:
1.为了提高用户体验
2.避免ANR(Application is not responding)
但这两个原因其实也是因果关系,因为会出现ANR,所以会导致用户体验很差

二.线程的生命周期

创建并运行线程:

① 新建状态(New Thread):在Java语言中使用new 操作符创建一个线程后,该线程仅仅是一个空对象,它具备类线程的一些特征,但此时系统没有为其分配资源,这时的线程处于创建状态。

线程处于创建状态时,可通过Thread类的方法来设置各种属性,如线程的优先级(setPriority)、线程名(setName)和线程的类型(setDaemon)等。

② 就绪状态(Runnable):使用start()方法启动一个线程后,系统为该线程分配了除CPU外的所需资源,使该线程处于就绪状态。此外,如果某个线程执行了yield()方法,那么该线程会被暂时剥夺CPU资源,重新进入就绪状态。

③ 运行状态(Running):Java运行系统通过调度选中一个处于就绪状态的线程,使其占有CPU并转为运行状态。此时,系统真正执行线程的run()方法。

a) 可以通过Thread类的isAlive方法来判断线程是否处于就绪/运行状态:当线程处于就绪/运行状态时,isAlive返回true,当isAlive返回false时,可能线程处于阻塞状态,也可能处于停止状态。

④ 阻塞和唤醒线程

阻塞状态(Blocked):一个正在运行的线程因某些原因不能继续运行时,就进入阻塞 状态。这些原因包括:
a) 当执行了某个线程对象的sleep()等阻塞类型的方法时,该线程对象会被置入一个阻塞集内,等待超时而自动苏醒。
b) 当多个线程试图进入某个同步区域时,没能进入该同步区域的线程会被置入锁定集,直到获得该同步区域的锁,进入就绪状态。
c) 当线程执行了某个对象的wait()方法时,线程会被置入该对象的等待集中,知道执行了该对象的notify()方法wait()/notify()方法的执行要求线程首先获得该对象的锁。

⑤ 死亡状态(Dead):线程在run()方法执行结束后进入死亡状态。此外,如果线程执行了interrupt()或stop()方法,那么它也会以异常退出的方式进入死亡状态。

三.多线程的几种实现方法

1)Activity.runOnUiThread(Runnable)

2)View.post(Runnable) ;View.postDelay(Runnable , long)

3)Handler

4)AsyncTask

四.终止线程的方法

① 使用退出标志,使线程正常退出,也就是当run方法完成后线程终止,推荐使用。

② 使用stop方法强制终止线程(这个方法不推荐使用,因为stop和suppend、resume一样,也可能发生不可预料的结果)。

③ 使用interrupt方法中断线程。

五.跨线程更新UI

方式一:其他线程委托UI线程更新UI
方式二:通过Hnadler发送Message给UI线程,令UI线程根据Message消息更新UI
方式三:使用Android提供的AsyncTask

六.Handler多线程

1.异步信息处理机制

首先是异步信息处理的机制
安卓多线程

2.Handler介绍

一、 Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI.
当应用程序启动时,Android首先会开启一个主线程(即main线程), 主线程为管理界面中的UI控件,进行事件分发,更新UI只能在主线程中更新,子线程中操作是危险的(会抛异常)。这个时候,Handler就需要出来解决这个复杂的问题。由于Handler运行在主线程中(UI线程中),它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传递)Message对象(里面包含数据), 把这些消息放入主线程队列中,配合主线程进行更新UI。
二、Handler的特点
Handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中,
它有两个作用:
(1)安排消息或Runnable 在某个主线程中某个地方执行
(2)安排一个动作在不同的线程中执行
Handler中分发消息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post类方法允许你排列一个Runnable对象到主线程队列中,
sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.

3.简单的Handler示例

1.布局文件

<?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=".HandlerActivity">
<TextView
    android:text="Hello world!"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:textSize="32dp"
    android:id="@+id/textView"/>

<Button
    android:id="@+id/button14"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="downloadClick"
    android:layout_below="@id/textView"
    android:layout_alignParentStart="true"
    android:layout_alignParentEnd="true"
    android:text="下载" />

<ProgressBar
    android:id="@+id/progressBar"
    style="?android:attr/progressBarStyleHorizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentStart="true"
    android:layout_below="@id/button14"
    android:layout_alignParentEnd="true"
   />
</RelativeLayout>

2.activity文件

import android.content.Intent;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class HandlerActivity extends AppCompatActivity {

private static TextView textView;
private ProgressBar pb;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_handler);
    textView = findViewById(R.id.textView);
    pb = (ProgressBar)findViewById(R.id.progressBar);
}

//    private Handler handler = new Handler(){
//        @Override
//        public void handleMessage(Message msg) {
//            super.handleMessage(msg);
//            switch (msg.what){
//                case 100:
//                    textView.setText("下载完成");
//                    break;
//            }
//        }
//    };
private final MyHandler handler = new MyHandler(this);

private static class MyHandler extends Handler {
    private WeakReference<HandlerActivity> weakReference;

    public MyHandler(HandlerActivity activity) {
        weakReference = new WeakReference<HandlerActivity>(activity);
    }

    @Override
    public void handleMessage(Message msg) {
        HandlerActivity activity = weakReference.get();
        if (activity != null) {
            switch (msg.what) {
                case 100:
                    textView.setText("下载完成");
                    break;
            }
        }
    }
}

public void downloadClick(View view) {
//        new Thread(new Runnable() {
//            @Override
//            public void run() {
//                while (true){
//                    try {
//                        Thread.sleep(3000);
//                    } catch (InterruptedException e) {
//                        e.printStackTrace();
//                    }
//                    break;
//                }
    //textView.setText("下载完成");
    new DownloadAsyncTask(this).execute("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1554967923902&di=a39fa7eded960dbce9c670ebe01b8bec&imgtype=0&src=http%3A%2F%2Fs9.sinaimg.cn%2Fmw690%2F006fBWdFzy6XN6qNjm0b8%26690");
    //handler.sendEmptyMessage(100);//发送一个空消息,标记为100

}


private static class DownloadAsyncTask extends AsyncTask<String, Integer, Integer> {
    private  HandlerActivity activity;
    public DownloadAsyncTask(HandlerActivity activity){
        this.activity = activity;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        activity.pb.setProgress(0);
    }

    @Override
    protected Integer doInBackground(String... params) {
        String s = params[0];
        try {
            URL url = new URL(s);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            int size = conn.getContentLength();
            publishProgress(0,size);
            byte[] bytes = new byte[20];
            int len = -1;
            InputStream in = conn.getInputStream();
            FileOutputStream out = new FileOutputStream("/sdcard/"+System.currentTimeMillis()+".jpg");   //命名并且输出文件
            while ((len=in.read(bytes))!=-1){
                out.write(bytes,0,len);
                publishProgress(1,len);
                out.flush();
                //Thread.sleep(500);
            }
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        return 100;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        switch (values[0]){
            case 0:
                activity.pb.setMax(values[1]);
                break;
            case 1 :
                activity.pb.incrementProgressBy(values[1]);
                break;
        }
    }

    @Override
    protected void onPostExecute(Integer integer) {
        super.onPostExecute(integer);
        if (integer==100){
            activity.textView.setText("下载完成");
         }
     }
  }
}

进入
安卓多线程
点击下载,同时进度条按下载进度更新
安卓多线程
进度条到顶,下载完成,并更新textview
安卓多线程
在手机文件中找到下载的图片
安卓多线程

4.Handler优缺点

优点:
1.结构清晰,功能定义明确
2.对于多个后台任务时,简单,清晰

缺点:
Thread + Handler 是有一定的缺陷的

1.线程的开销较大,如果每个任务都要创建一个线程,那么程序的效率要低很多。
2.线程无法管理,匿名线程创建并启动后就不受程序的控制了,如果有很多个请求发送,那么就会启动非常多的线程,系统将不堪重负。
3.在新线程中更新UI还必须要引入handler,这让代码看上去非常臃肿。