【Android源码】Intent 源码分析

在Android中,我们经常需要使用到Intent类,它用于跳转Activity、启动Service、发布广播等功能,它是系统各组件之间的纽带,也可以通过Intent传递数据,因为有Intent才使得Android的组件耦合度降低。

原型模式

首先我们跳转到Intent.java:

public class Intent implements Parcelable, Cloneable {
}

我们可以发现Intent实现了Cloneable接口,所以我们可以得出一个结论,Intent使用了原型设计模式,(原型设计模式介绍参考:点击此处)我们搜索clone()方法:

@Override
public Object clone() {
   return new Intent(this);
}

public Intent(Intent o) {
   this.mAction = o.mAction;
   this.mData = o.mData;
   this.mType = o.mType;
   this.mPackage = o.mPackage;
   this.mComponent = o.mComponent;
   this.mFlags = o.mFlags;
   this.mContentUserHint = o.mContentUserHint;
   if (o.mCategories != null) {
       this.mCategories = new ArraySet<String>(o.mCategories);
   }
   if (o.mExtras != null) {
       this.mExtras = new Bundle(o.mExtras);
   }
   if (o.mSourceBounds != null) {
       this.mSourceBounds = new Rect(o.mSourceBounds);
   }
   if (o.mSelector != null) {
       this.mSelector = new Intent(o.mSelector);
   }
   if (o.mClipData != null) {
       this.mClipData = new ClipData(o.mClipData);
   }
}

Intent如何工作

