几篇关于Thread与AsyncTask的解析文章

来源:互联网 发布:药店软件哪个好 编辑:程序博客网 时间:2024/05/19 23:26

转载自:http://blog.csdn.net/easonx1990/article/details/7997521

AsyncTask实现的原理,和适用的优缺点

AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以通过接口实现UI进度更新),最后反馈执行的结果给UI主线程.

使用的优点:
 简单,快捷
 过程可控

使用的缺点:
 在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.


2 Handler异步实现的原理和适用的优缺点
在Handler 异步实现时,涉及到 Handler, Looper, Message,Thread四个对象,内存不足实现异步的流程是主线程启动Thread(子线程)àthread(子线程)运行并生成Message-àLooper获取Message并传递给HandleràHandler逐个获取Looper中的Message,并进行UI变更。

使用的优点:
 结构清晰,功能定义明确
 对于多个后台任务时,简单,清晰

---------------------------------------------------------------------------------------------




AsyncTask与Handler的相同点跟区别
问题1:有人说异步任务比Handler轻量级,对吗?
答:
通过看源码,发现AsyncTask实际上就是一个线程池,而网上的说法是AsyncTask比handler要轻量级,显然上不准确的,只能这样说,AsyncTask在代码上比handler要轻量级别,而实际上要比handler更耗资源,因为AsyncTask底层是一个线程池!而Handler仅仅就是发送了一个消息队列,连线程都没有开。
但是,如果异步任务的数据特别庞大,AsyncTask这种线程池结构的优势就体现出来了。
 
AsyscTask定义了三种泛型类型params,progress和result.
1,  params启动任务执行的输入参数,比如http请求的URL
2,  progress后台任务执行的百分比
3,  result后台执行任务最终返回的结果,比如String,比如我需要得到的list
 
AsyncTask方法:
必选方法:
1,  doinbackground(params…) 后台执行,比较耗时的操作都可以放在这里。
注意这里不能直接操作UI。此方法在后台线程执行,完成任务的主要工作
,通常需要较长的时间。在执行过程中可以调用
Public progress(progress…)来更新任务的进度。
 
2,  onpostexecute(result)相当于handler处理UI的方式,在这里可以使用在
doinbackground得到的结果处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。
 
可选方法:
1,  onprogressupdate(progress…) 可以使用进度条增加用户体验度。此方法在主线程执行,用户显示任务执行的进度。
2,  onpreExecute()  这里是最新用户调用excute时的接口,当任务执行之前开始调用此方法,可以在这里显示进度对话框。
3,  onCancelled()  用户调用取消时,要做的操作
 
 
 
AsyncTask三个状态:pending,running,finished
 
使用AsyncTask类,遵守的准则:
1,  Task的实例必须在UI thread中创建;
2,  Execute方法必须在UI thread中调用
3,  不要手动的调用onPfreexecute(),onPostExecute(result)
Doinbackground(params…),onProgressupdate(progress…)这几个方法;
4,  该task只能被执行一次,否则多次调用时将会出现异常;
 
AsyncTask的整个调用过程都是从execute方法开始的,一旦在主线程中调用execute方法,就可以通过onpreExecute方法,这是一个预处理方法,比如可以在这里开始一个进度框,同样也可以通过onprogressupdate方法给用户一个进度条的显示,增加用户体验;最后通过onpostexecute方法,相当于handler处理UI的方式,在这里可以使用在
doinbackground得到的结果处理操作UI。此方法在主线程执行,任务执行的结果作为此方法的参数返回。



关于AsyncTask和Handler+Thread的一点破事


其实都是和线程操作有关的东西
thread的run()残缺返回值于是+handler组合就可以做出时候用户体验的界面+线程运作了
 
而asyn直接实现就可以了
 
class InitTask extends AsyncTask<Integer, Integer, String>{  
   //后面尖括号内分别是参数(例子里是线程休息时间),进度(publishProgress用到),返回值 类型  
     
   @Override  
   protected void onPreExecute() {  
       //第一个执行方法  
   pdialog.show();
       super.onPreExecute();  
   }  
     
   @Override  
   protected String doInBackground(Integer... params) {  
       //第二个执行方法,onPreExecute()执行完后执行  
       initoken();
       return "初始化完毕,请继续操作!";  
   }  
 
