Intent匹配规则以及解析框架深入分析



                                                                          本文原创 ,转载必须注明出处 :http://blog.csdn.net/qinjuning

 


       前言: 本文博客是公司培训之间的PPT整理而成,在此把相关的细节发布出来,希望对大家能有所帮助。

 

 

    关于Intent以及IntentFilter的基本知识,大家可以参阅如下资料,

                      SDK中对Intent与IntentFilter的介绍  ---- 英文

                       其中文翻译如下:

                              Android开发之旅: Intents和Intent Filters(理论部分)

 

    我重点分析一下两个方面:

         第一部分 、Intent以及IntentFilter说明以及匹配规则分析

         第二部分:Intent的解析过程分析
 
 

 

 第一部分 、Intent以及IntentFilter说明以及匹配规则分析

 

       想当初我看Intent相关知识时,对Intent、IntentFilter的理解就很差劲,总觉得系统定义了一个Intent,为何还要整理个

  IntentFilter出来"祸害"广大程序猿呢?但不解归不解,在具体使用咱可不能含糊,于是只好依葫芦画瓢了,反正绝对还不错。

   

 一、温故而知新 :Intent与IntentFilter两问。


        *  它们是什么 ?
 
       *  它们的区别在哪儿 ?
 
   事实上,这两个问题可以归纳为Intent和Intent的主要功能是什么 ? 大家可以先扪心自问下,看看你的掌握程度如何 ?
 
   我的理解如下:
 
        * Intent  :   主要功能是根据特定的条件找到匹配的组件,继而对该组件执 行一些操作。比如执行startActivity()时,系统
              首先要找到特定的Activity组件,然后执行onCreate()方法;startService()也得先找的特定的Service组件,然后执行  
               onCreate()或者onStart()方法 。
 
       * IntentFilter  :主要功能是为某个组件向系统注册一些特性(当然一个组件可以注册多个IntentFilter),以便Intent找到对应
             的组件。
 
 

 二、它们之间的关系是如何呢?

 
      通过前面对Intent以及IntentFilter的分析,我们很容易在语意上得出它们其实是个 前后关系
 
          * IntentFilter在前 :任何一个组件必须先通过IntentFilter注册。
 
         * Intent 在后        :根据特定信息,找到之前以及注册过的组件。
 
 
  源码分析:
 
    Intent类源码(部分) 路径位于:\frameworks\base\core\java\android\content\Intent.java

[java]  view plain copy print ?
  1. public class Intent implements Parcelable, Cloneable {  
  2.   
  3.     private String mAction;           //action值  
  4.     private Uri mData;                //uri  
  5.     private String mType;             //MimeType  
  6.     private String mPackage;          //所在包名  
  7.     private ComponentName mComponent; //组件信息  
  8.     private int mFlags;               //Flag标志位  
  9.     private HashSet<String> mCategories; //Category值  
  10.     private Bundle mExtras;           //附加值信息  
  11.     //...  
  12. }  

   IntentFilter类源码(部分) 路径位于:\frameworks\base\core\java\android\content\IntentFilter.java

[java]  view plain copy print ?
  1. public class IntentFilter implements Parcelable {  
  2.     //...  
  3.     //保存了所有action字段的值  
  4.     private final ArrayList<String> mActions;  
  5.     //保存了所有Category的值  
  6.     private ArrayList<String> mCategories = null;  
  7.     //保存了所有Schema(模式)的值  
  8.     private ArrayList<String> mDataSchemes = null;  
  9.     //保存了所有Authority字段的值  
  10.     private ArrayList<AuthorityEntry> mDataAuthorities = null;  
  11.     //保存了所有Path的值  
  12.     private ArrayList<PatternMatcher> mDataPaths = null;  
  13.     //保存了所有MimeType的值  
  14.     private ArrayList<String> mDataTypes = null;  
  15.     //...  
  16. }  

 

       PS :大家可以参详下Intent与IntentFilter类中不同字段的属性类型。Intent中属性类型基本上都是单个类型的,而IntentFilter

属性都是集合类型的。从这方面思考,更可以加深我们的理解。

 

 三、Intent匹配规则

 

      匹配种类有如下三种:      

                  *  动作(Action)检测
                 *  种类(Category)检测
                 *  数据(Data & MimeType)检测
 

      比较好理解的是,进行匹配时Intent携带的Action字段值和Category字段值必须包含在IntentFilter中,否则匹配失败。

 

       SDK中说明的具体规则如下:


            *      一个Intent对象既不包含URI,也不包含数据类型 ; 仅当过滤器也不指定任何URIs和数据类型时,才能通过检测;
              否则不能通过。

 

            *     一个Intent对象包含URI,但不包含数据类型:仅当过滤器也不指定数据类型,同时它们的URI匹配,才能通过检测。
            例如,mailto:和tel:都不指定实际数据。
 
            *    一个Intent对象包含数据类型,但不包含URI:仅当过滤也只包含数据类型且与Intent相同,才通过检测。
 
            *    一个Intent对象既包含URI,也包含数据类型(或数据类型能够从URI推断出) ; 数据类型部分,只有与过滤器中之一
             匹配才算通过;URI部分,它的URI要出现在过滤器中,或者它有content:或file: URI,又或者过滤器没有指定URI。
             换句话说,如果它的过滤器仅列出了数据类型,组件假定支持content:和file: 。

 

         PS :可别说我不会总结出来给大家分享,其实我觉得很多知识都需要自己去尝试,去努力吸收,只要经过自己的消化,

          学到的知识就是自己的了。

 

      上面的规则比较生硬吧。我们去源码中去看看Intent与IntentFilter的具体匹配方法吧。

       该方法是IntentFilter中的match()方法,该方法的内部处理逻辑就是按照上面的规则去判断的,大家可以仔细体味下,该方法

  我们在后面讲到Intent解析过程时也会用到。 具体逻辑在代码中进行了说明。

[java]  view plain copy print ?
  1. public class IntentFilter implements Parcelable {  
  2.     //匹配算法,,按照匹配规则进行  
  3.     //Intent与该IntentFilter进行匹配时调用该方法参数表示Intent的相关属性值  
  4.     public final int match(String action, String type, String scheme,  
  5.             Uri data, Set<String> categories, String logTag){  
  6.         //首先、匹配Action字段  
  7.         if (action != null && !matchAction(action)) {  
  8.             if (Config.LOGV) Log.v(  
  9.                 logTag, "No matching action " + action + " for " + this);  
  10.             return NO_MATCH_ACTION;  
  11.         }  
  12.         //其次、匹配数据(Uri和MimeType)字段  
  13.         int dataMatch = matchData(type, scheme, data);  
  14.         //...  
  15.         //最后,匹配Category字段值  
  16.         String categoryMatch = matchCategories(categories);  
  17.         //...  
  18.     }  
  19.     //...  
  20. }  


    这部分我们重点讲解的知识点有如下:
 
           1、Intent以及IntentFilter的主要职能
           2、Intent与IntentFilter的关系
           3、匹配规则说明
 
 

