美文网首页Android随笔程序员
Android 浅析 ContentProvider (二) 安

Android 浅析 ContentProvider (二) 安

作者: CodePlayer_Jz | 来源:发表于2016-04-12 10:53 被阅读297次

    Android 浅析 ContentProvider (二) 安装原理

    前言

    Linus Benedict Torvalds : RTFSC – Read The Fucking Source Code

    ContentProvider下文将会简称CP。
    ContentResolver下文将会简称CR。

    概括

    ContentProvider的安装原理其实就是Apk的安装原理,这里仅仅分析从安装过程中安装provider的过程。

    CP 安装原理

    PackageManagerService

    负责系统中Package的管理,应用程序的安装、卸载、信息查询等。

    我们的安装流程从scanDirLI()开始,无论是android开机安装还是手动安装还是其他安装方式,最终都会走到PMS的scanDirLI()函数里面,对于我们provider的安装从scanDirLI()开始就够了。

    Step1、scanDirLI()

    private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = dir.listFiles();
        ...
        scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                scanFlags, currentTime, null);
        ...
    }
    

    这个函数会对于后缀为APK的文件进行解析和安装。

    Step2、scanPackageLI()

    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
                long currentTime, UserHandle user) {
        ...
        parseFlags |= mDefParseFlags;
        PackageParser pp = new PackageParser();
        ...
        final PackageParser.Package pkg;
        pkg = pp.parsePackage(scanFile, parseFlags);
        ...
        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);
        }
        ...
        return scannedPkg;
    }
    

    这个函数首先会为这个Apk文件创建一个PackageParser实例,接着调用这个实例的parsePackage函数来对这个Apk文件进行解析。这个函数最后还会调用另外一个scanPackageLI函数来把解析后得到的应用程序信息保存在PackageManagerService中,其中就包括Provider信息。

    Step3、PackageParser.parsePackage()

    public Package parsePackage(File sourceFile, String destCodePath, DisplayMetrics metrics, int flags) {
        ...
        mArchiveSourcePath = sourceFile.getPath();
        ...
        XmlResourceParser parser = null;
        AssetManager assmgr = null;
        boolean assetError = true;
        
        assmgr = new AssetManager();
        int cookie = assmgr.addAssetPath(mArchiveSourcePath);
        if(cookie != 0) {
            parser = assmgr.openXmlResourceParser(cookie, "AndroidManifest.xml");
            assetError = false;
        }
        ...
        String[] errorText = new String[1];
        Package pkg = null;
        Exception errorException = null;
    
        Resources res = new Resources(assmgr, metrics, null);
        pkg = parsePackage(res, parser, flags, errorText);
        ...
        parser.close();
        assmgr.close();
    
        pkg.mPath = destCodePath;
        pkg.mScanPath = mArchiveSourcePath;
        pkg.mSignatures = null;
    
        return pkg;
    }
    

    每一个Apk文件都是一个归档文件,里面包含了Android应用程序的配置文件AndroidManifest.xml,这里主要就是要对这个配置文件进行解析,从Apk归档文件中得到这个配置文件后调用另外一个parsePackage()。

    private Package parsePackage(Resources res, XmlResourceParser parser, int flags, String[] outError) {
        ...
        String pkgName = parsePackageName(parser, attrs, flags, outError);
        ...
        TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifest);
        ...
        int type;
        while ((type=parser.next()) != parser.END_DOCUMENT
           && (type != parser.END_TAG || parser.getDepth() > outerDepth)) {
            if (type == parser.END_TAG || type == parser.TEXT) {continue;}
            
            String tagName = parser.getName();
            if (tagName.equals("application")) {
                if (!parseApplication(pkg, res, parser, attrs, flags, outError)) {return null;}
            } else if (tagName.equals("permission-group")) {  
                ......  
            } else if (tagName.equals("permission")) {  
                ......  
            } else if (tagName.equals("permission-tree")) {  
                ......  
            } ...
        }
        return pkg;
    }
    

    这函数就是对AndroidManifest.xml里的标签进行解析。
    http://developer.android.com/guide/topics/manifest/manifest-intro.html是谷歌官方文档。
    接下来对provider的解析存在在"application"里,对"application"的解析是调用parseApplication函数。

    Step4、PackageParser.parseApplication()

    private boolean parseApplication(Package owner, Resources res,
                XmlPullParser parser, AttributeSet attrs, int flags, String[] outError){
        final ApplicationInfo ai = owner.applicationInfo;
        final String pkgName = owner.applicationInfo.packageName;
        
        TypedArray sa = res.obtainAttributes(attrs,
                    com.android.internal.R.styleable.AndroidManifestApplication);
                    
        int type;
        while ((type=parser.next()) != parser.END_DOCUMENT
               && (type != parser.END_TAG || parser.getDepth() > innerDepth)) {
            String tagName = parser.getName();
            if (tagName.equals("activity")) {
                ...
            }
        } else if (tagName.equals("provider")) {
            Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
            owner.providers.add(p);
        } ...
        return true;
    }
    

    parseApplication通过对AndroidManifest.xml文件中的application标签进行解析,这里我们可以看到对于provider的解析是调用了parseProvider函数。

    Step5、parseProvider()

    private Provider parseProvider(Package owner, Resources res,
            XmlPullParser parser, AttributeSet attrs, int flags, String[] outError) {
        TypedArray sa = res.obtainAttributes(attrs,
                com.android.internal.R.styleable.AndroidManifestProvider);
        if (mParseProviderArgs == null) {
            mParseProviderArgs = new ParseComponentArgs(owner, outError,
                    com.android.internal.R.styleable.AndroidManifestProvider_name,
                    com.android.internal.R.styleable.AndroidManifestProvider_label,
                    com.android.internal.R.styleable.AndroidManifestProvider_icon, 0,
                    mSeparateProcesses,
                    com.android.internal.R.styleable.AndroidManifestProvider_process,
                    com.android.internal.R.styleable.AndroidManifestProvider_description,
                    com.android.internal.R.styleable.AndroidManifestProvider_enabled);
            mParseProviderArgs.tag = "<provider>";
        }
        ...
        mParseProviderArgs.sa = sa;
        mParseProviderArgs.flags = flags;
        ...
        Provider p = new Provider(mParseProviderArgs, new ProviderInfo());
        ...
        sa.recycle();
        p.info.authority = cpname.intern();
        return p;
    }
    

    在这里解析我们的provider标签了,最后将解析完毕的Provider对象返回,这个返回会一直返回到Step2,然后调用另外一个installNewPackageLI()或者replacePackageLI()函数将获取的值保存。但其实在installNewPackageLI()或者replacePackageLI()函数核心里最后都会调用scanPackageLI()。所以将直接分析最后的scanPackageLI()函数

    Step6、scanPackageLI()

    // All available providers, for your resolving pleasure.
    final ProviderIntentResolver mProviders = new ProviderIntentResolver();
        
    private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,
            int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
        boolean success = false;
        final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,
                currentTime, user);
        success = true;
        return res;
    }
    
    private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags,
                int scanFlags, long currentTime, UserHandle user) {
        // writer
        synchronized (mPackages) {
            ...
            int N = pkg.providers.size();
            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); // 将Provider保存在本地了。
                ...
            }
        }
    }
    

    最后我们走过相当长的旅程,终于将Provider从AndroidManifest里面拿出来并且保存到系统的PackageManagerService里面。以后每当我们要获取对应的Provider时候就可以从PMS里面查询并且获取。
    需要注意的是:如果有相同的provider已经加载,新的就不加载进来了。

    总结

    通过六个步骤我们了解了APK里面的Provider怎么从安装包里面加载到PMS里,以提供外部使用。
    最后总结下这六个步骤:

    1. 扫描所有后缀为APK的文件并准备对其进行解析和安装。
    2. 扫描单个APK看是新安装还是覆盖安装,来做不同的区分解析和安装。
    3. 对每一个Apk文件包含的Android应用程序的配置文件AndroidManifest.xml进行解析。
    4. 深入解析AndroidManifest里的application标签。
    5. 具体解析provider标签。
    6. 将所有解析的信息保存到PMS相应的变量里。

    相关文章

      网友评论

        本文标题:Android 浅析 ContentProvider (二) 安

        本文链接:https://www.haomeiwen.com/subject/aszilttx.html