目录(?)[-]

  1. 5秒超时异常
  2. AsyncTask
    1. 实现AsyncTask抽象类
    2. 对AsyncTask的调用
    3. 在哪里运行
    4. 其他重要method

文章转载只能用于非商业性质,且不能带有虚拟货币、积分、注册等附加条件,转载须注明出处:http://blog.csdn.net/flowingflying/

之前,我们直接在activity中执行http通信,在通信过程中可能会出现连接超时、socket超时等情况,超时阈值一般是秒级,例如AndroidHttpClient中设置的20秒,如果出现超时,就会停在execute()语句中,等待20秒仍没有response,才给出异常。整个activity UI就会在这20秒内冻结,这样的用户体验显然不能接受,因此http应当放置在后台线程中处理。后台线程的处理可以参考Android学习笔记(三一):线程:Message和RunnableAndroid学习笔记(三二):线程:后台异步任务AsyncTask。本次我们将聚焦如何AsyncTask中实现http通信,关于AsyncTask,还会在未来中详细讨论。

5秒超时异常

之前的例子,我们都是在onCreate()中进行http的通信,如果出现连接超时,会一直等到异常出现,但是如果我们在onStart()之后,即UI可以和用户进行交互后再进行http通信,情况又会如何?

我们将之前的小例子改改,通过按Button来触发http通信,连接一个空的IP地址来测试一下。在等待连接的过程中,如果用户进行了UI操作(例如按返回键或者menu键),Android的UI处理是在且只在main thread(也成为UI线程)中进行,系统如果在5秒内不能对用户的UI操作进行响应,则会进行ANR(Application Not Responsing)处理,弹框让用户选择是否强制关闭activity。

AsyncTask

如果后台线程需要更新UI,可以考虑使用AsyncTask,它可以向主线程提供Callback,很方便进行UI处理。callback可以在后台线程运行的开始、中间和结束。

小例子有一个activity和一个AsyncTask类组成,用户按activity上button触发AsyncTask在后台线程从互联网中下载一个大图片。AsyncTask在下载开始、中间和结束后台线程均可以和主线程交互,将进度信息以及获取的图片在activity中显示。(这鱼是年前1.27和同事在外面打野食所拍,4.5斤的鲩鱼,一条鱼一顿饭。饭店没什么人,说做完今天就放假,难得清静,哈哈。)

实现AsyncTask抽象类

AsyncTask是一个抽象类,需要具体实现,这个类的参数有些特别,就是参数的类型可以自行设定。在若干方法中参数带有“…”,这表示参数的数目不固定,例如(String… arg),可以是(“hello”),也可以是(“hello”,“world”),也可以是三个参数、四个参数,分别用arg[0],arg[1],...来表示。

AsyncTask非常适合需要将中间值和最终结果返回给用户,在UI呈现的情况,起执行过程如图所示。

类的参数有三,本例为<String,Integer,Bitmap>。

  1. 第一个表示运行后台线程传递的参数,本例即是doInBackground(String … arg)的参数类型为String,用于传递web地址;
  2. 第二个参数是后台线程运行过程中,需要将某些中间值返回给主线程,使中间值可以呈现在界面中,第二个参数就是该中间值的类型,本例即是onProgressUpdate(Interger…)。
  3. 第三个参数就是后台线程运行结束后,返回给主线程的最终结果的数据类型,本例为Bitmap doInBackground(String arg)的返回值Bitmap。

/** AsyncTask是一个抽象类,需要具体实现。*/ 
public class DownloadImageTask extends AsyncTask<String, Integer,Bitmap>{  
    //【步骤0】AsyncTask适合于在UI中有反馈,因此,通过构造函数要获的相应的activity的句柄 
    private Context mContext = null;  //context就是activity,通过它可以在AsyncTask中对activity中的view进行操作
   public DownloadImageTask(Context context)
        mContext = context; 
    }   
        
    //步骤4: 后台线程执行完后,将结果带给主线程的onPostExcute(),在UI上对后台运行结果进行处理
    protected void onPostExecute(Bitmap result) { 
        Log.v("DownloadImageTask","onPostExecute() is called, run in " + Thread.currentThread().getName());
        if(result != null){ 
            ImageView image = (ImageView)((Activity)mContext).findViewById(R.id.image);
            image.setImageBitmap(result); 
        }else{ 
            TextView errorTv = (TextView)((Activity)mContext).findViewById(R.id.error_msg);
            errorTv.setText("Error: Can't get image!"); 
        }  
    }

    //步骤1: Override onPreExecute(),在中处理setup所需的工作。要注意,这是在main thread中运行的。
    protected void onPreExecute() {  
        Log.v("DownloadImageTask","onPreExecute() is called, run in " + Thread.currentThread().getName()); 
    }

//步骤3:后台线程中运行代码库通过publicProgress()触发UI线程的onProgressUpdate(),将信息反馈给UI。
    protected void onProgressUpdate(Integer... values) { 
       Log.v("DownloadImageTask","onProgressUpdate(" + values[0] +") is called, run in " + Thread.currentThread().getName());
        TextView tv = (TextView)((Activity)mContext).findViewById(R.id.text);
        tv.setText("Progress so far:" + values[0]); 
    }

