Android Intent的查找与匹配

App信息表的构建

在Android开发中,Intent及其重要,它是各个组件、进程之间通信的纽带。系统如何通过Intent来查找对应的组件?

在第一章中,系统启动之后就会注册各种系统服务,如WindowManagerService、ActivityManagerService等,其中就有一个PackageManagerService(PMS)。

PMS启动之后,会扫描系统中已安装的apk目录,例如系统App的安装目录为/system/app,第三方应用的目录为/data/app,PMS会解析apk包下的AndroidManifest文件得到App的相关信息,而每一个AndroidManifest.xml又包含了Activity、Service等组件的注册信息,当PMS扫描并且解析完这些信息之后就会构建好整个apk的信息树。

PMS解析已安装apk信息的过程

PMS,对于apk的解析工作在其构造函数时就已经开始了。

#PackageManagerService
public PackageManagerService(Context context, Installer installer,
        boolean factoryTest, boolean onlyCore) {
    ... ...
    //代码省略
    synchronized (mInstallLock) {
          //writer
          synchronized (mPackages) {
                ... ...
//代码省略
//获取/data目录
File dataDir = Environment.getDataDirectory();
// /data/app目录,即第三方应用的安装目录
mAppInstallDir = new File(dataDir, "app");
mAppLib32InstallDir = new File(dataDir, "app-lib");
mAsecInternalPath = new File(dataDir, "app-asec").getPath();
//系统app安装目录
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
... ...

File frameworkDir = new File(Environment.getRootDirectory(), "framework");
... ...
// Find base frameworks (resource packages without code).
scanDirTracedLI(frameworkDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR
        | PackageParser.PARSE_IS_PRIVILEGED,
        scanFlags | SCAN_NO_DEX, 0);

... ...
// Collect ordinary system packages.(普通系统包)
final File systemAppDir = new File(Environment.getRootDirectory(), "app");
scanDirTracedLI(systemAppDir, mDefParseFlags
        | PackageParser.PARSE_IS_SYSTEM
        | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);

          }
    }
}

 

#PackageManagerService
public void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) {
    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + dir.getAbsolutePath() + "]");
    try {
        scanDirLI(dir, parseFlags, scanFlags, currentTime);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}

 

从上述代码中可以看到,PMS不仅需要加载系统已安装的各类apk,在此之前还需要加载Framework资源与核心库,加载了资源与核心库之后才开始对扫描的指定目录下的apk文件进行解析,上述代码中只给出了系统apk的安装目录和第三方应用的安装目录,扫描函数为scanDirLI, 该函数如下:

#PackageManagerService
private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
    //1.获取该目录下的所有文件
    final File[] files = dir.listFiles();
    //代码省略
    
    
}

 

scanDirLI 就是扫描指定目录下的所有apk文件,然后通过调用scanPackageLI函数进行解析。重点是scanPackageLI函数中。

#PackageManagerService
/**扫描包并返回新解析的包
 *  Scans a package and returns the newly parsed package.
 *  Returns {@code null} in case of errors and the error code is stored in mLastScanError
 */
private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
        long currentTime, UserHandle user) throws PackageManagerException {
    if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
    //1.创建一个包解析器
    PackageParser pp = new PackageParser();
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setOnlyCoreApps(mOnlyCore);
    pp.setDisplayMetrics(mMetrics);
    pp.setCallback(mPackageParserCallback);

    if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
        parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
    }

    Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
    final PackageParser.Package pkg;
    try {
 	//2.解析apk包
        pkg = pp.parsePackage(scanFile, parseFlags);
    } catch (PackageParserException e) {
        throw PackageManagerException.from(e);
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }

    // Static shared libraries have synthetic package names
    if (pkg.applicationInfo.isStaticSharedLibrary()) {
        renameStaticSharedLibraryPackage(pkg);
    }
//3.解析apk包中Activity、Service等组件
    return scanPackageLI(pkg, scanFile, parseFlags, scanFlags, currentTime, user);
}

在 scanPackageLI 中首先构建一个PackageParser,也就是一个apk包解析器,然后调用PackageParser对象的parsePackage 函数。

