Android的线程和内存模型

Android的线程和内存模型

Android操作系统在boot后,会启动一个Zygote(受精卵)进程,Zygote进程负责创建大部分应用程序进程。Zygote进程启动加载核心程序库和数据结构到内存后会创建一个Dalvik虚拟机(DVM)进程--SystemServer,此进程会包含大部分的系统服务(包括管理Activity的服务ActivityManagerService),SystemServer初始化后,Zygote进程会侦听本地的socket端口, 等待进一步的指令。当新的app被启动时,Zygote会为这个app创建一个DVM----直接fork出一个子进程,这种架构的好处是同时启动多个App时,多个App进程可以访问共享内存。

Android App的进程也是一个DVM,内部有许多线程在执行,比如,主UI线程(Main Thread),垃圾回收线程等。其中主UI线程负责执行我们写的应用代码。对于只做很少的I/O操作或耗时操作的App,单一线程开发模式问题不大,但是如果有大量IO或者CPU计算的任务,我们就必须在其他线程内完成了。

因为主UI线程需要根据硬件刷新率[^3]同步用户界面的重绘。手机应用体验流畅要求界面帧率[^3]达到每秒60,也就是说每16.67毫秒就需要重绘一帧,这意味着如果我们在主线程上执行的任务超过16毫秒,就会出现丢帧现象,也就是界面会开始变卡。。。

Android异步执行任务的方法有以下几种:

AsyncTask

AsyncTask是最常用的异步方法,功能结构设计的也很丰富,给使用者足够的控制,使用上主要是将异步执行的任务放在下面方法里。

protected Result doInBackground(Params... params)

然后调用.execute(params)方法即可。

AsyncTask的执行逻辑在API Level 3只能串行执行, 在API Level 4改成了最多128个线程的线程池执行,API Level 11则改成了缺省所有的AsyncTask是在一个线程中顺序执行的,这样可以保证执行和提交的次序一致,如果希望能并发的执行,可以用下面的方法在线程池内执行:

task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);

AsyncTask.THREAD_POOL_EXECUTORThreadPoolExecutor的一个实例,配置是最少5个线程,最多128个。如果需要自己来配置线程池大小,你可以传递自己配置的一个实例到上述方法。

使用AsyncTask需要注意的几个问题:

碎片化问题

因为不同版本的Android AsyncTask缺省执行逻辑并不一样,可能在不同机型上表现不一致。如果要自己控制AsyncTask的并发度,解决这个问题的建议是复制Android SDK的AsyncTask源码自己实现一个AsyncTask。

Activity生命周期问题

Activity可能早于AsyncTask执行完被销毁,如果AsyncTask还继续执行,有可能会浪费资源,并且如果AsyncTask里引用了Activity或部分的View Hierarchy,还会造成引用的对象不会被垃圾回收而引起内存泄漏。通常AsyncTask会定义为Activity的一个匿名inner class,这会建立一个隐式的引用到Activity。

解决的方法是:

  1. 在Activity里的onPause方法里及时取消不需要再执行的AsyncTask(这种方法在切换到横屏时会重启异步任务,有点浪费)
  2. 更好的是使用retained headless fragment来解决生命周期问题,具体演示代码见参考链接

Async比较合适的是较短的(1,2秒),CPU密集计算或读写文件等阻塞IO操作。耗时较长的网络调用用Async不是最合适的。

Handler & HandlerThread

Handler的异步编程是基于消息队列模型的。执行任务的线程称之为Looper线程,其他线程则将需要异步执行的任务发送给Looper线程--插入其消息队列,方法有:post(较方便使用,但每次需要创建新对象)或sendMessage(较高效,复用消息实例,适合执行大量类似的异步任务)

Looper

Looper和它的名字意思一样就是Looper线程会永远循环,当没有消息的时候,Looper线程(消费者)会使用(Object.wait)方法等待其他线程(生产者)插入新的任务消息,这时候其他线程(Object.notify)

Android的主线程其实就是一个Looper线程。

需要注意的是,使用Handler和AsyncTask一样,要注意匿名inner class对Activity的隐式引用而造成内存泄漏,所以使用的时候要记得清理;

解决方法是使用对使用的Activity中的View对象用Weak Reference,并处理当View对象为null的情况。

Handler适合更长一点的(>2秒)的异步任务处理。

Loader

Loader在Android编程框架中被广泛用于后台加载数据(从文件,数据库甚至网络)。

AsyncTaskLoader

具体的Loader实现。

CursorLoader

用户数据库数据后台加载。

Loader在使用上比较大的优势是和Activity的部分解耦,更见到的生命周期管理。

IntentService

IntentService是Service的一个实现类。其内部实现包含了一个HandlerThread,当任务提交给IntentService时,会被加到队列并顺序处理。

public class MyIntentService extends IntentService {
     public MyIntentService() {
       super("thread-name");
     }
     protected void onHandleIntent(Intent intent) {
       // executes on the background HandlerThread.
     }
}

Intent intent = new Intent(context, MyIntentService.class);
intent.setData(uri); intent.putExtra("param", "some value");
startService(intent);

IntentService返回任务结果给Activity可以有下面几种方法(Service和Activity的通信方法):

  1. Activity启动IntentService时候,传递一个PendingIntent对象给IntentService,IntentService在处理完任何后调用PendingIntent.send()通知Activity,Activity在onActivityResult方法内处理收到的消息。
  2. 提交一个系统通知,这样App不在前台,用户也可以知道任务完成,比如下载地图数据。
  3. 使用Messenger发送一个消息给Activity里的Handler。
  4. 使用广播方式把结果封装在Intent中广播出去, 这样Fragment或Activity都可以通过监听广播获得处理结果。

