Android开机性能的优化是每个手机项目的必须点.针对开机性能,Apk的扫描安装耗时是大头,通常采用多线程方案,针对Dir或者Dir中的package进行多线程扫描.这里介绍我司项目中一种针对package进行优化扫描的方案(来源于高通)
相关代码:
PackageManagerService
private void scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
final File[] files = dir.listFiles();
if (ArrayUtils.isEmpty(files)) {
Log.d(TAG, "No files in app dir " + dir);
return;
}
if (DEBUG_PACKAGE_SCANNING) {
Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
+ " flags=0x" + Integer.toHexString(parseFlags));
}
Log.d(TAG, "start scanDirLI:"+dir);
// use multi thread to speed up scanning
int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);
Log.d(TAG, "max thread:" + iMultitaskNum);
final MultiTaskDealer dealer = (iMultitaskNum > 1) ? MultiTaskDealer.startDealer(
MultiTaskDealer.PACKAGEMANAGER_SCANER, iMultitaskNum) : null;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
if (RegionalizationEnvironment.isSupported()) {
if (RegionalizationEnvironment.isExcludedApp(file.getName())) {
Slog.d(TAG, "Regionalization Excluded:" + file.getName());
continue;
}
}
final File ref_file = file;
final int ref_parseFlags = parseFlags;
final int ref_scanFlags = scanFlags;
final long ref_currentTime = currentTime;
Runnable scanTask = new Runnable() {
public void run() {
try {
scanPackageTracedLI(ref_file, ref_parseFlags | PackageParser.PARSE_MUST_BE_APK,
ref_scanFlags, ref_currentTime, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to parse " + ref_file + ": " + e.getMessage());
// Delete invalid userdata apps
if ((ref_parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
logCriticalInfo(Log.WARN, "Deleting invalid package at " + ref_file);
removeCodePathLI(ref_file);
}
}
}
};
if (dealer != null)
dealer.addTask(scanTask);
else
scanTask.run();
}
if (dealer != null)
dealer.waitAll();
Log.d(TAG, "end scanDirLI:"+dir);
}
MultiTaskDealer
package com.android.server.pm;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
import android.util.Log;
public class MultiTaskDealer {
public static final String TAG = "MultiTaskDealer";
public static final String PACKAGEMANAGER_SCANER = "packagescan";
private static final boolean DEBUG_TASK = false;
private static HashMap> map = new HashMap>();
public static MultiTaskDealer getDealer(String name) {
WeakReference ref = map.get(name);
MultiTaskDealer dealer = ref!=null?ref.get():null;
return dealer;
}
public static MultiTaskDealer startDealer(String name,int taskCount) {
MultiTaskDealer dealer = getDealer(name);
if(dealer==null) {
dealer = new MultiTaskDealer(name,taskCount);
WeakReference ref = new WeakReference(dealer);
map.put(name,ref);
}
return dealer;
}
public void startLock() {
mLock.lock();
}
public void endLock() {
mLock.unlock();
}
private ThreadPoolExecutor mExecutor;
private int mTaskCount = 0;
private boolean mNeedNotifyEnd = false;
private Object mObjWaitAll = new Object();
private ReentrantLock mLock = new ReentrantLock();
public MultiTaskDealer(String name,int taskCount) {
final String taskName = name;
ThreadFactory factory = new ThreadFactory()
{
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(final Runnable r) {
if (DEBUG_TASK) Log.d(TAG, "create a new thread:" + taskName);
return new Thread(r, taskName + "-" + mCount.getAndIncrement());
}
};
mExecutor = new ThreadPoolExecutor(taskCount, taskCount, 5, TimeUnit.SECONDS,
new LinkedBlockingQueue(), factory){
protected void afterExecute(Runnable r, Throwable t) {
if(t!=null) {
t.printStackTrace();
}
MultiTaskDealer.this.TaskCompleteNotify(r);
if (DEBUG_TASK) Log.d(TAG, "end task");
super.afterExecute(r,t);
}
protected void beforeExecute(Thread t, Runnable r) {
if (DEBUG_TASK) Log.d(TAG, "start task");
super.beforeExecute(t,r);
}
};
}
public void addTask(Runnable task) {
synchronized (mObjWaitAll) {
mTaskCount+=1;
}
mExecutor.execute(task);
if (DEBUG_TASK) Log.d(TAG, "addTask");
}
private void TaskCompleteNotify(Runnable task) {
synchronized (mObjWaitAll) {
mTaskCount-=1;
if(mTaskCount<=0 && mNeedNotifyEnd) {
if (DEBUG_TASK) Log.d(TAG, "complete notify");
mObjWaitAll.notify();
}
}
}
public void waitAll() {
if (DEBUG_TASK) Log.d(TAG, "start wait all");
synchronized (mObjWaitAll) {
if(mTaskCount>0) {
mNeedNotifyEnd = true;
try {
mObjWaitAll.wait();
} catch (Exception e) {
}
mNeedNotifyEnd = false;
}
if (DEBUG_TASK) Log.d(TAG, "wait finish");
return;
}
}
}
简要分析
1. iMultitaskNum
int iMultitaskNum = SystemProperties.getInt("persist.pm.multitask", 6);
iMultitaskNum代表是task数量,即线程数量.一般来说,线程数量一般等于CPU数量,让CPU资源获得最大的利用.
2.MultiTaskDealer.startDealer
调用MultiTaskDealer.startDealer方法,建立线程池.
3.dealer.addTask
for循环中建立Runnable,调用scanPackageTracedLI搜索安装apk,dealer.addTask方法把该任务提交给线程池处理.
4. dealer.waitAll()
主线程等待方法,同步锁是mObjWaitAll.只有当mTaskCount>0,即当仍然存在任务的时候,主线程进入线程等待.
5. afterExecute(Runnable r, Throwable t)
afterExecute方法会在线程任务执行后发生,这里最后会调用mObjWaitAll.notify()方法,去唤醒处于等待的主线程,当然他的判断条件显示是mTaskCount<=0,即是任务全部执行完成后.
简单总结
- addTask针对每一个apkfile的scanpackage任务提交到线程池处理,并且mTaskCount+=1做任务的计数.
- TaskCompleteNotify在每个任务执行后进行, mTaskCount-=1做任务的计数,并且判断是否需要唤醒等待的主线程.(通过mTaskCount判断任务是否全部完成 )
- waitAll,添加所有任务到线程池后,主线程做等待,等待任务全部完成后的唤醒,然后继续执行.
文末推荐一篇关于线程池知识的博客:http://www.cnblogs.com/exe19/p/5359885.html