#PackageManagerService
/**
 *  Scans a package and returns the newly parsed package.
 *  @throws PackageManagerException on a parse error.
 */
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
        final int policyFlags, int scanFlags, long currentTime, @Nullable UserHandle user)
        throws PackageManagerException {
    // If the package has children and this is the first dive in the function
    // we scan the package with the SCAN_CHECK_ONLY flag set to see whether all
    // packages (parent and children) would be successfully scanned before the
    // actual scan since scanning mutates internal state and we want to atomically
    // install the package and its children.
    if ((scanFlags & SCAN_CHECK_ONLY) == 0) {
        if (pkg.childPackages != null && pkg.childPackages.size() > 0) {
            scanFlags |= SCAN_CHECK_ONLY;
        }
    } else {
        scanFlags &= ~SCAN_CHECK_ONLY;
    }

    // Scan the parent
    PackageParser.Package scannedPkg = scanPackageInternalLI(pkg, scanFile, policyFlags,
            scanFlags, currentTime, user);

    // Scan the children
    final int childCount = (pkg.childPackages != null) ? pkg.childPackages.size() : 0;
    for (int i = 0; i < childCount; i++) {
        PackageParser.Package childPackage = pkg.childPackages.get(i);
        scanPackageInternalLI(childPackage, scanFile, policyFlags, scanFlags,
                currentTime, user);
    }


    if ((scanFlags & SCAN_CHECK_ONLY) != 0) {
        return scanPackageLI(pkg, scanFile, policyFlags, scanFlags, currentTime, user);
    }

    return scannedPkg;
}


#PackageParser

/**
 * Equivalent to {@link #parsePackage(File, int, boolean)} with {@code useCaches == false}.
 */
public Package parsePackage(File packageFile, int flags) throws PackageParserException {
    return parsePackage(packageFile, flags, false /* useCaches */);
}


#PackageParser
/**
 * Parse the package at the given location. Automatically detects if the
 * package is a monolithic style (single APK file) or cluster style
 * (directory of APKs).
 * <p>
 * This performs sanity checking on cluster style packages, such as
 * requiring identical package name and version codes, a single base APK,
 * and unique split names.
 * <p>
 * Note that this <em>does not</em> perform signature verification; that
 * must be done separately in {@link #collectCertificates(Package, int)}.
 *
 * If {@code useCaches} is true, the package parser might return a cached
 * result from a previous parse of the same {@code packageFile} with the same
 * {@code flags}. Note that this method does not check whether {@code packageFile}
 * has changed since the last parse, it's up to callers to do so.
 *
 * @see #parsePackageLite(File, int)
 */
public Package parsePackage(File packageFile, int flags, boolean useCaches)
        throws PackageParserException {
    Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;
    if (parsed != null) {
        return parsed;
    }

    long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
    //是否是文件夹类型
    if (packageFile.isDirectory()) {
        parsed = parseClusterPackage(packageFile, flags);
    } else {
        //解析单个apk
        parsed = parseMonolithicPackage(packageFile, flags);
    }

    long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;
    cacheResult(packageFile, flags, parsed);
    if (LOG_PARSE_TIMINGS) {
        parseTime = cacheTime - parseTime;
        cacheTime = SystemClock.uptimeMillis() - cacheTime;
        if (parseTime + cacheTime > LOG_PARSE_TIMINGS_THRESHOLD_MS) {
            Slog.i(TAG, "Parse times for '" + packageFile + "': parse=" + parseTime
                    + "ms, update_cache=" + cacheTime + " ms");
        }
    }
    return parsed;
}

在parsePackage函数中,会根据packageFile的类型来选择不同的解析方法,例如:parseClusterPackage 是一个文件夹,会解析文件夹下的所有apk;如果是一个文件,那么会调用parseMonolithicPackage解析单个apk。

/**解析单个apk
 * Parse the given APK file, treating it as as a single monolithic package.
 * <p>
 * Note that this <em>does not</em> perform signature verification; that
 * must be done separately in {@link #collectCertificates(Package, int)}.
 *
 * @deprecated external callers should move to
 *             {@link #parsePackage(File, int)}. Eventually this method will
 *             be marked private.
 */
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
   //构造应用资源
    final AssetManager assets = newConfiguredAssetManager();
    final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);
    if (mOnlyCoreApps) {
        if (!lite.coreApp) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                    "Not a coreApp: " + apkFile);
        }
    }

    try {
//1.调用parseBaseApk(apkFile, assets, flags);方法
        final Package pkg = parseBaseApk(apkFile, assets, flags);
        pkg.setCodePath(apkFile.getAbsolutePath());
        pkg.setUse32bitAbi(lite.use32bitAbi);
        return pkg;
    } finally {
        IoUtils.closeQuietly(assets);
    }
}

 

