安卓多线程
一.前言
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,这让代码看上去非常臃肿。