Android开发实践:多线程编程小结

我们知道,Android系统为了提高程序的实时响应能力,不允许在UI线程中进行耗时的操作,否则会出现ANR异常,因此必须将耗时的任务放到非UI线程中执行。Android/Java提供了很多类来帮助大家完成异步操作,比如:Thread类,Timer类,AsyncTask类,HandlerThread类,以及Executor接口。这些类都分别在什么场合下使用呢?

本文简单地总结一下Android开发中常见的多线程类型和解决方案,并比较和分析了各个方案的区别,以便更好地理解和应用这些API接口。

1. 单项异步任务

[场景]:下载一个APK文件,拷贝一个大文件。

[方案]:Thread类,AsyncTask类

[比较]:

AsyncTask提供了onProgressUpdate和onPostExecute通知调用者任务执行的进度和结果,在函数内可以直接执行UI操作。

而Thread中不能直接操作UI界面,而要通过Activity.runOnUiThread或者借助Handler来完成UI的更新。

所以Thread更适合执行一些不需要跟UI频繁交互的单项任务,而AsyncTask相反。

2. 定时/循环执行的任务

[场景]:定时刷新UI(如:秒表),保持TCP心跳连接。

[方案]:“Thread + sleep”,定时器Timer

[比较]:

“Thread + sleep” 方案示例:

new Thread(new Runnable() {
    @Override
    public void run() {
	while(!EXIT) {
	    Thread.sleep(1000);
            mListener.onTimeArrived();
	}
    }
}).start();

两者都不能在循环中执行UI更新操作,而必须借助Activity.runOnUiThread或者Handler来完成UI的更新。

由于Thread方案中onTimeArrived()占用了部分时间,所以这种方案的定时并不准确,而Timer是由系统创建异步通知的定时器,会更加准确,所以推荐使用Timer来完成定时任务。

3. 工作线程

工作线程启动后处于一种等待“命令”/“消息”的休眠状态,当接收到“命令”/“消息”后,将它放入“命令”/“消息”队列,然后唤醒线程依次串行或者并行执行。

[场景]:“生产者--消费者”模式,TCP Server端命令处理程序

[方案]: “Thread + condition/lock” ,HandlerThread类,线程池Executor

[比较]:

HandlerThread类是Android系统提供了封装好了Loop循环的Thread类,可以更加便捷地完成CallerThread和WorkThread的命令/消息交互。当然,我们也可以用Thread和Condition/Lock方式实现同样的效果,只不过要自己实现更多的代码。

前面的两种方式,都是“串行”的方式在执行“命令”,如果希望提供并发性,同时开启和管理多个线程来执行任务,则可以考虑使用Executor。

以上就是我对Android多线程编程的简单小结,文中有任何不清楚或者不正确的地方,欢迎留言或者来信[email protected]交流讨论。

时间: 2024-11-25 13:23:14

Android开发实践:多线程编程小结的相关文章

Android开发实践:以“专业”的态度处理多线程

刚开始学一门编程语言的时候,我总是会有一种困惑,怎样让自己的代码看起来更"专业"?很多时候,我们可以照着教材实现一些基本的功能,比如用Socket发送/接收几个字符,写一个线程完成某个异步任务,但是在实际的项目中,往往不那么简单,比如需要设计Socket通信协议,需要处理Socket的连接异常断开,需要考虑在线程阻塞的情况下如何正常退出和释放资源等等,关于这些"实战经验",前面的文章也有所涉及,以后有空准备再开个专题跟大家分享探讨一下,今天先简单地说说怎样更&quo

Android开发实践:Android交叉编译工具链的使用

前面2篇文章分别介绍了Android NDK编译的命令行参数,以及如何在任意目录使用Android.mk来编译本地c/c++代码,Andriod.mk和ndk-build只不过是Android官方提供了一套封装过的Android交叉编译环境而已,其实,你可以不用它,而直接通过传统的Makefile文件来编译你的c/c++代码的,本文即介绍如何直接通过传统的Makefile文件来编译可用于Android平台的库文件. 经常搞嵌入式开发的朋友对于交叉编译环境应该并不陌生,说白了,就是一组运行在x86