在PackageParser的parseMonolithicPackage 函数中,主要是调用了3个参数的 parseBaseApk(File apkFile, AssetManager assets, int flags) 函数,这个3个参数的parseBaseApk 函数最终会调用4个参数的 parseBaseApk (String apkPath, Resources res, XmlResourceParser parser, int flags, String[] outError)函数来解析得到Package对象。在4个参数的parseBaseApk 方法中主要就是解析。

#PackageParser
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
            throws PackageParserException {
        final String apkPath = apkFile.getAbsolutePath();

        String volumeUuid = null;
        if (apkPath.startsWith(MNT_EXPAND)) {
            final int end = apkPath.indexOf('/', MNT_EXPAND.length());
            volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);
        }

        mParseError = PackageManager.INSTALL_SUCCEEDED;
        mArchiveSourcePath = apkFile.getAbsolutePath();

        if (DEBUG_JAR) Slog.d(TAG, "Scanning base APK: " + apkPath);

        final int cookie = loadApkIntoAssetManager(assets, apkPath, flags);

        Resources res = null;
        XmlResourceParser parser = null;
        try {
            res = new Resources(assets, mMetrics, null);
            parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);

            final String[] outError = new String[1];
            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
            if (pkg == null) {
                throw new PackageParserException(mParseError,
                        apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
            }

            pkg.setVolumeUuid(volumeUuid);
            pkg.setApplicationVolumeUuid(volumeUuid);
            pkg.setBaseCodePath(apkPath);
            pkg.setSignatures(null);
            //porting theme<
            // If the pkg is a theme, we need to know what themes it overlays
            // and determine if it has an icon pack
            if (pkg.mIsThemeApk) {
                //Determine existance of Overlays
                //<panjuan for overlay 0207
                ArrayList<String> overlayTargets = scanPackageOverlays(apkFile);
                for(String overlay : overlayTargets) {
                    pkg.mOverlayTargets.add(overlay);
                }//panjuan for overlay>

                pkg.hasIconPack = packageHasIconPack(apkFile);
            }
            //porting theme>
            return pkg;

        } catch (PackageParserException e) {
            throw e;
        } catch (Exception e) {
            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
                    "Failed to read manifest from " + apkPath, e);
        } finally {
            IoUtils.closeQuietly(parser);
        }
    }



#PackageParser
private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
        String[] outError) throws XmlPullParserException, IOException {
    final String splitName;
    final String pkgName;

    try {
        Pair<String, String> packageSplit = parsePackageSplitNames(parser, parser);
        //1.解析到AndroidManifest的包名
        pkgName = packageSplit.first;
        splitName = packageSplit.second;

        if (!TextUtils.isEmpty(splitName)) {
            outError[0] = "Expected base APK, but found split " + splitName;
            mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
            return null;
        }
    } catch (PackageParserException e) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
        return null;
    }

    if (mCallback != null) {
        String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
        if (overlayPaths != null && overlayPaths.length > 0) {
            for (String overlayPath : overlayPaths) {
                res.getAssets().addOverlayPath(overlayPath, null, null, null, null);//porting theme
            }
        }

        //panjuan for overlay theme 0301 begin
        List<PackageParser.Package> themeOverlayPkgs = mCallback.getThemeOverlayApks(pkgName);
        if (themeOverlayPkgs != null && themeOverlayPkgs.size() > 0) {
            for (PackageParser.Package overlayPkg : themeOverlayPkgs) {
                String idmapPath = ThemeUtils.getIdmapPath(pkgName, overlayPkg.packageName);
                String resCachePath = ThemeUtils.getTargetCacheDir(pkgName, overlayPkg.packageName);
                String resApkPath = resCachePath + "/resources.apk";
                String prefixPath = ThemeUtils.getOverlayPathToTarget(pkgName);
                res.getAssets().addOverlayPath(idmapPath, overlayPkg.baseCodePath, resApkPath, apkPath, prefixPath);
            }
        }
        //panjuan for overlay theme 0301 end
    }

//2.构建Package对象
    final Package pkg = new Package(pkgName);

    TypedArray sa = res.obtainAttributes(parser,
            com.android.internal.R.styleable.AndroidManifest);

    pkg.mVersionCode = pkg.applicationInfo.versionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_versionCode, 0);
    pkg.baseRevisionCode = sa.getInteger(
            com.android.internal.R.styleable.AndroidManifest_revisionCode, 0);
    pkg.mVersionName = sa.getNonConfigurationString(
            com.android.internal.R.styleable.AndroidManifest_versionName, 0);
    if (pkg.mVersionName != null) {
        pkg.mVersionName = pkg.mVersionName.intern();
    }

    pkg.coreApp = parser.getAttributeBooleanValue(null, "coreApp", false);

    sa.recycle();

    return parseBaseApkCommon(pkg, null, res, parser, flags, outError);
}


