Android 设计模式 笔记 - Intent信息树精确查找

Posted 鲨鱼丶

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 设计模式 笔记 - Intent信息树精确查找相关的知识,希望对你有一定的参考价值。

Intent 的使用我们都差不多了解了,比如启动一个Activity的时候我们的代码应该这样写:

startActivity(new Intent(MainActivity.this,BuilderActivity.class));

这个情况我们Intent中看到了两个参数,第一个参数是上下文参数 ,不管他,第二个参数就是我们指定要跳转的Activity参数了。

还有一种情况就是我们并不指定具体的组件:

startActivity(new Intent(Intent.ACTION_SENDTO));

这种方式我们称之为隐式Intent

不管哪种方式,这个最后的情况都是查找到对应的组件然后进行相应的操作,比如跳转Activity,与Service通信等等。这个查找的过程是怎么样的呐,我们下看下上面使用的startActivity函数,看下这个函数的实现:

    @Override
    public void startActivity(Intent intent) 
        startActivity(intent, null);
    

    @Override
    public void startActivity(Intent intent, 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);
        
    

看到代码,这个函数的实现主要时调用了startActivityForResult函数,我们去追踪这个函数:

   public void startActivityForResult(Intent intent, int requestCode) 
        startActivityForResult(intent, requestCode, null);
    

    public void startActivityForResult(Intent intent, int requestCode, 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());
            
            //代码省略
         else 
          //代码省略
        
    

这个函数做了两件事情:

  • 启动Ativity
  • 发送请求

我们首先看下启动Activity的方法,startActivityForResult的启动Activity函数主要是Instrumentation类中的execStartActivity函数所做的,这个函数在里面做了哪些操作怎么去查找对应的Activity是我们想要知道的,追踪一下代码:

    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) 
        IApplicationThread whoThread = (IApplicationThread) contextThread;
     //代码省略
        try 
            //将Intent中的数据迁移到黏贴板中
            intent.migrateExtraStreamToClipData();
            //准备离开进程
            intent.prepareToLeaveProcess();
            //调用ActivityManagerNative.getDefault()的startActivity的方法
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, null, options);
                //监测结果,并回调给调用端
            checkStartActivityResult(result, intent);
         catch (RemoteException e) 
        
        return null;
    

这个函数做了几件事情:

  • 迁移Intent中的数据
  • 调用ActivityManagerNative.getDefault()中的startActivity函数,追踪代码之后可以知道ActivityManagerNative.getDefault()函数返回ActivityManagerService
  • 回调结果给调用者

所以主要的查找和启用组价的方法在ActivityManagerService中的startActivity函数里面,我们去看下这个函数的具体实现:

  @Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags,
            String profileFile, ParcelFileDescriptor profileFd, Bundle options) 
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode,
                startFlags, profileFile, profileFd, options, UserHandle.getCallingUserId());
    

追踪函数startActivityAsUser:

 @Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo,
            String resultWho, int requestCode, int startFlags,
            String profileFile, ParcelFileDescriptor profileFd, Bundle options, int userId) 
        enforceNotIsolatedCaller("startActivity");
        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, true, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent, resolvedType,
                resultTo, resultWho, requestCode, startFlags, profileFile, profileFd,
                null, null, options, userId, null);
    

我们看了代码之后发现startActivityMayWait最后调用了一个我们很熟悉的一个类的函数,就是PMS的resolveIntent函数,现在这个方法可以和上次讲PackageManagerService联系起来了,我们看下这个代码的主要实现:

    @Override
    public ResolveInfo resolveIntent(Intent intent, String resolvedType,
            int flags, int userId) 
        if (!sUserManager.exists(userId)) return null;
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "resolve intent");
        List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
        return chooseBestActivity(intent, resolvedType, flags, query, userId);
    

这个方法调用了queryIntentActivities和chooseBestActivity方法,看了但是看了之后你就知道其实这个函数的主要功能其实是在选择从queryIntentActivities中获取的list值,那么queryIntentActivities函数做了什么操作,实现什么功能呢,我们看下代码:

 @Override
    public List<ResolveInfo> queryIntentActivities(Intent intent,
            String resolvedType, int flags, int userId) 
        if (!sUserManager.exists(userId)) return Collections.emptyList();
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false, "query intent activities");
        //获取Intent的Component对象
        ComponentName comp = intent.getComponent();
        //Component为空,则为隐式Intent
        if (comp == null) 
            if (intent.getSelector() != null) 
                intent = intent.getSelector(); 
                comp = intent.getComponent();
            
        
        //精确跳转时Component对象不为空
        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 mActivities.queryIntent(intent, resolvedType, flags, userId);
            
            //通过包名获取Package对象
            final PackageParser.Package pkg = mPackages.get(pkgName);
            //通过package对象获取ActivityInfo
            if (pkg != null) 
                return mActivities.queryIntentForPackage(intent, resolvedType, flags,
                        pkg.activities, userId);
            
            return new ArrayList<ResolveInfo>();
        
    

上面的函数做的事情:

  • 判断Intent有没有指明Component对象,指明了之后直接通过Component获取到ActivityInfo列表,列表数量只有一个,这个就是我们要找的组件。如果没有指明这个Component对象,我们就要查看Intent是否已经指定了包名如果有包名就可以通过包名获取ActivityInfo列表,如果没有包名就需要通过ActivityIntentResolver进行模糊配对,比如androidMainnifest中的Action、Category等等。

Intent的跳转过程,到这里我们就了解了:

  • 在系统启动时PMS会启动,这个时候PMS会解析所有已经安装的应用的信息,构建信息表,当用户通过Intent跳转到某个组件的时候,会根据Intent所传导的信息从PMS中查找对应的组件列表,最后查找到目标组件,进行跳转。


以上是关于Android 设计模式 笔记 - Intent信息树精确查找的主要内容,如果未能解决你的问题,请参考以下文章

Android 设计模式 笔记 - PackageManagerService信息树

Android学习笔记 Intent

微信团队讲课笔记 Android 开发UI设计

Android笔记02:Intent机制详解

Android笔记:intent

Android学习笔记之Intent