AsnyncTask与handler(一)——AsyncTask异步处理
来源:互联网 发布:网络盒子哪个好 编辑:程序博客网 时间:2024/05/29 13:40
前言:以前就遇到过AsnyncTask,但由于当时对其理解比较懵懂,所以就先放了一下,没写博客,时隔几月,当再次遇到此问题时,感觉难度更大,却有必要仔细研究一下,这里分两篇写,这篇先写AsncTask的异步处理的实现,第二篇对AsyncTask进行补充,讲述handler消息机制。
一、android开发中存在的问题
当一个程序第一次启动时,Android会同时启动一个对应的主线程(Main Thread),主线程主要负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,并把相关的事件分发到对应的组件进行处理。所以主线程通常又被叫做UI线程。
在开发Android应用时必须遵守单线程模型的原则: Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。也就是说所有有关更新界面的操作必须在主线程中操作,除主线程外的其它自建线程都没有这个权力处理这些UI更新操作,否则将报错!在后面的讲解中会看到即便是最常用的Toast.makeText()也是不允许在非主线程中使用的!
比如说从网上获取一个网页,在一个TextView中将其源代码显示出来,这种涉及到网络操作的程序一般都是需要开一个线程完成网络访问,但是在获得页面源码后,是不能直接在网络操作线程中调用TextView.setText()的.因为其他线程中是不能直接访问主UI线程成员
在单线程模型中始终要记住两条法则:
1. 不要阻塞UI线程
2. 确保只在UI线程中访问Android UI工具包 ,即更新UI
二、AsyncTask
但我们在处理程序时,常常是先做数据处理,然后再数据准备好后更新UI,如果按照上面的单线程模式,就出现了一个问题,更新UI必须写在OnCreate函数中,但我们数据处理操作一般是要自建线程的,如果全部写在OnCreate函数中,那么代码就难以规范。所以为了解决数据处理后更新UI的问题,就产生了AsyncTask!
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由应用程序调用,开发者需要做的就是实现这些方法。
<一>子类化AsyncTask
AsyncTask是抽象类 ,我们必须自己写一个类来继承AsyncTask 类
<二>实现AsyncTask中定义及须重写的方法
重写AsyncTask后可以或必须重写的函数如下:
- onPreExecute() 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。
- doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。
- publishProgress 该方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
- onProgressUpdate(Progress...), 在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,可以通过一个进度条进行展示。
- onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
自己写一个类继承自AsyncTask后的形式如下所示:
- class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>
- {
- ……………………
- }
可以看到有三个参数:
AsyncTask的三个泛型参数说明(三个参数可以是任何类型)
第一个参数:传入doInBackground()方法的参数类型,这里是String
第二个参数:传入onProgressUpdate()方法的参数类型,这里是Integer
第三个参数:传入onPostExecute()方法的参数类型,也是doInBackground()方法返回的类型。这里是Bitmap
<三>AsyncTask遵守准则
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:
<1>Task的实例必须在UI thread中创建
<2>execute方法必须在UI thread中调用
<3>不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法
<4> 该task只能被执行一次,否则多次调用时将会出现异常
<四>实例
效果:
初始状态 加载中 完成
效果讲解:总体上来讲是从网上加载一张图片并贴到当前XML的指定位置
1、添加网络访问权限
在AndroidManifest.xml文件中,添加下面一行代码,获取互联网访问权限
- <uses-permission android:name="android.permission.INTERNET"/>
2、XML布局
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical"
- tools:context=".MainActivity" >
- <Button
- android:id="@+id/show"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="下一个" />
- <ProgressBar
- android:id="@+id/processBar"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:visibility="gone"/>
- <HorizontalScrollView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:scrollbars="none" >
- <ImageView
- android:id="@+id/image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </HorizontalScrollView>
- </LinearLayout>
关于布局,难度不大,也不是本篇重点,不再多讲,这里仅说下作用。
progressBar初始化为不显示,仅当用户点击“下一个”按钮时,显示,图片加载完成后取消显示。
imageView用来显示加载后的图片。
3、JAVA代码
先贴出完整代码,然后再逐步讲解
- package com.example.try_asynctask;
- import java.io.InputStream;
- import java.net.URL;
- import java.net.URLConnection;
- import java.util.ArrayList;
- import java.util.List;
- import android.os.AsyncTask;
- import android.os.Bundle;
- import android.app.Activity;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.util.Log;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.ImageView;
- import android.widget.ProgressBar;
- import android.widget.Toast;
- public class MainActivity extends Activity {
- private ImageView image = null;
- private Button show;
- private ProgressBar progressBar = null;
- private int number = 0;
- List<String> imageUrl = null;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- progressBar = (ProgressBar) findViewById(R.id.processBar);
- image = (ImageView) findViewById(R.id.image);
- show = (Button) findViewById(R.id.show);
- show.setOnClickListener(new showButtonListener());
- imageUrl = new ArrayList<String>(); // 图片地址List
- imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");
- imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");
- imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");
- imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");
- imageUrl.add("http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg");
- }
- public class showButtonListener implements OnClickListener
- {
- @Override
- public void onClick(View v)
- {
- number++;
- MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());
- myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));
- }
- }
- class MyAsyncTask extends AsyncTask<String, Integer, Bitmap>
- {
- // 可变长的输入参数,与AsyncTask.exucute()对应
- public MyAsyncTask(Context context)
- {
- progressBar.setVisibility(View.VISIBLE);
- image.setVisibility(View.GONE);
- }
- @Override
- protected Bitmap doInBackground(String... params)
- {
- Bitmap bitmap = null;
- try
- {
- //根据URL取得图片并返回
- URL url = new URL(params[0]);
- URLConnection conn = url.openConnection();
- conn.connect();
- InputStream inputStream = conn.getInputStream();
- bitmap = BitmapFactory.decodeStream(inputStream);
- Toast.makeText(getApplicationContext(), "传回图片了", Toast.LENGTH_SHORT).show();
- inputStream.close();
- }
- catch (Exception e)
- {
- Log.e("msg", e.getMessage());
- }
- return bitmap;
- }
- /**
- * 在doInBackground 执行完成后,onPostExecute方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.
- */
- @Override
- protected void onPostExecute(Bitmap bitmap)
- {
- progressBar.setVisibility(View.GONE);
- image.setVisibility(View.VISIBLE);
- if (bitmap != null)
- {
- image.setImageBitmap(bitmap);
- }
- else
- {
- Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();
- }
- }
- /**
- * 该方法将在执行实际的后台操作前被UI thread调用。这个方法只是做一些准备工作,如在界面上显示一个进度条。
- */
- @Override
- protected void onPreExecute()
- {
- // 任务启动
- Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();
- }
- }
- }
1、先看OnCreate函数中操作
- imageUrl = new ArrayList<String>(); // 图片地址List
- imageUrl.add("http://image.tianjimedia.com/uploadImages/2011/266/AIO90AV2508S.jpg");
- imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/063N2L5N2HID.jpg");
- imageUrl.add("http://comic.sinaimg.cn/2011/0824/U5237P1157DT20110824161051.jpg");
- imageUrl.add("http://image.tianjimedia.com/uploadImages/2012/090/1429QO6389U8.jpg");
- imageUrl.add(<a target="_blank" href="http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg">http://new.aliyiyao.com/UpFiles/Image/2011/01/13/nc_129393721364387442.jpg</a>);
定义了一个StringArrayList,用来保存要访问的网址集合,点击“下一个”时,逐个循环显示
- progressBar = (ProgressBar) findViewById(R.id.processBar);
- image = (ImageView) findViewById(R.id.image);
- show = (Button) findViewById(R.id.show);
- show.setOnClickListener(new showButtonListener());
然后是初始化控件变量,并为“下一个”按钮设置监听函数showButtonListener。
2、“下一个”按钮监听函数showButtonListener
- public class showButtonListener implements OnClickListener
- {
- @Override
- public void onClick(View v)
- {
- number++;
- MyAsyncTask myAsyncTask = new MyAsyncTask(getApplicationContext());
- myAsyncTask.execute(imageUrl.get(number % imageUrl.size()));
- }
- }
核心在于最后一句话,imageUrl.get(number % imageUrl.size())循环得到传进去的网址,然后传给myAsyncTask.execute(),所以这里往MyAsyncTask传的参数是String类型,所以在MyAsyncTask派生自AsyncTask的时候,第一个参数也应当是String类型!这就是上面所讲的三个参数中的第一个的意义。
下面就开始今天的核心问题MyAsyncTask的讲解:
3-1 MyAsyncTask构造函数(可省略)
- public MyAsyncTask(Context context)
- {
- progressBar.setVisibility(View.VISIBLE);
- image.setVisibility(View.GONE);
- }
这里是对XML作初始化设置,其实完全可以在onPreExecute()函数中操作是一样的。
3-2 onPreExecute()处理前操作(可省略)
- @Override
- protected void onPreExecute()
- {
- // 任务启动
- Toast.makeText(getApplicationContext(), "任务开始......", Toast.LENGTH_SHORT).show();
- }
代码没什么好讲的,这里只是想说,这个函数对于类派生不是必须要重写的,如果需要在数据处理前要在UI上加以提示等等的预处理操作,都可以放在这里操作。
3-3 doInBackground()后台数据处理操作(必写)
这里必须注意的一点是,在这个函数里只能做数据处理操作,不能涉及任何更新UI的操作,即便使用Toast.makeText()也会报错!
代码:
- @Override
- protected Bitmap doInBackground(String... params)
- {
- Bitmap bitmap = null;
- try
- {
- //根据URL取得图片并返回
- URL url = new URL(params[0]);
- URLConnection conn = url.openConnection();
- conn.connect();
- InputStream inputStream = conn.getInputStream();
- bitmap = BitmapFactory.decodeStream(inputStream);
- //注意!!!!这里写了个Toast.makeText!!!
- Toast.makeText(getApplicationContext(), "传回图片了", Toast.LENGTH_SHORT).show();
- inputStream.close();
- }
- catch (Exception e)
- {
- Log.e("msg", e.getMessage());
- }
- return bitmap;
- }
这行的核心意思就是根据得到的URL获取BitMap对象,然后返回!几点注意:
1、获取传递过来值的方法:
- URL url = new URL(params[0]);
2、程序报错
在实际运行时,根本无法看到“传回图片了”消息提示,在看LogCat,已经报错了,错误信息如下:
这里不影响程序运行是因为外面加着try-catch呢,如果去掉程序就会直接崩了。所以,在doInBackground中不准做任何的UI更新操作,即便是Toast也不行!!!!
3-4 onPostExecute()对返回结果处理,更新UI(必写)
- @Override
- protected void onPostExecute(Bitmap bitmap)
- {
- progressBar.setVisibility(View.GONE);
- image.setVisibility(View.VISIBLE);
- if (bitmap != null)
- {
- image.setImageBitmap(bitmap);
- }
- else
- {
- Toast.makeText(getApplicationContext(), "网络异常", Toast.LENGTH_SHORT).show();
- }
- }
这个函数中处理doInBackground的返回结果,更新UI
至此本篇关于AsyncTask异步处理的操作就全部讲完了,这里存在一个问题,如果我们想在doInBackground处理过程中更新UI怎么办?下篇讲述的handler消息机制,就可以解决这个问题。
参考文章:
《Android进阶2之AsyncTask实现异步处理任务》:http://www.cnblogs.com/snake-hand/archive/2012/03/30/2454368.html
《AsyncTask<>的参数介绍》:http://my.eoe.cn/xuliangbo/archive/6063.html
源码地址:http://download.csdn.net/detail/harvic880925/7275089 不要分,仅供分享
- AsnyncTask与handler(一)——AsyncTask异步处理
- AsnyncTask与handler(一)——AsyncTask异步处理
- AsnyncTask与handler(一)——AsyncTask异步处理
- AsnyncTask与handler(二)——handler消息机制
- AsnyncTask与handler(二)——handler消息机制
- AsnyncTask与handler(二)——handler消息机制
- Android多线程异步操作总结——Handler与AsyncTask
- AsnyncTask——AsnyncTask内部机制
- 异步消息处理机制-Handler、AsyncTask
- AsyncTask和Handler处理异步消息
- android异步加载之Handler、AsyncTask(一)
- 【Android归纳】Asynctask与Handler异步综述
- 异步处理——AsyncTask(二)
- handler和异步AsyncTask
- Handler与异步消息处理
- Handler与异步消息处理
- android os;异步消息处理机制:AsyncTask和Handler
- Android 线程Handler与异步加载AsyncTask的比较
- 关于微博的正文的整个思维逻辑
- hadoop学习笔记六 --- mapreduce原理笔记
- PullToRefresh使用详解(五)--下拉刷新的ScrollView
- socket服务器如何读取http协议的一行
- Mvc 3天 ajax下拉框更改数据(明天做省级联动吧)
- AsnyncTask与handler(一)——AsyncTask异步处理
- wamp真是问题多!!!
- Extjs form.submit()提交与Ext.Ajax.request的区别
- AsnyncTask与handler(二)——handler消息机制
- Python安装环境搭建(python+pycharm+pid)
- android 之杂七杂八
- Android提示版本更新
- BaseAdapter——convertView回收机制与动态控件响应
- android之Dialog相关