2024年Android最新上部分:Android startActivity原理分析(基于Android 8(1),2024年最新ssm面试必备知识点

最后

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们!

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

以下是今天给大家分享的一些独家干货:

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

                intent, requestCode, options);

        if (ar != null) {

            mMainThread.sendActivityResult(

                mToken, mEmbeddedID, requestCode, ar.getResultCode(),

                ar.getResultData());

        }

        if (requestCode >= 0) {

            mStartedActivity = true;

        }



        cancelInputsAndStartExitTransition(options);

    } else {

        if (options != null) {

            mParent.startActivityFromChild(this, intent, requestCode, options);

        } else {

            // Note we want to go through this method for compatibility with

            // existing applications that may have overridden it.

            mParent.startActivityFromChild(this, intent, requestCode);

        }

    }

}



上面有四个函数,基本上都是简单的判断和一些代码的封装,在最后一个函数中,执行了mInstrumentation对象下的execStartActivity函数,这个函数会做一些脱离应用进程的操作,准备通过IBinder机制将请求发送到Ams中。简单说一下传入参数



> **1.this指的就是我们的MainActivity对象,标明谁是发起者  

> 2.getApplicationThread 获取的是应用标示,因为Android系统中每一个应用都是一个客户,而Ams更像是柜台服务人员  

> 3.mToken是系统的一个内部标示,是ActivityRecord类下的一个静态内部类,里边用弱引用存了一个ActivityRecord对象,会在startActivityForResult函数中应用,标明应该调用谁的onActivityResult函数,后面会有讲到  

> 4.同第一个参数,标明了发起者是谁  

> 5.intent就略过吧,很常用了  

> 6.requestCode这个值也很常用,所以也不做详细介绍了,默认-1  

> 7.options是我们跳转页面需要携带的参数,当然没有参数自然也会是null了**



[]( )二 接下来我们了解完参数后看一下内部实现吧

---------------------------------------------------------------------------------------



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;

    if (referrer != null) {

        intent.putExtra(Intent.EXTRA_REFERRER, referrer);

    }

    if (mActivityMonitors != null) {

        synchronized (mSync) {

            final int N = mActivityMonitors.size();

            for (int i=0; i<N; i++) {

                final ActivityMonitor am = mActivityMonitors.get(i);

                ActivityResult result = null;

                if (am.ignoreMatchingSpecificIntents()) {

                    result = am.onStartActivity(intent);

                }

                if (result != null) {

                    am.mHits++;

                    return result;

                } else if (am.match(who, null, intent)) {

                    am.mHits++;

                    if (am.isBlocking()) {

                        return requestCode >= 0 ? am.getResult() : null;

                    }

                    break;

                }

            }

        }

    }

    try {

        intent.migrateExtraStreamToClipData();

        intent.prepareToLeaveProcess(who);

        int result = ActivityManager.getService()

            .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;

}



这个实现就是执行intent脱离我们的应用线程,然后发送IBinder消息(俗称IPC)到Ams中,到此处便结束了应用之旅,开始进入系统内部.



另外我在补充一下ActivityMonitor这个类的知识,这个是一个Activity的监视器,可以在写测试类的时候用到,通常是写一个测试类,然后继承InstrumentationTestCase,有兴趣的可以试一下



系统进程内(system\_server)



@Override

public final int startActivity(IApplicationThread caller, String callingPackage,

        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {

    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,

            resultWho, requestCode, startFlags, profilerInfo, bOptions,

            UserHandle.getCallingUserId());

}



这个函数定义在Ams(ActivityManagerService),因为是对外调用的接口,所以为了不做过多的逻辑处理,紧接着转发给了startActivityAsUser函数校验一下发起者身份。我们可以看到其余的参数都是透传过来的,但是最后多出来了一个UserId



UserId:手机上的用户标示,类似于电脑多用户,通过 应用uid / 100000 计算得出,0为手机持有者,默认值为0



**Uid:应用id,普通用户从 10000  

Pid:进程id**



[]( )三我们继续向下看吧

---------------------------------------------------------------------------



@Override

public final int startActivityAsUser(IApplicationThread caller, String callingPackage,

        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {

    enforceNotIsolatedCaller("startActivity");

    userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),

            userId, false, ALLOW_FULL_ONLY, "startActivity", null);

    // TODO: Switch to user app stacks here.

    return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,

            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,

            profilerInfo, null, null, bOptions, false, userId, null, "startActivityAsUser");

}



通过了mUserController这个对象下的handleIncomingUser函数校验,这个函数会自己在通过UserHandle.getCallingUserId()获取一遍userid,与传入参数的userId做校验,如果一支便返回,所以我们用普通用户的话,这个userId便是0了。随后转交给了ActivityStarter这个类来正式执行开启过程,首先第一站便是startActivityMayWait函数,接下来如果函数过长,我会在代码上加注释,方便阅读。



这个函数的参数也开始变得非常多了。因为后续涉及到的功能太广了,我会把我所知的都写出来分享给大家



*   Instant App