Android开发实践:利用ProGuard进行代码混淆

由于Android的代码大都是Java代码,所以挺容易被反编译的,好在Android ADT为我们集成了混淆代码的工具,一来可以混淆我们的代码,让程序被反编译后基本看不懂,另外还能起到代码优化的作用.发布项目前,建议打开Android的代码混淆功能. Android ADT主要通过ProGuard工具来提供代码混淆,网上也有挺多博客文章讲这个的,但感觉很多都介绍得太过于复杂,这里我就以问答的方式来更加简洁地介绍下ProGuard吧. 1. ProGuard是什么 ProGuard是一个工具,用来

Android开发实践:WIFI连接功能的封装

在上一篇文章<Android开发实践:WIFI扫描功能的封装>介绍了如何利用Andriod的API实现WIFI的扫描,本文则重点讲述一下如何连接WIFI吧,在此,也给出一个封装WIFI连接过程的类,提供简单的接口以供在各个代码工程中复用. 与WIFI扫描类似,WIFI的连接同样是一个耗时的过程,所以需要放到线程中执行,通过回调来通知调用者连接结果.该回调接口的定义如下: public interface WifiConnectListener { public void OnWifiConne

Android开发实践:由new Handler()说开去

最近面试一些Android开发的应聘者,除了基本的Activity生命周期等基础问题以外,我一般还会问如下两个问题: (1) Service与Thread有什么区别? (2) 在Activity里new Handler()和在自己创建的Thread中new Handler()有什么区别? 第一个问题其实是一个伪命令,因为Service是Android四大组件之一,而Thread只是Java提供的一个封装了线程管理的工具类,无论是Activity还是Service,都可以通过Thread来创建一个

Android开发实践:为什么要继承onMeasure()

Android开发中偶尔会用到自定义View,一般情况下,自定义View都需要继承View类的onMeasure方法,那么,为什么要继承onMeasure()函数呢?什么情况下要继承onMeasure()?系统默认的onMeasure()函数行为是怎样的 ?本文就探究探究这些问题. 首先,我们写一个自定义View,直接调用系统默认的onMeasure函数,看看会是怎样的现象: package com.titcktick.customview; import android.content.Con

Android开发实践:Java层与Jni层的数组传递

Android开发中,经常会在Java代码与Jni层之间传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层.我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下. 示例代码的主要文件有两个,一个是Native.java,是Java层的类:另一个是Native.c,是JNI层的文件,关键的地

iOS开发之多线程编程

iOS开发之多线程编程 1. 多线程简述 1.1什么是多线程? 解决的问题? 多线程是指,编程中在主线程之外开辟的新线程,用于处理一些耗时的.并发的任务.使用多线程可以避免主线程的阻塞,也对一个线程不容易实现的任务提供了思路.在多线程的知识中也涉及队列,锁等概念. 在这里科普一下队列的概念,队列:是管理线程的,相当于线程池,能管理线程什么时候执行.队列分为串行队列和并行队列. 串行队列:队列中的线程按顺序执行(不会同时执行) 并行队列:队列中的线程会并发执行,可能会有一个疑问,队列不是先进先出吗

Linux多线程编程小结

 Linux多线程编程小结 前一段时间由于开题的事情一直耽搁了我搞Linux的进度,搞的我之前学的东西都遗忘了,非常烦躁的说,如今抽个时间把之前所学的做个小节.文章内容主要总结于<Linux程序设计第3版>. 1.Linux进程与线程 Linux进程创建一个新线程时,线程将拥有自己的栈(由于线程有自己的局部变量),但与它的创建者共享全局变量.文件描写叙述符.信号句柄和当前文件夹状态. Linux通过fork创建子进程与创建线程之间是有差别的:fork创建出该进程的一份拷贝,这个新进程拥有自己的