首页 > 其他分享 >深入理解Activity启动流程和AMS框架(一)

深入理解Activity启动流程和AMS框架(一)

时间:2024-07-29 20:29:51浏览次数:9  
标签:调用 流程 app Activity msg new AMS final

链接 https://mp.weixin.qq.com/s?__biz=MzIwNjQ1NzQxNA==&mid=2247484149&idx=1&sn=fea623b475af3f05c657c1e55e3c478f&chksm=97201ddca05794cab15fa098ffb0ce4b5ca7791e1023e0e87969d89e0dbcfce28327ac9221e8&scene=21#wechat_redirect

一、前言

  • 一个App是怎么启动起来的?

  • App的程序入口到底是哪里?

  • Activity生命周期到底是什么时候调用的?被谁调用的?

  • 听说还有个AMS的东西,它是做什么的?它是怎样管理和启动Activity的?

  • ActivityThread、ApplicationThread、ActivityRecord、ActivityStack、TaskRecord都是些什么鬼?它们之间又有什么样的联系?

  • 我们项目中遇到的关于Activity启动流程的例子?

  • 等等...

你是不是还有很多类似的疑问一直没有解决?没关系,今天我们将结合源码和一些优秀文章,站在巨人的肩膀上,用更加通俗易懂的方式来试着解开谜底。毕竟代码繁多、经验有限,如果有纰漏的地方希望大家指正,相互学习。

Android应用程序的载体是APK文件,它是一个组件和资源的容器。APK文件和我们常见可执行文件的区别是:每个可执行文件在一个单独的进程中,但是APK文件可能运行在一个单独的进程也可能和其他APK文件运行在同一个进程中。Android的设计理念是弱化进程的存在,取而代之以组件的概念。

本篇知识框架:

图片

二、Activity的生命周期

Activity是最复杂的一种组件,它负责UI的显示以及处理各种输入事件。Activity通常表现为一个可视化的用户界面,包含了各种各样的控件,让用户来完成某项工作,例如打电话、发短信、拍照等。

我们来看一下这一张经典的生命周期流程图:

图片

Android SDK中提供的Activity生命周期图,隐含了Activity运行时的3种状态:

  • 激活态 :新启动的Activity位于屏幕的最前端,接收用户的输入。>>onResume()

  • 暂停态 :当Activity被另一个透明或半透明的Activity覆盖时所处的状态,例如:Dialog。此时的Activity虽然已经不能接收用户的输入,但还是可见的。>>onPause()

  • 停止态 :当一个Activity完全被另外一个Activity覆盖,不能接收用户输入也不可见。>>onStop()

Question:Activity生命周期到底是什么时候调用的?被谁调用的?

三、应用进程的组成

Android建立在Linux系统之上,基础的运行环境还是由进程组成。所有Android的应用进程都是由Zygote进程fork出来,因此,构成进程的底层基础,包括虚拟机、动态库等都是相同的。除了从Zygote中继承而来的基础设施外,Android需要在应用的Java层建立一套框架来管理运行的组件。由于应用的配置各不相同,因此,不能在Zygote中完全建好后再继承,只能在应用启动时创建。而这套框架就构成了Android应用的基础。

1、ActivityThread

Android应用进程的核心是ActivityThread 类,App的真正入口。每启动一个App进程,都会创建ActivityThread与之对应的实例,会调用main()开始运行,开启消息循环队列,这就是传说中的UI线程或者叫主线程。这个类包含了应用框架中其他重要的类。

public final class ActivityThread {
...
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final H mH = new H();
Application mInitialApplication;
Instrumentation mInstrumentation;
private final ResourcesManager mResourcesManager;
final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
final ArrayMap<String, WeakReference<LoadedApk>> mPackages
        = new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
        = new ArrayMap<String, WeakReference<LoadedApk>>();
final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
    = new ArrayMap<ProviderKey, ProviderClientRecord>();
...
public static void main(String[] args) {  
    SamplingProfilerIntegration.start();  

    // CloseGuard defaults to true and can be quite spammy.  We  
    // disable it here, but selectively enable it later (via  
    // StrictMode) on debug builds, but using DropBox, not logs.  
    CloseGuard.setEnabled(false);  

    Environment.initForCurrentUser();  // 初始化应用中需要使用的系统路径

    // Set the reporter for event logging in libcore  
    EventLogger.setReporter(new EventLoggingReporter());  

    Process.setArgV0("<pre-initialized>");  // 设置进程名称 

    Looper.prepareMainLooper();  

    ActivityThread thread = new ActivityThread();  // 创建ActivityThread实例
    thread.attach(false);                          // 调用attach

    if (sMainThreadHandler == null) {   // 保存主线程的handler 
        sMainThreadHandler = thread.getHandler();  
    }  

    AsyncTask.init();  // 初始化AsynTask类

    Looper.loop();  // 进入消息循环

    throw new RuntimeException("Main thread loop unexpectedly exited");  
}  
...
/**
* 调用mH发送消息排队处理事件
*/
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
 }
