Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Intent 源碼學習

Android Intent 源碼學習

編輯:關於Android編程

前言

這篇文章主要是介紹一下Android Intent,並且從Android源碼的角度對Intent查詢匹配過程進行分析。

Intent介紹

Intent的中文是“意圖”的意思,而意圖是一個非常抽象的概念,那麼在Android的編碼設計中,如何實例化意圖呢?因此Android系統明確指定一個Intent可由兩方面屬性來衡量。 主要屬性:包括Action和Data。其中Action用於表示該Intent所表達的動作意圖,Data用於表示該Action所操作的數據。次要屬性:包括Category、Type、Component和Extras。其中Category表示類別,Type表示數據的MIME類型,Component可用於指定特定的Intent的響應者(例如指定intent為某個包下的某個class類),Extras用於承載其他的信息。 Android系統中主要有兩種類型的Intent,顯示Intent(Explicit Intent)和隱式Intent(Implicit Intent)。 Explicit Intent:這類Intent明確指明了要找哪個Component。在代碼中可以通過setClassName或者setComponent來鎖定目標對象。Implicit Intent:這類Intent不明確指明要啟動哪個Component,而是設置Action、Data、Category讓系統來篩選出合適的Component。 接下來,寫兩個代碼示例,來介紹一下Explicit Intent和Implict Inent。首先是Explicit Intent:
	private void startExplicitIntentWithComponent() {
		Intent intent = new Intent();
		ComponentName component = new ComponentName(com.example.photocrop, com.example.photocrop.MainActivity);
		intent.setComponent(component);
		startActivity(intent);
	}
	
	private void startExplicitIntentWithClassName() {
		Intent intent = new Intent();
		intent.setClassName(com.example.photocrop, com.example.photocrop.MainActivity);
		startActivity(intent);
	}
但是,從源碼裡面去看,發現setClassName也是借助了ComponentName實現了Explicit Intent。源碼如下:
    public Intent setClassName(String packageName, String className) {
        mComponent = new ComponentName(packageName, className);
        return this;
    }
然後,在給出一個Implict Intent的代碼示例。我這裡用一個Activity標注一些Intent Filter為例,然後在寫一個Intent用於啟動它。
        
            
                
                
            
        
在當前應用的AndroidManifest.xml中,給SendIntentType類增加了intent-filter,action的名字為“justtest”,category的名字為“justcategory”。啟動該Activity的代碼如下:
	private void startImplictIntent() {
		Intent intent = new Intent();
		intent.setAction(justaction);
		intent.addCategory(justcategory);
		startActivity(intent);
	}
系統在匹配Implict Intent的過程中,將以Intent Filter列出的3項內容為參考標准,具體步驟如下: 首先匹配IntentFilter的Action,如果Intent設置的action不滿足IntentFilter的Action,則匹配失敗。如果IntentFilter未設定Action或者設定的Action相同,則匹配成功。然後檢查IntentFilter的Category,匹配方法同Action的匹配相同,唯一例外的是當Category為CATEGORY_DEFAULT的情況。最後檢查Data。

Activityi信息的管理

從上面的分析可以看出,系統的匹配Intent的過程中,首先需要管理當前系統中所有Activity信息。Activity的信息是PackageManagerService在掃描APK的時候進行收集和管理的。相關源碼如下:
			// 處理該package的activity信息
			N = pkg.activities.size();
			r = null;
			for (i = 0; i < N; i++) {
				PackageParser.Activity a = pkg.activities.get(i);
				a.info.processName = fixProcessName(pkg.applicationInfo.processName, a.info.processName,
						pkg.applicationInfo.uid);
				mActivities.addActivity(a, activity);
			}
