Android应用程序安装过程浅析
我们知道在android中。安装应用是由PackageManager来管理的,可是我们发现PackageManager是一个抽象类。他的installPackage方法也没有详细的实现。
那在安装过程中是怎么运行的呐?
调用方
查看代码能够知道ApplicationPackageManager是直接继承自PackageManager的。所以终于代码会调用ApplicationPackageManager下的installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,String installerPackageName),而在installPackage里面又调用了installCommon。
installCommon的实现例如以下:
 private void installCommon(Uri packageURI,
            PackageInstallObserver observer, int flags, String installerPackageName,
            VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
        if (!"file".equals(packageURI.getScheme())) {
            throw new UnsupportedOperationException("Only file:// URIs are supported");
        }
        if (encryptionParams != null) {
            throw new UnsupportedOperationException("ContainerEncryptionParams not supported");
        }
        final String originPath = packageURI.getPath();
        try {
            mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                    verificationParams, null);
        } catch (RemoteException ignored) {
        }
    }
能够看到在installCommon终于调用了mPm.installPackage那mPm又是什么?能够发现mPM是一个IPackageManager,他在是ApplicationPackageManager的构造函数中传入的。那是什么时候调用构造函数的呐?在ContextImpl中调用getPackageManager时会进行调用,传入的pm在是ActivityThread中获取的。
    @Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }
        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn't matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }
        return null;
    }
我们再来看看ActivityThread.getPackageManager()的获取过程。它的代码例如以下:
public static IPackageManager getPackageManager() {
        if (sPackageManager != null) {
            //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
            return sPackageManager;
        }
        IBinder b = ServiceManager.getService("package");
        //Slog.v("PackageManager", "default service binder = " + b);
        sPackageManager = IPackageManager.Stub.asInterface(b);
        //Slog.v("PackageManager", "default service = " + sPackageManager);
        return sPackageManager;
    }
从上面能够看到IPackageManager是进程间通信的客户端, 首先是IPackageManager是通过IPackageManager.aidl文件生成,同一时候生成了存根类IPackageManager.Stub。代理类:IPackageManager.Stub.Proxy。他是IBinder类型,那远端又是谁呐?远端就是PackageManagerService,PackageManagerService继承自IPackageManager.Stub,因此终于的调用都是通过aidl由PackageManagerService运行。
因此我们主要来看看PackageManagerService中的运行过程。
安装方式
主要有几种方式。这里先来看看下面两种: 
       1:系统启动后扫描安装,会调用PackageManagerService的scanPackageLI函数, 
       2:应用市场安装,应用市场下载后会默认调用PackageManagerService的intallPackage函数。该函数终于也会调用到scanPackageLI。因此仅仅须要分析另外一种
流程图
我们能够大致看看代码调用的过程,流程图例如以下: 
运行过程
因为PackageManager终于是由PackageManagerService来运行的
 @Override
public void installPackage(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride) {
    installPackageAsUser(originPath, observer, installFlags, installerPackageName,
            verificationParams, packageAbiOverride, UserHandle.getCallingUserId());
}
主要传递了6个參数:
1,originPath,安装包的位置,他必须是file类型活在content的URI类型。
这里传递过来的是toString的一个字符串。
2,observer,是一个IPackageInstallObserver类型,通知回调调用者安装完毕
3。installFlags,他的值是INSTALL_FORWARD_LOCK,INSTALL_REPLACE_EXISTING。INSTALL_ALLOW_TEST三个中的一个,INSTALL_FORWARD_LOCK表示安装过程中是否锁定。INSTALL_REPLACE_EXISTING表示是否替换安装包。INSTALL_ALLOW_TEST是否測试安装包。假设有改标志,manifest必须配置android:testOnly
4。installerPackageName,安装包包名
5。verificationParams。代表验证參数用于验证包安装。
6,packageAbiOverride,一般传null
该函数调用了installPackageAsUser函数,installPackageAsUser函数例如以下:
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
        int installFlags, String installerPackageName, VerificationParams verificationParams,
        String packageAbiOverride, int userId) {
    mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);
    final int callingUid = Binder.getCallingUid();
    enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");
    if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
        try {
            if (observer != null) {
                observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);
            }
        } catch (RemoteException re) {
        }
        return;
    }
    if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
        installFlags |= PackageManager.INSTALL_FROM_ADB;
    } else {
        // Caller holds INSTALL_PACKAGES permission, so we're less strict
        // about installerPackageName.
        installFlags &= ~PackageManager.INSTALL_FROM_ADB;
        installFlags &= ~PackageManager.INSTALL_ALL_USERS;
    }
    UserHandle user;
    if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
        user = UserHandle.ALL;
    } else {
        user = new UserHandle(userId);
    }
    // Only system components can circumvent runtime permissions when installing.
    if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
            && mContext.checkCallingOrSelfPermission(Manifest.permission
            .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
        throw new SecurityException("You need the "
                + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
                + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
    }
    verificationParams.setInstallerUid(callingUid);
    final File originFile = new File(originPath);
    final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);
    final Message msg = mHandler.obtainMessage(INIT_COPY);
    msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,
            null, verificationParams, user, packageAbiOverride, null);
    mHandler.sendMessage(msg);
}
该函数主要做了下面操作,第一获取权限,假设被拒绝则退出运行,接着设置installFlags參数,最后一步发送了一个what为INIT_COPY的message。參数为InstallParams,记住该參数,后面还会多次用到。看what的名称INIT_COPY,看起来是表达初始化而且拷贝,那是不是真是这样呐?我们去看看这个操作,这个操做是PackageHandler来运行的。PackageHandler继续字Handler,那我们来详细看看运行代码:
case INIT_COPY: {
    HandlerParams params = (HandlerParams) msg.obj;
    int idx = mPendingInstalls.size();
    if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);
    // If a bind was already initiated we dont really
    // need to do anything. The pending install
    // will be processed later on.
    if (!mBound) {
        // If this is the only one pending we might
        // have to bind to the service again.
        if (!connectToService()) {
            Slog.e(TAG, "Failed to bind to media container service");
            params.serviceError();
            return;
        } else {
            // Once we bind to the service, the first
            // pending request will be processed.
            mPendingInstalls.add(idx, params);
        }
    } else {
        mPendingInstalls.add(idx, params);
        // Already bound to the service. Just make
        // sure we trigger off processing the first request.
        if (idx == 0) {
            mHandler.sendEmptyMessage(MCS_BOUND);
        }
    }
    break;
}
能够看到首先取出參数params,这个params就是之前传入的InstallParams。接着获取等待安装队列的内容个数,因为初始mBound为false,因此会进入该推断。之后运行了connectToService函数。如个返回false表示连接失败,直接行使params的serviceError函数来结束当前运行,假设为true这时将params加入到mPendingInstalls的最后一个位置,connectToService函数又是什么呐?当前代码也没有不论什么初始化和copy有关的操作啊?那我们就去看看connectToService到底干了什么?
private boolean connectToService() {
    if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +
            " DefaultContainerService");
    Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
    if (mContext.bindServiceAsUser(service, mDefContainerConn,
            Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        mBound = true;
        return true;
    }
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    return false;
}
能够看到这里bind到了一个service,service的名称是DefaultContainerService,这又是个什么service呐?而且在绑定之前先设置该进程的优先级为THREAD_PRIORITY_DEFAULT,运行完毕后再次设置为THREAD_PRIORITY_BACKGROUND。这里我们也没有看到有不论什么init/copy的操作。那init/copy操作到底在什么地方,绑定的这个服务又是什么?我们来分析一下代码,看到bindServiceAsUser中有一个connection參数。我们来看看connection參数,这个connection类型是DefaultContainerConnection:
class DefaultContainerConnection implements ServiceConnection {
    public void onServiceConnected(ComponentName name, IBinder service) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
        IMediaContainerService imcs =
            IMediaContainerService.Stub.asInterface(service);
        mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
    }
    public void onServiceDisconnected(ComponentName name) {
        if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
    }
}
能够看到当绑定成功后在onServiceConnected中将一个IBinder转换成了一个IMediaContainerService,这个又是什么呐?这个就是在onServiceConnected回调函数中依据參数传进来的IMediaContainerService.Stub的对象引用创建的一个远程代理对象。以后PackageManagerService服务通过该代理对象訪问DefaultContainerService服务。
DefaultContainerService是一个应用服务。详细负责实现APK等相关资源文件在内部或外部存储器上的存储工作,DefaultContainerService服务中提供了一个IMediaContainerService.Stub桩对象。
转换后,接下来我们看到这里又发送了一个what为MCS_BOUND的message,參数为之前获得的IMediaContainerService,这里也没有不论什么init/copy操作。那我们继续跟进看看该what运行了什么?
case MCS_BOUND: {
    if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
    if (msg.obj != null) {
        mContainerService = (IMediaContainerService) msg.obj;
    }
    if (mContainerService == null) {
        if (!mBound) {
            // Something seriously wrong since we are not bound and we are not
            // waiting for connection. Bail out.
            Slog.e(TAG, "Cannot bind to media container service");
            for (HandlerParams params : mPendingInstalls) {
                // Indicate service bind error
                params.serviceError();
            }
            mPendingInstalls.clear();
        } else {
            Slog.w(TAG, "Waiting to connect to media container service");
        }
    } else if (mPendingInstalls.size() > 0) {
        HandlerParams params = mPendingInstalls.get(0);
        if (params != null) {
            if (params.startCopy()) {
                // We are done...  look for more work or to
                // go idle.
                if (DEBUG_SD_INSTALL) Log.i(TAG,
                        "Checking for more work or unbind...");
                // Delete pending install
                if (mPendingInstalls.size() > 0) {
                    mPendingInstalls.remove(0);
                }
                if (mPendingInstalls.size() == 0) {
                    if (mBound) {
                        if (DEBUG_SD_INSTALL) Log.i(TAG,
                                "Posting delayed MCS_UNBIND");
                        removeMessages(MCS_UNBIND);
                        Message ubmsg = obtainMessage(MCS_UNBIND);
                        // Unbind after a little delay, to avoid
                        // continual thrashing.
                        sendMessageDelayed(ubmsg, 10000);
                    }
                } else {
                    // There are more pending requests in queue.
                    // Just post MCS_BOUND message to trigger processing
                    // of next pending install.
                    if (DEBUG_SD_INSTALL) Log.i(TAG,
                            "Posting MCS_BOUND for next work");
                    mHandler.sendEmptyMessage(MCS_BOUND);
                }
            }
        }
    } else {
        // Should never happen ideally.
        Slog.w(TAG, "Empty queue");
    }
    break;
}
能够看到这里首先获取了传入的參数,假设參数为空,则调用HandlerParams的serviceError,而且清空mPendingInstalls列表,否则获取到等待安装列表中的第一个对象,就是我们最初始加入进的InstallParams,这里我们看到调用了InstallParams的startCopy函数。运行完毕后移除该參数,假设等待安装列表为空且当前绑定状态为true。则发一个what为MCS_UNBIND的解绑操作,否则就继续运行该操作。将等待列表中的一个一个运行。MCS_UNBIND与MCS_RECONNECT,这就不详细说了。MCS_UNBIND主要是解绑之前的链接,MCS_RECONNECT是又一次绑定链接,那我们继续看看startCopy函数:
final boolean startCopy() {
    boolean res;
    try {
        if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
        if (++mRetries > MAX_RETRIES) {
            Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
            mHandler.sendEmptyMessage(MCS_GIVE_UP);
            handleServiceError();
            return false;
        } else {
            handleStartCopy();
            res = true;
        }
    } catch (RemoteException e) {
        if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
        mHandler.sendEmptyMessage(MCS_RECONNECT);
        res = false;
    }
    handleReturnCode();
    return res;
}
这里最多会重试4次,假设超过最大次数则发送一个what为MCS_GIVE_UP的message表示安装失败,否则调用handleStartCopy。我们来看看handleStartCopy。因为取出的是InstallParams參数。因此这里调用的是InstallParams的handleStartCopy函数:
/*
 * Invoke remote method to get package information and install
 * location values. Override install location based on default
 * policy if needed and then create install arguments based
 * on the install location.
 */