...
}

我们梳理一下这个ActivityThread类:

图片

  • mActivities、mServices和mProviderMap分别保存了应用中所有的Activity对象、Service对象和ContentProvider对象。BroadcastReceiver对象的生命周期很短暂,属于调用一次运行一次的类型,因此不需要保存其对象。

  • mInitialApplication变量是一个唯一的Application对象,允许自定义。

  • mResourceManager管理应用的资源。

  • mPackages和mResourcesPackages保存的是应用apk包的信息。(比如,通过属性process设置相同的应用名称后,两个有着相同ShareUserId和签名的应用可以合并在同一个进程运行)

ActivityThread 管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),并根据AMS的要求(通过IApplicationThread接口,AMS为Client、ActivityThread.ApplicationThread为Server)负责调度和执行四大组件activities、services、broadcasts、providers,以及其它操作。

2、main()方法

main()法的逻辑比较简单,主要是初始化环境,然后让线程进入消息循环。在进行消息循环前,main方法创建了ActivityThread对象,并使用参数false调用了他的attach()方法。

3、attach()方法

我们看看attach()方法中参数为false时的分支代码:

    private void attach(boolean system) {
            sCurrentActivityThread = this;
            mSystemThread = system;
            if (!system) {
                ......
                android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
                        UserHandle.myUserId());
                RuntimeInit.setApplicationObject(mAppThread.asBinder());
                // 客户端调用ActivityManagerNative.getDefault()返回的是ActivityManagerProxy
                // 也就是从ServiceManger获取AMS的IBinder对象
                final IActivityManager mgr = ActivityManagerNative.getDefault();
                try {
                    logAppLaunchTime(TAG, "attachApplication -> AMS"); /// M: It's for debugging App Launch time
                    mgr.attachApplication(mAppThread);  // 调用AMS的attachApplication方法
                } catch (RemoteException ex) {
                    // Ignore
                }
               ......
            } else {
            ......
            }
        }
    } 

attach()方法中主要做了两件事情,一是调用setApplicationObject()方法把对象mAppThread放到了RuntimeInit类中的静态变量mApplicationObject中。

public class RuntimeInit {
    ......
    public static final void setApplicationObject(Ibinder app) {
        mApplicationObject = app;
    }
    ......
}

第二件事情是调用ActivityManagerService的attachApplication方法,同时将mAppThread作为参数传递给了AMS中,这样AMS就能通过它来调用应用的接口了(Binder通信)。attachApplication方法最终实现是attachApplicationLocked。

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
        ......
    private final boolean attachApplicationLocked(IApplicationThread thread, int pid) {
        ......
        try {
           ......
           // 调用应用的bindApplication接口
           thread.bindApplication(processName, appInfo, providers, app.instrumentationClass,
                    profilerInfo, app.instrumentationArguments, app.instrumentationWatcher,
                    app.instrumentationUiAutomationConnection, testMode, enableOpenGlTrace,
                    isRestrictedBackupMode || !normalMode, app.persistent,
                    new Configuration(mConfiguration), app.compat,
                    getCommonServicesLocked(app.isolated),
                    mCoreSettingsObserver.getCoreSettingsLocked());
            ......
        } catch (Exception e) {
            ......
            return false;
        }
        ......
        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                // 调用mStackSupervisor.attachApplicationLocked接口
                // 如果进程没有启动,最后就是调用realStartActivityLocked()启动进程
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                badApp = true;
            }
        }

        // Find any services that should be running in this process...
        if (!badApp) {
            try {
                didSomething |= mServices.attachApplicationLocked(app, processName);
            } catch (Exception e) {
                Slog.e(TAG, "Exception thrown starting services in " + app, e);
                badApp = true;
            }
        }

        // Check if a next-broadcast receiver is in this process...
        if (!badApp && isPendingBroadcastProcessLocked(pid)) {
            try {
                didSomething |= sendPendingBroadcastsLocked(app);
            } catch (Exception e) {
                // If the app died trying to launch the receiver we declare it 'bad'
                Slog.wtf(TAG, "Exception thrown dispatching broadcasts in " + app, e);
                badApp = true;
            }
        }
        ......
    }
    ......
}

attachApplicationLocked()方法首先检查调用的参数,然后又回调ApplicationThread的接口bindApplication(),bindApplication()发送完消息BIND_APPLICATION然后就返回了,这样执行过程就转到应用的消息处理代码中。除了开启Activity能启动进程外,启动Service、发送广播也可能需要启动进程,因此,这里也调用了mServices.attachApplication()方法,以及sendPendingBroadcastsLocked来启动进程。

下面再回头看看ActivityThread是如何处理BIND_APPLICATION消息的。

    private void handleBindApplication(AppBindData data) {
            mBoundApplication = data;
            // 创建系统配置对象
            mConfiguration = new Configuration(data.config);
            mCompatConfiguration = new Configuration(data.config);
            ......
            // 设置进程的名称和DDMS中的进程名
            Process.setArgV0(data.processName);
            android.ddm.DdmHandleAppName.setAppName(data.processName,
                                                    UserHandle.myUserId());
            if (data.persistent) {
                // 带有Persistent标记的进程在低内存的设备上不能使用硬件加速
                if (!ActivityManager.isHighEndGfx()) {
                    /// M: ALPS01978329 for 512m project
                    final long LOW_MEMORY_SIZE = 256 * 1024 * 1024;
                    MemInfoReader minfo = new MemInfoReader();
                    minfo.readMemInfo();
                    if ((minfo.getTotalSize() <= LOW_MEMORY_SIZE) || (!"com.android.systemui".equals(data.processName))) {
                        HardwareRenderer.disable(false);
                        Slog.w(TAG, "Disable HWUI for Process(" + data.processName + "); Total Memory = " + minfo.getTotalSize());
                    }
                }
            }
            ......
            // 用系统的配置设置应用的时区
            TimeZone.setDefault(null);
            // 用系统的配置设置应用的地区
            Locale.setDefault(data.config.locale);

            // 生成资源管理对象mResourcesManager,并用系统配置初始化
            mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
            mCurDefaultDisplayDpi = data.config.densityDpi;
            applyCompatConfiguration(mCurDefaultDisplayDpi);
            ......

            // 生成ContentImpl对象
            final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
            if (!Process.isIsolated()) {
                // 设置cache目录
                final File cacheDir = appContext.getCacheDir();

                if (cacheDir != null) {
                    // Provide a usable directory for temporary files
                    System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
                } else {
                    Log.v(TAG, "Unable to initialize \"java.io.tmpdir\" property due to missing cache directory");
                }

                // Use codeCacheDir to store generated/compiled graphics code
                final File codeCacheDir = appContext.getCodeCacheDir();
                if (codeCacheDir != null) {
                    setupGraphicsSupport(data.info, codeCacheDir);
                } else {
                    Log.e(TAG, "Unable to setupGraphicsSupport due to missing code-cache directory");
                }
            }
            ......

            IBinder b = ServiceManager.getService(Context.CONNECTIVITY_SERVICE);
            if (b != null) {
                // 设置网络代理
                IConnectivityManager service = IConnectivityManager.Stub.asInterface(b);
                try {
                    final ProxyInfo proxyInfo = service.getProxyForNetwork(null);
                    Proxy.setHttpProxySystemProperty(proxyInfo);
                } catch (RemoteException e) {}
            }

            if (data.instrumentationName != null) {
               ......
            } else { // 创建Instrumentation对象
               mInstrumentation = new Instrumentation();
            }

            if ((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP) != 0) {
                // 如果应用指定使用big heap,则清除dalvik中的限制
                dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();
            } else {
                // Small heap, clamp to the current growth limit and let the heap release
                // pages after the growth limit to the non growth limit capacity. b/18387825
                dalvik.system.VMRuntime.getRuntime().clampGrowthLimit();
            }

            final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskWrites();
            try {
                // 最终,生成Application对象
                Application app = data.info.makeApplication(data.restrictedBackupMode, null);
                mInitialApplication = app;
                if (!data.restrictedBackupMode) {
                    List<ProviderInfo> providers = data.providers;
                    if (providers != null) {
                        // 初始化应用的ContentProvider
                        installContentProviders(app, providers);
                        // For process that contains content providers, we want to
                        // ensure that the JIT is enabled "at some point".
                        mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
                    }
                }

                try { 
                    // 调用mInstrumentation对象的onCreate()方法
                    mInstrumentation.onCreate(data.instrumentationArgs);
                }
                catch (Exception e) {
                    throw new RuntimeException(
                        "Exception thrown in onCreate() of "
                        + data.instrumentationName + ": " + e.toString(), e);
                }

                try {
                    // 调用Application对象的onCreate()方法
                    mInstrumentation.callApplicationOnCreate(app);
                } catch (Exception e) {
                    if (!mInstrumentation.onException(app, e)) {
                        throw new RuntimeException(
                            "Unable to create application " + app.getClass().getName()
                            + ": " + e.toString(), e);
                    }
                }
            } finally {
                StrictMode.setThreadPolicy(savedPolicy);
            }
        }