上面代碼中,有兩個比較重要的數據結構,如下圖所示。 \ 結合代碼和上圖的數據結構,可知: mAcitivitys為ActivityIntentResolver類型,是PKMS的成員變量,用於保存系統中所有與Activity相關的信息。此數據結構內部也有一個mActivities變量,它以ComponentName為key,保存PackageParser.Activity對象。從APK中解析得到的所有和Acitivity相關的信息(包括XML中聲明的IntentFilter標簽)都由PackageParser.Activity來保存。 前面代碼中調用addActivity函數完成了私有信息的公有化。addActivity函數的代碼如下:
		public final void addActivity(PackageParser.Activity a, String type) {
			final boolean systemApp = isSystemApp(a.info.applicationInfo);
			mActivities.put(a.getComponentName(), a);
			final int NI = a.intents.size();
			for (int j = 0; j < NI; j++) {
				PackageParser.ActivityIntentInfo intent = a.intents.get(j);
				if (!systemApp && intent.getPriority() > 0 && activity.equals(type)) {
					// 非系統APK的priority必須為0
					intent.setPriority(0);
				}
				addFilter(intent);
			}
		}
接下來看一下addFilter函數。函數源碼如下:
    public void addFilter(F f) {
    	// mFilters保存所有IntentFilter信息
        mFilters.add(f);
        int numS = register_intent_filter(f, f.schemesIterator(),
                mSchemeToFilter,       Scheme: );
        int numT = register_mime_types(f,       Type: );
        if (numS == 0 && numT == 0) {
            register_intent_filter(f, f.actionsIterator(),
                    mActionToFilter,       Action: );
        }
        if (numT != 0) {
            register_intent_filter(f, f.actionsIterator(),
                    mTypedActionToFilter,       TypedAction: );
        }
    }
這裡又出現了幾種數據結構,它們的類似都是ArrayMap,其中F為模板參數。 mSchemeToFilter:用於保存uri中與scheme相關的IntentFilter信息。mActionToFilter:用於保存僅設置Action條件的IntentFilter信息。mTypedActionToFilter:用於保存既設置了Action又設置了Data的MIME類型的IntentFilter信息。 了解了大概的數據結構之後,我們來看一下register_intent_filter的函數實現:
    private final int register_intent_filter(F filter, Iterator i,
            ArrayMap dest, String prefix) {
        if (i == null) {
            return 0;
        }

        int num = 0;
        while (i.hasNext()) {
            String name = i.next();
            num++;
            addFilter(dest, name, filter);
        }
        return num;
    }
然後又是一個addFilter函數,明顯是一個函數重載,我們來看一下這個addFilter的實現:
    private final void addFilter(ArrayMap map, String name, F filter) {
        F[] array = map.get(name);
        if (array == null) {
            array = newArray(2);
            map.put(name,  array);
            array[0] = filter;
        } else {
            final int N = array.length;
            int i = N;
            while (i > 0 && array[i-1] == null) {
                i--;
            }
            if (i < N) {
                array[i] = filter;
            } else {
                F[] newa = newArray((N*3)/2);
                System.arraycopy(array, 0, newa, 0, N);
                newa[N] = filter;
                map.put(name, newa);
            }
        }
    }
其實代碼還是很簡單的,如果F數組存在,則判斷容量,不夠則擴容,夠的話就找到位置插入。如果F數組不存在,則創建一個容量為2的數組,將0號元素賦值為該filter。

Intent匹配查詢分析

客戶端通過ApplicationPackageManager輸出的queryIntentActivities函數向PackageManagerService發起一次查詢請求,代碼如下:
    @Override
    public List queryIntentActivities(Intent intent,
                                                   int flags) {
        return queryIntentActivitiesAsUser(intent, flags, mContext.getUserId());
    }

    /** @hide Same as above but for a specific user */
    @Override
    public List queryIntentActivitiesAsUser(Intent intent,
                                                   int flags, int userId) {
        try {
            return mPM.queryIntentActivities(
                intent,
                intent.resolveTypeIfNeeded(mContext.getContentResolver()),
                flags,
                userId);
        } catch (RemoteException e) {
            throw new RuntimeException(Package manager has died, e);
        }
    }