public void handleStartCopy() throws RemoteException {
    int ret = PackageManager.INSTALL_SUCCEEDED;
    // If we're already staged, we've firmly committed to an install location
    if (origin.staged) {
        if (origin.file != null) {
            installFlags |= PackageManager.INSTALL_INTERNAL;
            installFlags &= ~PackageManager.INSTALL_EXTERNAL;
        } else if (origin.cid != null) {
            installFlags |= PackageManager.INSTALL_EXTERNAL;
            installFlags &= ~PackageManager.INSTALL_INTERNAL;
        } else {
            throw new IllegalStateException("Invalid stage location");
        }
    }
    final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
    final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
    PackageInfoLite pkgLite = null;
    if (onInt && onSd) {
        // Check if both bits are set.
        Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
        ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
    } else {
        pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                packageAbiOverride);
        /*
         * If we have too little free space, try to free cache
         * before giving up.
         */
        if (!origin.staged && pkgLite.recommendedInstallLocation
                == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
            // TODO: focus freeing disk space on the target device
            final StorageManager storage = StorageManager.from(mContext);
            final long lowThreshold = storage.getStorageLowBytes(
                    Environment.getDataDirectory());
            final long sizeBytes = mContainerService.calculateInstalledSize(
                    origin.resolvedPath, isForwardLocked(), packageAbiOverride);
            if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) {
                pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                        installFlags, packageAbiOverride);
            }
            /*
             * The cache free must have deleted the file we
             * downloaded to install.
             *
             * TODO: fix the "freeCache" call to not delete
             *       the file we care about.
             */
            if (pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                pkgLite.recommendedInstallLocation
                    = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
            }
        }
    }
    // 设置ret
    final InstallArgs args = createInstallArgs(this);
    mArgs = args;
    if (ret == PackageManager.INSTALL_SUCCEEDED) {
         /*
         * ADB installs appear as UserHandle.USER_ALL, and can only be performed by
         * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER.
         */
        int userIdentifier = getUser().getIdentifier();
        if (userIdentifier == UserHandle.USER_ALL
                && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {
            userIdentifier = UserHandle.USER_OWNER;
        }
        /*
         * Determine if we have any installed package verifiers. If we
         * do, then we'll defer to them to verify the packages.
         */
        final int requiredUid = mRequiredVerifierPackage == null ? -1
                : getPackageUid(mRequiredVerifierPackage, userIdentifier);
        if (!origin.existing && requiredUid != -1
                && isVerificationEnabled(userIdentifier, installFlags)) {
            final Intent verification = new Intent(
                    Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
            verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
            verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
                    PACKAGE_MIME_TYPE);
            verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            final List<ResolveInfo> receivers = queryIntentReceivers(verification,
                    PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS,
                    0 /* TODO: Which userId? */);
            if (DEBUG_VERIFY) {
                Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
                        + verification.toString() + " with " + pkgLite.verifiers.length
                        + " optional verifiers");
            }
            final int verificationId = mPendingVerificationToken++;
            verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);、
            .......
            设置verification的參数
            final PackageVerificationState verificationState = new PackageVerificationState(
                    requiredUid, args);
            mPendingVerification.append(verificationId, verificationState);
            final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
                    receivers, verificationState);
            // Apps installed for "all" users use the device owner to verify the app
            UserHandle verifierUser = getUser();
            if (verifierUser == UserHandle.ALL) {
                verifierUser = UserHandle.OWNER;
            }
            /*
             * If any sufficient verifiers were listed in the package
             * manifest, attempt to ask them.
             */
            if (sufficientVerifiers != null) {
                final int N = sufficientVerifiers.size();
                if (N == 0) {
                    Slog.i(TAG, "Additional verifiers required, but none installed.");
                    ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
                } else {
                    for (int i = 0; i < N; i++) {
                        final ComponentName verifierComponent = sufficientVerifiers.get(i);
                        final Intent sufficientIntent = new Intent(verification);
                        sufficientIntent.setComponent(verifierComponent);
                        mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
                    }
                }
            }
            final ComponentName requiredVerifierComponent = matchComponentForVerifier(
                    mRequiredVerifierPackage, receivers);
            if (ret == PackageManager.INSTALL_SUCCEEDED
                    && mRequiredVerifierPackage != null) {
                /*
                 * Send the intent to the required verification agent,
                 * but only start the verification timeout after the
                 * target BroadcastReceivers have run.
                 */
                verification.setComponent(requiredVerifierComponent);
                mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
                        android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
                        new BroadcastReceiver() {
                            @Override
                            public void onReceive(Context context, Intent intent) {
                                final Message msg = mHandler
                                        .obtainMessage(CHECK_PENDING_VERIFICATION);
                                msg.arg1 = verificationId;
                                mHandler.sendMessageDelayed(msg, getVerificationTimeout());
                            }
                        }, null, 0, null, null);
                /*
                 * We don't want the copy to proceed until verification
                 * succeeds, so null out this field.
                 */
                mArgs = null;
            }
        } else {
            /*
             * No package verification is enabled, so immediately start
             * the remote call to initiate copy using temporary file.
             */
            ret = args.copyApk(mContainerService, true);
        }
    }
    mRet = ret;
}
@Override
void handleReturnCode() {
    // If mArgs is null, then MCS couldn't be reached. When it
    // reconnects, it will try again to install. At that point, this
    // will succeed.
    if (mArgs != null) {
        processPendingInstall(mArgs, mRet);
    }
}
首先设置installFlags參数,之后设置ret參数与verification參数,之后依据參数创建了InstallArgs參数,依据參数这里实际返回的是AsecInstallArgs类型,假设该包须要被验证,则发送一个广播进行包验证,否则直接拷贝apk。广播的onReceive中发送了一个what为CHECK_PENDING_VERIFICATION的message,參数为verificationId。handleReturnCode中直接调用了processPendingInstall来进行安装处理,我们先来看看CHECK_PENDING_VERIFICATION的处理,这里终于也会调用到processPendingInstall。
case CHECK_PENDING_VERIFICATION: {
    final int verificationId = msg.arg1;
    final PackageVerificationState state = mPendingVerification.get(verificationId);
    if ((state != null) && !state.timeoutExtended()) {
        final InstallArgs args = state.getInstallArgs();
        final Uri originUri = Uri.fromFile(args.origin.resolvedFile);
        Slog.i(TAG, "Verification timed out for " + originUri);
        mPendingVerification.remove(verificationId);
        int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
        if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) {
            Slog.i(TAG, "Continuing with installation of " + originUri);
            state.setVerifierResponse(Binder.getCallingUid(),
                    PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT);
            broadcastPackageVerified(verificationId, originUri,
                    PackageManager.VERIFICATION_ALLOW,
                    state.getInstallArgs().getUser());
            try {
                ret = args.copyApk(mContainerService, true);
            } catch (RemoteException e) {
                Slog.e(TAG, "Could not contact the ContainerService");
            }
        } else {
            broadcastPackageVerified(verificationId, originUri,
                    PackageManager.VERIFICATION_REJECT,
                    state.getInstallArgs().getUser());
        }
        processPendingInstall(args, ret);
        mHandler.sendEmptyMessage(MCS_UNBIND);
    }
    break;
}
验证完毕后发送一个验证完毕的广播,之后调用InstallArgs的copyApk函数拷贝包。这里的InstallArgs是什么类型呐?就是前面创建的AsecInstallArgs类型。因此运行的是AsecInstallArgs的copyApk函数。运行完毕后调用processPendingInstall。copyApk主要是拷贝包到指定的文件夹下。这里就不详述了。接着看看processPendingInstall函数:
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
    // Queue up an async operation since the package installation may take a little while.
    mHandler.post(new Runnable() {
        public void run() {
            mHandler.removeCallbacks(this);
             // Result object to be returned
            PackageInstalledInfo res = new PackageInstalledInfo();
            res.returnCode = currentStatus;
            res.uid = -1;
            res.pkg = null;
            res.removedInfo = new PackageRemovedInfo();
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                args.doPreInstall(res.returnCode);
                synchronized (mInstallLock) {
                    installPackageLI(args, res);
                }
                args.doPostInstall(res.returnCode, res.uid);
            }
            // A restore should be performed at this point if (a) the install
            // succeeded, (b) the operation is not an update, and (c) the new
            // package has not opted out of backup participation.
            final boolean update = res.removedInfo.removedPackage != null;
            final int flags = (res.pkg == null) ?
0 : res.pkg.applicationInfo.flags;
            boolean doRestore = !update
                    && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
            // Set up the post-install work request bookkeeping.  This will be used
            // and cleaned up by the post-install event handling regardless of whether
            // there's a restore pass performed.  Token values are >= 1.
            int token;
            if (mNextInstallToken < 0) mNextInstallToken = 1;
            token = mNextInstallToken++;
            PostInstallData data = new PostInstallData(args, res);
            mRunningInstalls.put(token, data);
            if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
            if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
                // Pass responsibility to the Backup Manager.  It will perform a
                // restore if appropriate, then pass responsibility back to the
                // Package Manager to run the post-install observer callbacks
                // and broadcasts.
                IBackupManager bm = IBackupManager.Stub.asInterface(
                        ServiceManager.getService(Context.BACKUP_SERVICE));
                if (bm != null) {
                    if (DEBUG_INSTALL) Log.v(TAG, "token " + token
                            + " to BM for possible restore");
                    try {
                        if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {
                            bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
                        } else {
                            doRestore = false;
                        }
                    } catch (RemoteException e) {
                        // can't happen; the backup manager is local
                    } catch (Exception e) {
                        Slog.e(TAG, "Exception trying to enqueue restore", e);
                        doRestore = false;
                    }
                } else {
                    Slog.e(TAG, "Backup Manager not found!");
                    doRestore = false;
                }
            }
            if (!doRestore) {
                // No restore possible, or the Backup Manager was mysteriously not
                // available -- just fire the post-install work request directly.
                if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
                Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                mHandler.sendMessage(msg);
            }
        }
    });
}
这里post一个Runnable来运行内部的逻辑,主要做了例如以下操作: 
       1。锁定后安装包。通过调用installPackageLI来进行的 
       2,接下来都是运行备份操作,备份是通过BackupManagerService来完毕的。备份完毕后,通过发送what为POST_INSTALL的message来继续处理 