handleBindApplication()方法的主要功能就是创建应用框架中的各种对象,这些对象大都已经介绍过了。最终调用到应用的Application.onCreate(),整个应用进程也就创建完毕。

4、ApplicationThread的作用

ApplicationThread是ActivityThead的一个内部类,是一个Binder服务类,ActivityManagerService操作应用就是通过ApplicationThread提供的接口完成的

final ApplicationThread mAppThread = new ApplicationThread();

private class ApplicationThread extends ApplicationThreadNative { 
    ...
    public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
                    configChanges);
        }

        public final void scheduleStopActivity(IBinder token, boolean showWindow,
                int configChanges) {
           sendMessage(
                showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
                token, 0, configChanges);
        }

        public final void scheduleSleeping(IBinder token, boolean sleeping) {
            sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
        }

        public final void scheduleResumeActivity(IBinder token, int processState,
                boolean isForward, Bundle resumeArgs) {
            updateProcessState(processState, false);
            sendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
        }

        public final void scheduleSendResult(IBinder token, List<ResultInfo> results) {
            ResultData res = new ResultData();
            res.token = token;
            res.results = results;
            sendMessage(H.SEND_RESULT, res);
        }

        // we use token to identify this activity without having to send the
        // activity itself back to the activity manager. (matters more with ipc)
        @Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            ...

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
    ...  
}  

ApplicationThread类从ApplicationThreadNative类派生,ApplicationThreadNative类中封装了Binder的实现。ApplicationThread虽然定义了大量的接口,但是接口的实现模式都是把Binder调用转换成**mH 的消息来排队处理 **。

5、H(Handler)消息处理

final H mH = new H();

private class H extends Handler {  
    ...
    public void handleMessage(Message msg) {  
        switch (msg.what) {  
            case LAUNCH_ACTIVITY: {  
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");  
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;  

                r.packageInfo = getPackageInfoNoCheck(  
                        r.activityInfo.applicationInfo, r.compatInfo);  
                handleLaunchActivity(r, null);  
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
            } break; 
            case RELAUNCH_ACTIVITY: {
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");
                ActivityClientRecord r = (ActivityClientRecord)msg.obj;
                handleRelaunchActivity(r);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            } break;
            case PAUSE_ACTIVITY:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                handlePauseActivity((IBinder)msg.obj, false, (msg.arg1&1) != 0, msg.arg2,
                        (msg.arg1&2) != 0);
                maybeSnapshot();
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break; 
            ...  
        }  
    }  
    ...  
} 

消息的处理最终会调用ActivityThread类的某个方法完成。 通过这种模式,从Binder来的调用就转换成异步的方式来执行了。理解了这个过程之后,再分析ApplicationThread类的接口是时,我们可以忽略中间的消息传递过程,直接查看ActivityThread中对应的方法。