可以看到,queryIntentActivities的真正實現是在PackageManagerService.java中,函數代碼如下:
	public List queryIntentActivities(Intent intent, String resolvedType, int flags, int userId) {
		if (!sUserManager.exists(userId))
			return Collections.emptyList();
		enforceCrossUserPermission(Binder.getCallingUid(), userId, false, query intent activities);
		ComponentName comp = intent.getComponent();
		if (comp == null) {
			if (intent.getSelector() != null) {
				intent = intent.getSelector();
				comp = intent.getComponent();
			}
		}

		if (comp != null) {
			// Explicit的Intent,直接根據component得到對應的ActivityInfo
			final List list = new ArrayList(1);
			final ActivityInfo ai = getActivityInfo(comp, flags, userId);
			if (ai != null) {
				final ResolveInfo ri = new ResolveInfo();
				ri.activityInfo = ai;
				list.add(ri);
			}
			return list;
		}

		// reader
		synchronized (mPackages) {
			final String pkgName = intent.getPackage();
			if (pkgName == null) {
				// Implicit Intent
				return mActivities.queryIntent(intent, resolvedType, flags, userId);
			}
			final PackageParser.Package pkg = mPackages.get(pkgName);
			if (pkg != null) {
				// 指定了包名的Intent
				return mActivities.queryIntentForPackage(intent, resolvedType, flags, pkg.activities, userId);
			}
			return new ArrayList();
		}
	}
可以看到,Explicit Intent的實現較為簡單,我們重點來看一下Implict Intent實現。Implicit Intent調用了queryIntent方法,我們來看一下queryIntent的實現代碼:
		public List queryIntent(Intent intent, String resolvedType, int flags, int userId) {
			if (!sUserManager.exists(userId))
				return null;
			mFlags = flags;
			return super.queryIntent(intent, resolvedType, (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0, userId);
		}
繼續跟蹤到IntentResolver.java的queryIntent方法,源碼如下:
    public List queryIntent(Intent intent, String resolvedType, boolean defaultOnly,
            int userId) {
        String scheme = intent.getScheme();

        ArrayList finalList = new ArrayList();

        // 最多有4輪匹配操作
        F[] firstTypeCut = null;
        F[] secondTypeCut = null;
        F[] thirdTypeCut = null;
        F[] schemeCut = null;

        // If the intent includes a MIME type, then we want to collect all of
        // the filters that match that MIME type.
        if (resolvedType != null) {
            int slashpos = resolvedType.indexOf('/');
            if (slashpos > 0) {
                final String baseType = resolvedType.substring(0, slashpos);
                if (!baseType.equals(*)) {
                    if (resolvedType.length() != slashpos+2
                            || resolvedType.charAt(slashpos+1) != '*') {
                        // Not a wild card, so we can just look for all filters that
                        // completely match or wildcards whose base type matches.
                        firstTypeCut = mTypeToFilter.get(resolvedType);
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                    } else {
                        // We can match anything with our base type.
                        firstTypeCut = mBaseTypeToFilter.get(baseType);
                        secondTypeCut = mWildTypeToFilter.get(baseType);
                    }
                    // Any */* types always apply, but we only need to do this
                    // if the intent type was not already */*.
                    thirdTypeCut = mWildTypeToFilter.get(*);
                } else if (intent.getAction() != null) {
                    // The intent specified any type ({@literal *}/*).  This
                    // can be a whole heck of a lot of things, so as a first
                    // cut let's use the action instead.
                    firstTypeCut = mTypedActionToFilter.get(intent.getAction());
                }
            }
        }

        // If the intent includes a data URI, then we want to collect all of
        // the filters that match its scheme (we will further refine matches
        // on the authority and path by directly matching each resulting filter).
        if (scheme != null) {
            schemeCut = mSchemeToFilter.get(scheme);
        }

        // If the intent does not specify any data -- either a MIME type or
        // a URI -- then we will only be looking for matches against empty
        // data.
        if (resolvedType == null && scheme == null && intent.getAction() != null) {
            firstTypeCut = mActionToFilter.get(intent.getAction());
        }

        FastImmutableArraySet categories = getFastIntentCategories(intent);
        if (firstTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, firstTypeCut, finalList, userId);
        }
        if (secondTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, secondTypeCut, finalList, userId);
        }
        if (thirdTypeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, thirdTypeCut, finalList, userId);
        }
        if (schemeCut != null) {
            buildResolveList(intent, categories, debug, defaultOnly,
                    resolvedType, scheme, schemeCut, finalList, userId);
        }
        sortResults(finalList);

        return finalList;
    }
具體的查詢匹配過程是由buildResolveList函數完成了。查詢的匹配實現我就不貼代碼了,大家自己去查詢看就好了。

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved