Android 性能优化内存篇Word格式文档下载.docx
- 文档编号:20170923
- 上传时间:2023-01-17
- 格式:DOCX
- 页数:18
- 大小:25.36KB
Android 性能优化内存篇Word格式文档下载.docx
《Android 性能优化内存篇Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《Android 性能优化内存篇Word格式文档下载.docx(18页珍藏版)》请在冰豆网上搜索。
使用建议:
当数据量比较小的时候(小于1000),优先使用ArrayMap,否则使用HashMap。
map里嵌套map。
使用方法:
ArrayMap和HashMap的使用方法都是一样的,ArrayMap也实现了Map接口。
另外,ArrayMap可以通过keyAt(index)方法来获取第index位置的key,keyValue(intindex)同理。
但是HashMap是不可以的。
arrayMap.keyAt(0);
arrayMap.valueAt(0);
SparseArray和ArrayMap非常像,它们都是通过两种紧密包装的数组,而不是一个大的哈希散列,从而减少了整个内存的覆盖区。
但是查询的速度就慢了。
只不过SparseArray和ArrayMap最大的区别是SparseArray的key是一个基本类型。
SparseArray的key是int类型,而不是Integer。
像以前使用HashMap的时候,如果key是整形,必须是Integer。
Integer占16个字节,int只占4个字节,如果元素比较多,从而可以很好的减少内存的占用。
除了SparseArray类还有如下类可供使用:
SparseBooleanMap<
boolean,Object>
SparseIntMap<
int,Object>
SparseLongMap<
long,Object>
SparseArray和ArrayMap的使用建议和使用方法都是一样的。
三、Thread与ThreadPool
在android开发中,一些耗时的操作都会放到后台线程去执行,比如:
网络、本地文件、数据库等。
newThread(newRunnable(){
@Override
publicvoidrun(){
//dosomething...
}
}).start();
每个线程至少消耗64k的内存,如果你在某个时间点,迅速开启了很多线程(比如加载列表图片,然后用户滑动列表),这个时候可能内存使用量就会飙升。
会出现内存抖动(memorychurn),因为短时间开启了很多线程,完成任务后,这些线程都会被回收。
内存表现为:
低-高-低。
甚至可能出现OOM。
一个系统所能处理的线程数量是有限的,如果超多了最大承载量,性能会受到很大的影响。
而且可能还会影响用户的后续操作。
这时候ThreadPool线程池的作用就凸显出来了。
Java为我们提供了操作线程池的apiThreadPoolExecutor,ExecutorService是一个接口,相关的线程池的类都实现了该接口,如ThreadPoolExecutor。
创建一个线程池可以通过ThreadPoolExecutor类来实现。
ThreadPoolExecutorpool=newThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueue<
Runnable>
workQueue);
//新建一个线程池
pool.execute(Runnable);
//执行任务
下面是官方对ThreadPoolExecutor的参数说明:
Parameters:
corePoolSize-thenumberofthreadstokeepinthepool,eveniftheyareidle,unlessallowCoreThreadTimeOutisset
maximumPoolSize-themaximumnumberofthreadstoallowinthepool
keepAliveTime-whenthenumberofthreadsisgreaterthanthecore,thisisthemaximumtimethatexcessidlethreadswillwaitfornewtasksbeforeterminating.
unit-thetimeunitforthekeepAliveTimeargument
workQueue-thequeuetouseforholdingtasksbeforetheyareexecuted.ThisqueuewillholdonlytheRunnabletaskssubmittedbytheexecutemethod.
corePoolSize核心线程数,核心线程会一直存活,即使没有任务需要处理。
当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。
核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。
maxPoolSize线程池允许最大的线程数量。
keepAliveTime当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。
如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。
allowCoreThreadTimeout是否允许核心线程空闲keepAliveTime退出,默认值为false。
workQueue任务队列。
pool.execute(runnable)提交的task都会放到workQueue。
下面来一个简单的sample:
publicclassMyClass{
privateThreadPoolExecutorpool;
privateMyClass(){
//创建线程池
pool=newThreadPoolExecutor(4,7,60,TimeUnit.MILLISECONDS,newLinkedBlockingQueue<
());
publicstaticvoidmain(String[]args){
MyClassmyClass=newMyClass();
for(inti=0;
10;
i++){
//提交任务
myClass.pool.execute(newMyRunnable(myClass));
myClass.pool.shutdown();
privateStringgetCount(){
returnpool.getCorePoolSize()+"
-"
+pool.getActiveCount()+"
+pool.getMaximumPoolSize();
privatestaticclassMyRunnableimplementsRunnable{
MyClassmyClass;
MyRunnable(MyClassmyClass){
this.myClass=myClass;
System.out.println("
threadname:
"
+Thread.currentThread().getName()+"
start"
+myClass.getCount());
try{
//模拟耗时任务
Thread.sleep(3000L);
end"
}catch(InterruptedExceptione){
e.printStackTrace();
上面的代码很简单:
创建了一个corePoolSize为4,maxPoolSize为7的线程池。
然后往线程池里提交10个任务,每个任务打印pool.getCorePoolSize()+"
+pool.getMaximumPoolSize(),即corePoolSize(核心线程数),activeCount(正在活动的线程总数)和maximumPoolSize(线程池允许的最大线程数)值。
测试结果如下:
pool-1-thread-1start4-2-7
pool-1-thread-2start4-4-7
pool-1-thread-3start4-4-7
pool-1-thread-4start4-4-7
pool-1-thread-1end4-4-7
pool-1-thread-2end4-3-7
pool-1-thread-4end4-4-7
pool-1-thread-1start4-4-7
pool-1-thread-3end4-4-7
pool-1-thread-2end4-4-7
pool-1-thread-1end4-3-7
pool-1-thread-4end4-2-7
pool-1-thread-2end4-2-7
Processfinishedwithexitcode0
从测试结果来看,我们打印pool.getCorePoolSize()+"
+pool.getMaximumPoolSize()的值是正常的。
但是只创建了4个线程:
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
我们设置了线程池的最大数为7,我们提交了10个任务,但是为什么只创建了corePoolSize=4个线程?
查看官方文档可以找到答案:
当通过execute(Runnable)提交一个新任务,并且小于corePoolSize正在运行的线程数,将会创建一个新的线程来处理这个任务,不管线程池里有没有空闲的线程。
IftherearemorethancorePoolSizebutlessthanmaximumPoolSizethreadsrunning,anewthreadwillbecreatedonlyifthequeueisfull.
大于corePoolSize小于maximumPoolSize,workQueue队列满了,才会创建新的线程。
如果corePoolSize和maximumPoolSize值设置成一样的,相当于创建了一个固定数量的线程池。
多数情况下,都是通过构造方法来设置corePoolSize和maximumPoolSize,但是也可以通过setCorePoolSize和setMaximumPoolSize来动态设置。
所以上面的例子,只创建了4个线程,因为虽然我们提交了10个任务,但是构建workQueue时候没有传入队列大小,默认大小是Integer.MAX_VALUE,所以workQueue是不会满的。
所以最多就创建了4个线程。
据此,我把workQueue队列容量改成4:
pool=newThreadPoolExecutor(4,7,60,TimeUnit.MILLISECONDS,newLinkedBlockingQueue<
(4));
测试结果:
pool-1-thread-2start4-2-7
pool-1-thread-3start4-3-7
pool-1-thread-5start4-6-7
pool-1-thread-6start4-6-7
pool-1-thread-1end4-6-7
pool-1-thread-2end4-6-7
pool-1-thread-2start4-5-7
pool-1-thread-1start4-6-7
pool-1-thread-3end4-6-7
pool-1-thread-3start4-6-7
pool-1-thread-4end4-6-7
pool-1-thread-5end4-6-7
pool-1-thread-4start4-6-7
pool-1-thread-6end4-5-7
pool-1-thread-3end4-2-7
pool-1-thread-4end4-1-7
发现创建了6个线程,大于上一次的测试结果(上一次是创建了4个线程),可是我们设置的maximumPoolSize为7,按道理应该是创建7个线程才对呀,这是为什么呢?
这需要了解下workQueue队列的策略了。
我们上面的列子使用的是LinkedBlockingQueue。
下面来看看官方文档对BlockingQueue的描述:
AnylinkBlockingQueuemaybeusedtotransferandhold
submittedtasks.Theuseofthisqueueinteractswithpoolsizing:
IffewerthancorePoolSizethreadsarerunning,theExecutor
alwaysprefersaddinganewthread
ratherthanqueuing.
IfcorePoolSizeormorethreadsarerunning,theExecutor
alwaysprefersqueuingarequestratherthanaddinganew
thread.
Ifarequestcannotbequeued,anewthreadiscreatedunless
thiswouldexceedmaximumPoolSize,inwhichcase,thetaskwillbe
rejected.
主要意思就是:
如果运行的线程少于corePoolSize,Executor会创建新线程来执行任务,不会把任务放进queue。
如果运行的线程等于或多于corePoolSize,Executor将请求加入队列,而不是创建新的线程。
如果队列已满,无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。
这样就能解释为什么只创建6个线程了。
总共有10个task,核心线程corePoolSize=4,所以3个任务是不会放进queue的,直接创建3个新线程来处理task了,然后再执行execute(Runnable)的时候,就会大于等于corePoolSize,所以就会把接下来的4个任务放进queue(容量为4),然后就剩下3个task了,发现队列已经满了,创建3个线程来处理这剩下的3个task,所以总共只创建6个线程了。
maximumPoolSize=7,我就是想让它创建7个线程,我们知道了上面的原理就很简单了,可以把队列的最大容量改成3或者添加11个任务就可以了:
newLinkedBlockingQueue<
(3)
或
11;
效果如下:
pool-1-thread-1start0-1-7
pool-1-thread-2start0-2-7
pool-1-thread-3start1-4-7
pool-1-thread-4start4-5-7
pool-1-thread-5start4-7-7
pool-1-thread-6start4-7-7
pool-1-thread-7start4-7-7
.....
Java提供了3种策略的Queue
SynchronousQueue直接传送task(Directhandoffs)
LinkedBlockingQueue无边界队列(Unboundedqueues)
ArrayBlockingQueue有边界队列(Boundedqueues)
更多详细信息可以查看官方文档。
Java给我们提供了一些工厂方法来来创建线程池():
Executors.newFixedThreadPool(intthreads);
Executors.newCachedThreadPool();
Executors.newSingleThreadExecutor();
Executors.newScheduledThreadPool(intthreads);
这些方法都是通过构建ThreadPoolExecutor来实现的,具体的细节可以去看看文档,如果都不满足你的需求,可以自己构造ThreadPoolExecutor。
四、IntentService与Service
一般我们在app里的版本更新逻辑在Service里起一个线程来检测。
为了避免Service一直存在,减少内存消耗,检测版本后,还需要手动stopSelf,略麻烦。
这时候用IntentService就比较合适了,默认就给你启动了一个线程来执行耗时操作,完成自动关闭service。
Service和IntentService主要区别:
IntentService执行完会自动关闭(stopSelf),而Service不会。
IntentService会启动一个线程来执行耗时操作,把耗时操作放到onHandleIntent(Intentintent)方法里。
而Service需要自己newThread。
如果调用startService(intent)多次,IntentService会执行多次onHandleIntent(Intentintent),且必须等本次的onHandleIntent(Intentintent)执行完,才会执行下一次onHandleIntent(Intentintent),说白了就是如果正在执行任务,会把后面启动的命令放到队列里。
而多次调用startService(intent),Service仅仅会多次调用onStartCommand方法。
五、避免常见的内存泄露
1、CountDownTimer、TimerTask、Handler导致的内存泄露
在项目中,我们常常可能要做活动倒计时的功能,我是用CountDownTimer来做的。
如:
publicstaticclassTimeCounterextendsCountDownTimer{
publicTimeCounter(longmillisInFuture,longcountDownInterval){
super(millisInFuture,countDownInterval);
publicvoidonFinish(){
//倒计时结束
publicvoidonTick(longm
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Android 性能优化内存篇 性能 优化 内存