IntentService可以完全和Activity解耦。适合即使App主界面退出情况下也必须完成的任务,比如后台上传日志,图片之类的任务。

Service

Service合适执行独立于任何Activity或Fragment的任务,即使App主界面退出情况下也必须完成的任务;或者需要比IntentService更高或更细致的并发控制。

AlarmManager

AlarmManager可以不在人工干预下定时执行任务,比如定时检查邮件,下载更新,或同步内容到云端。

参考链接

  1. Asynchronous Android
  2. Android内核
  3. [^3]帧率与刷新率的区别
时间: 2024-11-03 22:05:18

Android的线程和内存模型的相关文章

Java调优之jvm和线程的内存分析

[转载声明] 转载时必须标注:本文来源于铁木箱子的博客http://www.mzone.cc[本文地址] 本文永久地址是:http://www.mzone.cc/article/321.html       这几天因为自己开发的一个网站在768M内存的机器上撑不起100多个用户的运行,因为每个用户启用功能后,系统将为每个用户分配8个左右的独立线程,我的这篇文章http://www.mzone.cc/article/311.html也有介绍的.在内存小的机器上经常出现的问题就是Cann't all

Java线程/内存模型的缺陷和增强

Java在语言层次上实现了对线程的支持.它提供了Thread/Runnable/ThreadGroup等一系列封装的类和接口,让程序员可以高效的开发Java多线程应用.为了实现同步,Java提供了synchronize关键字以及object的wait()/notify()机制,可是在简单易用的背后,应藏着更为复杂的玄机,很多问题就是由此而起. 一.Java内存模型 在了解Java的同步秘密之前,先来看看JMM(Java Memory Model). Java被设计为跨平台的语言,在内存管理上,显

【转载】JAVA内存模型和线程安全

本文转载自http://shift-alt-ctrl.iteye.com/blog/1845309   一.JAVA内存模型(JMM,JAVA Memory Model):     运行时涉及到两种内存,主内存和工作区内存,其中工作区内存通常为CPU的高速缓存区用来加快内存数据读取操作的(各线程独立).所有的变量内容都存在主内存中,当需要对内存数据进行操作时,数据将会从主存中load到工作区缓存并由CPU计算和赋值操作,然后再由工作区内存write到主存中,读取时如果工作区内存中已经有(load

java内存模型与线程(转) good

java内存模型与线程 参考 http://baike.baidu.com/view/8657411.htm  http://developer.51cto.com/art/201309/410971_all.htm http://www.cnblogs.com/skywang12345/p/3447546.html 计算机的CPU计算能力超强,其计算速度与 内存等存储 和通讯子系统的速度相比快了几个数量级, 数据加载到内存中后,cpu处理器运算处理时,大部分时间花在等待获取去获取磁盘IO.网络

《深入解析Android 虚拟机》——2.6 Java内存模型

2.6 Java内存模型 不同的平台,内存模型是不一样的,但是JVM的内存模型规范是统一的.其实Java的多线程并发问题最终都会反映在Java内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改.总结Java的内存模型,要解决两个主要的问题:可见性和有序性. 人们都知道计算机有高速缓存的存在,处理器并不是每次处理数据都是取内存的.JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节,对于Java开发人员,要清楚在JVM内存模型的基础上,如果解决多线程的可见性和有序性. 那么,

JAVA内存模型和线程安全

一.JAVA内存模型(JMM,JAVA Memory Model):     运行时涉及到两种内存,主内存和工作区内存,其中工作区内存通常为CPU的高速缓存区用来加快内存数据读取操作的(各线程独立).所有的变量内容都存在主内存中,当需要对内存数据进行操作时,数据将会从主存中load到工作区缓存并由CPU计算和赋值操作,然后再由工作区内存write到主存中,读取时如果工作区内存中已经有(loaded)则直接使用;工作区内存保存了线程使用的变量的副本,线程不可以直接操作主内存,只能操作工作区内存,对

《深入解析Android 虚拟机》——第2章,第2.6节Java内存模型

2.6 Java内存模型不同的平台,内存模型是不一样的,但是JVM的内存模型规范是统一的.其实Java的多线程并发问题最终都会反映在Java内存模型上,所谓线程安全无非是要控制多个线程对某个资源的有序访问或修改.总结Java的内存模型,要解决两个主要的问题:可见性和有序性. 人们都知道计算机有高速缓存的存在,处理器并不是每次处理数据都是取内存的.JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节,对于Java开发人员,要清楚在JVM内存模型的基础上,如果解决多线程的可见性和有序性. 那么,何

Java内存模型和线程安全的使用例子

网上很多资料在描述Java内存模型的时候,都会介绍有一个主存,然后每个工作线程有自己的工作内存.数据在主存中会有一份,在工作内存中也有一份.工作内存和主存之间会有各种原子操作去进行同步. 下图来源于 这篇Blog 但是由于Java版本的不断演变,内存模型也进行了改变.本文只讲述Java内存模型的一些特性,无论是新的内存模型还是旧的内存模型,在明白了这些特性以后,看起来也会更加清晰. 1. 原子性 原子性是指一个操作是不可中断的.即使是在多个线程一起执行的时候,一个操作一旦开始,就不会被其它线程干

探寻Android的线程问题_Android

什么是线程? 线程或者线程执行本质上就是一串命令(也是程序代码),然后我们把它发送给操作系统执行. Multithreaded_process 一般来说,我们的CPU在任何时候一个核只能处理一个线程.多核处理器(目前大多数Android设备已经都是多核)顾名思义,就是可以同时处理多线程(通俗地讲就是可以同时处理多件事). 多核处理与单核多任务处理的实质 上面我说的是一般情况,并不是所有的描述都是一定正确的.因为单核也可以用多任务模拟出多线程. 每个运行在线程中的任务都可以分解成多条指令,而且这些