private Package parseBaseApkCommon(Package pkg, Set<String> acceptedTags, Resources res,
    XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException,
    IOException {
   ... ... 
    	//3.解析AndroidManifest.xml中元素
int outerDepth = parser.getDepth();
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    continue;
}

String tagName = parser.getName();

if (acceptedTags != null && !acceptedTags.contains(tagName)) {
    Slog.w(TAG, "Skipping unsupported element under <manifest>: "
            + tagName + " at " + mArchiveSourcePath + " "
            + parser.getPositionDescription());
    XmlUtils.skipCurrentTag(parser);
    continue;
}

if (tagName.equals(TAG_APPLICATION)) {
    if (foundApp) {
        if (RIGID_PARSER) {
            outError[0] = "<manifest> has more than one <application>";
            mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
            return null;
        } else {
            Slog.w(TAG, "<manifest> has more than one <application>");
            XmlUtils.skipCurrentTag(parser);
            continue;
        }
    }

    foundApp = true;
    //4.解析Application标签,我们的activity、service等都在这个标签内
    if (!parseBaseApplication(pkg, res, parser, flags, outError)) {
        return null;
    }
}

else if (tagName.equals(TAG_PERMISSION_GROUP)) { =》"permission-group"
                if (!parsePermissionGroup(pkg, flags, res, parser, outError)) {
                    return null;
                }
            }
  else if (tagName.equals(TAG_USES_PERMISSION)) { =》"uses-permission"
//5.解析用户权限标签
        if (!parseUsesPermission(pkg, res, parser)) {
            return null;
        }
    }

解析其他标签
           这个parseBaseApk(parseBaseApkCommon)  函数才是真正解析AndroidManifest中的地方,这里我们给出了两个标签,即Application和user-perssion。 Application中包含了Activity、Service等标签,也就是Intent所需要的标签。
我们看看解析Application标签的函数parseBaseApplication。

#PackageParser
/**
 * Parse the {@code application} XML tree at the current parse location in a
 * <em>base APK</em> manifest.
 * <p>
 * When adding new features, carefully consider if they should also be
 * supported by split APKs.
 */(在这个方法中有Gome修改的地方)
private boolean parseBaseApplication(Package owner, Resources res,
        XmlResourceParser parser, int flags, String[] outError)
    throws XmlPullParserException, IOException {
    ... ...
    //应用信息
    final ApplicationInfo ai = owner.applicationInfo;
   //包名
    final String pkgName = owner.applicationInfo.packageName;
   //1.获取Application标签的typedArray
     TypedArray sa = res.obtainAttributes(parser,
                com.android.internal.R.styleable.AndroidManifestApplication);

//获取应用名称
    String manageSpaceActivity = sa.getNonConfigurationString(
             com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity,
                Configuration.NATIVE_CONFIG_VERSION);
    if (manageSpaceActivity != null) {
        //可以打印一下log,这个manageSpaceActivityName 是什么?
        ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity,
                outError);
    }
//其他属性,省略
//获取taskAffinity 属性
ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName,
                str, outError);


final int innerDepth = parser.getDepth();
// IMPORTANT: These must only be cached for a single <application> to avoid components
// getting added to the wrong package.
final CachedComponentArgs cachedArgs = new CachedComponentArgs();
int type;

//2.迭代Application下元素的所有子元素
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
    && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
    continue;
}
//获取标签名
String tagName = parser.getName();
//3.解析Activity
if (tagName.equals("activity")) {
    Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs, false,
            owner.baseHardwareAccelerated);
    if (a == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return false;
    }

    owner.activities.add(a);(owner是Package 类型,说明Package对象本身维护了一个activity的列表,将这个Activity实例添加到Package对象的activities列表中)

} else if (tagName.equals("receiver")) {
    Activity a = parseActivity(owner, res, parser, flags, outError, cachedArgs,
            true, false);
    if (a == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return false;
    }

    owner.receivers.add(a);

} else if (tagName.equals("service")) {
    Service s = parseService(owner, res, parser, flags, outError, cachedArgs);
    if (s == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return false;
    }

    owner.services.add(s);

} else if (tagName.equals("provider")) {
    Provider p = parseProvider(owner, res, parser, flags, outError, cachedArgs);
    if (p == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return false;
    }

    owner.providers.add(p);

} else if (tagName.equals("activity-alias")) {
    Activity a = parseActivityAlias(owner, res, parser, flags, outError, cachedArgs);
    if (a == null) {
        mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
        return false;
    }

    owner.activities.add(a);
}
}

 

看到Actiivty、Service、Provider、Receiver等标签时,就到了解析过程的关键点,从parseApplication中发现这是一个普通的xml解析,根据不同的标签调用不同的解析方法,例如,解析Activity则会调用parseActivity函数,然后返回一个Activity实例,并且将这个实例添加到Package对象的activities列表中。

此时,我们需要回到上述的PackageManagerService类中的scanPackageLI的最后一步,也就是 scanPackageLI(PackageParser.Package pkg, final int policyFlags,

int scanFlags, long currentTime, @Nullable UserHandle user) 函数,该函数实现如下:

#PackageManagerService
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, final int policyFlags,
            int scanFlags, long currentTime, @Nullable UserHandle user)
                    throws PackageManagerException {
        boolean success = false;
        try {
//调用scanPackageDirtyLI    进行扫描apk包
            final PackageParser.Package res = scanPackageDirtyLI(pkg, policyFlags, scanFlags,
                    currentTime, user);
            success = true;
            return res;
        } finally {
            if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
                // DELETE_DATA_ON_FAILURES is only used by frozen paths
                destroyAppDataLIF(pkg, UserHandle.USER_ALL,
                        StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE);
                destroyAppProfilesLIF(pkg, UserHandle.USER_ALL);
            }
        }
    }

 

这个函数调用了scanPackageDirtyLI 函数,scanPackageDirtyLI 函数会将解析到的activity、service等添加到对应的mActivities、mServices等列表中。

#PackageManagerService
private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg,
        final int policyFlags, final int scanFlags, long currentTime, @Nullable UserHandle user)
                throws PackageManagerException {
        ... ...
        final File scanFile = new File(pkg.codePath);
... ...

// writer
       synchronized (mPackages) {
//代码省略
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);
//添加receiver到mReceivers
                mReceivers.addActivity(a, "receiver");
                if (chatty) {
                    if (r == null) {
                        r = new StringBuilder(256);
                    } else {
                        r.append(' ');
                    }
                    r.append(a.info.name);
                }
            }
}

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);

    //porting themes: handle case where app was installed after icon mapping applied
    if (mIconPackHelper != null) {
        //panjuan add for third themed icon
        a.info.preThemedIcon = mIconPackHelper
                .getPreResourceIdForActivityIcon(a.info);
        if (0 == a.info.preThemedIcon) {
            a.info.themedIcon = mIconPackHelper
                    .getResourceIdForActivityIcon(a.info);
        }
    }
   //添加Activity到mActivities
    mActivities.addActivity(a, "activity");
    if (chatty) {
        if (r == null) {
            r = new StringBuilder(256);
        } else {
            r.append(' ');
        }
        r.append(a.info.name);
    }
}
}

 

我们看到,这里将上一步解析到的Activity、Service添加到了mActivities、mServices中,这些类型定义是PackageManagerSercie的字段。

#PackageManagerService
public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
        ... ...
}

 // All available activities, for your resolving pleasure.
//保存了所有Activity的节点信息
    final ActivityIntentResolver mActivities =
            new ActivityIntentResolver();

    // All available receivers, for your resolving pleasure.
//保存了所有BroadcastReceiver节点信息
    final ActivityIntentResolver mReceivers =
            new ActivityIntentResolver();

    // All available services, for your resolving pleasure.
 //保存了所有Service节点信息
    final ServiceIntentResolver mServices = new ServiceIntentResolver();

    // All available providers, for your resolving pleasure.
//保存了所有ContentProvider节点信息
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();
#PackageManagerService
public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
        ... ...
}

 // All available activities, for your resolving pleasure.
//保存了所有Activity的节点信息
    final ActivityIntentResolver mActivities =
            new ActivityIntentResolver();

    // All available receivers, for your resolving pleasure.
//保存了所有BroadcastReceiver节点信息
    final ActivityIntentResolver mReceivers =
            new ActivityIntentResolver();

    // All available services, for your resolving pleasure.
 //保存了所有Service节点信息
    final ServiceIntentResolver mServices = new ServiceIntentResolver();

    // All available providers, for your resolving pleasure.