Intent在使用的时候,可以通过添加flag、category还有需要跳转的对象等来实现功能,那么Intent是如何通过查找并匹配所需要跳转的Activity呢?

  1. app信息表的构建

    参见PackageManagerService 浅析

  2. 匹配

    当app信息表被构建好之后,Intent就可以通过信息表来匹配。

    我们以启动某个具体的Activity来分析,首先我们启动的代码是这样的:

    // 显式Intent
    Intent intent = new Intent(this, SecondActivity.class);
    startActivity(intent);
    
    // 隐式Intent
    Intent intent = new Intent(Intent.ACTION_SENDTO);
    startActivity(intent);
    

    startActivity方法,通过一系列的调用:

    @Override
    public void startActivity(Intent intent) {
       this.startActivity(intent, null);
    }
    @Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
       if (options != null) {
           startActivityForResult(intent, -1, options);
       } else {
           // Note we want to go through this call for compatibility with
           // applications that may have overridden the method.
           startActivityForResult(intent, -1);
       }
    }
    public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
           @Nullable Bundle options) {
       if (mParent == null) {
            // 启动Activity
           Instrumentation.ActivityResult ar =
               mInstrumentation.execStartActivity(
                   this, mMainThread.getApplicationThread(), mToken, this,
                   intent, requestCode, options);
          // 发送启动请求
           if (ar != null) {
               mMainThread.sendActivityResult(
                   mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                   ar.getResultData());
           }
           cancelInputsAndStartExitTransition(options);
       } else {
       }
    }
    

    最终调用的是startActivityForResult方法,在这个方法中直接调用execStartActivity方法启动Activity:

    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;
       try {
            // 将Intent数据添加到剪切板上
           intent.migrateExtraStreamToClipData();
            // 准备离开当前进程
           intent.prepareToLeaveProcess(who);
            // 调用ActivityManagerService的startActivity方法
           int result = ActivityManagerNative.getDefault()
               .startActivity(whoThread, who.getBasePackageName(), intent,
                       intent.resolveTypeIfNeeded(who.getContentResolver()),
                       token, target != null ? target.mEmbeddedID : null,
                       requestCode, 0, null, options);
            // 检查并回调给调用者
           checkStartActivityResult(result, intent);
       } catch (RemoteException e) {
           throw new RuntimeException("Failure from system", e);
       }
       return null;
    }
    

    execStartActivity方法里面其实就是调用了ActivityManagerService的startActivity方法:

    final int startActivity(Intent intent, ActivityStackSupervisor.ActivityContainer container) {
        enforceNotIsolatedCaller("ActivityContainer.startActivity");
        final int userId = mUserController.handleIncomingUser(Binder.getCallingPid(),
                Binder.getCallingUid(), mStackSupervisor.mCurrentUser, false,
                ActivityManagerService.ALLOW_FULL_ONLY, "ActivityContainer", null);
    
        // TODO: Switch to user app stacks here.
        String mimeType = intent.getType();
        final Uri data = intent.getData();
        if (mimeType == null && data != null && "content".equals(data.getScheme())) {
            mimeType = getProviderMimeType(data, userId);
        }
        container.checkEmbeddedAllowedInner(userId, intent, mimeType);
    
        intent.addFlags(FORCE_NEW_TASK_FLAGS);
        return mActivityStarter.startActivityMayWait(null, -1, null, intent, mimeType, null, null, null,
                null, 0, 0, null, null, null, null, false, userId, container, null);
    }
    

    这个方法会调用ActivityStarter的startActivityMayWait方法,这个方法中又会调用ActivityStackSupervisor.resolveIntent方法,而这个方法就是调用的PMS的resolveIntent方法:

    // ActivityStackSupervisor.java
    ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags) {
       try {
           return AppGlobals.getPackageManager().resolveIntent(intent, resolvedType,
                   PackageManager.MATCH_DEFAULT_ONLY | flags
                   | ActivityManagerService.STOCK_PM_FLAGS, userId);
       } catch (RemoteException e) {
       }
       return null;
    }   
    
    // PackageManagerService.java
    @Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType,
           int flags, int userId) {
       try {
           Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveIntent");
    
           if (!sUserManager.exists(userId)) return null;
           flags = updateFlagsForResolve(flags, userId, intent);
           enforceCrossUserPermission(Binder.getCallingUid(), userId,
                   false /*requireFullPermission*/, false /*checkShell*/, "resolve intent");
    
           Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "queryIntentActivities");
           // 获取列表
           final List<ResolveInfo> query = queryIntentActivitiesInternal(intent, resolvedType,
                   flags, userId);
           Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
                // 通过列表选择出最合适的info对象
           final ResolveInfo bestChoice =
                   chooseBestActivity(intent, resolvedType, flags, query, userId);
    
           if (isEphemeralAllowed(intent, query, userId)) {
               Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
               final EphemeralResolveInfo ai =
                       getEphemeralResolveInfo(intent, resolvedType, userId);
               if (ai != null) {
                   if (DEBUG_EPHEMERAL) {
                       Slog.v(TAG, "Returning an EphemeralResolveInfo");
                   }
                   bestChoice.ephemeralInstaller = mEphemeralInstallerInfo;
                   bestChoice.ephemeralResolveInfo = ai;
               }
               Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
           }
           return bestChoice;
       } finally {
           Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
       }
    }   
    

    queryIntentActivitiesInternal方法返回的结果就是符合intent的ActivityInfo列表:

    private @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
           String resolvedType, int flags, int userId) {
       // 获取Intent的Component对象
       ComponentName comp = intent.getComponent();
       if (comp == null) {
           if (intent.getSelector() != null) {
               intent = intent.getSelector();
               comp = intent.getComponent();
           }
       }
        // 不为空,是显式Intent,直接获取到ActivityInfo返回
       if (comp != null) {
           final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
           final ActivityInfo ai = getActivityInfo(comp, flags, userId);
           if (ai != null) {
               final ResolveInfo ri = new ResolveInfo();
               ri.activityInfo = ai;
               list.add(ri);
           }
           return list;
       }
        
       // 为空,是隐式Intent
       synchronized (mPackages) {
           final String pkgName = intent.getPackage();
           if (pkgName == null) {
               // 代码省略
               return result;
           }
           // 通过包名获取到Package对象
           final PackageParser.Package pkg = mPackages.get(pkgName);
           if (pkg != null) {
                // 在获取到ActivityInfo对象
               return filterIfNotSystemUser(
                       mActivities.queryIntentForPackage(
                               intent, resolvedType, flags, pkg.activities, userId),
                       userId);
           }
           return new ArrayList<ResolveInfo>();
       }
    }   
    

    上面这个方法就是Intent获取到ActivityInfo的核心,它的大致过程如下:

    1. 首先获取Intent的Component对象,如果不为空,说明指定了Componet,那么就直接通过Componet找到ActivityInfo列表,并且这个列表size为1,所以这个ActivityInfo就是指定需要跳转的组件。
    2. 如果没有指定Component,那就是隐式Intent调用,接着获取Intent传递的需要跳转的包名。
    3. 如果包名为空,则会通过ActivityIntentResolver等进行模糊匹配,比如根据Action、Category等。
    4. 如果包名不为空,则直接根据包名来获取到对应的ActivityInfo对象,而mActivities就是PMS存储的activity信息表。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容