第二部分、 Intent的解析过程分析

  
       在继续看本部分之前,希望您最好对PackageManagerService-----程序包管理服务有一定的认知(没有也是OK的咯)。
  可以参考下面这篇问题去看看PackageManagerService的功能和相关流程。

         老罗的博客: <<Android应用程序安装过程源代码分析>>
           
 

    一、引入PackageManager

           我们知道Android源码总是贴心的(不知道有没有10086贴心),它对外提供了很多借口供应用程序调用,例如AudioManger

    (音频管理)、TelephoneManger(电话管理)、同样也提供了一个包管理-----PackageManager,通过它我们可以、获取应用

    程序包得信息,例如图标、Lable标签等。具体关于PackageManager的使用,可以参考我的另外一篇文章 :

 

                       <<Android中获取应用程序(包)的信息-----PackageManager的使用(一)>>

 

   二、PackageManagerService  ---- 重量级选手

          前面所说PackageManager不过是个傀儡,所有相关的操作都是由PackageManagerService 完成的。这儿我们简单的

      分析下PackageManagerService 的特性:

             ①、开机就启动,由SystemServer进程启动 ;

             ②、启动后它会扫描系统中所有应用程序Apk包下的AndroidManifest.xml文件,然后解析所有的

           AndroidManifest.xml文件,继而形成一个庞大的信息结构树,并且保存在PackageManagerService 的相关属性下。

                 它会扫描这两个目录:

                                /system/app  ------------------> 系统应用程序

                                /data/app      ------------------> 第三方应用程序(所有安装的Apk包都会在该目录下保存一份拷贝)

          扫描完成后,于是所有的信息结构就构建了。PackageManagerService 的四个重要属性如下:

[java]  view plain copy print ?
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     //...  
  3.     //保存了所有Activity节点信息 。         自定义类  
  4.     // All available activities, for your resolving pleasure.  
  5.     final ActivityIntentResolver mActivities =  
  6.             new ActivityIntentResolver();  
  7.     //保存了所有BroadcastReceiver节点信息  。  自定义类  
  8.     // All available receivers, for your resolving pleasure.  
  9.     final ActivityIntentResolver mReceivers =  
  10.             new ActivityIntentResolver();  
  11.     //保存了所有Service节点信息。 。  自定义类  
  12.     // All available services, for your resolving pleasure.  
  13.     final ServiceIntentResolver mServices = new ServiceIntentResolver();  
  14.     //保存了所有ContentProvider节点信息 , 以Hash值保存  
  15.     // Keys are String (provider class name), values are Provider.  
  16.     final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =  
  17.             new HashMap<ComponentName, PackageParser.Provider>();  
  18.     //...  
  19. }  

 

 

       值得注意这些属性类型的不同。Activity、BroadcastReceiver、Service都采用了自定义类去保存相关信息,从类名上看,
  类结构应该很相似。而ContentProvider只是简单的采用了HashMap键值对去保存了信息? 莫非有错 ? 我们回忆下
 AndroidManifest.xml定义组件信息时,Activity、BroadcastReceiver、Service都可以通过<intent-filter>去隐式匹配的,而
 ContentProvider只需要一个Uri数据即可找到对应的ContentProvider组件信息了。 因此才采用了这两种结构去保存信息。
 
 
       其实我们通过getPackageManager()方法获得的PackageManager对象,只是PackageManagerService的客户端,
  该客户端类是ApplicationPackageManager,它是ContextIml类的内部类,显然该类存在于用户空间中。
 
       源代码(部分)如下:
 
[java]  view plain copy print ?
  1. @Override  
  2. public PackageManager getPackageManager() {  
  3.    //...  
  4.    // Doesn't matter if we make more than one instance.  
  5.    return (mPackageManager = new ApplicationPackageManager(this, pm));  
  6.    //...  
  7. }  
  8. class ContextImpl extends Context {  
  9.     //...  
  10.     /*package*/  
  11.     static final class ApplicationPackageManager extends PackageManager{  
  12.            //...  
  13.            @Override  
  14.            public ActivityInfo getActivityInfo(ComponentName className, int flags){  
  15.                //...  
  16.            }  
  17.              
  18.     }  
  19.     //...  
  20. }  

      它与PackageManagerService的简单关系如下:

                     Intent匹配规则以及解析框架深入分析_第1张图片
     
   三 、保存数据采用的数据结构和解析算法
 

     1、保存数据采用的数据结构

 
       毫无疑问,保存所有信息是一项很复杂的工程,在具体讲解匹配过程时,我们先看看系统为了保存这些结构定义的一些
   数据结构。
           
       IntentInfo类:继承至IntentFilter类

               作用:保存了每个<intent-filter>节点信息

     ActivityIntentInfo类:继承至IntentInfo类

               作用:保存了<activity />节点下的< intent-filter>节点信息

      ServiceIntentInfo:继承至IntentInfo类

             作用:保存了<service />节点下的< intent-filter >节点信息

     Activity类:保存了<activity />节点信息

     Service类:保存了<service />节点信息


 

    PS:这些都是PackageParser类的内部类 。PackageParser的主要功能就是解析AndroidManifest.xml文件

 

      IntentResolver类:模板类,父类,保存了<activity/>、<service/>、<receiver />节点的共同信息。

      ActivityIntentResolver类:继承至IntentResolver类。

            作用:保存了所有<activity/>或者<receiver/>节点信息。(Activity或者BroadcastReceiver信息就是用该自定义类保存的)

     ServiceIntentResolver类:继承至IntentResolver类,保存了

            作用:保存了所有<service/>节点信息。(Service信息就是用该自定义类保存的)。

 

  一个简单的UML图表示如下:  

                                                  Intent匹配规则以及解析框架深入分析_第2张图片

 

   

     2、Intent解析采用的算法

        不同的数据结构决定了不同的算法,而不同的算法又决定着性能,例如时间复杂度以及空间复杂度等。 在具体讲解解析

  采用的算法时,我们先理解下这个情景。

        假设一个女人决定参加一个相亲节目(大家可以理解成《非诚勿扰》),然后她向主办方提出如下条件:       

                1、身高 ?     175cm以上 ;

                        2、 财富 ?     100万 ;

                        3、 学历 ?     本科以上 ;

                           ……

 

     主办发经理一看,你丫的要求还真高。但客户是万能的,该经理也只能去找到满足这些条件的男人咯。

 

       最开始,该经理是这么想的,我把所有男的都给遍历一遍,肯定把满足这些条件的男人给揪出来。他找啊找,觉得这么找下去

  是不是太二B了(呵呵,你也是这么想的吗?)。经理就开始想:“我为什么不能根据这些条件把所有男的给分成三个种群呢?有钱

  的男人在一起,高个子的男人在一起,高学历的男人在一起,这样查找起来不是更快吗 ? “


      于是,有了如下的划分:     PS, 你是属于哪一类额 ? 


                      二B算法(没有分类)                                                                                    高级点的算法(分类后)

        Intent匹配规则以及解析框架深入分析_第3张图片           Intent匹配规则以及解析框架深入分析_第4张图片


 

       可能大家对这种根据关键值分类的好处不能一目了然。我们举个一般例子吧:

           假设当前共有100个男的。 其中有钱的有20人,高个子有30人,高学历男人有10人。


       根据第一种算法分类,我们需要比较100次,而第二种算法我们总共只需要比较60次。从整个基数来分析,算法肯定优化了

       最后,对不同的分类中查询的结果进行组合重新排列下,即可得到我们的满足该女性的要求。

          

        同样的,在进行Intent匹配时,Android也采用了第二种方法来进行算法匹配。它根据一些关键值Action、MimeType、

   Schema字段去进行分类。分类之后的集合大致如下:


                               Intent匹配规则以及解析框架深入分析_第5张图片


       于是在进行具体匹配时,我们只是需要根据关键值从不同集合中获取即可。


       事实上,由于MimeType的通配符(*)的特性,它的匹配可以说是最难的。参考IntentResolver类,真正的关键值如下:

[java]  view plain copy print ?
  1. //模板类 F类型可能为ActivityIntentInfo或ServiceIntentInfo,R对象是ResolverInfo类  
  2. public class IntentResolver<F extends IntentFilter, R extends Object> {  
  3.     //保存了所有<intent-filter>节点信息  
  4.     //All filters that have been registered.   
  5.     private final HashSet<F> mFilters = new HashSet<F>();  
  6.     /** All of the MIME types that have been registered, such as "image/jpeg", 
  7.      * "image/*", or "{@literal *}/*". 
  8.      */  
  9.     //关键值表示MimeType形如:  image/jpeg 、 image/*、/*  类型  
  10.     private final HashMap<String, ArrayList<F>> mTypeToFilter   
  11.       
  12.     /**  
  13.      * The base names of all of all fully qualified MIME types that have been  
  14.      * registered, such as "image" or "*".  Wild card MIME types such as  
  15.      * "image/*" will not be here.  
  16.      */  
  17.     //关键值表示MimeType形如:image、 image/*、*  类型  
  18.     private final HashMap<String, ArrayList<F>> mBaseTypeToFilter  
  19.       
  20.     /**  
  21.      * The base names of all of the MIME types with a sub-type wildcard that  
  22.      * have been registered.  For example, a filter with "image/*" will be  
  23.      * included here as "image" but one with "image/jpeg" will not be  
  24.      * included here.  This also includes the "*" for the "{@literal *}/*"  
  25.      * MIME type.  
  26.      */  
  27.     //这个关键字段表示MimeType形如 :image、* 类型  
  28.     private final HashMap<String, ArrayList<F>> mWildTypeToFilter   
  29.       
  30.     //All of the URI schemes (such as http) that have been registered.  
  31.     //关键值字段表示Schema  
  32.     private final HashMap<String, ArrayList<F>> mSchemeToFilter   
  33.     /** 
  34.      * All of the actions that have been registered, but only those that did 
  35.      * not specify data. 
  36.      */  
  37.     //关键值字段表示:Action  
  38.     private final HashMap<String, ArrayList<F>> mActionToFilter  
  39.     //All of the actions that have been registered and specified a MIME type.  
  40.     //关键值字段表示:Action和MimeType。 即该<intent-filter>节点必须包含action和MimeType  
  41.     private final HashMap<String, ArrayList<F>> mTypedActionToFilter  
  42. }  

 

      于是,通过这些关键字段我们可以去特定集合去查找,最后将结果在重新组合下,那不就万事大吉了。


      最后,我们通过代码走读的方式,以一个查询Activity的信息的方法,带领大家去熟悉具体流程。该方法原型为:

          //通过给定的intent,查询所有匹配的Activity组件信息

               abstract List<ResolveInfo>   queryIntentActivities(Intent intent, int flags) 

 

 

        PS:其实查询Activity、Service、BroadcastReceiver的流程基本上是相同的。

 

 

       Step 1、获取PackageManager代理对象,调用该方法:

[java]  view plain copy print ?
  1. PackageManager mPackageManger = this.getPackageManager() ;  
  2.           
  3.         //为了说明,这儿我们简单查询一个Intent对象,即所有应用程序的启动Activity  
  4.         Intent mainIntent = new Intent() ;  
  5.         mainIntent.setAction(Intent.ACTION_MAIN);  
  6.         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  7.         mPackageManger.queryIntentActivities(mainIntent, 0);  
  8.           
  9.           
  10.        //获取PackageManager对象  
  11.        @Override  
  12.        public PackageManager getPackageManager() {  
  13.            //...  
  14.            IPackageManager pm = ActivityThread.getPackageManager();  
  15.            if (pm != null) {  
  16.                // Doesn't matter if we make more than one instance.  
  17.                return (mPackageManager = new ApplicationPackageManager(this, pm));  
  18.            }  
  19.        }  
  20.              

       Step 2、该PackageManager代理对象实则为ApplicatonPackageManager 对象,该对象是ContextIml的内部类。

[java]  view plain copy print ?
  1. @Override  
  2.         public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {  
  3.             try {  
  4.                 //mPM对象就是PackageManagerService的客户端  
  5.                 return mPM.queryIntentActivities(  
  6.                     intent,  
  7.                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),  
  8.                     flags);  
  9.             } catch (RemoteException e) {  
  10.                 throw new RuntimeException("Package manager has died", e);  
  11.             }  
  12.         }  

 

       Step 3、调用服务端PackageManagerService对象的对应方法。该方法位于PackageManagerService.java类中

[java]  view plain copy print ?
  1. public List<ResolveInfo> queryIntentActivities(Intent intent,  
  2.         String resolvedType, int flags) {  
  3.     //是否设置了组件ComponetName 信息  
  4.  ComponentName comp = intent.getComponent();  
  5.     if (comp != null) {  
  6.         List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);  
  7.         ActivityInfo ai = getActivityInfo(comp, flags);  
  8.         if (ai != null) {  
  9.             ResolveInfo ri = new ResolveInfo();  
  10.             ri.activityInfo = ai;  
  11.             list.add(ri);  
  12.         }  
  13.         return list;  
  14.     }  
  15.   
  16.     synchronized (mPackages) {  
  17.         //是否设置了包名  
  18.         String pkgName = intent.getPackage();  
  19.         if (pkgName == null) {  
  20.             //调用mActivities去查询  
  21.             return (List<ResolveInfo>)mActivities.queryIntent(intent,  
  22.                     resolvedType, flags);  
  23.         }  
  24.         PackageParser.Package pkg = mPackages.get(pkgName);  
  25.         if (pkg != null) {  
  26.             return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,  
  27.                     resolvedType, flags, pkg.activities);  
  28.         }  
  29.         return null;  
  30.     }  
  31.  }    


     首先、该方法判断IntentComponentName是否存在,如果存在则为显示匹配了,直接返回特定组件相关信息;

     其次、判断是否设置了包名,即packageName,如果没有指定包名,则查询所有的应用程序包去找匹配的组件信息。如果

   指定了packageName,则去指定包下去查找;

     接着,调用特定的类继续查找。由于我们找的是Activity组件信息,因此去ActivityIntentResolver类去查找。

 

     Step 4、调用mActivities自定义类去查找

 