//保存了所有ContentProvider节点信息
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();

 到了这一步,整个已安装apk的信息树就建立了,每一个apk的应用名、包名、图标、Activity、Service等信息都存储在系统中。当用户使用Intent跳转到某一个Activity或启动某一个Servcie时,系统会到这个信息表中查找。符合要求的组件则会被启动起来。这样就通过Intent将系统的各个组件连接到一起,使得Android系统成为一个可复用、灵活的系统。

 

精确匹配

上面分析了apk信息表的构建过程,下面来分析一下Intent的查找与匹配过程。

在开发中,我们需要启动某一个具体的activity时,代码大致如下:

Intent intent = new Intent(mContext, MainActivity.class);

startActivity(intent);

这种情况下指定了具体的组件,也就是MainActivity,此时在系统查找组件时会使用精确匹配,我们称为显示Intent。

还有一种情况是不指定具体的组件,而是给出一些模糊的查询属性。

Intent intent = new Intent(intent, ACTION_SENDTO);

startActivty(intent);

我们称为隐式Intent。

Intent的查找与匹配过程:

startActivity这个函数经过几个函数的转发,最终会调用startActivityForResult。

#Activity
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {
    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
       //1.启动actiivty
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(
                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);
      //2.发送启动请求
        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }
        if (requestCode >= 0) {
            // If this start is requesting a result, we can avoid making
            // the activity visible until the result is received.  Setting
            // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
            // activity hidden during this time, to avoid flickering.
            // This can only be done when a result is requested because
            // that guarantees we will get information back when the
            // activity is finished, no matter what happens to it.
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);
        // TODO Consider clearing/flushing other event sources and events for child windows.
    } else {
        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {
            // Note we want to go through this method for compatibility with
            // existing applications that may have overridden it.
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

Activity中的startActivityForResult函数直接调用了Instrumentation中的execStartActivity函数, 具体代码如下:

#Instrumentation
  public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    ActivityResult result = null;
                    if (am.ignoreMatchingSpecificIntents()) {
                        result = am.onStartActivity(intent);
                    }
                    if (result != null) {
                        am.mHits++;
                        return result;
                    } else if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        try {
          //将Intent中的数据迁移到粘贴板中
            intent.migrateExtraStreamToClipData();
         //准备离开当前进程
            intent.prepareToLeaveProcess(who);
         //1.调用ActivityMangerService的startActivity方法
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
  //2.检测结果,并且回调给客户端
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

 

execStartActivity里面其实就是调用了ActivityManagerService的startActivity方法,这个方法里面其实就是调用了ActivityStackSupervisor的startActivityMayWait方法(Android 8.1就不是这个结果了,在Android 8.1中,startActivityMayWait方法现在在ActivityStarter中了),该方法最后调用PMS的resolveIntent方法。在resolveIntent方法中调用了queryIntentActivities方法,queryIntentActivities会返回一个ActivityInfo对象列表,也就是符合Intent的ActivityInfo列表,而每一个ActivityInfo对象就是一个Activity的档案对象,记录了一个Activity的相关信息。

#ActivityStarter
  final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult,
            Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,
            TaskRecord inTask, String reason) {
    ... ...
    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);
}


#ActivityStackSupervisor
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId) {
    return resolveIntent(intent, resolvedType, userId, 0);
}

#ActivityStackSupervisor
ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
    synchronized (mService) {
        return mService.getPackageManagerInternalLocked().resolveIntent(intent, resolvedType,
                PackageManager.MATCH_INSTANT | PackageManager.MATCH_DEFAULT_ONLY | flags
                | ActivityManagerService.STOCK_PM_FLAGS, userId);
    }
}

#ActivityManagerService
  PackageManagerInternal getPackageManagerInternalLocked() {
        if (mPackageManagerInt == null) {
            mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
        }
        return mPackageManagerInt;
    }

#PackageManagerInternal(Abstract)
  /**
     * Resolves an activity intent, allowing instant apps to be resolved.
     */
    public abstract ResolveInfo resolveIntent(Intent intent, String resolvedType,
            int flags, int userId);


public class PackageManagerService extends IPackageManager.Stub
        implements PackageSender {
        ... ...
}

//这个PackageManagerInternalImpl   是 PackageManagerService   的 内部类
 private class PackageManagerInternalImpl extends PackageManagerInternal {

#PackageManagerInternalImpl   
@Override
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
        int flags, int userId) {
    return resolveIntentInternal(
            intent, resolvedType, flags, userId, true /*resolveForStart*/);
}

}

