Android中使用线程

翻译 2016年08月31日 10:03:51
 当你第一次启动一个Android程序的时候,一个被称为"main"的线程就被自动创建了。它被称为主线程或者UI线程,它是非常重要的因为负责分发事件给对应的widget,还包含画图的事件。主线程贯穿用户和Android widget的交互的整个过程。例如,你触摸了屏幕上的按钮(Button),UI线程派发(dispatch)触摸(touch)事件给widget,widget设置为按下状态并向事件队列发送一个无效的请求。UI线程把这个请求弹出栈并且通知widget去重画它自己。

        单线程模型导致Android程序低效。因为每次单线程去执行长时间的操作如网络请求,数据库查询和drawing event(绘图事件),在这个过程中会阻塞程序的界面(UI)。当长时间的任务正在执行的时候,没有事件(Event)会被派发(dispatch),包括drawing event(绘图事件)。从用户的角度来看,该程序出现了终止。甚至更糟糕的是,如果UI程序被阻塞几秒后(大约5s)就会出现臭名昭著的ANR 对话框。

        如果想看一下有多糟糕,你可以写一个简单的带有按钮的程序,按钮的点击事件执行Thread.sleep(2000)代码。该按钮将会保持2秒的按下状态然后恢复正常的状态。这样很容易让用户感到程序很慢。

        现在你知道了一定要避免在主线程中执行长时间的操作,你可能会使用额外的线程(后台线程或者工作线程)去执行操作。让我们来看看点击从网络下载图片到ImageView里的例子:
public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      Bitmap b = loadImageFromNetwork();
      mImageView.setImageBitmap(b);
    }
  }).start();
}
首先,代码看上去很好的解决我们的问题,因为它不会阻塞UI线程,不幸的是,它违背了单一线程模型:Android UI工具箱(toolkit)不是一个线程安全的,并且它总是被放在主线程上操作。
这个ImageView被一个工作线程操作,这导致非常不可思议的问题。跟踪和修复这样一个bug很难并且也耗时。

        Android提供了几种从其他线程访问主线程的方式。你可能已经很清楚如何使用他们,但是这里是齐全的列表:
以上任何一个类都能修正我们的代码:
public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
      final Bitmap b = loadImageFromNetwork();
      mImageView.post(new Runnable() {
        public void run() {
          mImageView.setImageBitmap(b);
        }
      });
    }
  }).start();
}
不幸的是,这些类和方法导致我们的代码变得复杂和可读性差。当你实现复杂的操作来频繁的更新界面,使用这种方式变得更加糟糕。为了解决这个问题,Android1.5提供了一个公共类叫做AsyncTask,它简化了任务线程和主线程之间的通信。

        在Android1.0和1.1也可使用AsyncTask只不过它的名字为UserTask。

        AsyncTask的目的就是帮助你管理线程。我们之前的例子很容易被改写如下形式:
public void onClick(View v) {
  new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask {
     protected Bitmap doInBackground(String... urls) {
         return loadImageFromNetwork(urls[0]);
     }

     protected void onPostExecute(Bitmap result) {
         mImageView.setImageBitmap(result);
     }
 }
AsyncTask通过它的子类才能使用。要记住,一个AsyncTask实例必须在主线程创建并且只能被执行一次。完全理解和使用这个类,你可以阅读AsyncTask文档。这里快速的说一下AsyncTask是怎么工作的:
1>可以通过泛型指定它的类型:参数,进度值,任务的结果值。
2>doInBackGround()方法自动在工作线程中只想能够。
3>onPreExecute(),onPostExecute(),onProgressUpdate()方法都在UI线程中执行。
4>doInBackground()方法返回的值被当作参数传递给onPostExecute()方法。
5>你能够在doInBackground()方法里任何时候调用publishProgress()方法在UI线程中去执行onProgressUpdate()方法。

除了官方文档,你可以阅读几个复杂的例子源代码如Shelves(ShelvesActivity.java和AddBookActivity.java)和Photostream(LoginActivity.java,PhotostreamActivity.java和ViewPhotoActivity.java)。我强烈建议阅读Shelves的源代码,看它在配置改变(configuration changes)的时候是如何保存任务的(persist task),当Activity销毁的时候是怎样取消任务的。


不要管它是否使用AsyncTask,总之要记住单线程模型的两个原则(rule):不要阻塞(block)UI线程;确保Android UI toolkit 只能在UI线程中被访问(access)。
AsyncTask使得做这些事情变得更简单。

Android中线程的正确使用

最近看到一篇讲解Android线程及其使用的文章,比较详细,在此分享出来,感谢原作者的分享。 ----------------------------- 来源:http://android.job...
  • wangsf1112
  • wangsf1112
  • 2016年05月05日 19:25
  • 3249

android 中的线程倒计时

CountDownTimer这个类,从名字上面大家就可以看出来,记录下载时间。将后台线程的创建和Handler队列封装成为了一个方便的类调用。  查看了一下官方文档,这个类及...
  • u011404611
  • u011404611
  • 2014年04月06日 16:06
  • 800

NDK 5 使用POSIX线程

Java中使用线程特别简单,实现Runnable接口 或者 继承Thread. NDK中使用线程可以在Activity中直接调用java线程,也可以通过JNI启动线程,这个线程源于POSIX中的线程...
  • birdsaction
  • birdsaction
  • 2014年01月05日 01:30
  • 3746

Android开发之在子线程中使用Toast

在子线程中使用Toast的时候,出现Force close。 错误提示:Can't create handler inside thread that has not called Looper.p...
  • zlb_lover
  • zlb_lover
  • 2016年11月06日 01:03
  • 1736

Android中的消息队列和线程队列机制

ndroid通过Looper、Handler来实现消息循环机制。Android的消息循环是针对线程的,每个线程都可以有自己的消息队列和消息循环。Android系统中的Looper负责管理线程的消息队列...
  • zhoupenglei
  • zhoupenglei
  • 2017年06月13日 10:02
  • 1245

Android开发之线程池使用总结

线程池算是Android开发中非常常用的一个东西了,只要涉及到线程的地方,大多数情况下都会涉及到线程池。Android开发中线程池的使用和Java中线程池的使用基本一致。那么今天我想来总结一下Andr...
  • u012702547
  • u012702547
  • 2016年08月20日 20:49
  • 22389

Android Service 线程问题

原文:百度文库 started servcie总结 Service的线程问题 默认情况下Service是运行在启动该Service的应用主线程的,如果Service中的操作占用大量的C...
  • guoshaobei
  • guoshaobei
  • 2014年01月22日 17:40
  • 6111

[新手记录]在ProgressDialog中执行子线程取消的问题

做项目时用到通信部分大多都是要弹出加载框,然后开始数据传输,但是碰到网卡的时候,用户可能会取消登陆,不过按下返回键的话,当前的ProgressDialog会消失,但是数据可能已经通过接口传输到服务端了...
  • new_7M
  • new_7M
  • 2015年11月02日 11:20
  • 1002

Android Toast在子线程中为什么无法正常使用

通过android Toast,Looper,ActivityThread的源码分析,在子线程中Toast提示失效的原因,并加上解决办法。...
  • ycarl
  • ycarl
  • 2017年06月27日 17:25
  • 836

android中对线程池的理解与使用

前段时间有幸接到腾讯上海分公司的 android开发面试,sur
  • yaya_soft
  • yaya_soft
  • 2014年04月24日 10:39
  • 38440
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android中使用线程
举报原因:
原因补充:

(最多只允许输入30个字)