[java]  view plain copy print ?
  1. //调用父类IntentResolver方法去查找  
  2.    public List queryIntent(Intent intent, String resolvedType, int flags) {  
  3.         mFlags = flags;  
  4.         return super.queryIntent(intent, resolvedType,  
  5.             (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);  
  6.    }  

        该过程只是简单的调用了父类IntentResolver去查找。

 

     Step5 、进入IntentResolver类去真正的实现查找,该方法为于IntentResolver.java类中。

 

[java]  view plain copy print ?
  1. public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {  
  2.      String scheme = intent.getScheme();  
  3.      //用来保存查找到的组件信息,如Activity等  
  4.      ArrayList<R> finalList = new ArrayList<R>();  
  5.      //根据关键值去特定集合查询到的一个可能结果  
  6.      ArrayList<F> firstTypeCut = null;  
  7.      ArrayList<F> secondTypeCut = null;  
  8.      ArrayList<F> thirdTypeCut = null;  
  9.      ArrayList<F> schemeCut = null;  
  10.   
  11.      //首先是否制定的数据类型 MimeType  
  12.      // If the intent includes a MIME type, then we want to collect all of  
  13.      // the filters that match that MIME type.  
  14.      if (resolvedType != null) {  
  15.          int slashpos = resolvedType.indexOf('/');  
  16.          if (slashpos > 0) {  
  17.              final String baseType = resolvedType.substring(0, slashpos);  
  18.              if (!baseType.equals("*")) {  
  19.                 //匹配特定的MimeType  
  20.                  if (resolvedType.length() != slashpos+2|| resolvedType.charAt(slashpos+1) != '*') {  
  21.                      firstTypeCut = mTypeToFilter.get(resolvedType);  
  22.                      secondTypeCut = mWildTypeToFilter.get(baseType);  
  23.                  }   
  24.                  //...  
  25.          }  
  26.      }  
  27.      //根据模式去匹配特定的集合  
  28.      if (scheme != null) {  
  29.          schemeCut = mSchemeToFilter.get(scheme);  
  30.      }  
  31.      //可能的话在去匹配Action所在集合  
  32.      if (resolvedType == null && scheme == null && intent.getAction() != null) {  
  33.          firstTypeCut = mActionToFilter.get(intent.getAction());  
  34.      }  
  35.      //对我们前面通过关键字查询的一个集合,在此循环遍历匹配,将匹配到的结果保存在finalList集合中  
  36.      if (firstTypeCut != null) {  
  37.          buildResolveList(intent, debug, defaultOnly,  
  38.                  resolvedType, scheme, firstTypeCut, finalList);  
  39.      }  
  40.      if (secondTypeCut != null) {  
  41.          buildResolveList(intent, debug, defaultOnly,  
  42.                  resolvedType, scheme, secondTypeCut, finalList);  
  43.      }  
  44.      if (thirdTypeCut != null) {  
  45.          buildResolveList(intent, debug, defaultOnly,resolvedType, scheme, thirdTypeCut, finalList);  
  46.      }  
  47.      if (schemeCut != null) {  
  48.          buildResolveList(intent, debug, defaultOnly,  
  49.                  resolvedType, scheme, schemeCut, finalList);  
  50.      }  
  51.      //根据IntentFilter的一些优先级进行排序  
  52.      sortResults(finalList);  
  53.   
  54.      return finalList;  
  55.  }  

     buildResolveList()方法的主要作用是将可能的集合在循环遍历,将匹配的结果值保存在finalList集合中。

   该方法为于IntentResolver.java类中,方法原型如下:

 

[java]  view plain copy print ?
  1. //通过前面关键字查找的可能集合,循环遍历进行匹配,匹配成功就加入到dest集合中,即finalList集合中  
  2.     private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly,  
  3.             String resolvedType, String scheme, List<F> src, List<R> dest) {  
  4.         Set<String> categories = intent.getCategories();  
  5.   
  6.         final int N = src != null ? src.size() : 0;  
  7.         boolean hasNonDefaults = false;  
  8.         int i;  
  9.         for (i=0; i<N; i++) {  
  10.             F filter = src.get(i);  
  11.             int match;  
  12.             //是否已经加入到匹配结果中去了,不允许重复添加  
  13.             // Do we already have this one?  
  14.             if (!allowFilterResult(filter, dest)) {  
  15.                 continue;  
  16.             }  
  17.             //调用Intent-filter方法去匹配该Intent信息  
  18.             match = filter.match(  
  19.                     intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG);  
  20.             //匹配成功,就存放在finalList集合中  
  21.             if (match >= 0) {  
  22.                 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {  
  23.                     //调用子类的newResult()方法去返回一个ResolvInfo对象  
  24.                     final R oneResult = newResult(filter, match);  
  25.                     if (oneResult != null) {  
  26.                         dest.add(oneResult);  
  27.                     }  
  28.                 } else {  
  29.                     hasNonDefaults = true;  
  30.                 }  
  31.             } else {  
  32.                 //...  
  33.             }  
  34.         }  
  35.         //...  
  36.     }     

    这个函数的逻辑判断如下:


        首先、通过给定的关键字去特定集合查询一个可能的匹配集合,然后将这些集合信息保存在如下集合中:

                          ArrayList<F>firstTypeCut =null;

                          ArrayList<F>secondTypeCut =null;

                          ArrayList<F>thirdTypeCut =null;

                          ArrayList<F>schemeCut =null;

      

       然后、连续四次调用buildResolveList()去进行匹配。每次调用结束后,参数finalList保存的是匹配结果的累加值,所以这

    四次调用过程中finalList集合包含的结果是一次累加的过程。当然了,四次连续调用buildResolveList()的次序可以不分先后。

 

       最后、调用sortResults()从匹配集合中进行一些排序等。



         总结:第二部分通过重点介绍了PackageManagerService的功能匹配Intent采用的数据结构和算法,也只是简单入了下门,希

   望大家能参考源码,认真理解Intent匹配过程。

                                                                           本文原创 ,转载必须注明出处 :http://blog.csdn.net/qinjuning

 


       前言: 本文博客是公司培训之间的PPT整理而成,在此把相关的细节发布出来,希望对大家能有所帮助。

 

 

    关于Intent以及IntentFilter的基本知识,大家可以参阅如下资料,

                      SDK中对Intent与IntentFilter的介绍  ---- 英文

                       其中文翻译如下:

                              Android开发之旅: Intents和Intent Filters(理论部分)

 

    我重点分析一下两个方面:

         第一部分 、Intent以及IntentFilter说明以及匹配规则分析

         第二部分:Intent的解析过程分析
 
 

 

 第一部分 、Intent以及IntentFilter说明以及匹配规则分析

 

       想当初我看Intent相关知识时,对Intent、IntentFilter的理解就很差劲,总觉得系统定义了一个Intent,为何还要整理个

  IntentFilter出来"祸害"广大程序猿呢?但不解归不解,在具体使用咱可不能含糊,于是只好依葫芦画瓢了,反正绝对还不错。

   

 一、温故而知新 :Intent与IntentFilter两问。


        *  它们是什么 ?
 
       *  它们的区别在哪儿 ?
 
   事实上,这两个问题可以归纳为Intent和Intent的主要功能是什么 ? 大家可以先扪心自问下,看看你的掌握程度如何 ?
 
   我的理解如下:
 
        * Intent  :   主要功能是根据特定的条件找到匹配的组件,继而对该组件执 行一些操作。比如执行startActivity()时,系统
              首先要找到特定的Activity组件,然后执行onCreate()方法;startService()也得先找的特定的Service组件,然后执行  
               onCreate()或者onStart()方法 。
 
       * IntentFilter  :主要功能是为某个组件向系统注册一些特性(当然一个组件可以注册多个IntentFilter),以便Intent找到对应
             的组件。
 
 

 二、它们之间的关系是如何呢?

 
      通过前面对Intent以及IntentFilter的分析,我们很容易在语意上得出它们其实是个 前后关系
 
          * IntentFilter在前 :任何一个组件必须先通过IntentFilter注册。
 
         * Intent 在后        :根据特定信息,找到之前以及注册过的组件。
 
 
  源码分析:
 
    Intent类源码(部分) 路径位于:\frameworks\base\core\java\android\content\Intent.java

[java]  view plain copy print ?
  1. public class Intent implements Parcelable, Cloneable {  
  2.   
  3.     private String mAction;           //action值  
  4.     private Uri mData;                //uri  
  5.     private String mType;             //MimeType  
  6.     private String mPackage;          //所在包名  
  7.     private ComponentName mComponent; //组件信息  
  8.     private int mFlags;               //Flag标志位  
  9.     private HashSet<String> mCategories; //Category值  
  10.     private Bundle mExtras;           //附加值信息  
  11.     //...  
  12. }  

   IntentFilter类源码(部分) 路径位于:\frameworks\base\core\java\android\content\IntentFilter.java

[java]  view plain copy print ?
  1. public class IntentFilter implements Parcelable {  
  2.     //...  
  3.     //保存了所有action字段的值  
  4.     private final ArrayList<String> mActions;  
  5.     //保存了所有Category的值  
  6.     private ArrayList<String> mCategories = null;  
  7.     //保存了所有Schema(模式)的值  
  8.     private ArrayList<String> mDataSchemes = null;  
  9.     //保存了所有Authority字段的值  
  10.     private ArrayList<AuthorityEntry> mDataAuthorities = null;  
  11.     //保存了所有Path的值  
  12.     private ArrayList<PatternMatcher> mDataPaths = null;  
  13.     //保存了所有MimeType的值  
  14.     private ArrayList<String> mDataTypes = null;  
  15.     //...  
  16. }  

 

       PS :大家可以参详下Intent与IntentFilter类中不同字段的属性类型。Intent中属性类型基本上都是单个类型的,而IntentFilter

属性都是集合类型的。从这方面思考,更可以加深我们的理解。

 

 三、Intent匹配规则

 

      匹配种类有如下三种:      

                  *  动作(Action)检测
                 *  种类(Category)检测
                 *  数据(Data & MimeType)检测
 

      比较好理解的是,进行匹配时Intent携带的Action字段值和Category字段值必须包含在IntentFilter中,否则匹配失败。

 

       SDK中说明的具体规则如下:


            *      一个Intent对象既不包含URI,也不包含数据类型 ; 仅当过滤器也不指定任何URIs和数据类型时,才能通过检测;
              否则不能通过。

 

            *     一个Intent对象包含URI,但不包含数据类型:仅当过滤器也不指定数据类型,同时它们的URI匹配,才能通过检测。
            例如,mailto:和tel:都不指定实际数据。
 
            *    一个Intent对象包含数据类型,但不包含URI:仅当过滤也只包含数据类型且与Intent相同,才通过检测。
 
            *    一个Intent对象既包含URI,也包含数据类型(或数据类型能够从URI推断出) ; 数据类型部分,只有与过滤器中之一
             匹配才算通过;URI部分,它的URI要出现在过滤器中,或者它有content:或file: URI,又或者过滤器没有指定URI。
             换句话说,如果它的过滤器仅列出了数据类型,组件假定支持content:和file: 。

 

         PS :可别说我不会总结出来给大家分享,其实我觉得很多知识都需要自己去尝试,去努力吸收,只要经过自己的消化,

          学到的知识就是自己的了。

 

      上面的规则比较生硬吧。我们去源码中去看看Intent与IntentFilter的具体匹配方法吧。

       该方法是IntentFilter中的match()方法,该方法的内部处理逻辑就是按照上面的规则去判断的,大家可以仔细体味下,该方法

  我们在后面讲到Intent解析过程时也会用到。 具体逻辑在代码中进行了说明。

[java]  view plain copy print ?
  1. public class IntentFilter implements Parcelable {  
  2.     //匹配算法,,按照匹配规则进行  
  3.     //Intent与该IntentFilter进行匹配时调用该方法参数表示Intent的相关属性值  
  4.     public final int match(String action, String type, String scheme,  
  5.             Uri data, Set<String> categories, String logTag){  
  6.         //首先、匹配Action字段  
  7.         if (action != null && !matchAction(action)) {  
  8.             if (Config.LOGV) Log.v(  
  9.                 logTag, "No matching action " + action + " for " + this);  
  10.             return NO_MATCH_ACTION;  
  11.         }  
  12.         //其次、匹配数据(Uri和MimeType)字段  
  13.         int dataMatch = matchData(type, scheme, data);  
  14.         //...  
  15.         //最后,匹配Category字段值  
  16.         String categoryMatch = matchCategories(categories);  
  17.         //...  
  18.     }  
  19.     //...  
  20. }  


    这部分我们重点讲解的知识点有如下:
 
           1、Intent以及IntentFilter的主要职能
           2、Intent与IntentFilter的关系
           3、匹配规则说明
 
 

第二部分、 Intent的解析过程分析

  
       在继续看本部分之前,希望您最好对PackageManagerService-----程序包管理服务有一定的认知(没有也是OK的咯)。
  可以参考下面这篇问题去看看PackageManagerService的功能和相关流程。

         老罗的博客: <<Android应用程序安装过程源代码分析>>
           
 

    一、引入PackageManager

           我们知道Android源码总是贴心的(不知道有没有10086贴心),它对外提供了很多借口供应用程序调用,例如AudioManger

    (音频管理)、TelephoneManger(电话管理)、同样也提供了一个包管理-----PackageManager,通过它我们可以、获取应用

    程序包得信息,例如图标、Lable标签等。具体关于PackageManager的使用,可以参考我的另外一篇文章 :

 

                       <<Android中获取应用程序(包)的信息-----PackageManager的使用(一)>>

 

   二、PackageManagerService  ---- 重量级选手

          前面所说PackageManager不过是个傀儡,所有相关的操作都是由PackageManagerService 完成的。这儿我们简单的

      分析下PackageManagerService 的特性:

             ①、开机就启动,由SystemServer进程启动 ;

             ②、启动后它会扫描系统中所有应用程序Apk包下的AndroidManifest.xml文件,然后解析所有的

           AndroidManifest.xml文件,继而形成一个庞大的信息结构树,并且保存在PackageManagerService 的相关属性下。

                 它会扫描这两个目录:

                                /system/app  ------------------> 系统应用程序

                                /data/app      ------------------> 第三方应用程序(所有安装的Apk包都会在该目录下保存一份拷贝)

          扫描完成后,于是所有的信息结构就构建了。PackageManagerService 的四个重要属性如下:

[java]  view plain copy print ?
  1. class PackageManagerService extends IPackageManager.Stub {  
  2.     //...  
  3.     //保存了所有Activity节点信息 。         自定义类  
  4.     // All available activities, for your resolving pleasure.  
  5.     final ActivityIntentResolver mActivities =  
  6.             new ActivityIntentResolver();  
  7.     //保存了所有BroadcastReceiver节点信息  。  自定义类  
  8.     // All available receivers, for your resolving pleasure.  
  9.     final ActivityIntentResolver mReceivers =  
  10.             new ActivityIntentResolver();  
  11.     //保存了所有Service节点信息。 。  自定义类  
  12.     // All available services, for your resolving pleasure.  
  13.     final ServiceIntentResolver mServices = new ServiceIntentResolver();  
  14.     //保存了所有ContentProvider节点信息 , 以Hash值保存  
  15.     // Keys are String (provider class name), values are Provider.  
  16.     final HashMap<ComponentName, PackageParser.Provider> mProvidersByComponent =  
  17.             new HashMap<ComponentName, PackageParser.Provider>();  
  18.     //...  
  19. }  

 

 

       值得注意这些属性类型的不同。Activity、BroadcastReceiver、Service都采用了自定义类去保存相关信息,从类名上看,
  类结构应该很相似。而ContentProvider只是简单的采用了HashMap键值对去保存了信息? 莫非有错 ? 我们回忆下
 AndroidManifest.xml定义组件信息时,Activity、BroadcastReceiver、Service都可以通过<intent-filter>去隐式匹配的,而
 ContentProvider只需要一个Uri数据即可找到对应的ContentProvider组件信息了。 因此才采用了这两种结构去保存信息。
 
 
       其实我们通过getPackageManager()方法获得的PackageManager对象,只是PackageManagerService的客户端,
  该客户端类是ApplicationPackageManager,它是ContextIml类的内部类,显然该类存在于用户空间中。
 
       源代码(部分)如下:
 
[java]  view plain copy print ?
  1. @Override  
  2. public PackageManager getPackageManager() {  
  3.    //...  
  4.    // Doesn't matter if we make more than one instance.  
  5.    return (mPackageManager = new ApplicationPackageManager(this, pm));  
  6.    //...  
  7. }  
  8. class ContextImpl extends Context {  
  9.     //...  
  10.     /*package*/  
  11.     static final class ApplicationPackageManager extends PackageManager{  
  12.            //...  
  13.            @Override  
  14.            public ActivityInfo getActivityInfo(ComponentName className, int flags){  
  15.                //...  
  16.            }  
  17.              
  18.     }  
  19.     //...  
  20. }  

      它与PackageManagerService的简单关系如下:

                     Intent匹配规则以及解析框架深入分析_第6张图片
     
   三 、保存数据采用的数据结构和解析算法
 

     1、保存数据采用的数据结构

 
       毫无疑问,保存所有信息是一项很复杂的工程,在具体讲解匹配过程时,我们先看看系统为了保存这些结构定义的一些
   数据结构。
           
       IntentInfo类:继承至IntentFilter类

               作用:保存了每个<intent-filter>节点信息

     ActivityIntentInfo类:继承至IntentInfo类

               作用:保存了<activity />节点下的< intent-filter>节点信息

      ServiceIntentInfo:继承至IntentInfo类

             作用:保存了<service />节点下的< intent-filter >节点信息

     Activity类:保存了<activity />节点信息

     Service类:保存了<service />节点信息


 

    PS:这些都是PackageParser类的内部类 。PackageParser的主要功能就是解析AndroidManifest.xml文件

 

      IntentResolver类:模板类,父类,保存了<activity/>、<service/>、<receiver />节点的共同信息。

      ActivityIntentResolver类:继承至IntentResolver类。

            作用:保存了所有<activity/>或者<receiver/>节点信息。(Activity或者BroadcastReceiver信息就是用该自定义类保存的)

     ServiceIntentResolver类:继承至IntentResolver类,保存了

            作用:保存了所有<service/>节点信息。(Service信息就是用该自定义类保存的)。

 

  一个简单的UML图表示如下:  

                                                  Intent匹配规则以及解析框架深入分析_第7张图片

 

   

     2、Intent解析采用的算法

        不同的数据结构决定了不同的算法,而不同的算法又决定着性能,例如时间复杂度以及空间复杂度等。 在具体讲解解析

  采用的算法时,我们先理解下这个情景。

        假设一个女人决定参加一个相亲节目(大家可以理解成《非诚勿扰》),然后她向主办方提出如下条件:       

                1、身高 ?     175cm以上 ;

                        2、 财富 ?     100万 ;

                        3、 学历 ?     本科以上 ;

                           ……

 

     主办发经理一看,你丫的要求还真高。但客户是万能的,该经理也只能去找到满足这些条件的男人咯。

 

       最开始,该经理是这么想的,我把所有男的都给遍历一遍,肯定把满足这些条件的男人给揪出来。他找啊找,觉得这么找下去

  是不是太二B了(呵呵,你也是这么想的吗?)。经理就开始想:“我为什么不能根据这些条件把所有男的给分成三个种群呢?有钱

  的男人在一起,高个子的男人在一起,高学历的男人在一起,这样查找起来不是更快吗 ? “


      于是,有了如下的划分:     PS, 你是属于哪一类额 ? 


                      二B算法(没有分类)                                                                                    高级点的算法(分类后)

        Intent匹配规则以及解析框架深入分析_第8张图片           Intent匹配规则以及解析框架深入分析_第9张图片


 

       可能大家对这种根据关键值分类的好处不能一目了然。我们举个一般例子吧:

           假设当前共有100个男的。 其中有钱的有20人,高个子有30人,高学历男人有10人。


       根据第一种算法分类,我们需要比较100次,而第二种算法我们总共只需要比较60次。从整个基数来分析,算法肯定优化了

       最后,对不同的分类中查询的结果进行组合重新排列下,即可得到我们的满足该女性的要求。

          

        同样的,在进行Intent匹配时,Android也采用了第二种方法来进行算法匹配。它根据一些关键值Action、MimeType、

   Schema字段去进行分类。分类之后的集合大致如下:


                               Intent匹配规则以及解析框架深入分析_第10张图片


       于是在进行具体匹配时,我们只是需要根据关键值从不同集合中获取即可。


       事实上,由于MimeType的通配符(*)的特性,它的匹配可以说是最难的。参考IntentResolver类,真正的关键值如下:

[java]  view plain copy print ?
  1. //模板类 F类型可能为ActivityIntentInfo或ServiceIntentInfo,R对象是ResolverInfo类  
  2. public class IntentResolver<F extends IntentFilter, R extends Object> {  
  3.     //保存了所有<intent-filter>节点信息  
  4.     //All filters that have been registered.   
  5.     private final HashSet<F> mFilters = new HashSet<F>();  
  6.     /** All of the MIME types that have been registered, such as "image/jpeg", 
  7.      * "image/*", or "{@literal *}/*". 
  8.      */  
  9.     //关键值表示MimeType形如:  image/jpeg 、 image/*、/*  类型  
  10.     private final HashMap<String, ArrayList<F>> mTypeToFilter   
  11.       
  12.     /**  
  13.      * The base names of all of all fully qualified MIME types that have been  
  14.      * registered, such as "image" or "*".  Wild card MIME types such as  
  15.      * "image/*" will not be here.  
  16.      */  
  17.     //关键值表示MimeType形如:image、 image/*、*  类型  
  18.     private final HashMap<String, ArrayList<F>> mBaseTypeToFilter  
  19.       
  20.     /**  
  21.      * The base names of all of the MIME types with a sub-type wildcard that  
  22.      * have been registered.  For example, a filter with "image/*" will be  
  23.      * included here as "image" but one with "image/jpeg" will not be  
  24.      * included here.  This also includes the "*" for the "{@literal *}/*"  
  25.      * MIME type.  
  26.      */  
  27.     //这个关键字段表示MimeType形如 :image、* 类型  
  28.     private final HashMap<String, ArrayList<F>> mWildTypeToFilter   
  29.       
  30.     //All of the URI schemes (such as http) that have been registered.  
  31.     //关键值字段表示Schema  
  32.     private final HashMap<String, ArrayList<F>> mSchemeToFilter   
  33.     /** 
  34.      * All of the actions that have been registered, but only those that did 
  35.      * not specify data. 
  36.      */  
  37.     //关键值字段表示:Action  
  38.     private final HashMap<String, ArrayList<F>> mActionToFilter  
  39.     //All of the actions that have been registered and specified a MIME type.  
  40.     //关键值字段表示:Action和MimeType。 即该<intent-filter>节点必须包含action和MimeType  
  41.     private final HashMap<String, ArrayList<F>> mTypedActionToFilter  
  42. }  

 

      于是,通过这些关键字段我们可以去特定集合去查找,最后将结果在重新组合下,那不就万事大吉了。


      最后,我们通过代码走读的方式,以一个查询Activity的信息的方法,带领大家去熟悉具体流程。该方法原型为:

          //通过给定的intent,查询所有匹配的Activity组件信息

               abstract List<ResolveInfo>   queryIntentActivities(Intent intent, int flags) 

 

 

        PS:其实查询Activity、Service、BroadcastReceiver的流程基本上是相同的。

 

 

       Step 1、获取PackageManager代理对象,调用该方法:

[java]  view plain copy print ?
  1. PackageManager mPackageManger = this.getPackageManager() ;  
  2.           
  3.         //为了说明,这儿我们简单查询一个Intent对象,即所有应用程序的启动Activity  
  4.         Intent mainIntent = new Intent() ;  
  5.         mainIntent.setAction(Intent.ACTION_MAIN);  
  6.         mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);  
  7.         mPackageManger.queryIntentActivities(mainIntent, 0);  
  8.           
  9.           
  10.        //获取PackageManager对象  
  11.        @Override  
  12.        public PackageManager getPackageManager() {  
  13.            //...  
  14.            IPackageManager pm = ActivityThread.getPackageManager();  
  15.            if (pm != null) {  
  16.                // Doesn't matter if we make more than one instance.  
  17.                return (mPackageManager = new ApplicationPackageManager(this, pm));  
  18.            }  
  19.        }  
  20.              

       Step 2、该PackageManager代理对象实则为ApplicatonPackageManager 对象,该对象是ContextIml的内部类。