//步骤2:Override  doInBackground(),在进行初始化的工作后,将创建线程,并在线程中运行doInBackground()中的代码,至于线程建立和操作的相关代码均以封装好,开发者不需要处理。如果在线程的运行中,我们需要有些信息反馈给UI,由于不能够在线程中去操作主线程的UI,通过publishProgress(),它将触发运行在主线程中onProgressUpdate()来进行UI操控。
    protected Bitmap doInBackground(String... arg0) {  
        Log.v("DownloadImageTask","doInBackground() is called, run in " + Thread.currentThread().getName());
        return downloadImage(arg0); 
    }

private Bitmap downloadImage(String... urls){        
        HttpClient httpClient = CustomHttpClient.getCustomHttpClient();
        try{ 
            HttpGet request = new HttpGet(urls[0]); 
            HttpParams params = new BasicHttpParams(); 
            HttpConnectionParams.setConnectionTimeout(params, 60000); 
            request.setParams(params); 
            publishProgress(25); 
            
            HttpResponse response = httpClient.execute(request);
            publishProgress(50);

           ...... //这里本应进行resposne code的判断,小例子目标是考察AsyncTask,一切从简,就略过这部分

byte[] image = EntityUtils.toByteArray(response.getEntity());
            publishProgress(75); 
            
            Bitmap mBitmap = BitmapFactory.decodeByteArray(image, 0, image.length);
            publishProgress(100); 
            return mBitmap;             
        }catch(Exception e){ 
            e.printStackTrace(); 
        } 
        return null; 
    } 
}

对AsyncTask的调用

小例子的Activity代码如下:

public class DownloadImageActivity extends Activity{ 
    private DownloadImageTask download = null; 
    
    @Override 
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.layout_image); 
    } 
    //触发下载图片,通过状态监测避免重复创建后台线程。此外,需要注意,AsyncTask指定被执行一次,即一个对象只能运行execute()方法一次,否则会抛出异常。在本例,我们可以再次下载图片,此时,我们需要创建一个新对象。
    public void onClickedAction(View v){ 
        if(download != null){ 
           //可以获取AsyncTask的运行情况,包括RUNNING、PENDING、FINISHED
            AsyncTask.Status  status = download.getStatus(); 
            Log.v("DownloadImageActivity","status is " + status); 
            if(status != AsyncTask.Status.FINISHED){ 
               Log.v("DownloadImageActivity","It is doing, not need to start again");
                return; 
            } 
        } 
        ImageView image = (ImageView)findViewById(R.id.image);
        image.setImageBitmap(null); 
        //创建AsyncTask的实例,并执行execute()。
        download = new DownloadImageTask(this); 
        download.execute
("http://ww1.sinaimg.cn/large/5cf79a90jw1ecy18vfwlrj20xc18gtgd.jpg");
    } 
}

在哪里运行

我们通过LogCat来查看代码具体运行在哪个线程。

其他重要method

在某种情况下,我们可能需要终止后台进行的运行,这样可以调用cancel(),例如task.cancel(true);,我们可以用task.isCanceled()来询问是否已经药企后台线程AsynTask停止,避免重复叫停。执行cancel(),将会触发AysnTask中的onCanceled(),我们可以在当中设置某些状态,例如bool isCancel=true,在后台运行的doInBackground()中,检测该状态位来判断是否继续运行。

在上面的例子,后台线程运行结束后,通过onPostExcute()的方式,向UI线程返回结果,这是常规的方式。AsynTask还提供get(),主动获取结果的方式。我们将小例子改一下:

public void onClickedAction(View v){  
    ……       
    checkDownload(); 
}

//这是测试主线程主动查询结果 
private void checkDownload(){ 
    try{ 
        Bitmap bp = download.get(); //主线程将在此主动获取后台线程运行的结果,并一直等待到有结果。因此如果后台线程此时有onProgressUpdate()处理UI,这些处理将会被block,直至download()等到结果,这些被阻塞的UI处理才会被执行,也包括最后的onPostExcute(),所以如果采用get()方式,一般不在onPostExcute()中进行UI操作。
        ImageView image = (ImageView)findViewById(R.id.image); 
        image.setImageBitmap(bp); 
    }catch(Exception e){ 
        e.printStackTrace(); 
    } 
}

为了避免get()中等待的时间过长,可以设置超时,如下:

Bitmap bp = download.get(2000,TimeUnit.MILLISECONDS);

上面例子是如果在2秒内仍得不到结果,将抛出java.uril.concurrent.TimeoutException的异常,在异常处理中,我们可能会主动cancel掉后台线程。

本博文涉及的例子代码,可以在Pro Android学习:Http service小例子中下载。

相关链接: 我的Android开发相关文章