final int startActivityMayWait(IApplicationThread caller, int callingUid,

        String callingPackage, Intent intent, String resolvedType,

        IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,

        IBinder resultTo, String resultWho, int requestCode, int startFlags,

        ProfilerInfo profilerInfo, WaitResult outResult,

        Configuration globalConfig, Bundle bOptions, boolean ignoreTargetSecurity, int userId,

        TaskRecord inTask, String reason) {

    if (intent != null && intent.hasFileDescriptors()) {

        throw new IllegalArgumentException("File descriptors passed in Intent");

    }

    mSupervisor.mActivityMetricsLogger.notifyActivityLaunching();

    boolean componentSpecified = intent.getComponent() != null;



    // Save a copy in case ephemeral needs it

    final Intent ephemeralIntent = new Intent(intent);

    // Don't modify the client's object!

    intent = new Intent(intent);

    //判断是否为 instant app,instant app可以通过URL打开,不需要component,所以置为null

    //关于 instant app,大家可以去查询一下资料哦

    if (componentSpecified

            && intent.getData() != null

            && Intent.ACTION_VIEW.equals(intent.getAction())

            && mService.getPackageManagerInternalLocked()

                    .isInstantAppInstallerComponent(intent.getComponent())) {

        // intercept intents targeted directly to the ephemeral installer the

        // ephemeral installer should never be started with a raw URL; instead

        // adjust the intent so it looks like a "normal" instant app launch

        intent.setComponent(null /*component*/);

        componentSpecified = false;

    }



    //通过Pms(PackageManagerService),并且通过我们传递过来的intent去查询目标页面的R

    //esolveInfo。这个类里放了ActivityInfo、ServiceInfo、ProviderInfo,大家可以在

    //demo中通过PackageManager.resolveActivity函数来获取该对象

    ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId);

    if (rInfo == null) {

        UserInfo userInfo = mSupervisor.getUserInfo(userId);

        if (userInfo != null && userInfo.isManagedProfile()) {

            // Special case for managed profiles, if attempting to launch non-cryto aware

            // app in a locked managed profile from an unlocked parent allow it to resolve

            // as user will be sent via confirm credentials to unlock the profile.

            UserManager userManager = UserManager.get(mService.mContext);

            boolean profileLockedAndParentUnlockingOrUnlocked = false;

            long token = Binder.clearCallingIdentity();

            try {

                UserInfo parent = userManager.getProfileParent(userId);

                profileLockedAndParentUnlockingOrUnlocked = (parent != null)

                        && userManager.isUserUnlockingOrUnlocked(parent.id)

                        && !userManager.isUserUnlockingOrUnlocked(userId);

            } finally {

                Binder.restoreCallingIdentity(token);

            }

            if (profileLockedAndParentUnlockingOrUnlocked) {

                rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,

                        PackageManager.MATCH_DIRECT_BOOT_AWARE

                                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE);

            }

        }

    }

    // 这块便是通过解析ResolveInfo,得到ActivityInfo对象,这个ActivityInfo大家应该比较熟悉了。就是我们开发中偶尔也会用到ActivityInfo

    ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);



    // 这个便是通过解析bundle,查看是否有对activity的启动要求,比如动画之类附加属性,

    // 当然了,我们的bundle都是null了,options自然也会是null

    ActivityOptions options = ActivityOptions.fromBundle(bOptions);

    synchronized (mService) {

        //这块通过Binder获取到了真实的pid和uid,便把行参中的uid和pid置为了-1

        //因为callingUid在在调用该方法的时候被传入了-1,大家可以去上边仔细看看

        // caller是不会为空的。它是我们的应用标示,IApplicationThread对象

        final int realCallingPid = Binder.getCallingPid();

        final int realCallingUid = Binder.getCallingUid();

        int callingPid;

        if (callingUid >= 0) {

            callingPid = -1;

        } else if (caller == null) {

            callingPid = realCallingPid;

            callingUid = realCallingUid;

        } else {

            callingPid = callingUid = -1;

        }



        final ActivityStack stack = mSupervisor.mFocusedStack;

        stack.mConfigWillChange = globalConfig != null

                && mService.getGlobalConfiguration().diff(globalConfig) != 0;



        final long origId = Binder.clearCallingIdentity();



        //这块是一个系统级别的判断,我们普通的应用是没有权限使用PRIVATE_FLAG_CANT_SAVE_STATE这个标记的,这块是对重量级线程的处理过程,由于是系统级别的处理,我就没仔细研读,抱歉啦~大概意思便是替换了intent,换上了新的配置

        if (aInfo != null &&

                (aInfo.applicationInfo.privateFlags

                        & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) {

            // This may be a heavy-weight process!  Check to see if we already

            // have another, different heavy-weight process running.

            if (aInfo.processName.equals(aInfo.applicationInfo.packageName)) {

                final ProcessRecord heavy = mService.mHeavyWeightProcess;

                if (heavy != null && (heavy.info.uid != aInfo.applicationInfo.uid

                        || !heavy.processName.equals(aInfo.processName))) {

                    int appCallingUid = callingUid;

                    if (caller != null) {

                        ProcessRecord callerApp = mService.getRecordForAppLocked(caller);

                        if (callerApp != null) {

                            appCallingUid = callerApp.info.uid;

                        } else {

                            Slog.w(TAG, "Unable to find app for caller " + caller

                                    + " (pid=" + callingPid + ") when starting: "

                                    + intent.toString());

                            ActivityOptions.abort(options);

                            return ActivityManager.START_PERMISSION_DENIED;

                        }

                    }



                    IIntentSender target = mService.getIntentSenderLocked(

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

droid开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

[外链图片转存中…(img-swzwezo8-1715671668901)]

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值