*【线程优化】AsyncTask 异步任务

为什么需要使用异步任务?

  • android单线程模型。
  • 耗时操作放在非主线程中执行。
  • 解释:
    Android中只有UI线程,也就是主线程才能进行对UI的更新操作,而其他线程是不能直接操作UI的.好处是保证了UI的稳定性和准确性,避免多个线程同时对UI进行操作而造成UI的混乱.但Android是一个多线程的操作系统,我们总不能把所有的任务都放在主线程中进行实现,比如网络操作,文件读取等耗时操作,如果全部放到主线程去执行,就可能会造成后面任务的阻塞.Android会去检测这种阻塞,当阻塞时间太长的时候,就会抛出Application Not Responsed(ANR)错误.所以我们需要将这些耗时操作放在非主线程中去执行.这样既避免了Android的单线程模型,又避免了ANR

AsyncTask为何而生?

  • 子线程中更新UI。
  • 封装,简化异步操作。
  • 解释:
    提到异步任务,我们能想到用线程,线程池去实现.确实,Android给我们提供了主线程与其他线程通讯的机制.但同时,Android也给我们提供了一个封装好的组件–AsyncTask.利用AsyncTask,我们可以很方便的实现异步任务处理.AsyncTask可以在子线程中更新UI,也封装简化了异步操作.使用线程,线程池处理异步任务涉及到了线程的同步,管理等问题.而且当线程结束的时候还需要使用Handler去通知主线程来更新UI.而AsyncTask封装了这一切,使得我们可以很方便的在子线程中更新UI

异步任务____AsyncTask
1. 用处:
AsyncTask在子线程中更新UI,封装、简化异步操作

将耗时操作放在非主线程中执行,既保证了Android单线程模型,也保证了程序的响应(不出现ANR)
2. AsyncTask< Params, Progress, Result>
是一个抽象类,通常用于被继承,继承AsyncTask需要指定如下三个泛型参数:
Params:启动任务时输入参数的类型
Progress:后台任务执行中返回进度值的类型
Result:后台执行任务完成后返回结果的类型
注意:参数不是一定要使用,也可以都为Void

3.在继承AsyncTask的子类中需要重写的回调方法
doInBackground:必须重写,异步执行后台线程将要完成的任务。
onPreExecute:执行后台耗时操作前被调用,通常用户完成一些初始化操作。
onPostExecute:当doInBackground完成后,系统会自动调用。
onPostExecute方法:并将doInBackground方法返回的值传给该方法。
onProgressUpdate:在doInBackground方法中调用publishProgress方法更新任务的执行进度后,就会触发该方法。


网络操作作为不稳定的废时操作,从android 4.0开始就被严禁放入主线程中
通常采用在异步线程处理下载图像
在UI线程设置图像

ProgressBar XML属性 visibility=”gone”可设置为默认状态下为隐藏
VISIBLE:设置控件可见
INVISIBLE:设置控件不可见
GONE:设置控件隐藏

ps:INVISIBLE和GONE的主要区别是:当控件visibility属性为INVISIBLE时,界面保留了view控件所占有的空间;而控件属性为GONE时,界面则不保留view控件所占有的空间。


doInBackground方法传入的是一个可变长数组,在execute方法中可以传递不止一个参数,存入params数组中

String url = params[0];//本例中只传入一个地址
Bitmap bitmap = null;
URLConnection connection;//定义网络连接对象
InputStream is;//用于获取数据的输入流
connection = new URL(url).openConnection();//获取网络连接对象,需要被try_catch
is = connection.getInputStream();//获取输入流
BufferedInputStream bis = new BufferedInputStream(is);
bitmap = BitmapFactory.decodeStream(bis);//将输入流解析成bitmap
is.close();//关闭输入流
bis.close();

ruturn bitmap;//将bitmap作为返回值返回给后面调用的方法


eg.

从网络中获取一张图片是一个耗时操作,可放在AsyncTask的doInBackground()方法中执行,在获取图片前调用 onPreExecute方法,设置一个ProgressBar给用户看,当耗时操作完成时,把获取到的图片 在onPostExecute方法展现出来。

/**
 * 开启异步线程去做耗时操作 
 */ 
class MyAsyncTask extends AsyncTask { 

    //onPreExecute执行后台耗时操作前被调用,通常用户完成一些初始化操作。 
    protected void onPreExecute() {
        super.onPreExecute(); 
        progressBar.setVisibility(View.VISIBLE);
    } 

    //onPostExecute:当doInBackground完成后,
    //系统会自动调用onPostExecute方法,并将doInBackground方法返回的值传给该方法。
    protected void onPostExecute(Bitmap bitmap) { 
        super.onPostExecute(bitmap); 
        progressBar.setVisibility(View.GONE); 
        image.setImageBitmap(bitmap); 
    } 

    //doInBackground是异步执行后台线程将要完成的任务 
    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 bis=new BufferedInputStream(is); 
            Thread.sleep(1000);
            //通过decodeStream解析输入流 
            bitmap= BitmapFactory.decodeStream(bis); 
            is.close();
            bis.close(); 
        }
        catch (IOException e) { 
            e.printStackTrace(); 
        }
        catch (InterruptedException e) { 
            e.printStackTrace(); 
        }
        return bitmap; 
    } 
}

最后在onCreate方法中调用AsyncTask的execute方法。

myAsyncTask=new MyAsyncTask(); //设置传递进去的参数 
myAsyncTask.execute(URL);

模拟进度条例子
在doInBackground方法中进行进度值的赋值,把值赋给publishProgress方法。

