本文介绍安卓的多线程与异步任务处理的机制
每个 Android 应用都有一个主线程,负责处理界面(包括测量和绘制视图)、协调用户互动以及接收生命周期事件。
主线程一般被用于操作UI界面,与用户进行交互,所以也被称为UI线程。
与此相对的,在一些耗时比较久的任务处理中,比如从网络上下载资源、进行后台服务等,这些是用户看不到的地方。我们不能直接将这些任务放在主线程中进行处理,不然会耗费大量资源并且出现错误,降低用户体验。
所以一般遇到这些任务,需要在主线程之外重新开一个子线程进行任务处理,也就是所谓的非UI线程。
注:子线程(非UI线程)里的操作是不能直接对UI界面进行更新的。
通过一个从网络上下载图片的案例来理解主线程与子线程。
主要逻辑:在界面上创建一个Button按钮,点击后从网络上下载一幅图片。
主要步骤:
1.在布局文件中创建按钮和图片视图,给按钮添加事件。
2.在Java文件中编写事件处理逻辑。
3.在AndroidManifest清单文件中添加网络权限。
activity_main.xml文件:
<Button
android:id="@+id/bt1"
adnroid:onClick="download"
android:layout_width="128dp"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_alignParentEnd="true"
android:layout_marginStart="134dp"
android:layout_marginTop="100dp"
android:layout_marginEnd="134dp"
android:text="Download" />
<ImageView
android:id="@+id/image"
android:layout_width="300dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="@color/colorAccent" />
Java类文件
package com.example.handler;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MainActivity extends AppCompatActivity {
//实例化ImageView对象
ImageView mimageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
//初始化方法中将imageView对象绑定视图
mimageView = (ImageView)findViewById(R.id.image);
}
public void download(View view){//按键处理函数
//创建字符串变量,将网络图片的地址传入
String urlstr = "https://www.baidu.com/s?wd=%E4%BB%8A%E6%97%A5%E6%96%B0%E9%B2%9C%E4%BA%8B&tn=SE_Pclogo_6ysd4c7a&sa=ire_dl_gh_logo&rsv_dl=igh_logo_pc";
URL url = null;
try {//捕获异常
//实例化URL对象,将地址传入
url = new URL(urlstr);
//创建URLConnection对象,进行连接
URLConnection con = url.openConnection();
//创建输入流对象接受输入流
InputStream in = con.getInputStream();
//创建流对象解码输入流
Bitmap bm = BitmapFactory.decodeStream(in);
//将解码后的流(图片)传入视图
mimageView.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
}
}
AndroidManifest清单文件
<uses-permission android:name="android.permission.INTERNET"/>
这样基本逻辑就已经完善了,但是实际编译过程中却无法实现功能,查看日志可以发现这一行:
在高版本的安卓中,网络请求是不能放在主线程里的,一定要开启一个子线程去处理网络任务。
开启子线程后的Java类文件
package com.example.handler;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MainActivity extends AppCompatActivity {
//实例化ImageView对象
ImageView mimageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
//初始化方法中将imageView对象绑定视图
mimageView = (ImageView)findViewById(R.id.image);
}
public void download(View view){//按键处理函数
//创建一个内部类开启子线程
Thread t = new Thread(){
@Override
public void run(){
String urlStr = "https://www.baidu.com/s?wd=%E4%BB%8A%E6%97%A5%E6%96%B0%E9%B2%9C%E4%BA%8B&tn=SE_Pclogo_6ysd4c7a&sa=ire_dl_gh_logo&rsv_dl=igh_logo_pc";
URL url = null;
try {//捕获异常
//实例化URL对象,将地址传入
url = new URL(urlStr);
//创建URLConnection对象,进行连接
URLConnection con = url.openConnection();
//创建输入流对象接受输入流
InputStream in = con.getInputStream();
//创建流对象解码输入流
Bitmap bm = BitmapFactory.decodeStream(in);
//将解码后的流(图片)传入视图
mimageView.setImageBitmap(bm);
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
}
但是依旧会进行报错,原因在于:
子线程是不能对UI界面直接进行更新的。、如果要对UI界面进行更新,一定是子线程给主线程发消息,由主线程对界面进行更新。
UI线程与非UI线程之间通过Handler进行通信。
完整的Java类代码
package com.example.handler;
import androidx.appcompat.app.AppCompatActivity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.ImageView;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
public class MainActivity extends AppCompatActivity {
//实例化ImageView对象
ImageView mimageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
//创建一个Handler对象
Handler h = new Handler(){
@Override
public void handleMessage(Message msg){
//接受来自子线程中Message的消息并强制转换为数据流类型
Bitmap bm = (Bitmap)msg.obj;
//更新视图
mimageView.setImageBitmap(bm);
}
};
private void initView() {
//初始化方法中将imageView对象绑定视图
mimageView = (ImageView)findViewById(R.id.image);
}
public void download(View view){//按键处理函数
//创建一个内部类开启子线程
Thread t = new Thread(){
@Override
public void run(){
//直接复制网络图片的地址传入参数
String urlStr = "http://img4.imgtn.bdimg.com/it/u=1906469856,4113625838&fm=26&gp=0.jpg";
URL url = null;
try {//捕获异常
//实例化URL对象,将地址传入
url = new URL(urlStr);
//创建URLConnection对象,进行连接
URLConnection con = url.openConnection();
//创建输入流对象接受输入流
InputStream in = con.getInputStream();
//创建流对象解码输入流
Bitmap bm = BitmapFactory.decodeStream(in);
//将解码后的流(图片)传入视图
//mimageView.setImageBitmap(bm);
//创建一个Message对象用来发送消息
Message m = new Message();
m.obj = bm;
//向主线程发送消息
h.sendMessage(m);
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}
}
通过第一节的例子可以看到在主线程与子线程之间使用Handler这个消息处理器进行消息的传递与处理。
第一节的例子中是使用Handler的Message方法进行发送消息,在子线程中进行网络资源下载后传递消息给主线程进行UI更新。
Handler还有其他的请求方法,如post请求。
通过一个实例来理解post请求的一些方法。
主要功能:通过post请求实现一个图片的轮播行为。