文章目录
多线程那些事儿
何为线程、多线程?
线程
把一个进程比喻为一个车间,那么线程就是车间里面的一条条流水线。一个车间可以有多条流水线,流水线属于车间。一个车间的工作过程是一个进程,一个流水线的工作过程是一个线程。进程是操作系统资源分配的最小单位(生产科3号车间负责生产3万件包装纸箱),线程是cup调度的最小单位(3号车间里面的每一条生产线负责生产纸箱这个具体的任务)。
多线程
就是一个进程中存在多条线程。多线程是数据共享的(共享进程中的空间地址啥的),即3号车间有多条流水线,都共用了一个车间的资源。
多线程的应用举例
办公三件套之word为例,打开word软件就开启了一个进程,这个进程至少要干几件事儿:1监控键盘输入;2处理文字;3定时自动保存文字信息等。这几件事儿操作的都是同一块数据,所以不能使用多进程(进程间数据隔离啊),只能在一个进程里并发的开启三个线程,如果是单线程,那就只能是键盘输入时,不能处理文字和自动保存,自动保存时又不能打字。
ANR(Application Not Responding)
应用程序未响应
原因: 在安卓中,定义了一个UI线程,与控件、界面相关,一旦线程做一件事卡住了(比如:下载音乐,耗时2分钟),10秒内未下载完成,就算卡住了。在下载的过程中,点击控件无响应,界面卡死了,这时候就ANR了
解决: 另开一个线程,在该线程中下载音乐
什么时候使用多线程?
不想ANR(有耗时操作时),就使用多线程
Main/UI Thread和Worker Thread
之间的通信
Thread/Runnable线程安全
普通的主线程更新
1、
Activity.runOnUiThread(Runnable)
2、
View.post(Runnable)
3、
View.postDelayed(Runnable,long)
Handler
AsyncTask
目的:方便后台线程中操作后更新UI
实现:Thread和Headler进行了封装
实质:Handler异步消息处理机制
AsyncTask(抽象类)
1、 泛型参数 <Params,Progress,Result>
Params:参数
Progress:显示当前进度
Result:结果
2、需要重写的4个方法:
onPreExecute:在后台任务开始执行之前调用,用于进行一些界面上的初始化操作
doInBackground(Params…):在子线程中执行,处理耗时任务
onProgressUpdate(Progress…):对UI进行操作
onPostExecute(Result…):后台任务执行完毕后返回结果
UI操作:onPreExecute、onPostExecute
1、onPreExecute
/**
* 在异步任务之前,在主线程中
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
//可操作UI
}
2、onPostExecute
/**
* 在主线程中
*/
@Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
//执行结果处理(doInBackground返回的Boolean结果)
}
后台线程操作doInBackground
继承AsyncTask必须实现doInBackground()方法:
public class DownloadAsyncTask extends AsyncTask<String,Integer,Boolean>{
/**
* 在另外一个线程中处理参数
* @param params 入参
* @return 结果
*/
@Override
protected Boolean doInBackground(String... strings) {
return null;
}
}
具体代码
package com.example.asynctaskstudy;
import androidx.appcompat.app.AppCompatActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new DownloadAsyncTask().execute("imooc","good");
}
public class DownloadAsyncTask extends AsyncTask<String,Integer,Boolean> {
/**
* 在另外一个线程中处理参数
* @param params 入参
* @return 结果
*/
@Override
protected Boolean doInBackground(String... params) {
for (int i = 0; i <10000 ; i++) {
Log.i(TAG, "doInBackground: "+params[0]);
}
try {
Thread.sleep(100*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return true;
}
}
}
其中,休眠100秒,测试会不会卡死:
try {
Thread.sleep(100*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
休眠100秒也不会卡死,界面仍然正常应用,因为是在另外一个线程中sleep()
结果:
10000遍的
02-15 03:15:37.765 8297-8316/com.example.asynctaskstudy I/MainActivity: doInBackground: imooc
进度条显示 onProgressUpdate
/**
* 在主线程中
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//收到进度,处理
}
具体代码
目标:
具体步骤:
- 1、网络上请求数据:申请网络权限 申请读写存储权限
- 2、布局layout
- 3、下载之前我们要做什么? 处理UI
- 4、下载中我们要做什么? 处理数据
- 5、下载后我们要做什么? 处理UI
网络上请求数据:申请权限权限
网络权限
<uses-permission android:name="android.permission.INTERNET"/>
读写存储权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
布局layout
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="match_parent"
android:layout_height="15dp"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
/>
<Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="点击下载"
/>
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
/>
</LinearLayout>
MainActivity
package com.example.asynctaskstudy;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* 1、网络上请求数据:申请网络权限 申请读写存储权限
* 2、布局layout
* 3、下载之前我们要做什么? 处理UI
* 4、下载中我们要做什么? 处理数据
* 5、下载后我们要做什么? 处理UI
*/
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";//快捷键【logt】
private ProgressBar mProgressBar;
private Button mDownloadButton;
private TextView mResultTextView;
private static final int INIT_PROGRESS = 0;
private static final String APK_URL = "http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk";
private static final String FILE_NAME = "imooc3.apk";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化视图
initView();
//设置点击监听
setListener();
//初始化UI数据
setData();
}
/**
* 初始化视图
*/
private void initView() {
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mDownloadButton = (Button) findViewById(R.id.button);
mResultTextView = (TextView) findViewById(R.id.textView);
}
/**
* 设置点击监听
*/
private void setListener() {
mDownloadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//做下载的事情
// TODO: 2020-02-15 下载任务
DownloadAsyncTask asyncTask = new DownloadAsyncTask();
asyncTask.execute(APK_URL);
}
});
}
/**
* 初始化UI数据
*/
private void setData() {
mProgressBar.setProgress(INIT_PROGRESS);
mDownloadButton.setText(R.string.click_download);
mResultTextView.setText(R.string.download_text);
}
/**
* String 入参
* Integer 进度
* Boolean 返回值
*/
public class DownloadAsyncTask extends AsyncTask<String, Integer, Boolean> {
private String mFilePath;
/**
* 在异步任务之前,在主线程中
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
//可操作UI 类似于淘米,之前的准备工作
mDownloadButton.setText(R.string.downloading);
mResultTextView.setText(R.string.downloading);
mProgressBar.setProgress(INIT_PROGRESS);
}
/**
* 在另外一个线程中处理参数
*
* @param params 入参 煮米
* @return 结果
*/
@Override
protected Boolean doInBackground(String... params) {
//下载中
if (params != null && params.length > 0) {
String apkUrl = params[0];
try {
//构造URL
URL url = new URL(apkUrl);
//构造连接,并打开
URLConnection urlConnection = url.openConnection();
//
InputStream inputStream = urlConnection.getInputStream();
//获取了下载内容的总长度
int contentLength = urlConnection.getContentLength();
//下载地址准备 File.separator:/
mFilePath = Environment.getExternalStorageDirectory()
+ File.separator + FILE_NAME;
//对下载地址进行处理
File apkFile = new File(mFilePath);
if (apkFile.exists()) {
boolean result = apkFile.delete();
if (!result) {//删除失败
return false;
}
}
//已下载的大小
int downloadSize = 0;
//byte数组
byte[] bytes = new byte[1024];
int length;
//创建一个输入管道
OutputStream outputStream = new FileOutputStream(mFilePath);
/**
* read(byte[] b) :
* 从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。
*/
//不断的一车一车挖土,直到挖不到为止
while ((length = inputStream.read(bytes)) != -1) {
//挖到的放到我们的文件管道里
outputStream.write(bytes, 0, length);
//累加我们的大小
downloadSize += length;
// 发送进度
publishProgress(downloadSize * 100 / contentLength);
}
//关闭流,防止内存泄漏
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
} else {
return false;
}
return true;
}
/**
* 在主线程中
*/
@Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
//执行结果处理(doInBackground返回的Boolean结果)
mDownloadButton.setText(result ? getString(R.string.download_finish) : getString(R.string.download_fail));
mResultTextView.setText(result ? getString(R.string.download_finish) + mFilePath : getString(R.string.download_fail));
}
/**
* 在主线程中
*/
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//收到进度,然后处理
if (values != null) {
mProgressBar.setProgress(values[0]);
}
}
@Override
protected void onCancelled(Boolean aBoolean) {
super.onCancelled(aBoolean);
}
}
}