PMS开机扫描APK多线程处理方案

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

你可能感兴趣的:(PMS开机扫描APK多线程处理方案)