一、Android多线程的介绍
本质上来说,多线程的定义都是一样的,软件或硬件上并发地执行多条指令,将CPU的时间片按照调度算法,分配给各个线程,实际上是分时执行的,只是这个切换的时间很短,用户感觉的时候是同时而已。
Android大名鼎鼎的四大组件Activity,service,content provider,broadcast receiver,你可能以为它们是多线程运行的,但是实际上默认情况下,在同一个应用程序当中里面的组件都是运行在同一个线程里的。这个线程就是Main线程,更通俗的叫法是UI线程,主要是因为它用来加载UI界面,处理大部分的系统与用户交互的工作,又可以把经过逻辑处理的结果返回给用户。Android就是遵循的这种单线程模型的规则。
那么问题来了,一个基本Android的项目肯定是要用到多线程的,举个例子,某著名交友app有个实时更换头像的功能,它可以直接选取网络上的图片作为你的头像,而不用通过保存到相册再通过相册选取来设置。这个功能我刚使用的时候其实并没有觉察到它的技术难度,但是当我真正去实现的时候发现挺不简单的。因为你获取到图片之后还要显示出来,那么你当前的UI线程是肯定不能终止的,但是我又要去下载图片,这显然就是异步操作了,所以第一步肯定是要new Theard(),算法逻辑都在里面编写,但是运行的时候直接抛出异常android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views,stackoverflow之后才知道,Android中的多线程挺不简单有讲究的。
首先一个,UI控件是线程不安全的,Google规定不能再UI线程之外的线程操纵UI元素,否则就会抛出上面的异常,还有一个我没踩到的雷就是,不能在UI线程内进行耗时操作,否则就会出现程序无响应的情况,这都是挺致命的。
二、如何实现线程间的通信
1.我最开始想到的是Handler,在里面写好UI执行逻辑,通过sendMessage()等方法通知UI更新。
2.后来无意中了解到还有个方法叫Activity.runOnUiThread(Runnable),把更新UI的逻辑代码写到Runnable里面,再把对象传进来就可以了。
三、AsyncTask的介绍及使用
虽然上述的两个方法都能实现我的需求,但是逻辑代码的编写都让我感觉到很吃力。于是我又发现了另外一个方法,使用AsyncTask类来完成需求。
(1)AsyncTask的优势
AsyncTask是一个官方提供的已经封装好的轻量级异步类,逻辑代码编写起来简单,最受我青睐的一点就是它的进度是可控的!相比起Handler,AsyncTask显得更加简单,快捷。
(2)介绍一下AsyncTask的使用方法
1)构建子类的参数
AsyncTask <Params, Progress, Result>
- Params:这个泛型指定的是我们传递给异步任务执行时的参数的类型
- Progress:这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
- Result:这个泛型指定的异步任务执行完后返回给UI线程的结果的类型
2)相关方法与执行流程
要注意的是红色部分是一定要覆写的!
四、AsyncTask实例演示
(1)模拟图片下载的基础Demo
1.主要代码
public class DelayOperator {
//延时操作,用来模拟下载
public void delay()
{
try {
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
public class MyAsyncTask extends AsyncTask<Integer,Integer,String>
{
private TextView textView;
private ProgressBar progressBar;
public MyAsyncTask(TextView textView,ProgressBar progressBar)
{
super();
this.textView = textView;
this.progressBar = progressBar;
}
//该方法不运行在UI线程中,主要用于异步操作,通过调用publishProgress()方法
//触发onProgressUpdate对UI进行操作
@Override
protected String doInBackground(Integer... params) {
DelayOperator dop = new DelayOperator();
int i ;
for (i = 10;i <= 100;i+=10)
{
//判断是否为取消状态
dop.delay();
publishProgress(i);
}
return i + params[0].intValue() + "";
}
//该方法运行在UI线程中,可对UI控件进行设置
@Override
protected void onPreExecute() {
textView.setText("开始执行异步线程~");
}
//在doBackground方法中,每次调用publishProgress方法都会触发该方法
//运行在UI线程中,可对UI控件进行操作
@Override
protected void onProgressUpdate(Integer... values) {
int value = values[0];
progressBar.setProgress(value);
}
}
2.实现效果
(2)进阶版从网络下载图片的实际应用
1.主要代码
public class ImageAsyncTask extends AsyncTask<String, Bitmap, Integer> {
private TextView textView;
private ImageView imageView;
public ImageAsyncTask(TextView textView,ImageView imageView)
{
super();
this.textView = textView;
this.imageView=imageView;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
//页面提示
textView.setText("下载中...");
}
@Override
protected Integer doInBackground(String... params) {
//记录成功下载的图片个数
int downloadSuccess = 0;
try {
for (int i = 0; i < params.length; i++) {
//睡眠2秒,制造耗时操作效果
Thread.sleep(2000);
if(isCancelled()){
break;
}
//循环取出可变参数中图片地址
String url = params[i];
//Bitmap下载
Bitmap bitmap ;
URLConnection connection;
InputStream is;
connection = new URL(url).openConnection();
is = connection.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bitmap = BitmapFactory.decodeStream(bis);
//
bis.close();
is.close();
//将每一次下载好的图片,作为阶段性结果发回给 onProgressUpdate()
publishProgress(bitmap);
//成功下载一张则累加
downloadSuccess++;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return downloadSuccess;
}
@Override
protected void onProgressUpdate(Bitmap... values) {
super.onProgressUpdate(values);
//接收到的值-图片,可以直接进行更新UI,因为运行在主线程上
imageView.setImageBitmap(values[0]);
}
@Override
protected void onPostExecute(Integer integer) {
super.onPostExecute(integer);
if (integer == 3) {
textView.setText("全部成功下载");
}
}
@Override
protected void onCancelled(Integer integer) {
super.onCancelled(integer);
//取消后的响应在这边写
textView.setText("取消下载");
}
}
2.实现效果
最后,本文若有描述不当之处还望指出,非常感谢。