   @Override  
   protected void onProgressUpdate(Integer... progress) {  
       //这个函数在doInBackground调用publishProgress时触发,虽然调用时只有一个参数  
       //但是这里取到的是一个数组,所以要用progesss[0]来取值  
       //第n个参数就用progress[n]来取值  
   
// if (progress[0] == 100)setTitle(R.string.app_name);
 
 
       super.onProgressUpdate(progress);  
   }  
 
   @Override  
   protected void onPostExecute(String result) {  
       //doInBackground返回时触发,换句话说,就是doInBackground执行完后触发  
       //这里的result就是上面doInBackground执行后的返回值,所以这里是"执行完毕"  
    pdialog.dismiss();
    setTitle(result); 
        super.onPostExecute(result);  
   }  
     
}  
 
这个的功能就是在线程运行时候显示progressdialog,线程结束dialog消失
效果和http://hi.baidu.com/snk4/blog/item/b08ac13f2b92f7f355e7232c.html一样
 
不过需要注意的是,asyn结构比较"整齐"
thread+handler比较灵活.










AsyncTask对比Thread加Handler

    很多网友可能发现Android平台很多应用使用的都是AsyncTask,而并非Thread和Handler去更新UI,这里Android123给大家说下他们到底有什么区别,我们平时应该使用哪种解决方案。从Android 1.5开始系统将AsyncTask引入到android.os包中,过去在很早1.1和1.0 SDK时其实官方将其命名为UserTask,其内部是JDK 1.5开始新增的concurrent库,做过J2EE的网友可能明白并发库效率和强大性,比Java原始的Thread更灵活和强大,但对于轻量级的使用更为占用系统资源。Thread是Java早期为实现多线程而设计的,比较简单不支持concurrent中很多特性在同步和线程池类中需要自己去实现很多的东西,对于分布式应用来说更需要自己写调度代码,而为了Android UI的刷新Google引入了Handler和Looper机制,它们均基于消息实现,有事可能消息队列阻塞或其他原因无法准确的使用。


    推荐大家使用AsyncTask代替Thread+Handler的方式,不仅调用上更为简单,经过实测更可靠一些,Google在Browser中大量使用了异步任务作为处理耗时的I/O操作,比如下载文件、读写数据库等等,它们在本质上都离不开消息,但是AsyncTask相比Thread加Handler更为可靠,更易于维护,但AsyncTask缺点也是有的比如一旦线程开启即dobackground方法执行后无法给线程发送消息,仅能通过预先设置好的标记来控制逻辑,当然可以通过线程的挂起等待标志位的改变来通讯,对于某些应用Thread和Handler以及Looper可能更灵活。

转载自:http://www.zhihu.com/question/30804052/answer/49562693

Android 原生的 AsyncTask.java 是对线程池的一个封装,使用其自定义的 Executor 来调度线程的执行方式(并发还是串行),并使用 Handler 来完成子线程和主线程数据的共享。


预先了解 AsyncTask,必先对线程池有所了解。


一般情况下,如果使用子线程去执行一些任务,那么使用 new Thread 的方式会很方便的创建一个线程,如果涉及到主线程和子线程的通信,我们将使用 Handler(一般需要刷新 UI 的适合用到)。

如果我们创建大量的(特别是在短时间内,持续的创建生命周期较长的线程)野生线程,往往会出现如下两方面的问题:

  1. 每个线程的创建与销毁(特别是创建)的资源开销是非常大的;
  2. 大量的子线程会分享主线程的系统资源,从而会使主线程因资源受限而导致应用性能降低。

各位开发一线的前辈们为了解决这个问题,引入了线程池(ThreadPool)的概念,也就是把这些野生的线程圈养起来,统一的管理他们。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。


那么线程池是如何使用的呢?

我们可以通过ThreadPoolExecutor来创建一个线程池。

new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);