我们先来看看installPackageLI的运行过程:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
    //...... 初始化參数....
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setDisplayMetrics(mMetrics);
    final PackageParser.Package pkg;
    try {
        pkg = pp.parsePackage(tmpPackageFile, parseFlags);
    } catch (PackageParserException e) {
        res.setError("Failed parse during installPackageLI", e);
        return;
    }
    .................
    String oldCodePath = null;
    boolean systemApp = false;
    synchronized (mPackages) {
        // Check if installing already existing package
        if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
            String oldName = mSettings.mRenamedPackages.get(pkgName);
            if (pkg.mOriginalPackages != null
                    && pkg.mOriginalPackages.contains(oldName)
                    && mPackages.containsKey(oldName)) {
                // This package is derived from an original package,
                // and this device has been updating from that original
                // name.  We must continue using the original name, so
                // rename the new package here.
                pkg.setPackageName(oldName);
                pkgName = pkg.packageName;
                replace = true;
                if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName="
                        + oldName + " pkgName=" + pkgName);
            } else if (mPackages.containsKey(pkgName)) {
                // This package, under its official name, already exists
                // on the device; we should replace it.
                replace = true;
                if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
            }
            // Prevent apps opting out from runtime permissions
            if (replace) {
                PackageParser.Package oldPackage = mPackages.get(pkgName);
                final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion;
                final int newTargetSdk = pkg.applicationInfo.targetSdkVersion;
                if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1
                        && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                    res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE,
                            "Package " + pkg.packageName + " new target SDK " + newTargetSdk
                                    + " doesn't support runtime permissions but the old"
                                    + " target SDK " + oldTargetSdk + " does.");
                    return;
                }
            }
        }
        ...............
        // Check whether the newly-scanned package wants to define an already-defined perm
        int N = pkg.permissions.size();
        for (int i = N-1; i >= 0; i--) {
            PackageParser.Permission perm = pkg.permissions.get(i);
            BasePermission bp = mSettings.mPermissions.get(perm.info.name);
            if (bp != null) {
                // If the defining package is signed with our cert, it's okay.  This
                // also includes the "updating the same package" case, of course.
                // "updating same package" could also involve key-rotation.
                final boolean sigsOk;
                if (bp.sourcePackage.equals(pkg.packageName)
                        && (bp.packageSetting instanceof PackageSetting)
                        && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting,
                                scanFlags))) {
                    sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg);
                } else {
                    sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures,
                            pkg.mSignatures) == PackageManager.SIGNATURE_MATCH;
                }
                if (!sigsOk) {
                    // If the owning package is the system itself, we log but allow
                    // install to proceed; we fail the install on all other permission
                    // redefinitions.
                    if (!bp.sourcePackage.equals("android")) {
                        res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package "
                                + pkg.packageName + " attempting to redeclare permission "
                                + perm.info.name + " already owned by " + bp.sourcePackage);
                        res.origPermission = perm.info.name;
                        res.origPackage = bp.sourcePackage;
                        return;
                    } else {
                        Slog.w(TAG, "Package " + pkg.packageName
                                + " attempting to redeclare system permission "
                                + perm.info.name + "; ignoring new declaration");
                        pkg.permissions.remove(i);
                    }
                }
            }
        }
    }
    if (systemApp && onExternal) {
        // Disable updates to system apps on sdcard
        res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
                "Cannot install updates to system apps on sdcard");
        return;
    }
    if (args.move != null) {
        // We did an in-place move, so dex is ready to roll
        scanFlags |= SCAN_NO_DEX;
        scanFlags |= SCAN_MOVE;
        synchronized (mPackages) {
            final PackageSetting ps = mSettings.mPackages.get(pkgName);
            if (ps == null) {
                res.setError(INSTALL_FAILED_INTERNAL_ERROR,
                        "Missing settings for moved package " + pkgName);
            }
            // We moved the entire application as-is, so bring over the
            // previously derived ABI information.
            pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString;
            pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString;
        }
    } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {
        // Enable SCAN_NO_DEX flag to skip dexopt at a later stage
        scanFlags |= SCAN_NO_DEX;
        try {
            derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
                    true /* extract libs */);
        } catch (PackageManagerException pme) {
            Slog.e(TAG, "Error deriving application ABI", pme);
            res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI");
            return;
        }
        // Run dexopt before old package gets removed, to minimize time when app is unavailable
        int result = mPackageDexOptimizer
                .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
                        false /* defer */, false /* inclDependencies */);
        if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
            res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
            return;
        }
    }
    if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
        res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
        return;
    }
    startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
    if (replace) {
        replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                installerPackageName, volumeUuid, res);
    } else {
        installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                args.user, installerPackageName, volumeUuid, res);
    }
    synchronized (mPackages) {
        final PackageSetting ps = mSettings.mPackages.get(pkgName);
        if (ps != null) {
            res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
        }
    }
}
主要是运行了下面操作:
1,初始一些參数 2,调用PackageParser来解析包 3,推断是否是当前已有应用升级还是全新安装。这两个都须要坚持签名
4。检測权限,检測新扫描的权限是否是已经定义的权限 5,依据条件是否进行derivePackageAbi操作,这个操作的凝视为Derive
the ABI of a non-system package located at {@code scanFile}. This
information is derived purely on the basis of the contents of {@code
scanFile} and{@code cpuAbiOverride}. 6,開始intent filter验证7,依据是否是已有应用进行升级还是全新安装运行不同的操作
这里重要的主要是第2点和第7点,可是因为这里主要讲述android过程,因此对第2点不做详述。之后来详细解释该内容。我们假设这里是全新安装着调用installNewPackageLI:
/*
 * Install a non-existing package.
 */