标签:调用,流程,app,Activity,msg,new,AMS,final
From: https://www.cnblogs.com/zhanyaowang/p/18331009

相关文章

  • 深入理解Activity启动流程和AMS框架(二)
    链接https://mp.weixin.qq.com/s?__biz=MzIwNjQ1NzQxNA==&mid=2247484150&idx=1&sn=4007a9cff85df88941e73869b89b1ed4&chksm=97201ddfa05794c9e33c8d0d9e83f407922d24282dc9bd8d67c72debf9713abb1b0586f00bc9&scene=21#wechat_redirect续 深入理解Activity启动流程......
  • Android 10.0 Launcher 启动流程
    在前面SystemUI启动流程中说到,在SystemServer中会去启动各种系统服务,这里的launcher也是启动的其中一个服务ActivityManagerService去启动的。在android10之前,系统四大组件的启动都是在ActivityManagerService中,在android10中,单独抽出了一个ActivityTaskManagerService,主要......
  • (六)Redis 消息队列 List、Streams
    Redis适合做消息队列吗?有什么解决方案?首先要明白消息队列的消息存取需求和工作流程。1、消息队列我们一般把消息队列中发送消息的组件称为生产者,把接收消息的组件称为消费者,下图是一个通用的消息队列的架构模型:消息队列在存取消息时,必须要满足三个需求,分别是消息保序、处理重......
  • 发布npm包流程
    npm发布包流程背景:假设你写了一些js方法想发布参考:https://www.youtube.com/watch?v=xnfdm-s8adI、1.初始化环境mkdirmethodjscdmethodjsnpminit-ynpmi-Dtsup@5typescript@4#node<14时的兼容版本,tsup用于编译打包ts代码#确保用的是npm地址npmconfig......
  • SpringMVC请求执行流程
    SpringMVC是一个基于Spring框架的MVC(Model-View-Controller)框架,它简化了Web应用程序的开发。在SpringMVC中,请求的执行流程是一个精心设计的过程,涉及多个核心组件的协同工作。以下是对SpringMVC请求执行流程的详细解析,内容不少于2000字。一、SpringMVC请求执行流程概述Spri......
  • 流程控制语句结构
    流程控制语句结构目录流程控制语句结构1.顺序结构定义与特点示例2.分支结构2.1.if-else语句2.2.switch语句3.循环结构1.for循环2.while循环3.do-while循环使用场景和注意事项4.跳转语句5.绘制程序流程图基本步骤常用符号示例工具1.顺序结构定义与特点定义:顺序结构是指程......
  • 计算机毕业设计实现流程,看这篇就够了(1.2w字超详细流程)
    计算机专业毕业设计实现流程 目录如何完成毕业设计选题提交毕业设计任务书完成系统分析和系统设计完成项目编码工作撰写论文项目答辩 1.完成毕业设计选题选题的类型计算机毕业相关的设计最近几年类型比较多的题目为:XX管理系统、XX网站建设、XX小程序设计与实现、XX公......
  • 【版本控制系统Git】Git的基本操作与工作流程
    版本控制系统GitGit的基本操作与工作流程目录引言Git概述Git的基本操作安装Git配置Git创建仓库常用命令Git的工作流程克隆仓库分支管理提交更改合并分支解决冲突最佳实践结论引言在现代软件开发中,版本控制系统(VCS)是不可或缺的工具。它能够帮助开发团队管理代码的......
  • 干货!全网最权威最全面介绍网络短剧上线资质以及备案流程
    今天,小编给大家详细梳理汇总一个全网最权威最全面的介绍网络微短剧上线所需要的资质,以及短剧备案流程到底是怎么样的?100%内行人实战总结的干货,希望对广大短剧从业者有所帮助。一、从事网络微短剧需要哪些资质?1、广播电视节目制作经营许可证(必备)根据《广播电视节目制作经营管......
  • Spring 前后端数据交互到数据库流程
            前端通过HTTP请求(如AJAX、Fetch或Axios等)向后端发送数据请求。后端Spring框架接收到请求后,由控制器(Controller)负责解析请求,并根据请求类型(如GET、POST等)和路径调用相应的服务(Service)。服务层处理业务逻辑,可能涉及调用数据访问层(DAO)来操作数据库。数据访问层使......