【转】 Pro Android学习笔记(七四):HTTP服务(8):使用后台线程AsyncTask的更多相关文章

  1. 【转】 Pro Android学习笔记(四十):Fragment(5):适应不同屏幕或排版

    目录(?)[-] 设置横排和竖排的不同排版风格 改写代码 对于fragment,经常涉及不同屏幕尺寸和不同的排版风格.我们在基础小例子上做一下改动,在横排的时候,仍是现实左右两个fragment,在竖 ...

  2. 【转】 Pro Android学习笔记(四二):Fragment(7):切换效果

    目录(?)[-] 利用setTransition 利用setCustomAnimations 通过ObjectAnimator自定义动态效果 程序代码的编写 利用fragment transactio ...

  3. 【转】 Pro Android学习笔记(四七):Dialog(4):一些补充和思考

    目录(?)[-] 编程思想封装接口 fragment和activity以其他fragment之间的通信 编程思想:封装接口 在小例子中,fragment会调用activity的onDialogDone ...

  4. 【转】 Pro Android学习笔记(四八):ActionBar(1):Home图标区

    目录(?)[-] Home Icon 源代码 TextView的滚动 返回主activity或指定activity     ActionBar在Android 3.0 SDK中为平板引入,在4.0中也 ...

  5. 【转】 Pro Android学习笔记(四三):Fragment(8):再谈Transaction和管理器

    目录(?)[-] Transaction的一些操作 再谈FragmentManager 调用其他fragment的方法 唤起activity 唤起fragment和相互通信 一些其它 Transact ...

  6. 【转】Pro Android学习笔记(四):了解Android资源(下)

    处理任意的XML文件 自定义的xml文件放置在res/xml/下,可以通过R.xml.file_name来获取一个XMLResourceParser对象.下面是xml文件的例子: <rootna ...

  7. Pro Android学习笔记 ActionBar(1):Home图标区

     Pro Android学习笔记(四八):ActionBar(1):Home图标区 2013年03月10日 ⁄ 综合 ⁄ 共 3256字 ⁄ 字号 小 中 大 ⁄ 评论关闭 ActionBar在A ...

  8. 【转】 Pro Android学习笔记(五六):配置变化

    目录(?)[-] Activity的destorycreate过程 Fragment的destorycreate过程 onSaveInstanceState saveFragmentInstanceS ...

  9. 【转】 Pro Android学习笔记(二九):用户界面和控制(17):include和merge

    目录(?)[-] xml控件代码重用include xml控件代码重用merge 横屏和竖屏landsacpe portrait xml控件代码重用:include 如果我们定义一个控件,需要在不同的 ...

  10. 【转】 Pro Android学习笔记(五七):Preferences(1):ListPreference

    目录(?)[-] 例子1ListPreference小例子 定义一个preferences XML文件 继承PreferenceActivity 用户定制偏好的读取 第一次运行时设置缺省值 设置Cat ...

随机推荐

  1. react-native 使用 antd-mobile-rn UI进行开发app

    1.创建 react-native 项目 react-native init app03 2.安装组件 npm install antd-mobile-rn --save 3.配置按需加载 npm i ...

  2. Android系统移植与调试之------->如何修改Android自带的apk出现一圈圈类似鸡蛋的花纹

    最近被一个问题烦恼到了,就是android4.1系统自带的Email.文件管理器.信息等apk都出现同一个问题,就是现实在平板上的时候会出现一圈圈类似鸡蛋的花纹. 我想了两种方法来解决,第一种方法没有 ...

  3. 第7条:用列表推导式来取代map和filter

    核心知识点: 1.列表推导式要比内置的map和filter函数清晰,因为它无需额外编写lambda表达式. 2.列表推导式可以跳过输入列表中的某些元素,如果改用map来做,那就必须辅以filter方能 ...

  4. putty screen

    http://www.cnblogs.com/xupeizhi/archive/2013/05/20/3088779.html screen 会创建一个跑着shell的单一窗口 Ctrl+a d退出刚 ...

  5. 每天一个Linux命令(15)tail命令

    tail命令用于输入文件中的尾部内容.tail命令默认在屏幕上显示指定文件的末尾10行. 如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题.     (1)用法: 用法:   tail ...

  6. 微信小程序消息模板

    wxml: <form bindsubmit='sendSms' report-submit='true' id='fo'> <button form-type='submit'&g ...

  7. 第二十二篇、IO多路复用 一

    一.简介io多路复用 可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,就会感知到 Linux中的 select,poll,epoll(内核2.6以上) 都是IO多路复用的 ...

  8. 广义表(C++实现)

    广义表是非线性结构,其定义是递归的. 以下给出几种简单的广义表模型: 由上图我们可以看到,广义表的节点类型无非head.value.sub三种,这里设置枚举类型,利用枚举变量来记录每个节点的类型: e ...

  9. Idea 包名按树形结构展示

    Idea默认包名展示如图: 感觉这样展示,在包下面建包的时候不方便,可以在 设置按钮 里面去掉 Flatten Packages 和 Compact Empty Middle Packages,设置如 ...

  10. 算法(Algorithms)第4版 练习 2.2.11(2)

    关键代码: private static void sort(Comparable[] input, int lo, int hi) { if(lo >= hi)//just one entry ...