`
suiyuchen
  • 浏览: 30461 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

如何在Android中用好多线程

阅读更多

——Android中高级技术免费培训(118949422)第三期专题培训

本期的多线程主题与Android相关,侧重讲解在Android中如何用好多线程,需要你有Java的多线程基础。

首先我们思考几个问题,在Android应用中为什么要用多线程?为了解决哪些问题?或者为了实现哪些功能?有哪些好处?请先思考一分钟,再继续往下看。

学习而不思考就像吃东西而不嚼,要么无法下咽,要么尝不出味道,同时都会影响消化吸收。控制一下你那脱缰野马一样的好奇心吧,先思考再往下看。

————————————————飘过这条分隔线,我们继续——————————————————

1. 为什么要用多线程

这里列出几个原因:

a) 提高用户体验或者避免ANR

在事件处理代码中需要使用多线程,否则会出现ANR(Application is not responding),或者因为响应较慢导致用户体验很差。

5

图1 ANR对话框

测试ANR的方法见示例工程《EX10_01ANR》

b) 异步

应用中有些情况下并不一定需要同步阻塞去等待返回结果,可以通过多线程来实现异步,例如:上一点中提到的,你的应用中的某个Activity需要从云端获取一些图片,加载图片比较耗时,这时需要使用异步加载,加载完成一个图片刷新一个,见下面图2、图3 。

c) 多任务

例如多线程下载。

后两点与Java中的多线程应用没有太大区别,不细说。

下面重点说明第一点,即如何减少事件响应的时间从而提高用户体验,以及如何避免ANR。


2. 为什么通过多线程可以提高用户体验、避免ANR

大家还记得我在群里说过的移动开发的“三不要”原则吗?即:不要让我想、不要让我等、不要让我烦。响应慢了用户需要等,等的次数多了就会烦,你的应用离被卸载不远了。

首先我们来了解一下Android应用程序的main线程,它负责处理UI的绘制,Android系统为了防止应用程序反应较慢导致系统无法正常运行做了一个处理,一种情况是当用户输入事件在5秒内无法得到响应,那么系统会弹出ANR对话框,由用户决定继续等待还是强制结束应用程序(另一种情况是BroadcastReciever 超过10秒没执行完也会弹出ANR对话框)。

即使你的程序中某个事件响应不超过5秒钟,人眼可以分辨的时间是0.1秒,小于0.1秒基本感觉不出来,超过0.2秒用户就能感觉到有点儿卡了,俗称打嗝现象,2秒以上就很慢了,用户体验会很差。有同学说我可以用进度条啊,但你的程序中不能到处都是进度条,否则那个圈圈会把用户转晕的,好像在对用户说,画个圈圈烦死你……

比如某些应用,它要显示很多图片,还好它是异步的,不过在图片加载完成前每个图片的位置上都有一个圈圈,让人看了很烦。你可以变通一下,图片加载成功之前显示一个默认的图片,加载成功后再刷新一下即可,何必弄那么多进度条呢?

1

图2 加载图片完成前显示默认图片,加载完成后再刷新

2

图3 加载图片完成前显示默认图片,加载完成后再刷新

3

图4 转晕你,烦死你

事件处理的原则:所有可能耗时的操作都放到其他线程去处理。

Android中的Main线程的事件处理不能太耗时,否则后续的事件无法在5秒内得到响应,就会弹出ANR对话框。那么哪些方法会在 Main线程执行呢?

1) Activity的生命周期方法,例如:onCreate()、onStart()、onResume()等

2) 事件处理方法,例如onClick()、onItemClick()等

通常Android基类中以on开头的方法是在Main线程被回调的。

提高应用的响应性,可以从这两方面入手。

一般来说,Activity的onCreate()、onStart()、onResume()方法的执行时间决定了你的应用首页打开的时间,这里要尽量把不必要的操作放到其他线程去处理,如果仍然很耗时,可以使用SplashScreen。使用SplashScreen最好用动态的,这样用户知道你的应用没有死掉。

6

图5 动态SplashScreen

7

图6 静态SplashScreen

当用户与你的应用交互时,事件处理方法的执行快慢决定了应用的响应性是否良好,这里分两种情况:

1) 同步,需要等待返回结果。例如用户点击了注册按钮,需要等待服务端返回结果,那么需要有一个进度条来提示用户你的程序正在运行没有死掉。一般与服务端交互的都要有进度条,例如系统自带的浏览器,URL跳转时会有进度条。

2) 异步,不需要等待返回结果。例如微博中的收藏功能,点击完收藏按钮后是否成功执行完成后告诉我就行了,我不想等它,这里最好实现为异步的。

无论同步异步,事件处理都可能比较耗时,那么需要放到其他线程中处理,等处理完成后,再通知界面刷新。

这里有一点要注意,不是所有的界面刷新行为都需要放到Main线程处理,例如TextView的setText()方法需要在Main线程中,否则会抛出CalledFromWrongThreadException,而ProgressBar的setProgress()方法则不需要在Main线程中处理。

当然你也可以把所有UI组件相关行为都放到Main线程中处理,没有问题。可以减轻你的思考负担,但你最好了解他们之间的差别,掌握事物之间细微差别的是专家。把事件处理代码放到其他线程中处理,如果处理的结果需要刷新界面,那么需要线程间通讯的方法来实现在其他线程中发消息给Main线程处理。

3. 如何实现线程间通讯

在Android中有多种方法可以实现其他线程与Main线程通讯,我们这里介绍常见的两种。

1) 使用AsyncTask

AsyncTask是Android框架提供的异步处理的辅助类,它可以实现耗时操作在其他线程执行,而处理结果在Main线程执行,对于开发者而言,它屏蔽掉了多线程和后面要讲的Handler的概念。你不了解怎么处理线程间通讯也没有关系,AsyncTask体贴的帮你做好了。不过封装越好越高级的API,对初级程序员反而越不利,就是你不了解它的原理。当你需要面对更加复杂的情况,而高级API无法完成得很好时,你就杯具了。所以,我们也要掌握功能更强大,更自由的与Main线程通讯的方法:Handler的使用。

AsyncTask的使用方法见示例工程《EX10_02AsyncTask》

2) 使用Handler

这里需要了解Android SDK提供的几个线程间通讯的类。

2.1 Handler

Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。

2.2 Looper

Looper负责管理线程的消息队列和消息循环

2.3 Message

Message是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息。

2.4 MessageQueue

MessageQueue是消息队列,先进先出,它的作用是保存有待线程处理的消息。

它们四者之间的关系是,在其他线程中调用Handler.sendMsg()方法(参数是Message对象),将需要Main线程处理的事件添加到Main线程的MessageQueue中,Main线程通过MainLooper从消息队列中取出Handler发过来的这个消息时,会回调Handler的handlerMessage()方法。

Handler的使用方法见示例工程《EX10_03HandlerAndMsg》

除了以上两种常用方法之外,还有几种比较简单的方法

3) Activity.runOnUiThread(Runnable)

4) View.post(Runnable)

View.postDelayed(Runnable, long)

5) Handler.post

Handler.postDelayed(Runnable, long)

4. 利用线程池提高性能

这里我们建议使用线程池来管理临时的Thread对象,从而达到提高应用程序性能的目的。

线程池是资源池在线程应用中的一个实例。了解线程池之前我们首先要了解一下资源池的概念。在JAVA中,创建和销毁对象是比较消耗资源的。我们如果在应用中需要频繁创建销毁某个类型的对象实例,这样会产生很多临时对象,当失去引用的临时对象较多时,虚拟机会进行垃圾回收(GC),CPU在进行GC时会导致应用程序的运行得不到相应,从而导致应用的响应性降低。

资源池就是用来解决这个问题,当你需要使用对象时,从资源池来获取,资源池负责维护对象的生命周期。

了解了资源池,就很好理解线程池了,线程池就是存放对象类型都是线程的资源池。

线程池的使用方法见示例工程《EX10_04ThreadPool》

线程池与Handler结合使用的方法见示例工程《EX10_05HandlerAndPool》

示例工程见《EX10_06CustomHandler》

我增加了如何在其他线程中创建Handler的例子作为选学,前面都掌握好了的同学可以看一下,如果你需要实现一个跟Main线程类似的消息处理机制,需要其他线程可以跟你的线程通讯,可以通过这种方法实现。

本期培训课后作业:为你的应用添加一个SplashScreen。

示例工程源代码及答疑在中。

分享到:
评论

相关推荐

    jni中用多线程调用java代码

    描述中提到的“在JNI中用多线程调用Java对象”,意味着在C/C++代码中创建并管理多个线程,每个线程负责调用不同的Java方法或者处理不同的Java对象。在Android中,线程管理对于优化应用性能和避免UI阻塞至关重要。...

    在android中用jdbc连接数据库

    在Android平台上,通过Java Database Connectivity (JDBC) 连接数据库是一种常见的技术需求,尤其是在开发需要后台数据支持的应用时。本文将深入探讨如何在Android应用中使用JDBC连接MySQL、Oracle和SQL Server等...

    android_线程间的通信 详细

    - **概念**:Handler是Android中用于发送消息和处理消息的对象。它是连接消息队列和应用程序的桥梁。 - **作用**: - 发送消息到消息队列。 - 接收并处理来自消息队列的消息。 - **创建方式**:Handler可以在任何...

    断点多线程下载.

    这通常通过在子线程中更新一个共享的进度变量,然后在主线程中用这个变量刷新UI来实现。Android提供了ProgressBar和ProgressDialog等组件来创建进度条,可以通过调用其方法如setProgress()来改变进度值。 为了实现...

    Android-猎豹"快切App"中用到的Android开发技巧探索[附源码]

    在“Android-猎豹'快切App'中用到的Android开发技巧探索”这个主题中,我们可以深入探讨一些Android开发中的关键技术和实践,这些技术在猎豹移动的“快切App”中得到了应用。源码的提供使得我们有机会直接查看和学习...

    andriod多线程通信handler方法

    1. Handler(处理器):Handler是Android中用于发送和处理Message或Runnable对象的类。我们可以创建一个Handler实例并关联到特定的Looper,然后在这个Handler上post Runnable或者send Message到消息队列。 2. ...

    收集整理Android开发所需的Android SDK、开发中用到的工具、Android开发教程、Android设计规范,免费的

    教程可能涵盖了Java或Kotlin编程语言基础、UI设计、网络编程、数据库操作、多线程、权限管理、动画效果等方面,帮助开发者系统学习Android开发。此外,可能还包括了一些热门话题,如Android Jetpack组件的使用、MVVM...

    Android代码-MediaUtils

    一个在 Android 上实现录像和录音功能的小例子。 GitHub 地址:https://github.com/Werb/MediaUtils 也可直接点击下载体验 Android 的 MediaRecorder 相信用过的同学一定踩过很多坑 之前写的代码很乱,现在重构了...

    Android中文翻译组——Android中文合集(6)

    7. **ContentProvider**:ContentProvider是Android中用于数据共享的机制,使得不同应用之间可以安全地访问和操作数据,尤其适用于跨应用的数据交换。 8. **Android权限管理**:Android系统通过权限模型来保护用户...

    高焕堂 android 讲义

    此外,还会涉及XML,它是Android中用来定义界面布局和资源的关键语言。 Android应用程序的生命周期管理是另一个重点,包括Activity、Service、BroadcastReceiver和ContentProvider等组件的创建与管理,以及它们在...

    java多线程编程_java多线程_

    2.讲解了在多线程中对并发访问的控制, 主要就是synchronized的使用, 由于此关键字在使用上非常灵活, 所以书中用了很多案例来介绍此关键字的使用, 为读者学习同步相关内容打好坚实的基础。3.介绍线程并不是孤独的...

    Android课件(完整版)

    Content Provider是Android中用于数据共享的机制,可以将应用的数据暴露给其他应用访问。 【Android UI设计】 Android提供了丰富的UI组件,如Button、TextView、EditText等,通过XML布局文件进行界面设计。同时,...

    ANDROID应用开发试题.pdf

    AndroidManifest.xml 是程序的配置文件,程序中用到的所有Activity 、Service、BroadcastIntent Receiver和 Content Provider 都必须在这里进行声明 答案:C.Assets目录:在该目录下存放的文件,在打包过程中将会...

    Android网络请求视频源码

    WebView是Android中用来显示网页内容的组件。这里可能是创建一个WebView实例,加载指定的网页,并可能涉及到与JavaScript交互,实现页面的定制和控制。 8. **Android之AsyncTask**: AsyncTask是Android提供的轻...

    Android下用线程池实现Http下载程序

    在Android平台上,实现一个高效的多线程下载程序是一项挑战,特别是在处理多个HTTP请求时。...以下是关于这个主题的详细知识点:...通过分析和学习这个类,你可以深入理解如何在Android中用线程池实现高效的HTTP下载功能。

    Android 水波效果工程源码

    2. **Canvas与Paint**: `Canvas`是Android中用于图形绘制的基础对象,而`Paint`则用于设置绘制颜色、线条样式等属性。在水波效果中,开发者会用到`Canvas`的`drawCircle()`、`drawPath()`等方法来描绘雨滴和涟漪,...

    android service下载资源,同时解压资源

    - BroadcastReceiver是Android中用来接收系统或自定义广播事件的组件。 - 在Service中发送广播,UI界面注册对应的Receiver,接收到广播后更新UI,如显示下载进度或提示下载完成。 - 需要注意,BroadcastReceiver...

    Android SDK开发范例大全(完整版)

    2. **布局设计**:XML是Android中用来定义用户界面的主要语言。开发者可以通过XML文件创建和组织视图组件,如按钮、文本框等,并通过布局管理器(如线性布局、相对布局、网格布局等)控制组件的排列方式。 3. **...

    Android-AndroidGraphics示例

    1. **Bitmaps**: Bitmap是Android中用于存储像素数据的基本类。处理Bitmap时,需要注意内存占用,过大可能导致内存溢出。可以使用`BitmapFactory`来解码图片,并通过设置解码选项(如`inSampleSize`)来调整图片大小...

    android基础开发整套代码

    7. **ContentProvider**:Android中用于数据共享的机制,允许应用间的数据访问。 8. **Android SDK**:开发者需要熟悉Android SDK中的各种工具,如Android Studio IDE、模拟器、构建工具(Gradle)等。 9. **权限...

Global site tag (gtag.js) - Google Analytics