private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,
        UserHandle user, String installerPackageName, String volumeUuid,
        PackageInstalledInfo res) {
    // Remember this for later, in case we need to rollback this install
    String pkgName = pkg.packageName;
    if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg);
    final boolean dataDirExists = Environment
            .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists();
    ...........
    try {
        PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,
                System.currentTimeMillis(), user);
        updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);
        // delete the partially installed application. the data directory will have to be
        // restored if it was already existing
        if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
            // remove package from internal structures.  Note that we want deletePackageX to
            // delete the package data and cache directories that it created in
            // scanPackageLocked, unless those directories existed before we even tried to
            // install.
            deletePackageLI(pkgName, UserHandle.ALL, false, null, null,
                    dataDirExists ?
PackageManager.DELETE_KEEP_DATA : 0,
                            res.removedInfo, true);
        }
    } catch (PackageManagerException e) {
        res.setError("Package couldn't be installed in " + pkg.codePath, e);
    }
}
主要是调用了scanPackageLI来进行包的安装,之后调用了updateSettingsLI,updateSettingsLI主要是更新了包的PackageSetting对象,主要更新了权限信息与安装完毕信息。这里我们继续查看scanPackageLI的运行:
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
        int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    boolean success = false;
    try {
        final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
                currentTime, user);
        success = true;
        return res;
    } finally {
        if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
            removeDataDirsLI(pkg.volumeUuid, pkg.packageName);
        }
    }
}
scanPackageLI主要调用了scanPackageDirtyLI,假设调用失败则调用removeDataDirsLI来移除安装信息。scanPackageDirtyLI的代码例如以下:
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
        int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
    final File scanFile = new File(pkg.codePath);
    ............
    if (pkg.packageName.equals("android")) {
       .............
    }
    ................
    // Initialize package source and resource directories
    File destCodeFile = new File(pkg.applicationInfo.getCodePath());
    File destResourceFile = new File(pkg.applicationInfo.getResourcePath());
    SharedUserSetting suid = null;
    PackageSetting pkgSetting = null;
    // writer
    synchronized (mPackages) {
        // Check if we are renaming from an original package name.
        PackageSetting origPackage = null;
        String realName = null;
        if (pkg.mOriginalPackages != null) {
            // This package may need to be renamed to a previously
            // installed name.  Let's check on that...
            final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage);
            if (pkg.mOriginalPackages.contains(renamed)) {
                // This package had originally been installed as the
                // original name, and we have already taken care of
                // transitioning to the new one.  Just update the new
                // one to continue using the old name.
                realName = pkg.mRealPackage;
                if (!pkg.packageName.equals(renamed)) {
                    // Callers into this function may have already taken
                    // care of renaming the package; only do it here if
                    // it is not already done.
                    pkg.setPackageName(renamed);
                }
            } else {
                for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) {
                    if ((origPackage = mSettings.peekPackageLPr(
                            pkg.mOriginalPackages.get(i))) != null) {
                        // We do have the package already installed under its
                        // original name...  should we use it?
if (!verifyPackageUpdateLPr(origPackage, pkg)) {
                            // New package is not compatible with original.
                            origPackage = null;
                            continue;
                        } else if (origPackage.sharedUser != null) {
                            // Make sure uid is compatible between packages.
                            if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
                                Slog.w(TAG, "Unable to migrate data from " + origPackage.name
                                        + " to " + pkg.packageName + ": old uid "
                                        + origPackage.sharedUser.name
                                        + " differs from " + pkg.mSharedUserId);
                                origPackage = null;
                                continue;
                            }
                        } else {
                            if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package "
                                    + pkg.packageName + " to old name " + origPackage.name);
                        }
                        break;
                    }
                }
            }
        }
         .........
        // Just create the setting, don't add it yet. For already existing packages
        // the PkgSetting exists already and doesn't have to be created.
        pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
                destResourceFile, pkg.applicationInfo.nativeLibraryRootDir,
                pkg.applicationInfo.primaryCpuAbi,
                pkg.applicationInfo.secondaryCpuAbi,
                pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags,
                user, false);
       ...............
        if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
            // Check all shared libraries and map to their actual file path.
            // We only do this here for apps not on a system dir, because those
            // are the only ones that can fail an install due to this.  We
            // will take care of the system apps by updating all of their
            // library paths after the scan is done.
            updateSharedLibrariesLPw(pkg, null);
        }
        pkg.applicationInfo.uid = pkgSetting.appId;
        pkg.mExtras = pkgSetting;
        if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) {
            if (checkUpgradeKeySetLP(pkgSetting, pkg)) {
                // We just determined the app is signed correctly, so bring
                // over the latest parsed certs.
                pkgSetting.signatures.mSignatures = pkg.mSignatures;
            } else {
                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
                    throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE,
                            "Package " + pkg.packageName + " upgrade keys do not match the "
                            + "previously installed version");
                } else {
                    pkgSetting.signatures.mSignatures = pkg.mSignatures;
                    String msg = "System package " + pkg.packageName
                        + " signature changed; retaining data.";
                    reportSettingsProblem(Log.WARN, msg);
                }
            }
        } else {
            try {
                verifySignaturesLP(pkgSetting, pkg);
                // We just determined the app is signed correctly, so bring
                // over the latest parsed certs.
                pkgSetting.signatures.mSignatures = pkg.mSignatures;
            } catch (PackageManagerException e) {
            }
        }
        // Verify that this new package doesn't have any content providers
        // that conflict with existing packages.  Only do this if the
        // package isn't already installed, since we don't want to break
        // things that are installed.
        if ((scanFlags & SCAN_NEW_INSTALL) != 0) {
            final int N = pkg.providers.size();
            int i;
            for (i=0; i<N; i++) {
                PackageParser.Provider p = pkg.providers.get(i);
                if (p.info.authority != null) {
                    String names[] = p.info.authority.split(";");
                    for (int j = 0; j < names.length; j++) {
                        if (mProvidersByAuthority.containsKey(names[j])) {
                            PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
                            final String otherPackageName =
                                    ((other != null && other.getComponentName() != null) ?
                                            other.getComponentName().getPackageName() : "?");
                            throw new PackageManagerException(
                                    INSTALL_FAILED_CONFLICTING_PROVIDER,
                                            "Can't install because provider name " + names[j]
                                            + " (in package " + pkg.applicationInfo.packageName
                                            + ") is already used by " + otherPackageName);
                        }
                    }
                }
            }
        }
        if (pkg.mAdoptPermissions != null) {
            // This package wants to adopt ownership of permissions from
            // another package.
            for (int i = pkg.mAdoptPermissions.size() - 1; i >= 0; i--) {
                final String origName = pkg.mAdoptPermissions.get(i);
                final PackageSetting orig = mSettings.peekPackageLPr(origName);
                if (orig != null) {
                    if (verifyPackageUpdateLPr(orig, pkg)) {
                        Slog.i(TAG, "Adopting permissions from " + origName + " to "
                                + pkg.packageName);
                        mSettings.transferPermissionsLPw(origName, pkg.packageName);
                    }
                }
            }
        }
    }
    ..........
    File dataPath;
    if (mPlatformPackage == pkg) {
        // The system package is special.
        dataPath = new File(Environment.getDataDirectory(), "system");
        pkg.applicationInfo.dataDir = dataPath.getPath();
    } else {
        // This is a normal package, need to make its data directory.
        dataPath = Environment.getDataUserPackageDirectory(pkg.volumeUuid,
                UserHandle.USER_OWNER, pkg.packageName);
        boolean uidError = false;
        if (dataPath.exists()) {
            int currentUid = 0;
            try {
                StructStat stat = Os.stat(dataPath.getPath());
                currentUid = stat.st_uid;
            } catch (ErrnoException e) {
                Slog.e(TAG, "Couldn't stat path " + dataPath.getPath(), e);
            }
            // If we have mismatched owners for the data path, we have a problem.
            if (currentUid != pkg.applicationInfo.uid) {
                boolean recovered = false;
                if (currentUid == 0) {
                    // The directory somehow became owned by root.  Wow.
                    // This is probably because the system was stopped while
                    // installd was in the middle of messing with its libs
                    // directory.  Ask installd to fix that.
                    int ret = mInstaller.fixUid(pkg.volumeUuid, pkgName,
                            pkg.applicationInfo.uid, pkg.applicationInfo.uid);
                    if (ret >= 0) {
                        recovered = true;
                        String msg = "Package " + pkg.packageName
                                + " unexpectedly changed to uid 0; recovered to " +
                                + pkg.applicationInfo.uid;
                        reportSettingsProblem(Log.WARN, msg);
                    }
                }
                if (!recovered && ((parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
                        || (scanFlags&SCAN_BOOTING) != 0)) {
                    // If this is a system app, we can at least delete its
                    // current data so the application will still work.
                    int ret = removeDataDirsLI(pkg.volumeUuid, pkgName);
                    if (ret >= 0) {
                        // TODO: Kill the processes first
                        // Old data gone!
                        String prefix = (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0
                                ? "System package " : "Third party package ";
                        String msg = prefix + pkg.packageName
                                + " has changed from uid: "
                                + currentUid + " to "
                                + pkg.applicationInfo.uid + "; old data erased";
                        reportSettingsProblem(Log.WARN, msg);
                        recovered = true;
                        // And now re-install the app.
                        ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
                                pkg.applicationInfo.seinfo);
                        if (ret == -1) {
                            // Ack should not happen!
                            msg = prefix + pkg.packageName
                                    + " could not have data directory re-created after delete.";
                            reportSettingsProblem(Log.WARN, msg);
                            throw new PackageManagerException(
                                    INSTALL_FAILED_INSUFFICIENT_STORAGE, msg);
                        }
                    }
                    if (!recovered) {
                        mHasSystemUidErrors = true;
                    }
                }
                ..............
            }
            pkg.applicationInfo.dataDir = dataPath.getPath();
            if (mShouldRestoreconData) {
                Slog.i(TAG, "SELinux relabeling of " + pkg.packageName + " issued.");
                mInstaller.restoreconData(pkg.volumeUuid, pkg.packageName,
                        pkg.applicationInfo.seinfo, pkg.applicationInfo.uid);
            }
        } else {
            if (DEBUG_PACKAGE_SCANNING) {
                if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
                    Log.v(TAG, "Want this data dir: " + dataPath);
            }
            //invoke installer to do the actual installation
            int ret = createDataDirsLI(pkg.volumeUuid, pkgName, pkg.applicationInfo.uid,
                    pkg.applicationInfo.seinfo);
            ..................
        }
        pkgSetting.uidError = uidError;
    }
    final String path = scanFile.getPath();
    final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
    if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
        derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
        // Some system apps still use directory structure for native libraries
        // in which case we might end up not detecting abi solely based on apk
        // structure. Try to detect abi based on directory structure.
        if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp() &&
                pkg.applicationInfo.primaryCpuAbi == null) {
            setBundledAppAbisAndRoots(pkg, pkgSetting);
            setNativeLibraryPaths(pkg);
        }
    } else {
        // Set native library paths again. For moves, the path will be updated based on the
        // ABIs we've determined above. For non-moves, the path will be updated based on the
        // ABIs we determined during compilation, but the path will depend on the final
        // package path (after the rename away from the stage path).
        setNativeLibraryPaths(pkg);
    }
    if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
    final int[] userIds = sUserManager.getUserIds();
    synchronized (mInstallLock) {
        // Make sure all user data directories are ready to roll; we're okay
        // if they already exist
        if (!TextUtils.isEmpty(pkg.volumeUuid)) {
            for (int userId : userIds) {
                if (userId != 0) {
                    mInstaller.createUserData(pkg.volumeUuid, pkg.packageName,
                            UserHandle.getUid(userId, pkg.applicationInfo.uid), userId,
                            pkg.applicationInfo.seinfo);
                }
            }
        }
        // Create a native library symlink only if we have native libraries
        // and if the native libraries are 32 bit libraries. We do not provide
        // this symlink for 64 bit libraries.
        if (pkg.applicationInfo.primaryCpuAbi != null &&
                !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
            final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
            for (int userId : userIds) {
                if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
                        nativeLibPath, userId) < 0) {
                    throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
                            "Failed linking native library dir (user=" + userId + ")");
                }
            }
        }
    }
    .............
    ArrayList<PackageParser.Package> clientLibPkgs = null;
    // We also need to dexopt any apps that are dependent on this library.  Note that
    // if these fail, we should abort the install since installing the library will
    // result in some apps being broken.
    if (clientLibPkgs != null) {
        if ((scanFlags & SCAN_NO_DEX) == 0) {
            for (int i = 0; i < clientLibPkgs.size(); i++) {
                PackageParser.Package clientPkg = clientLibPkgs.get(i);
                int result = mPackageDexOptimizer.performDexOpt(clientPkg,
                        null /* instruction sets */, forceDex,
                        (scanFlags & SCAN_DEFER_DEX) != 0, false);
                if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
                    throw new PackageManagerException(INSTALL_FAILED_DEXOPT,
                            "scanPackageLI failed to dexopt clientLibPkgs");
                }
            }
        }
    }
    // Request the ActivityManager to kill the process(only for existing packages)
    // so that we do not end up in a confused state while the user is still using the older
    // version of the application while the new one gets installed.
    if ((scanFlags & SCAN_REPLACING) != 0) {
        killApplication(pkg.applicationInfo.packageName,
                    pkg.applicationInfo.uid, "replace pkg");
    }
    // Also need to kill any apps that are dependent on the library.
    if (clientLibPkgs != null) {
        for (int i=0; i<clientLibPkgs.size(); i++) {
            PackageParser.Package clientPkg = clientLibPkgs.get(i);
            killApplication(clientPkg.applicationInfo.packageName,
                    clientPkg.applicationInfo.uid, "update lib");
        }
    }
    // Make sure we're not adding any bogus keyset info
    KeySetManagerService ksms = mSettings.mKeySetManagerService;
    ksms.assertScannedPackageValid(pkg);
    // writer
    synchronized (mPackages) {
        // We don't expect installation to fail beyond this point
        // Add the package's KeySets to the global KeySetManagerService
        ksms.addScannedPackageLPw(pkg);
        int N = pkg.providers.size();
        StringBuilder r = null;
        int i;
        for (i=0; i<N; i++) {
            PackageParser.Provider p = pkg.providers.get(i);
            p.info.processName = fixProcessName(pkg.applicationInfo.processName,
                    p.info.processName, pkg.applicationInfo.uid);
            mProviders.addProvider(p);
            p.syncable = p.info.isSyncable;
            if (p.info.authority != null) {
                String names[] = p.info.authority.split(";");
                p.info.authority = null;
                for (int j = 0; j < names.length; j++) {
                    if (j == 1 && p.syncable) {
                        // We only want the first authority for a provider to possibly be
                        // syncable, so if we already added this provider using a different
                        // authority clear the syncable flag. We copy the provider before
                        // changing it because the mProviders object contains a reference
                        // to a provider that we don't want to change.
                        // Only do this for the second authority since the resulting provider
                        // object can be the same for all future authorities for this provider.
                        p = new PackageParser.Provider(p);
                        p.syncable = false;
                    }
                    if (!mProvidersByAuthority.containsKey(names[j])) {
                        mProvidersByAuthority.put(names[j], p);
                        if (p.info.authority == null) {
                            p.info.authority = names[j];
                        } else {
                            p.info.authority = p.info.authority + ";" + names[j];
                        }
                        if (DEBUG_PACKAGE_SCANNING) {
                            if ((parseFlags & PackageParser.PARSE_CHATTY) != 0)
                                Log.d(TAG, "Registered content provider: " + names[j]
                                        + ", className = " + p.info.name + ", isSyncable = "
                                        + p.info.isSyncable);
                        }
                    } else {
                        PackageParser.Provider other = mProvidersByAuthority.get(names[j]);
                        Slog.w(TAG, "Skipping provider name " + names[j] +
                                " (in package " + pkg.applicationInfo.packageName +
                                "): name already used by "
                                + ((other != null && other.getComponentName() != null)
                                        ? other.getComponentName().getPackageName() : "?"));
                    }
                }
            }
            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(p.info.name);
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Providers: " + r);
        }
        N = pkg.services.size();
        r = null;
        for (i=0; i<N; i++) {
            PackageParser.Service s = pkg.services.get(i);
            s.info.processName = fixProcessName(pkg.applicationInfo.processName,
                    s.info.processName, pkg.applicationInfo.uid);
            mServices.addService(s);
            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(s.info.name);
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Services: " + r);
        }
        N = pkg.receivers.size();
        r = null;
        for (i=0; i<N; i++) {
            PackageParser.Activity a = pkg.receivers.get(i);
            a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                    a.info.processName, pkg.applicationInfo.uid);
            mReceivers.addActivity(a, "receiver");
            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(a.info.name);
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Receivers: " + r);
        }
        N = pkg.activities.size();
        r = null;
        for (i=0; i<N; i++) {
            PackageParser.Activity a = pkg.activities.get(i);
            a.info.processName = fixProcessName(pkg.applicationInfo.processName,
                    a.info.processName, pkg.applicationInfo.uid);
            mActivities.addActivity(a, "activity");
            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(a.info.name);
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Activities: " + r);
        }
        N = pkg.permissionGroups.size();
        r = null;
        for (i=0; i<N; i++) {
            PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
            PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
            if (cur == null) {
                mPermissionGroups.put(pg.info.name, pg);
                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                    if (r == null) {
                        r = new StringBuilder(256);
                    } else {
                        r.append(' ');
                    }
                    r.append(pg.info.name);
                }
            } else {
                Slog.w(TAG, "Permission group " + pg.info.name + " from package "
                        + pg.info.packageName + " ignored: original from "
                        + cur.info.packageName);
                if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                    if (r == null) {
                        r = new StringBuilder(256);
                    } else {
                        r.append(' ');
                    }
                    r.append("DUP:");
                    r.append(pg.info.name);
                }
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permission Groups: " + r);
        }
        N = pkg.permissions.size();
        r = null;
        for (i=0; i<N; i++) {
            PackageParser.Permission p = pkg.permissions.get(i);
            // Assume by default that we did not install this permission into the system.
            p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
            // Now that permission groups have a special meaning, we ignore permission
            // groups for legacy apps to prevent unexpected behavior. In particular,
            // permissions for one app being granted to someone just becuase they happen
            // to be in a group defined by another app (before this had no implications).
            if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
                p.group = mPermissionGroups.get(p.info.group);
                // Warn for a permission in an unknown group.
                if (p.info.group != null && p.group == null) {
                    Slog.w(TAG, "Permission " + p.info.name + " from package "
                            + p.info.packageName + " in an unknown group " + p.info.group);
                }
            }
            ArrayMap<String, BasePermission> permissionMap =
                    p.tree ? mSettings.mPermissionTrees
                            : mSettings.mPermissions;
            BasePermission bp = permissionMap.get(p.info.name);
            // Allow system apps to redefine non-system permissions
            if (bp != null && !Objects.equals(bp.sourcePackage, p.info.packageName)) {
                final boolean currentOwnerIsSystem = (bp.perm != null
                        && isSystemApp(bp.perm.owner));
                if (isSystemApp(p.owner)) {
                    if (bp.type == BasePermission.TYPE_BUILTIN && bp.perm == null) {
                        // It's a built-in permission and no owner, take ownership now
                        bp.packageSetting = pkgSetting;
                        bp.perm = p;
                        bp.uid = pkg.applicationInfo.uid;
                        bp.sourcePackage = p.info.packageName;
                        p.info.flags |= PermissionInfo.FLAG_INSTALLED;
                    } else if (!currentOwnerIsSystem) {
                        String msg = "New decl " + p.owner + " of permission  "
                                + p.info.name + " is system; overriding " + bp.sourcePackage;
                        reportSettingsProblem(Log.WARN, msg);
                        bp = null;
                    }
                }
            }
            if (bp == null) {
                bp = new BasePermission(p.info.name, p.info.packageName,
                        BasePermission.TYPE_NORMAL);
                permissionMap.put(p.info.name, bp);
            }
            if (bp.perm == null) {
                if (bp.sourcePackage == null
                        || bp.sourcePackage.equals(p.info.packageName)) {
                    BasePermission tree = findPermissionTreeLP(p.info.name);
                    if (tree == null
                            || tree.sourcePackage.equals(p.info.packageName)) {
                        bp.packageSetting = pkgSetting;
                        bp.perm = p;
                        bp.uid = pkg.applicationInfo.uid;
                        bp.sourcePackage = p.info.packageName;
                        p.info.flags |= PermissionInfo.FLAG_INSTALLED;
                        if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                            if (r == null) {
                                r = new StringBuilder(256);
                            } else {
                                r.append(' ');
                            }
                            r.append(p.info.name);
                        }
                    } else {
                        Slog.w(TAG, "Permission " + p.info.name + " from package "
                                + p.info.packageName + " ignored: base tree "
                                + tree.name + " is from package "
                                + tree.sourcePackage);
                    }
                } else {
                    Slog.w(TAG, "Permission " + p.info.name + " from package "
                            + p.info.packageName + " ignored: original from "
                            + bp.sourcePackage);
                }
            } else if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append("DUP:");
                r.append(p.info.name);
            }
            if (bp.perm == p) {
                bp.protectionLevel = p.info.protectionLevel;
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Permissions: " + r);
        }
        N = pkg.instrumentation.size();
        r = null;
        for (i=0; i<N; i++) {
            PackageParser.Instrumentation a = pkg.instrumentation.get(i);
            a.info.packageName = pkg.applicationInfo.packageName;
            a.info.sourceDir = pkg.applicationInfo.sourceDir;
            a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
            a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
            a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
            a.info.dataDir = pkg.applicationInfo.dataDir;
            // TODO: Update instrumentation.nativeLibraryDir as well ? Does it
            // need other information about the application, like the ABI and what not ?
            a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
            mInstrumentation.put(a.getComponentName(), a);
            if ((parseFlags&PackageParser.PARSE_CHATTY) != 0) {
                if (r == null) {
                    r = new StringBuilder(256);
                } else {
                    r.append(' ');
                }
                r.append(a.info.name);
            }
        }
        if (r != null) {
            if (DEBUG_PACKAGE_SCANNING) Log.d(TAG, "  Instrumentation: " + r);
        }
        if (pkg.protectedBroadcasts != null) {
            N = pkg.protectedBroadcasts.size();
            for (i=0; i<N; i++) {
                mProtectedBroadcasts.add(pkg.protectedBroadcasts.get(i));
            }
        }
        ...............
    }
    return pkg;
}
这里主要做了下面操作:
1,检測路径代码路径是否存在不存在抛出异常
2,设置Package的applicationInfo信息
3。设置ResolverActivity信息
4,假设是系统程序则更改ResolverActivity信息
5,假设我们仅仅安装以及存在的包,则推断他的PackageSetting信息,假设路径不一致,測抛出异常
6,初始化包的代码与资源文件夹
7。检測我们是否须要重命名一个原始包
8。检測全部共享的libraries而且映射到真实的路径
9,如个是升级包则检測签名,假设新安装包则验证签名
10。检測新包不含有与已经存在包冲突的provider
11,检測当前包对于其它包所拥有的权限
12,创建包data文件夹。而且又一次调整uid。调用createDataDirsLI进行包的安装
13,设置包的本地的Library路径
14。创建包的用户数据。调用createUserData
15,对包进行opt操作,调用performDexOpt,终于调用的还是Install的dexopt函数
16。假设是已存在的包。则调用ActivityManager杀死该进程
17。解析包的provider,并加入到ProviderIntentResolver,解析包的service,并加入到ServiceIntentResolver,解析包的receiver,并加入到ActivityIntentResolver,解析包的activity,并加入到ActivityIntentResolver,解析包的权利组与权限。最后解析instrumentation这个是測试用的,上述的解析主要是为了在应用中调用getPackageManager().resolveActivity等方法使用的。
上面主要是调用了createDataDirsLI来进行包的安装:
private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) {
    int[] users = sUserManager.getUserIds();
    int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo);
    if (res < 0) {
        return res;
    }
    for (int user : users) {
        if (user != 0) {
            res = mInstaller.createUserData(volumeUuid, packageName,
                    UserHandle.getUid(user, uid), user, seinfo);
            if (res < 0) {
                return res;
            }
        }
    }
    return res;
}
这里终于调用了mInstaller的intall函数,mInstaller是一个InstallerConnection,InstallerConnection里面是通过输入输出流与一个LocalSocket进行安装操作的,所以这里终于调用的InstallerConnection的intall函数,运行完毕后假设user不为空,创建用户数据。
包的安装过程到此就结束了,我们再回头看看POST_INSTALL进行了什么操作?
case POST_INSTALL: {
    if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
    PostInstallData data = mRunningInstalls.get(msg.arg1);
    mRunningInstalls.delete(msg.arg1);
    boolean deleteOld = false;
    if (data != null) {
        InstallArgs args = data.args;
        PackageInstalledInfo res = data.res;
        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
            final String packageName = res.pkg.applicationInfo.packageName;
            res.removedInfo.sendBroadcast(false, true, false);
            Bundle extras = new Bundle(1);
            extras.putInt(Intent.EXTRA_UID, res.uid);
            // Now that we successfully installed the package, grant runtime
            // permissions if requested before broadcasting the install.
            if ((args.installFlags
                    & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {
                grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),
                        args.installGrantPermissions);
            }
            // Determine the set of users who are adding this
            // package for the first time vs. those who are seeing
            // an update.
            int[] firstUsers;
            int[] updateUsers = new int[0];
            if (res.origUsers == null || res.origUsers.length == 0) {
                firstUsers = res.newUsers;
            } else {
                firstUsers = new int[0];
                for (int i=0; i<res.newUsers.length; i++) {
                    int user = res.newUsers[i];
                    boolean isNew = true;
                    for (int j=0; j<res.origUsers.length; j++) {
                        if (res.origUsers[j] == user) {
                            isNew = false;
                            break;
                        }
                    }
                    if (isNew) {
                        int[] newFirst = new int[firstUsers.length+1];
                        System.arraycopy(firstUsers, 0, newFirst, 0,
                                firstUsers.length);
                        newFirst[firstUsers.length] = user;
                        firstUsers = newFirst;
                    } else {
                        int[] newUpdate = new int[updateUsers.length+1];
                        System.arraycopy(updateUsers, 0, newUpdate, 0,
                                updateUsers.length);
                        newUpdate[updateUsers.length] = user;
                        updateUsers = newUpdate;
                    }
                }
            }
            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                    packageName, extras, null, null, firstUsers);
            final boolean update = res.removedInfo.removedPackage != null;
            if (update) {
                extras.putBoolean(Intent.EXTRA_REPLACING, true);
            }
            sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
                    packageName, extras, null, null, updateUsers);
            if (update) {
                sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
                        packageName, extras, null, null, updateUsers);
                sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
                        null, null, packageName, null, updateUsers);
                // treat asec-hosted packages like removable media on upgrade
                if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
                    if (DEBUG_INSTALL) {
                        Slog.i(TAG, "upgrading pkg " + res.pkg
                                + " is ASEC-hosted -> AVAILABLE");
                    }
                    int[] uidArray = new int[] { res.pkg.applicationInfo.uid };
                    ArrayList<String> pkgList = new ArrayList<String>(1);
                    pkgList.add(packageName);
                    sendResourcesChangedBroadcast(true, true,
                            pkgList,uidArray, null);
                }
            }
            if (res.removedInfo.args != null) {
                // Remove the replaced package's older resources safely now
                deleteOld = true;
            }
            // If this app is a browser and it's newly-installed for some
            // users, clear any default-browser state in those users
            if (firstUsers.length > 0) {
                // the app's nature doesn't depend on the user, so we can just
                // check its browser nature in any user and generalize.
                if (packageIsBrowser(packageName, firstUsers[0])) {
                    synchronized (mPackages) {
                        for (int userId : firstUsers) {
                            mSettings.setDefaultBrowserPackageNameLPw(null, userId);
                        }
                    }
                }
            }
            // Log current value of "unknown sources" setting
            EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
                getUnknownSourcesSettings());
        }
        // Force a gc to clear up things
        Runtime.getRuntime().gc();
        // We delete after a gc for applications  on sdcard.
        if (deleteOld) {
            synchronized (mInstallLock) {
                res.removedInfo.args.doPostDeleteLI(true);
            }
        }
        if (args.observer != null) {
            try {
                Bundle extras = extrasForInstallResult(res);
                args.observer.onPackageInstalled(res.name, res.returnCode,
                        res.returnMsg, extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    } else {
        Slog.e(TAG, "Bogus post-install token " + msg.arg1);
    }
} break;
万里长征最后一步,这里主要先将安装信息从安装列表中移除,这也是在之前processPendingInstall中加入的。包成功安装之后,在发送成功安装广播之前先获取运行时权限,获取权限后发送ACTION_PACKAGE_ADDED广播。假设是更新包再发送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED广播来通知其它应用,安装的广播发送完毕后发送一个资源更改的广播通知其它应用。假设该应用是一个浏览器,则先清除默认的浏览器设置。又一次检查浏览器设置。
上诉几步调用完毕之后。强制调用gc,来触发jvm进行垃圾回收操作。
gc调用后删除旧的安装信息,假设初始传入的IPackageInstallObserver2不为空,这回调调用方安装包安装完毕。
总结
到此大致分析了整个安装过程,还有非常多细节能够分析,比方parsePackage,之后能够再进行解析。整篇文章可能有理解错误的地方。望指出。
Android应用程序安装过程浅析的更多相关文章
- Android应用程序安装过程源代码分析
		
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6766010 Android系统在启动的过程中, ...
 - Android应用打包安装过程具体解释
		
Android应用打包安装过程(Run as Android Application ): 1,过程:Android Project --> Compilation and Packaging ...
 - 第三次作业(1)  Visual Studio程序安装过程和练习过程
		
Visual Studio程序安装过程和练习过程 第一步 首先要在网上找一个VS2013的安装包,之后我安装在D盘上,C盘上也需要有5.2G空间,勾选相应的选项,才能继续安装. 安装的过程很漫长,接近 ...
 - android APK应用安装过程以及默认安装路径[转]
		
一:安装过程 APK是类似Symbian Sis或Sisx的文件格式.通过将APK文件直接传到Android模拟器或Android手机中执行即可安装. Android应用安装有如下四种方式 1. ...
 - Android apk 的安装过程
		
Android应用安装有如下四种方式 1.系统应用安装――开机时完成,没有安装界面 2.网络下载应用安装――通过market应用完成,没有安装界面 3.ADB工具安装――没有安装界面. 4.第三方应用 ...
 - Android应用程序安装与Launcher启动机制
		
以下资料摘录整理自老罗的Android之旅博客,是对老罗的博客关于Android底层原理的一个抽象的知识概括总结(如有错误欢迎指出)(侵删):http://blog.csdn.net/luoshe ...
 - Android应用程序启动过程源代码分析
		
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程 ...
 - Android 应用程序启动过程源代码分析
		
本文转自:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程序的Activity的启动过程.在And ...
 - Android中APK安装过程及原理解析
		
[原文] 来自华为内部资料 应用安装是智能机的主要特点,即用户可以把各种应用(如游戏等)安装到手机上,并可以对其进行卸载等管理操作.APK是Android Package的缩写,即android安装包 ...
 
随机推荐
- IT实习总结-上海的实习的日子
			
在上海实习两个多月,从找工作,到面试,最后到创业公司工作,体会了一个人在外工作的那种工作时的压力与双休时的寂寞,有时甚至有点迷惘,可能这就是生活,每个人都会经历,在经历的过程中会有不同的味道. 说说我 ...
 - WM
			
#define WM_NULL 0x0000 #define WM_CREATE 0x0001 #define WM_DESTROY 0x0002 #define WM_MOVE 0x0003 #de ...
 - slover层解读
			
void Solver<Dtype>::UpdateSmoothedLoss(Dtype loss, int start_iter, int average_loss) { if (los ...
 - C89:应用篇 文件管理器
			
一.简介 用C语言做的一个文件管理器的轮子,因为经常开发中会用到跟数据流有关的,做完会放到Github上
 - 正确地使用Context
			
Context应该是每个入门Android开发的程序员第一个接触到的概念,它代表当前的上下文环境,可以用来实现很多功能的调用,语句如下. //获取资源管理器对象,进而可以访问到例如 string, c ...
 - Core Animation教程
			
http://dev.wo.com.cn/bbs/viewthread.jsp?tid=141767&page=1 http://blog.csdn.net/lvxiangan/article ...
 - 操作iframe的方法
			
子页面 <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&q ...
 - react-native Android WARNING: API 'variant.getMergeAssets()' is obsolete and has been replaced with 'variant.getMergeAssetsProvider()'.
			
android Studio 中打开react-native项目的android文件夹 在sync的过程中 发生warning: WARNING: API 'variant.getMergeAsset ...
 - 懒癌晚期学图论的时候自己用C语言写了个求可达性矩阵的算法~
			
可达性矩阵算法~ 直接上代码 #include <iostream> #include <cstring> using namespace std; #define n 5 v ...
 - 笔试算法题(33):烙饼排序问题 & N!阶乘十进制末尾0的个数二进制最低1的位置
			
出题:不同大小烙饼的排序问题:对于N块大小不一的烙饼,上下累在一起,由于一只手托着所有的饼,所以仅有一只手可以翻转饼(假设手足够大可以翻转任意块数的 饼),规定所有的大饼都出现在小饼的下面则说明已经排 ...