如何使用多线程
一、AsyncTask
AsyncTask是Android提供的一种异步消息处理工具,可以非常简单地从子线程切换到主线程。AsyncTask <Params,Progress,Result> 类指定三个泛型参数:
◆ Params —— 在执行AsyncTask时需要传入的参数,是一个可以改变size的数组;
◆ Progress —— 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位;
◆ Result —— 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
.....
}
上面这个我们自定义的一个简单的AsyncTask,第一个泛型参数指定为Void,表示执行AsyncTask的时候不需要传入参数给后台;第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位;第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。
二、子线程和主线程的通信
很多时候,我们需要把子线程的数据在UI上展示出来,而我们也知道子线程中是不能直接进行UI操作的,这种情况下,我们就需要用异步线程来处理。下面是一个下载的子线程,我们要把子线程中的下载进度实时展示在UI上:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText mEditText;
private Button mButton;
private ProgressBar mProgressBar;
private String mUrlString;
private Handler mHandler = new DownloadHandler(this);
private TextView mTextView;
public TextView getTextView() {
return mTextView;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mEditText = (EditText) findViewById(R.id.edit_text);
mButton = (Button) findViewById(R.id.button);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
mTextView = (TextView) findViewById(R.id.textView);
mButton.setOnClickListener(this);
mEditText.setText("http://download.sj.qq.com/upload/connAssitantDownload/upload/MobileAssistant_1.apk");
}
@Override
public void onClick(View v) {
mUrlString = mEditText.getText().toString();
Log.i("我是URL",mUrlString);
switch (v.getId()){
case R.id.button:
new Thread(new Runnable() {
@Override
public void run() {
download(mUrlString);
}
}).start();
break;
}
}
private void download(String urlString) {
try {
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream();
int contentLength = connection.getContentLength(); //要下载的文件大小
Log.i("文件大小",contentLength+"");
String downloadFoldersName = Environment.getExternalStorageDirectory() + File.separator + "songhaoran" +File.separator;
File file = new File(downloadFoldersName);
if (!file.exists()){
file.mkdir(); //如果没有文件夹,创建文件夹
}
//下载后的文件名
String fileName = downloadFoldersName + "test.apk";
File apkFile = new File(fileName);
if (apkFile.exists()){
apkFile.delete(); //如果这个文件存在,删除这个文件
}
int downloadSize = 0;
byte[] bytes = new byte[1024];
int length;
OutputStream outputStream = new FileOutputStream(fileName); //创建文件
while ((length = inputStream.read(bytes)) != -1){ //读取每次循环的字节流的大小,当length1!=0,就一直循环
outputStream.write(bytes,0,length); //把数据通过循环写入到bytes中
downloadSize += length; //累加每次循环的数据大小
int progress = downloadSize*100/contentLength; //计算出下载进度
Log.i("我是进度", String.valueOf(progress));
mProgressBar.setProgress(progress); //这里直接就可以在子线程更新UI,因为在setProgress方法的源码中,已经封装了post异步机制
Message message = new Message();
message.obj = progress;
message.what = 0;
mHandler.sendMessage(message);
}
Log.i("下载完成","ok");
inputStream.close();
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
Log.i("下载失败","fail");
}
}
//handler类要写成静态的内部类,防止泄漏
public static class DownloadHandler extends Handler{
public final WeakReference<MainActivity> mActivity; //弱引用
public DownloadHandler(MainActivity activity) {
mActivity = new WeakReference<>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity mainActivity = mActivity.get();
switch (msg.what){
case 0:
int progress = (int) msg.obj;
mainActivity.getTextView().setText(progress+"%");
if (progress == 100){
Toast.makeText(mainActivity,"download success",Toast.LENGTH_SHORT).show();
}
break;
}
}
}
}
点击下载,开启一个子线程,在子线程中执行download()方法。在download()方法中,首先打开URL的连接,创建一个inputString来接收数据;接下来,我们要创建下载文件在手机中的路径和文件名,如果手机中没有指定文件夹就创建该文件夹,如果该路径下要下载的文件已存在,则删除该文件重新下载;
设置好下载环境,先创建一个字节流byte[ ],用outputString创建下载文件; 接下来执行while循环,读取上次循环得到的bytes作为判断条件;在循环中,写入每次循环的数据到bytes,累加每次循环的数据大小,这样就可以算出下载进度progress,然后用Handler把progress发送给指定位置;
接下来就是重点了,利用Handler发送progress给主线程,主线程接收到progress后在UI上展示出来。
拾遗:
1. 在整理、重构代码时,按F6可以将一个类移到其他地方;
2. UI线程是不安全的。也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常;