@Override 
protected Void doInBackground(Void... params) { 
    //模拟进度更新 
    for (int i=0;i<100;i++){ 
        if (isCancelled()){ 
            break; 
        }
        publishProgress(i); 
        try {
            Thread.sleep(300); 
        }
        catch (InterruptedException e) {
            e.printStackTrace(); 
        } 
    }
    return null;
}

在onProgressUpdate方法中进行进度值的更新。

@Override 
protected void onProgressUpdate(Integer... values) { 
    super.onProgressUpdate(values); 
    if (isCancelled()){ 
        return; 
    } 
    //获取进度更新值 
    mProgressBar.setProgress(values[0]);
}

但我们结束当前activity或者fragment是就需要结束当前的AsyncTask,可以和activity是onPause方法绑定,把异步标记为cancel,再在doInBackground和onProgressUpdate方法中进行判断,如果标记为cancel就不执行操作。

@Override 
protected void onPause() { 
    super.onPause(); 
    if (mAsyncTask!=null && mAsyncTask.getStatus()==AsyncTask.Status.RUNNING){ 
    //cancel方法只是将对应的AsyncTask标记为cancel,而不是正在取消一个线程 
    mAsyncTask.cancel(true); 
    } 
}

AsyncTask注意事项
- 必须在UI线程中创建AsyncTask实例。
- 必须在UI线程中调用AsyncTask的execute()方法。
- 重写的四个方法是系统自动调用的,不应手动调用。
- 每个AsyncTask只能被执行一次,多次调用会引发异常。


onPreExecute() 显示进度条
onPostExcute()隐藏进度条 都可以访问UI线程
mytask.execute(args)中传入的参数就是doInBackground中的参数
onPreExecute--加载进度条
doInBackGround--下载网络数据(耗时操作)
onPostExecute--显示图片

//与UI线程通信----在onPreExecute()方法中
mProgressBar.setVisibility(View.VISIBLE);//显示进度条

//在onPostExectute(Bitmap bitmap)方法中,参数是doInBackground()方法返回的参数
mProgressBar.setVisibility(View.GONE);//将进度条隐藏
mImageView.setImageBitmap(bitmap);//将图片设置为解析出来的网络图片

//然后在onCreate方法中
new MyAsyncTask().execute(URL);//开启AsyncTask的异步线程操作,设置传递进去的参数

异步任务——AsyncTask
获取一个网络图片
1.doInBackground()方法

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 stream = new BufferedInputStream(is);
        //通过decodeStream解析输入流,转换成Bitmap对象
        bitmap = BitmapFactory.decodeStream(stream);
        is.close();
        stream.close();
    } catch (MalformedURLException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
    //返回bitmap
    return bitmap;
}
//2.重写onPreExecute()、onPostExcute()
//在onPreExecute()中做一些初始化操作
protected void onPreExecute() {
    super.onPreExecute();
    mProgressBar.setVisibility(View.VISIBLE);
}

//onPostExcute()方法中做一些后续操作
protected void onPostExecute(Bitmap result) {
    super.onPostExecute(result);
    mProgressBar.setVisibility(View.GONE);
    mImageView.setImageBitmap(result);
}

3.在主线程中调用new MyAsyncTask().execute(URL)开启一个异步任务


//在AsyncTask的doInBackground()方法中调用publishProgress()方法可以将我们处理任务的进度反馈处理,我们这个时候就是用AsyncTask的onProgressUpdate()方法来承接我们传出来的进度,注意,由于在AsyncTask中,只有doInBackground()方法是工作在子线程中的,所以我们可以放心地在onProgressUpdate()方法中更新UI。

//模拟进度更新
for(int i=0;i<100;i++){
    publishProgress(i);
    try{
        Thread.sleep(300);//延时0.3秒
    }catch(InterruptedException e){
        e.printStackTrace();
    }
}

onProgressUpdate(Integer...values){
    super.onProgressUpdate(calues);
    //获取进度更新值
    mProgressBar.setProgress(values[0]);
}
//异步任务——AsyncTask
//AsyncTask默认情况下会等待前一个线程执行完毕后再执行下一个线程
//要取消该机制,可以让AsyncTask和Activity的生命周期保持一致
protected void onPause(){
    super.onPause();
    if(mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING){
    //只是发送了一个取消请求,将AsyncTask标记为cancel状态,但未真正取消线程的执行
    //实际上JAVA语音没办法粗暴地直接停止一个正在运行的线程
        mTask.cancel(true);
    }
}

所以需要在doInBackground方法和onProgressUpdate方法中增加isCancelled()方法进行判断,标记为cancel的,则跳出循环,尽快结束当前线程的剩余操作,开始下一个线程

AsyncTask实现的机制:底层通过线程池来作用的,当我们一个线程没有执行完毕时,后面的线程是无法执行的;
调用cancel方法去cancel一个asynctask线程,并没有将这个线程直接停止掉,只是给这个asynctask发送了一个cancel请求,将它标识为cancel状态;
在java中是无法直接将一个线程粗暴地停止掉,我们必须等一个线程执行完毕后才能做后面的操作。(需通过状态值判断去跳出子线程的循环操作)

只有doInBackground是在非UI线程中执行
mytask!=null&&mytask.getStatus()== AsyncTask.Status.RUNNING
ansystask 即使cancel设置为true 也不能立即取消,只是将状态设为取消
故在doInBackground和onUpdatexx的时候检测isCancled()是不是true
为啥老师在listView更新的时候写的是cancel(false)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值