#PackageManagerService   
private ResolveInfo resolveIntentInternal(Intent intent, String resolvedType,
        int flags, int userId, boolean resolveForStart) {
    try {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");

        if (!sUserManager.exists(userId)) return null;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForResolve(flags, userId, intent, callingUid, resolveForStart);
        enforceCrossUserPermission(callingUid, userId,
                false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");

        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
//在resolveIntent方法中就调用了自身的queryIntentActivitiesInternal   方法,
//queryIntentActivitiesInternal   方法会返回一个ActivityInfo对象列表,也就是符合Intent的ActivityInfo列表。
        final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
                flags, callingUid, userId, resolveForStart, true /*allowDynamicSplits*/);
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

        final ResolveInfo bestChoice =
                chooseBestActivity(intent, resolvedType, flags, query, userId);
        return bestChoice;
    } finally {
        Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
    }
}


#PackageManagerService   
private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
            String resolvedType, int flags, int filterCallingUid, int userId,
            boolean resolveForStart, boolean allowDynamicSplits) {
... ....
//1.获取Intent的Component对象
 ComponentName comp = intent.getComponent();






//2.精确跳转时这个对象不为空
if (comp != null) {
        final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
//3.通过Component直接获取到ActivityInfo对象
        final ActivityInfo ai = getActivityInfo(comp, flags, userId);
        if (ai != null) {
            // When specifying an explicit component, we prevent the activity from being
            // used when either 1) the calling package is normal and the activity is within
            // an ephemeral application or 2) the calling package is ephemeral and the
            // activity is not visible to ephemeral applications.
            final boolean matchInstantApp =
                    (flags & PackageManager.MATCH_INSTANT) != 0;
            final boolean matchVisibleToInstantAppOnly =
                    (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
            final boolean matchExplicitlyVisibleOnly =
                    (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
            final boolean isCallerInstantApp =
                    instantAppPkgName != null;
            final boolean isTargetSameInstantApp =
                    comp.getPackageName().equals(instantAppPkgName);
            final boolean isTargetInstantApp =
                    (ai.applicationInfo.privateFlags
                            & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
            final boolean isTargetVisibleToInstantApp =
                    (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
            final boolean isTargetExplicitlyVisibleToInstantApp =
                    isTargetVisibleToInstantApp
                    && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP) == 0;
            final boolean isTargetHiddenFromInstantApp =
                    !isTargetVisibleToInstantApp
                    || (matchExplicitlyVisibleOnly && !isTargetExplicitlyVisibleToInstantApp);
            final boolean blockResolution =
                    !isTargetSameInstantApp
                    && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
                            || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                    && isTargetHiddenFromInstantApp));
            if (!blockResolution) {
                final ResolveInfo ri = new ResolveInfo();
                ri.activityInfo = ai;
                list.add(ri);
            }
        }
        return applyPostResolutionFilter(
                list, instantAppPkgName, allowDynamicSplits, filterCallingUid, userId);
    }


//4.Compoenent为空,则为隐式Intent
synchronized (mPackages) {
    if (pkgName == null) {
... ...
//5.通过包名获取到Package对象
 final PackageParser.Package pkg = mPackages.get(pkgName);

result = null;
                if (pkg != null) {
                    result = filterIfNotSystemUser(
                          //6. 通过package对象获取到ActivityInfo
                            mActivities.queryIntentForPackage(
                                    intent, resolvedType, flags, pkg.activities, userId),
                            userId);
                }
                if (result == null || result.size() == 0) {
                    // the caller wants to resolve for a particular package; however, there
                    // were no installed results, so, try to find an ephemeral result
                    addEphemeral = !ephemeralDisabled
                            && isInstantAppAllowed(
                                    intent, null /*result*/, userId, true /*skipPackageCheck*/);
                    if (result == null) {
                        result = new ArrayList<>();
                    }
                }
    }
}

}


#PackageManagerService       
private List<ResolveInfo> applyPostResolutionFilter(List<ResolveInfo> resolveInfos,
        String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid, int userId) {
        ... ...
  return resolveInfos;        
}

上述函数大致的过程如下:

如果Intent指明了Component,那么直接通过Component就可以找到ActivityInfo列表,这个列表的数量只有一个,这个ActivityInfo就是指定的那个组件。

如果没有指定具体的组件,那么Component为空,此时查看Intent是否指定了要跳转到的目标组件所在的包名,如果有包名,则会通过包名获取到对应的ActivityInfo;否则需要通过ActivityIntentResolver等类的queryIntentForPackage进行模糊匹配,例如根据:Action、Category等。

还有一点需要注意的是:mActivities就是存储了从AndroidManifest.xml中解析到的Activity。

对于显示Intent来说,就是直接跳转到具体的Activity中;

对于隐式Intent来说,可能会弹出对话框进行选择。

整体思路:

在系统启动时,PackageManagerService会启动,此时PMS将解析所有已安装的应用的信息,构建信息列表,当用户通过Intent来跳转某一个组件是,会根据Intent包含的信息到PMS中查找对用的组件列表,最后跳转到目标组建中。

 

参考《Android源码设计模式》

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: calibratecamera 函数的flags 可以通过设置 CALIB_USE_INTRINSIC_GUESS 或者 CALIB_FIX_ASPECT_RATIO 来实现。具体方法是:将CALIB_USE_INTRINSIC_GUESS设置为True,CALIB_FIX_ASPECT_RATIO设置为False,然后它会自动寻找针孔模型的参数。 ### 回答2: 标定OpenCV相机的针孔模型需要设置calibratecamera函数的flags参数。 calibrateCamera函数OpenCV中用于相机标定的函数,它可以根据一组已知的三维物体点和对应的图像点来计算相机的内参矩阵和畸变系数。 flags参数是一个整数,用于指定标定过程中的一些选项。根据不同的需求,可以设置不同的标定标志位来调整标定的精确度和速度。 常用的标定标志位包括: 1. CALIB_USE_INTRINSIC_GUESS:使用初始猜测的内参矩阵和畸变系数进行标定。这样可以加快标定的速度,但精度可能会降低。 2. CALIB_FIX_INTRINSIC:固定内参矩阵和畸变系数。如果已经有了一个较好的内参矩阵和畸变系数,可以使用这个标志位来固定它们,仅求解外参矩阵。 3. CALIB_FIX_PRINCIPAL_POINT:固定主点坐标。如果已经知道主点的大致位置,可以使用这个标志位来固定主点坐标,仅求解其他参数。 4. CALIB_FIX_FOCAL_LENGTH:固定焦距。如果已经知道焦距的大致值,可以使用这个标志位来固定焦距,仅求解其他参数。 5. CALIB_FIX_ASPECT_RATIO:固定纵横比。如果已经知道相机的纵横比,可以使用这个标志位来固定纵横比,仅求解其他参数。 6. CALIB_ZERO_TANGENT_DIST:固定切向畸变系数。如果认为切向畸变系数为0,可以使用这个标志位来固定切向畸变系数,仅求解其他参数。 通过设置不同的标志位,可以根据实际需求来调整相机标定的过程,以达到较好的标定效果。 ### 回答3: 标定OpenCV相机的针孔模型需要使用`calibrateCamera`函数,并且可以通过设置`flags`参数来进行不同的标定方式选择。`flags`参数是一个整数,用于设定标定的方法和其他细节。 常见的flags参数设置方法有以下几种: 1. `cv2.CALIB_USE_INTRINSIC_GUESS`:使用已知的初始相机矩阵进行标定,加速标定过程; 2. `cv2.CALIB_FIX_PRINCIPAL_POINT`:固定主点坐标,不进行优化; 3. `cv2.CALIB_FIX_ASPECT_RATIO`:固定像素宽高比,不进行优化; 4. `cv2.CALIB_FIX_FOCAL_LENGTH`:固定焦距,不进行优化; 5. `cv2.CALIB_ZERO_TANGENT_DIST`:切向畸变系数初始化为0; 6. `cv2.CALIB_FIX_K1`:固定径向畸变系数k1,不进行优化; 7. `cv2.CALIB_FIX_K2`:固定径向畸变系数k2,不进行优化; 8. `cv2.CALIB_FIX_K3`:固定径向畸变系数k3,不进行优化; 9. `cv2.CALIB_FIX_K4`:固定径向畸变系数k4,不进行优化; 10. `cv2.CALIB_FIX_K5`:固定径向畸变系数k5,不进行优化; 11. `cv2.CALIB_FIX_K6`:固定径向畸变系数k6,不进行优化; 12. `cv2.CALIB_RATIONAL_MODEL`:使用鱼眼模型进行标定。 除了以上常见的flags参数设置方法,还有一些其他可选的标定方式可以根据实际需求进行设置。在调用`calibrateCamera`函数时,将`flags`参数设置为所需的数值即可选择相应的标定方法。这些标定方法的选择将直接影响标定结果的准确性和效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值