[java]  view plain copy print ?
  1. @Override  
  2.         public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {  
  3.             try {  
  4.                 //mPM对象就是PackageManagerService的客户端  
  5.                 return mPM.queryIntentActivities(  
  6.                     intent,  
  7.                     intent.resolveTypeIfNeeded(mContext.getContentResolver()),  
  8.                     flags);  
  9.             } catch (RemoteException e) {  
  10.                 throw new RuntimeException("Package manager has died", e);  
  11.             }  
  12.         }  

 

       Step 3、调用服务端PackageManagerService对象的对应方法。该方法位于PackageManagerService.java类中

[java]  view plain copy print ?
  1. public List<ResolveInfo> queryIntentActivities(Intent intent,  
  2.         String resolvedType, int flags) {  
  3.     //是否设置了组件ComponetName 信息  
  4.  ComponentName comp = intent.getComponent();  
  5.     if (comp != null) {  
  6.         List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);  
  7.         ActivityInfo ai = getActivityInfo(comp, flags);  
  8.         if (ai != null) {  
  9.             ResolveInfo ri = new ResolveInfo();  
  10.             ri.activityInfo = ai;  
  11.             list.add(ri);  
  12.         }  
  13.         return list;  
  14.     }  
  15.   
  16.     synchronized (mPackages) {  
  17.         //是否设置了包名  
  18.         String pkgName = intent.getPackage();  
  19.         if (pkgName == null) {  
  20.             //调用mActivities去查询  
  21.             return (List<ResolveInfo>)mActivities.queryIntent(intent,  
  22.                     resolvedType, flags);  
  23.         }  
  24.         PackageParser.Package pkg = mPackages.get(pkgName);  
  25.         if (pkg != null) {  
  26.             return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent,  
  27.                     resolvedType, flags, pkg.activities);  
  28.         }  
  29.         return null;  
  30.     }  
  31.  }    


     首先、该方法判断IntentComponentName是否存在,如果存在则为显示匹配了,直接返回特定组件相关信息;

     其次、判断是否设置了包名,即packageName,如果没有指定包名,则查询所有的应用程序包去找匹配的组件信息。如果

   指定了packageName,则去指定包下去查找;

     接着,调用特定的类继续查找。由于我们找的是Activity组件信息,因此去ActivityIntentResolver类去查找。

 

     Step 4、调用mActivities自定义类去查找

 

