今天开始写博客,目的就是为了方便记录学习的一些要点,同时也和大家交流。
第一篇是关于Android的异步任务(AsyncTask)的。异步任务是为了在其它线程中更新UI。AsyncTask是一个抽象类,通常用于继承。
构建AsyncTask子类的泛型参数
AsyncTask是一个抽象类,通常用于被继承.继承AsyncTask需要指定如下三个泛型参数:
Params:启动任务时输入的参数类型.
Progress:后台任务执行中返回进度值的类型.
Result:后台任务执行完成后返回结果的类型.
.构建AsyncTask子类的回调方法
AsyncTask主要有如下几个方法:
doInBackground:必须重写,异步执行后台线程要完成的任务,耗时操作将在此方法中完成.
onPreExecute:执行后台耗时操作前被调用,通常用于进行初始化操作.
onPostExecute:当doInBackground方法完成后,系统将自动调用此方法,并将doInBackground方法返回的值传入此方法.通过此方法进行UI的更新.
onProgressUpdate:当在doInBackground方法中调用publishProgress方法更新任务执行进度后,将调用此方法.通过此方法我们可以知晓任务的完成进度.
onPreExecute方法会首先执行,之后执行doInBackground方法,最后执行onPostExecute方法。onProgressUpdate方法需要在doInBackground方法中手动调用。
下面展示使用异步处理加载网络图片.网络操作作为一个不稳定的耗时操作,从4.0开始就被严禁放入主线程中.所以在显示一张网络图片时,我们需要在异步处理中下载图片,并在UI线程中设置图片.
ImageTest.java
用于处理异步操作,从网络中加载图片
import java.io.BufferedInputStream;
import java.net.URL;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLConnection;
public class ImageTest extends Activity {
private ImageView imageView;
private ProgressBar progressBar;
private static String URL = "http://www.deskcar.com/desktop/fengjing/2013812103350/11.jpg";//图片地址
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.image);
imageView = (ImageView)findViewById(R.id.image);
progressBar = (ProgressBar)findViewById(R.id.progress);
//通过AsyncTask的execute方法开启异步线程操作,传递的参数是doInBackground方法中的参数
new MyAsyncTask().execute(URL);
}
//通过一个内部类实现异步操作
class MyAsyncTask extends AsyncTask<String,Void,Bitmap>{
/**
* 对异步操作进行初始化操作
*/
@Override
protected void onPreExecute() {
super.onPreExecute();
progressBar.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
progressBar.setVisibility(View.GONE);
imageView.setImageBitmap(bitmap);
}
/**
* 进行异步操作
* @param params
* @return 返回AsyncTask中的第三个参数类型
*/
@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();
BufferedInputStream bfs = new BufferedInputStream(is);
//将url对应的图像解析成Bitmap
bitmap = BitmapFactory.decodeStream(bfs);
is.close();
bfs.close();
} catch (IOException e) {
e.printStackTrace();
}
return bitmap;
}
}
}
image.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:padding="16dp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/image"/>
<!--进度条,加载前显示,加载后隐藏-->
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
android:id="@+id/progress"/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button button;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(),ImageTest.class));
}
});
}
}
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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.unicorn.asynctask.MainActivity">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Image Test"
android:id="@+id/button"
/>
</LinearLayout>
记得配置Manifest文件,添加网络权限
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
运行效果:
下面展示另外一个示例,通过异步操作更新进度条
ProgressBarTest.java
public class ProgressBarTest extends Activity {
private ProgressBar progressBar;
//可以声明为变量
private MyAsyncTask myAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
progressBar = (ProgressBar)findViewById(R.id.pb);
//初始化并执行异步操作
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
//模拟进度更新
for (int i = 0;i<100;i++){
//向onProgressUpdate传递i
publishProgress(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
//传递进的是数组 取第0位
progressBar.setProgress(values[0]);
}
}
}
MainActivity.java中为按钮添加监听事件,个人觉得通过设置button的onclick属性来实现较好:
布局文件中的设置:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ProgressBar Test"
android:id="@+id/pgbn"
android:onClick="progress"
/>
java文件中添加按钮监听事件只需要实现一个函数就好,代码结构更易阅读
/**
* 按钮的监听事件
*/
public void progress(View view){
startActivity(new Intent(this,ProgressBarTest.class));
}
异步任务的取消:
AsyncTask是基于线程池进行实现的,当一个线程没有结束时,后面的线程是不能执行的.所以必须等到第一个task的for循环结束后,才能执行第二个task.
可以使异步任务的生命周期和Activity的生命周期保持一致。点击BACK键返回时会调用Activity的onPause()方法.如果需要取消异步任务时,我们需要在Activity的onPause()方法中将正在执行的task标记为cancel状态,在doInBackground方法中进行异步处理时判断是否是cancel状态来决定是否取消之前的task.
修改后的ProgressBarTest.java
public class ProgressBarTest extends Activity {
private ProgressBar progressBar;
//可以声明为变量
private MyAsyncTask myAsyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progressbar);
progressBar = (ProgressBar)findViewById(R.id.pb);
//初始化并执行异步操作
myAsyncTask = new MyAsyncTask();
myAsyncTask.execute();
}
class MyAsyncTask extends AsyncTask<Void,Integer,Void>{
@Override
protected Void doInBackground(Void... params) {
//模拟进度更新
for (int i = 0;i<100;i++){
if(isCancelled()){
break;
}
//向onProgressUpdate传递i
publishProgress(i);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if(isCancelled())
return;
//传递进的是数组 取第0位
progressBar.setProgress(values[0]);
}
}
@Override
protected void onPause() {
super.onPause();
//如果AsyncTask不为空且正在运行
if (myAsyncTask != null && myAsyncTask.getStatus() == AsyncTask.Status.RUNNING)
//cancel方法只是将对应的AsyncTask标记为cancel状态,并没有真正取消线程的执行
//在线程中监测状态
myAsyncTask.cancel(true);
}
}
在onPause()方法中将异步线程cancel标记为true,在线程中监测cancel状态,为true时采取措施结束进程。
牢记AsyncTask.cancel()方法只是对isCanceled进行标记,并没有真正取消线程,需要将AsyncTask和Activity或者Fragment生命周期进行绑定,监测isCanceled并进行修改。
使用AsyncTask的注意事项
① 必须在UI线程中创建AsyncTask的实例.
② 只能在UI线程中调用AsyncTask的execute方法.
③ 重写的AsyncTask的四个方法是系统自动调用的,不能手动调用.
④ 每个AsyncTask只能被执行(execute方法)一次,多次执行将会引发异常.
⑤ AsyncTask的四个方法,只有doInBackground方法是运行在其他线程中,其他三个方法都运行在UI线程中,也就说其他三个方法都可以进行UI的更新操作.
第一次写博客,不好的地方希望大家多多包涵~