创建一个线程池需要输入几个参数:

  • corePoolSize(线程池的基本大小):当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。如果调用了线程池的 prestartAllCoreThreads 方法,线程池会提前创建并启动所有基本线程。
  • runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。
    • ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
    • LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按 FIFO (先进先出) 排序元素,吞吐量通常要高于 ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool() 使用了这个队列。
    • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法 Executors.newCachedThreadPool 使用了这个队列。
    • PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
  • maximumPoolSize(线程池最大大小):线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
  • ThreadFactory:用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
  • RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。这个策略默认情况下是 AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
    • AbortPolicy:直接抛出异常。
    • CallerRunsPolicy:只用调用者所在线程来运行任务。
    • DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
    • DiscardPolicy:不处理,丢弃掉。
    • 当然也可以根据应用场景需要来实现 RejectedExecutionHandler 接口自定义策略。如记录日志或持久化不能处理的任务。
  • keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
  • TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。
如何向线程池提交线程任务呢?


  1. 我们可以使用线程池的 execute 提交的任务,但是 execute 方法没有返回值,所以无法判断任务是否被线程池执行成功:
threadsPool.execute(new Runnable() {            @Override            public void run() {                // TODO Auto-generated method stub            }        });

2. 我们也可以使用 submit 方法来提交任务,它会返回一个 future,那么我们可以通过这个 future 来判断任务是否执行成功,通过 future 的 get 方法来获取返回值,get 方法会阻塞住直到任务完成,而使用 get(long timeout, TimeUnit unit) 方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完:

Future<Object> future = executor.submit(harReturnValuetask);try {     Object s = future.get();} catch (InterruptedException e) {    // 处理中断异常} catch (ExecutionException e) {    // 处理无法执行任务异常} finally {    // 关闭线程池    executor.shutdown();}

线程池是如何关闭的呢?


ThreadPoolExecutor 提供了两个方法,用于线程池的关闭,分别是 shutdown()shutdownNow(),其中:

  • shutdown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务;
  • shutdownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务。

线程池的原理?


线程池中比较重要的规则:

  • corePoolSize 与 maximumPoolSize

由于 ThreadPoolExecutor 将根据 corePoolSize 和 maximumPoolSize 设置的边界自动调整池大小,当新任务在方法 execute(java.lang.Runnable) 中提交时:


    1. 如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的;
    2. 如果设置的 corePoolSize 和 maximumPoolSize 相同,则创建的线程池是大小固定的,如果运行的线程数与 corePoolSize 相同,当有新请求过来时,若 workQueue 未满,则将请求放入 workQueue 中,等待有空闲的线程去从 workQueue 中取任务并处理
    3. 如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程去处理请求;
    4. 如果运行的线程多于 corePoolSize 并且等于 maximumPoolSize,若队列已经满了,则通过RejectedExecutionHandler 所指定的策略来处理新请求;
    5. 如果将 maximumPoolSize 设置为基本的无界值(如 Integer.MAX_VALUE),则允许池适应任意数量的并发任务

也就是说,处理任务的优先级为:


    • corePoolSize > workQueue > maximumPoolSize,如果三者都满了,使用RejectedExecutionHandler 处理被拒绝的任务。
    • 当池中的线程数大于 corePoolSize 的时候,多余的线程会等待 keepAliveTime 长的时间,如果无请求可处理就自行销毁。




  • workQueue线程池所使用的缓冲队列,该缓冲队列的长度决定了能够缓冲的最大数量,缓冲队列有三种通用策略:

    1. 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性;
    2. 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过corePoolSize(因此,maximumPoolSize 的值也就无效了)。当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性;
    3. 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。

  • ThreadFactory
使用 ThreadFactory 创建新线程。如果没有另外说明,则在同一个 ThreadGroup 中一律使用Executors.defaultThreadFactory() 创建线程,并且这些线程具有相同的 NORM_PRIORITY 优先级和非守护进程状态。通过提供不同的 ThreadFactory,可以改变线程的名称、线程组、优先级、守护进程状态等等。如果执行 newThread 时 ThreadFactory 未能创建线程(返回 null),则执行程序将继续运行,但不能执行任何任务。

接下来我们看一下 ThreadPoolExecutor 中最重要的 execute 方法:



public void execute(Runnable command) {    if (command == null)       throw new NullPointerException();    //如果线程数小于基本线程数,则创建线程并执行当前任务     if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) {    //如线程数大于等于基本线程数或线程创建失败,则将当前任务放到工作队列中。        if (runState == RUNNING && workQueue.offer(command)) {            if (runState != RUNNING || poolSize == 0)                      ensureQueuedTaskHandled(command);        }    //如果线程池不处于运行中或任务无法放入队列,并且当前线程数量小于最大允许的线程数量,则创建一个线程执行任务        else if (!addIfUnderMaximumPoolSize(command))        //抛出RejectedExecutionException异常            reject(command); // is shutdown or saturated    }}


线程池容量的动态调整?


ThreadPoolExecutor 提供了动态调整线程池容量大小的方法:setCorePoolSize() setMaximumPoolSize():

  • setCorePoolSize:设置核心池大小
  • setMaximumPoolSize:设置线程池最大能创建的线程数目大小

当上述参数从小变大时,ThreadPoolExecutor 进行线程赋值,还可能立即创建新的线程来执行任务。



线程池的监控?

通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用

  • taskCount:线程池需要执行的任务数量。
  • completedTaskCount:线程池在运行过程中已完成的任务数量。小于或等于 taskCount
  • largestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
  • getPoolSize: 线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以这个大小只增不减。
  • getActiveCount:获取活动的线程数。

通过扩展线程池进行监控。通过继承线程池并重写线程池的 beforeExecuteafterExecute terminated 方法,我们可以在任务执行前,执行后和线程池关闭前干一些事情。如监控任务的平均执行时间,最大执行时间和最小执行时间等。



使用线程池的风险?

虽然线程池是构建多线程应用程序的强大机制,但使用它并不是没有风险的。用线程池构建的应用程序容易遭受任何其它多线程应用程序容易遭受的所有并发风险,诸如同步错误和死锁,它还容易遭受特定于线程池的少数其它风险,诸如与池有关的死锁、资源不足和线程泄漏。


  • 死锁

任何多线程应用程序都有死锁风险。当一组进程或线程中的每一个都在等待一个只有该组中另一个进程才能引起的事件时,我们就说这组进程或线程 死锁了。死锁的最简单情形是:线程 A 持有对象 X 的独占锁,并且在等待对象 Y 的锁,而线程 B 持有对象 Y 的独占锁,却在等待对象 X 的锁。除非有某种方法来打破对锁的等待(Java 锁定不支持这种方法),否则死锁的线程将永远等下去。


虽然任何多线程程序中都有死锁的风险,但线程池却引入了另一种死锁可能,在那种情况下,所有池线程都在执行已阻塞的等待队列中另一任务的执行结果的任务,但这一任务却因为没有未被占用的线程而不能运行。当线程池被用来实现涉及许多交互对象的模拟,被模拟的对象可以相互发送查询,这些查询接下来作为排队的任务执行,查询对象又同步等待着响应时,会发生这种情况。


  • 资源不足

线程池的一个优点在于:相对于其它替代调度机制(有些我们已经讨论过)而言,它们通常执行得很好。但只有恰当地调整了线程池大小时才是这样的。线程消耗包括内存和其它系统资源在内的大量资源。除了 Thread 对象所需的内存之外,每个线程都需要两个可能很大的执行调用堆栈。除此以外,JVM 可能会为每个 Java 线程创建一个本机线程,这些本机线程将消耗额外的系统资源。最后,虽然线程之间切换的调度开销很小,但如果有很多线程,环境切换也可能严重地影响程序的性能。


如果线程池太大,那么被那些线程消耗的资源可能严重地影响系统性能。在线程之间进行切换将会浪费时间,而且使用超出比您实际需要的线程可能会引起资源匮乏问题,因为池线程正在消耗一些资源,而这些资源可能会被其它任务更有效地利用。除了线程自身所使用的资源以外,服务请求时所做的工作可能需要其它资源,例如 JDBC 连接、套接字或文件。这些也都是有限资源,有太多的并发请求也可能引起失效,例如不能分配 JDBC 连接。


  • 并发错误

线程池和其它排队机制依靠使用 wait() 和 notify() 方法,这两个方法都难于使用。如果编码不正确,那么可能丢失通知,导致线程保持空闲状态,尽管队列中有工作要处理。使用这些方法时,必须格外小心;即便是专家也可能在它们上面出错。而最好使用现有的、已经知道能工作的实现,例如 util.concurrent 包。



  • 线程泄漏

各种类型的线程池中一个严重的风险是线程泄漏,当从池中除去一个线程以执行一项任务,而在任务完成后该线程却没有返回池时,会发生这种情况。发生线程泄漏的一种情形出现在任务抛出一个 RuntimeException 或一个 Error 时。如果池类没有捕捉到它们,那么线程只会退出而线程池的大小将会永久减少一个。当这种情况发生的次数足够多时,线程池最终就为空,而且系统将停止,因为没有可用的线程来处理任务。


有些任务可能会永远等待某些资源或来自用户的输入,而这些资源又不能保证变得可用,用户可能也已经回家了,诸如此类的任务会永久停止,而这些停止的任务也会引起和线程泄漏同样的问题。如果某个线程被这样一个任务永久地消耗着,那么它实际上就被从池除去了。对于这样的任务,应该要么只给予它们自己的线程,要么只让它们等待有限的时间。


  • 请求过载

仅仅是请求就压垮了服务器,这种情况是可能的。在这种情形下,我们可能不想将每个到来的请求都排队到我们的工作队列,因为排在队列中等待执行的任务可能会消耗太多的系统资源并引起资源缺乏。在这种情形下决定如何做取决于您自己;在某些情况下,您可以简单地抛弃请求,依靠更高级别的协议稍后重试请求,您也可以用一个指出服务器暂时很忙的响应来拒绝请求。


参考资料:


Java 7之多线程线程池


聊聊并发(三)——JAVA线程池的分析和使用


Java 理论与实践: 线程池与工作队列



以上。


转载自:http://blog.csdn.net/meng425841867/article/details/8497552


一、①AsyncTask的使用
    当一个异步任务被执行,任务经过四各步骤:

1onPreExecute(),在UI线程上调用任务后立即执行。这步通常被用于设置任务,例如在用户界面显示一个进度条。

2doInBackground(Params...),后台线程执行onPreExecute()完后立即调用,这步被用于执行较长时间的后台计算。异步任务的参数也被传到这步。计算的结果必须在这步返回,将传回到上一步。在执行过程中可以调用publishProgress(Progress...)来更新任务的进度。

3onProgressUpdate(Progress...),一次呼叫 publishProgress(Progress...)后调用 UI线程。执行时间是不确定的。这个方法用于当后台计算还在进行时在用户界面显示进度。例如:这个方法可以被用于一个进度条动画或在文本域显示记录。

4onPostExecute(Result)当后台计算结束时,调用 UI线程。后台计算结果作为一个参数传递到这步。 

②使用解析

在使用AsyncTask时,doInBackground是必须有的,该方法可以理解为线程中的Run,但是该方法在UI线程调用AsyncTask对象的execute(Params...)方法时,才启用该方法,该方法是后台线程方法,期中不能包含Ui线程的操作并且 AsyncTask对象的execute(Params...)方法在Ui线程中只能调用一次,该方法重写的时候必须有返回值,这个返回值传递给方法onPostExecute。

如果想在线程执行前执行一些操作(在Ui显示进度条),可以重写onPreExecute方法,这个方法在doInBackground方法执行之前使用,主要是用来设置任务或者创建UI控件,该方法不需要可以省略。

如果想在线程中操作Ui,可以重写onProgressUpdata,该方法在doInBackground执行时使用PublishProgress方法后自动调用,该方法操作UI,实现在后台操作UI,该方法多次调用,实现数据/进度更新的目的。

在线程执行完后,往往都需要一些Ui操作来获得线程执行的最后结果,onPostExecute方法获得线程的执行结果,并处理此结果后操作UI。

如果线程在执行的过程,用户想取消这个线程,可以使用cancel方法取消这个线程,在线程取消的时候线程调用onCancelled 方法,在这里可以做线程被取消后的操作。例如Ui显示一个进度条,用户可以取消此进度条。

UI线程可以通过AsyncTask的Get方法等待线程结束,并获取doInBackground的计算结果。

③使用实例:

 GetCSDNLogoTask task = new GetCSDNLogoTask();

    task.execute("http://csdnimg.cn/www/images/csdnindex_logo.gif"); //启动线程并传递参数

 

class GetCSDNLogoTask extends AsyncTask<String,Integer,Bitmap> {//继承AsyncTask

@Override

protected Bitmap doInBackground(String... params) {//处理后台执行的任务,在后台线程执行

publishProgress(0);//将会调用onProgressUpdate(Integer... progress)方法

HttpClient hc = new DefaultHttpClient();

publishProgress(30);

HttpGet hg = new HttpGet(params[0]);//获取csdn的logo

final Bitmap bm;

try {

HttpResponse hr = hc.execute(hg);

bm = BitmapFactory.decodeStream(hr.getEntity().getContent());

catch (Exception e) {

 

return null;

}

publishProgress(100);

//mImageView.setImageBitmap(result); 不能在后台线程操作ui

return bm;

}

 

protected void onProgressUpdate(Integer... progress) {//在调用publishProgress之后被调用,在ui线程执行

mProgressBar.setProgress(progress[0]);//更新进度条的进度

}

 

protected void onPostExecute(Bitmap result) {//后台任务执行完之后被调用,在ui线程执行

if(result != null) {

Toast.makeText(AsyncTaskActivity.this"成功获取图片", Toast.LENGTH_LONG).show();

mImageView.setImageBitmap(result);

}else {

Toast.makeText(AsyncTaskActivity.this"获取图片失败", Toast.LENGTH_LONG).show();

}

}

protected void onPreExecute () {//在 doInBackground(Params...)之前被调用,在ui线程执行

mImageView.setImageBitmap(null);

mProgressBar.setProgress(0);//进度条复位

}


protected void onCancelled () {//在ui线程执行

mProgressBar.setProgress(0);//进度条复位

}

}

}  

细节

在UI线程与AsyncTask线程之间传递参数时,传递的是一个参数的集合,使用”参数【0】“调用对应的参数。

例如:protected Integer doInBackground(Integer... arg0) {

Log.v("debug", arg0[0].toString());

return null;

}

二、①Thread的使用

创建新执行线程有两种方法。一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。接下来可以分配并启动该子类的实例。例如,计算大于某一规定值的质数的线程可以写成: 

--------------------------------------------------------------------------------

     class PrimeThread extends Thread {

         long minPrime;

         PrimeThread(long minPrime) {

             this.minPrime = minPrime;

         }

         public void run() {

             // compute primes larger than minPrime

              . . .

         }

     }

--------------------------------------------------------------------------------

然后,下列代码会创建并启动一个线程: 

     PrimeThread p = new PrimeThread(143);

     p.start();

 创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后可以分配该类的实例,在创建 Thread 时作为一个参数来传递并启动。采用这种风格的同一个例子如下所示: 

--------------------------------------------------------------------------------

     class PrimeRun implements Runnable {

         long minPrime;

         PrimeRun(long minPrime) {

             this.minPrime = minPrime;

         }

         public void run() {

             // compute primes larger than minPrime

              . . .

         }

     }

-------------------------------------------------------------------------------

然后,下列代码会创建并启动一个线程: 

     PrimeRun p = new PrimeRun(143);

     new Thread(p).start();

 每个线程都有一个标识名,多个线程可以同名。如果线程创建时没有指定标识名,就会为其生成一个新名称。

 ②Thread使用解析

在使用Thread线程时可以通过Run启动线程,可以使用sleep 使线程休眠,在现在的Jdk中已经不使用stop方法来结束线程了,在

做线程的实验的时候我就遇到了关于线程启动停止的问题。

问题:线程经run启动后,在主程序中用stop方法终止,再次调用run方法,应用会抛出线程正在运行的异常。

解决办法:在应用中需要多次启用与关闭线程时,可以通过控制标志位的方法,在线程里启动一个where死循环,判断标志位,来实现线程的启用与停止。

三、AsyncTask(异步)和Thread(线程)的对比

android应用程序的编写是在java的基础上进行的,android为了更方便的在线程中操作Ui,延伸出了AsyncTask,因此如果在做android的应用开发的时候,如果需要用到与界面相关的线程的时候首选AsyncTask,如果线程主要用来处理数据,不参与界面操作的时候尽量用Thread线程,因为Thread较AsyncTask比有很多优点(参考javaAPI),另外如果在应用中需要启动多个线程的时候,也要使用Thread,它自带多线程处理。

总的来说,如果需要简单的操作Ui,AsyncTask更好一些。如果不操作UI或者后台线程比较复杂,Thread更好一些。


0 0
原创粉丝点击