第十三课:继续Asynctask-调用远程图片(基于AndroidStudio3.2)
一、测试一
下面通过代码演示一个典型的异步处理的实例--加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片
1、 新建项目Async2
- 主窗口增加一个按钮
- 增加新的Activity,名称为ImageActivity,添加imageView和progressBar
MainActivity.java
package com.example.administrator.async2;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button btn_image;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_image = (Button) findViewById(R.id.btn_image);
btn_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,ImageActivity.class));
}
});
}
}
ImageActivity.java
package com.example.administrator.async2;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
public class ImageActivity extends AppCompatActivity {
private ImageView imageView ;
private ProgressBar progressBar ;
private static String URLs = "http://p5.qhimg.com/t0193f0ca27a17798d4.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_image);
imageView = (ImageView) findViewById(R.id.image);
progressBar = (ProgressBar) findViewById(R.id.progress);
//通过调用execute方法开始处理异步任务.相当于线程中的start方法.
new MyAsyncTask().execute(URLs);
}
class MyAsyncTask extends AsyncTask<String,Void,Bitmap> {
//onPreExecute用于异步处理前的操作
@Override
protected void onPreExecute() {
super.onPreExecute();
//此处将progressBar设置为可见.
progressBar.setVisibility(View.VISIBLE);
}
//在doInBackground方法中进行异步任务的处理.
@Override
protected Bitmap doInBackground(String... params) {
//获取传进来的参数
String url = params[0];
Bitmap bitmap = null;
URLConnection connection ;
InputStream is ;
try {
connection = new URL(url).openConnection();
is = connection.getInputStream();
//为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
Thread.sleep(3000);
BufferedInputStream bis = new BufferedInputStream(is);
//通过decodeStream方法解析输入流
bitmap = BitmapFactory.decodeStream(bis);
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
return bitmap;
}
//onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//隐藏progressBar
progressBar.setVisibility(View.GONE);
//更新imageView
imageView.setImageBitmap(bitmap);
}
}
}
由于涉及到网络操作,需要在AndroidManifest.xml中添加网络操作权限:<uses-permission android:name="android.permission.INTERNET"/>
在res文件夹下创建一个xml文件夹,然后创建一个network_security_config.xml文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
接着,在AndroidManifest.xml文件下的application标签增加以下属性:
<application
...
android:networkSecurityConfig="@xml/network_security_config"
...
/>
以上不设置会报错。
2、运行ok
二、模拟更新进度条
1、在以上例子中,activity_main.xml中新增按钮
2、新建类ProgressActivity,在activity_progress.xml中添加imageView、progressBar
MainActivity.java
package com.example.administrator.async2;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
private Button btn_image;
private Button btn_progress;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_image = (Button) findViewById(R.id.btn_image);
btn_image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,ImageActivity.class));
}
});
btn_progress = (Button) findViewById(R.id.btn_progress);
btn_progress.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this,ProgressActivity.class));
}
});
}
}
ProgressActivity.java
package com.example.administrator.async2;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ProgressBar;
public class ProgressActivity extends AppCompatActivity {
private ProgressBar progressBar;
private MyAsyncTask1 myAsyncTask1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
myAsyncTask1 = new MyAsyncTask1();
myAsyncTask1.execute();
}
class MyAsyncTask1 extends AsyncTask<Void,Integer,Void> {
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//通过publishProgress方法传过来的值进行进度条的更新.
progressBar.setProgress(values[0]);
}
@Override
protected Void doInBackground(Void... params) {
//使用for循环来模拟进度条的进度.
for (int i = 0;i < 100; i ++){
//调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
publishProgress(i);
try {
//通过线程休眠模拟耗时操作
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
}
}
3、运行
如果要进度条后显示图片,则修改部分代码就可以ProgressActivity.java
package com.example.administrator.async2;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
public class ProgressActivity extends AppCompatActivity {
private ImageView imageView ;
private ProgressBar progressBar;
private MyAsyncTask1 myAsyncTask1;
private static String URLs = "http://www.xinhuanet.com/photo/2018-10/23/1123600281_15402693610221n.jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_progress);
imageView = (ImageView) findViewById(R.id.imageView);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
//myAsyncTask1 = new MyAsyncTask1();
//myAsyncTask1.execute();
//通过调用execute方法开始处理异步任务.相当于线程中的start方法.
new MyAsyncTask2().execute(URLs);
}
//AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.
@Override
protected void onPause() {
super.onPause();
if (myAsyncTask1 != null && myAsyncTask1.getStatus() == AsyncTask.Status.RUNNING) {
//cancel方法只是将对应的AsyncTask标记为cancelt状态,并不是真正的取消线程的执行.
myAsyncTask1.cancel(true);
}
}
class MyAsyncTask1 extends AsyncTask<Void,Integer,Void> {
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//通过publishProgress方法传过来的值进行进度条的更新.
progressBar.setProgress(values[0]);
}
@Override
protected Void doInBackground(Void... params) {
//使用for循环来模拟进度条的进度.
for (int i = 0;i < 100; i ++){
//如果task是cancel状态,则终止for循环,以进行下个task的执行.
if (isCancelled()){
break;
}
//调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
publishProgress(i);
try {
//通过线程休眠模拟耗时操作
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
}
class MyAsyncTask2 extends AsyncTask<String,Integer,Bitmap> {
//onPreExecute用于异步处理前的操作
@Override
protected void onPreExecute() {
super.onPreExecute();
//此处将progressBar设置为可见.
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//通过publishProgress方法传过来的值进行进度条的更新.
progressBar.setProgress(values[0]);
}
//在doInBackground方法中进行异步任务的处理.
@Override
protected Bitmap doInBackground(String... params) {
//获取传进来的参数
String url = params[0];
Bitmap bitmap = null;
URLConnection connection ;
InputStream is ;
try {
connection = new URL(url).openConnection();
is = connection.getInputStream();
//为了更清楚的看到加载图片的等待操作,将线程休眠3秒钟.
// Thread.sleep(3000);
//使用for循环来模拟进度条的进度.
for (int i = 0;i < 100; i ++){
//如果task是cancel状态,则终止for循环,以进行下个task的执行.
if (isCancelled()){
break;
}
//调用publishProgress方法将自动触发onProgressUpdate方法来进行进度条的更新.
publishProgress(i);
try {
//通过线程休眠模拟耗时操作
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
BufferedInputStream bis = new BufferedInputStream(is);
//通过decodeStream方法解析输入流
bitmap = BitmapFactory.decodeStream(bis);
is.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
//onPostExecute用于UI的更新.此方法的参数为doInBackground方法返回的值.
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
//隐藏progressBar
progressBar.setVisibility(View.GONE);
//更新imageView
imageView.setImageBitmap(bitmap);
}
}
}
AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.所以必须等到第一个task的for循环结束后,才能执行第二个task.我们知道,当点击BACK键时会调用Activity的onPause()方法.为了解决这个问题,我们需要在Activity的onPause()方法中将正在执行的task标记为cancel状态,在doInBackground方法中进行异步处理时判断是否是cancel状态来决定是否取消之前的task.
使用AsyncTask的注意事项
① 必须在UI线程中创建AsyncTask的实例.
② 只能在UI线程中调用AsyncTask的execute方法.
③ AsyncTask被重写的四个方法是系统自动调用的,不应手动调用.
④ 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.
⑤ AsyncTask的四个方法,只有doInBackground方法是运行在其他线程中,其他三个方法都运行在UI线程中,也就说其他三个方法都可以进行UI的更新操作.