[java]  view plain copy print ?
  1. //调用父类IntentResolver方法去查找  
  2.    public List queryIntent(Intent intent, String resolvedType, int flags) {  
  3.         mFlags = flags;  
  4.         return super.queryIntent(intent, resolvedType,  
  5.             (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0);  
  6.    }  

        该过程只是简单的调用了父类IntentResolver去查找。

 

     Step5 、进入IntentResolver类去真正的实现查找,该方法为于IntentResolver.java类中。

 

[java]  view plain copy print ?
  1. public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly) {  
  2.      String scheme = intent.getScheme();  
  3.      //用来保存查找到的组件信息,如Activity等  
  4.      ArrayList<R> finalList = new ArrayList<R>();  
  5.      //根据关键值去特定集合查询到的一个可能结果  
  6.      ArrayList<F> firstTypeCut = null;  
  7.      ArrayList<F> secondTypeCut = null;  
  8.      ArrayList<F> thirdTypeCut = null;  
  9.      ArrayList<F> schemeCut = null;  
  10.   
  11.      //首先是否制定的数据类型 MimeType  
  12.      // If the intent includes a MIME type, then we want to collect all of  
  13.      // the filters that match that MIME type.  
  14.      if (resolvedType != null) {  
  15.          int slashpos = resolvedType.indexOf('/');  
  16.          if (slashpos > 0) {  
  17.              final String baseType = resolvedType.substring(0, slashpos);  
  18.              if (!baseType.equals("*")) {  
  19.                 //匹配特定的MimeType  
  20.                  if (resolvedType.length() != slashpos+2|| resolvedType.charAt(slashpos+1) != '*') {  
  21.                      firstTypeCut = mTypeToFilter.get(resolvedType);  
  22.                      secondTypeCut = mWildTypeToFilter.get(baseType);  
  23.                  }   
  24.                  //...  
  25.          }  
  26.      }  
  27.      //根据模式去匹配特定的集合  
  28.      if (scheme != null) {  
  29.          schemeCut = mSchemeToFilter.get(scheme);  
  30.      }  
  31.      //可能的话在去匹配Action所在集合  
  32.      if (resolvedType == null && scheme == null && intent.getAction() != null) {  
  33.          firstTypeCut = mActionToFilter.get(intent.getAction());  
  34.      }  
  35.      //对我们前面通过关键字查询的一个集合,在此循环遍历匹配,将匹配到的结果保存在finalList集合中  
  36.      if (firstTypeCut != null) {  
  37.          buildResolveList(intent, debug, defaultOnly,  
  38.                  resolvedType, scheme, firstTypeCut, finalList);  
  39.      }  
  40.      if (secondTypeCut != null) {  
  41.          buildResolveList(intent, debug, defaultOnly,  
  42.                  resolvedType, scheme, secondTypeCut, finalList);  
  43.      }  
  44.      if (thirdTypeCut != null) {  
  45.          buildResolveList(intent, debug, defaultOnly,resolvedType, scheme, thirdTypeCut, finalList);  
  46.      }  
  47.      if (schemeCut != null) {  
  48.          buildResolveList(intent, debug, defaultOnly,  
  49.                  resolvedType, scheme, schemeCut, finalList);  
  50.      }  
  51.      //根据IntentFilter的一些优先级进行排序  
  52.      sortResults(finalList);  
  53.   
  54.      return finalList;  
  55.  }  

     buildResolveList()方法的主要作用是将可能的集合在循环遍历,将匹配的结果值保存在finalList集合中。

   该方法为于IntentResolver.java类中,方法原型如下:

 

[java]  view plain copy print ?
  1. //通过前面关键字查找的可能集合,循环遍历进行匹配,匹配成功就加入到dest集合中,即finalList集合中  
  2.     private void buildResolveList(Intent intent, boolean debug, boolean defaultOnly,  
  3.             String resolvedType, String scheme, List<F> src, List<R> dest) {  
  4.         Set<String> categories = intent.getCategories();  
  5.   
  6.         final int N = src != null ? src.size() : 0;  
  7.         boolean hasNonDefaults = false;  
  8.         int i;  
  9.         for (i=0; i<N; i++) {  
  10.             F filter = src.get(i);  
  11.             int match;  
  12.             //是否已经加入到匹配结果中去了,不允许重复添加  
  13.             // Do we already have this one?  
  14.             if (!allowFilterResult(filter, dest)) {  
  15.                 continue;  
  16.             }  
  17.             //调用Intent-filter方法去匹配该Intent信息  
  18.             match = filter.match(  
  19.                     intent.getAction(), resolvedType, scheme, intent.getData(), categories, TAG);  
  20.             //匹配成功,就存放在finalList集合中  
  21.             if (match >= 0) {  
  22.                 if (!defaultOnly || filter.hasCategory(Intent.CATEGORY_DEFAULT)) {  
  23.                     //调用子类的newResult()方法去返回一个ResolvInfo对象  
  24.                     final R oneResult = newResult(filter, match);  
  25.                     if (oneResult != null) {  
  26.                         dest.add(oneResult);  
  27.                     }  
  28.                 } else {  
  29.                     hasNonDefaults = true;  
  30.                 }  
  31.             } else {  
  32.                 //...  
  33.             }  
  34.         }  
  35.         //...  
  36.     }     

    这个函数的逻辑判断如下:


        首先、通过给定的关键字去特定集合查询一个可能的匹配集合,然后将这些集合信息保存在如下集合中:

                          ArrayList<F>firstTypeCut =null;

                          ArrayList<F>secondTypeCut =null;

                          ArrayList<F>thirdTypeCut =null;

                          ArrayList<F>schemeCut =null;

      

       然后、连续四次调用buildResolveList()去进行匹配。每次调用结束后,参数finalList保存的是匹配结果的累加值,所以这

    四次调用过程中finalList集合包含的结果是一次累加的过程。当然了,四次连续调用buildResolveList()的次序可以不分先后。

 

       最后、调用sortResults()从匹配集合中进行一些排序等。



         总结:第二部分通过重点介绍了PackageManagerService的功能匹配Intent采用的数据结构和算法,也只是简单入了下门,希

   望大家能参考源码,认真理解Intent匹配过程。

你可能感兴趣的:(android,Intend)