深入Android系统(十一)AMS-1-应用组成与服务启动

牛年犇犇犇,刚刚完成AMS一半的学习进度,篇幅确实有点大,不过先更上两篇吧

我们不止一次提到Android一直想弱化进程的存在,但是,Android毕竟是建立在Linux系统之上,基础的运行环境还是由进程组成。

我们前面已经介绍,所有的Android的应用程序都是由Zygote进程fork而来,因此,构成应用进程的底层基础,像虚拟机、动态库等都是相同的。

有了从Zygote中继承来的通用基础环境,Android还在Java层建立一套框架来管理运行的组件来进一步弱化进程的存在感。

不过由于每个应用的上层配置都不相同,因此,这个框架不能提前在Zygote中完全建立好后继承,只能在应用启动时创建。

而这套框架就构成了Android应用开发的基础,而这套框架的核心就是大名鼎鼎的ActivityManagerServie

应用进程的组成

学习ActivityManagerServie前我们先简单了解下Android应用进程的组成

组成Android应用进程的核心是ActivityThread类,这个类包含了应用框架中其他重要的类。相关类变量声明如下:

public final class ActivityThread extends ClientTransactionHandler {
    ...
    // 一个Binder实体对象,AMS 通过它来调用应用的接口
    final ApplicationThread mAppThread = new ApplicationThread();
    Application mInitialApplication;
    // 用来保存应用中的 Activity 对象
    final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
    // 用来保存应用中的 Service 对象
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
    // 用来保存应用中的 ContentProvider 对象
    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap = new ArrayMap<ProviderKey, ProviderClientRecord>();
    // 用来保存包含代码的的应用
    final ArrayMap<String, WeakReference<LoadedApk>> mPackages = new ArrayMap<>();
    // 用来保存只包含资源文件的应用
    final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages = new ArrayMap<>();
    // 应用资源管理对象
    private final ResourcesManager mResourcesManager;
    // 上下文对象
    private ContextImpl mSystemContext;
    private ContextImpl mSystemUiContext;
}

我们先来看下ApplicationThread

ApplicationThread的作用

ApplicationThreadActivityThread的一个内部类,它和ActivityThread一样,虽然名字中都包含了Thread,但是它们并不是继承自线程类Thread

ApplicationThread是一个Binder服务类,AndroidActivityManagerService操作应用就是通过ApplicationThread来完成的。

我们先看下AIDL文件内容,路径frameworks/base/core/java/android/app/IApplicationThread.aidl,片段如下:

oneway interface IApplicationThread {
    ......
    void scheduleSleeping(IBinder token, boolean sleeping);
    ......
}

请留意AIDL中的oneway关键字,修饰interface意味着修饰接口中的所有方法

oneway修饰表示一个异步过程,调用时不需要等待返回值

ApplicationThread类的片段如下:

private class ApplicationThread extends IApplicationThread.Stub {
    ......
    public final void scheduleSleeping(IBinder token, boolean sleeping) {
        sendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
    }
    ......// 省略大量接口
}
  • ApplicationThread中接口的实现都是把Binder调用转换成消息来排队处理。
  • 以上面的scheduleSleeping()为例,只是发送了一个SLEEPING的消息
  • 所有消息的处理都集中在Handler对象mHhandleMessage()方法中集中处理。处理部分如下:
    public void handleMessage(Message msg) {
        switch (msg.what) {
            ...
            case SLEEPING:
                handleSleeping((IBinder)msg.obj, msg.arg1 != 0);
                break;
            ...
        }
    }
    
    • 最后通过ActivityThreadhandleSleeping()来执行

在源码中,ApplicationThread中的接口通常都是以schedule*开头,而真正处理的方法以handle*开头,很容易分辨。

为什么要这样设计呢?

主要是避免方法在调用和执行时出现耗时过长影响整个系统的运行:

  • 对于调用端:还记得IApplicationThread.aidl中的oneway
    • oneway修饰就表明客户端进行Binder调用时不会等待服务端结果的返回(这部分可以去看之前的Binder-3-原理部分
  • 对于服务端:通过sendMessage的方式将Binder调用转化为消息的异步处理
    • 消息处理部分交给了H mH这个Handler
    • ActivityThread的静态main()方法中,会通过Looper.loop()进入消息的循环处理

理解应用的Context

Context的字面含义是上下文环境。应用的上层代码通过Context类提供的接口来操作Android的组件和资源。在Android的应用程序中,Context无处不在,很多接口都要求使用Context对象作为参数。

Android的应用本质上是一系列组件加上资源文件的容器,但是,这些组件的使用方式各不相同,而且比较复杂,Context类的作用就是把这些细节封装起来,这样,即使开发人员不了解组件的运行原理也能使用它们。从某种意义上将,Context包含了整个应用框架的操作接口。

对于应用开发者来说,大部分代码都在Activity或者Service的继承类中实现。为了方便应用使用,这两个类的父类也是Context

Context是一个抽象类,Android相关类的继承关系设计如下:
image
AndroidContext有两个实现类

  • ContextImplContext真正的实现类
    • 目前Android中唯一的业务实现类
    • 集成了应用框架的一些核心对象,如ActivityThread对象
  • ContextWrapper:方法的实现只是转调成员变量mBase的方法
    • mBase本身也是Context类型对象,它的具体类型是ContextImpl

上面这种设计模式称为proxy模式或者wrapper模式,它的好处就是将接口和实现分离,可以动态的切换多种实现。

Activity类有些特殊(毕竟承载了应用的显示部分),Android又为它抽象出来了一个ContextThemeWrapper

  • 这样的目的是为了实现Activity中单独的Theme
  • 一个应用中可以有一套全局的Theme,同时每个Activity还可以有自己的Theme

Application

Application类也是继承ContextWrapper

Application类可以看做是应用本身的抽象,一个应用进程只有一个Application对象,在应用启动时由框架创建。

Application本身没有实现太多功能,它的主要作用是提供一些回调接口来通知应用进程状态的变化。相关的方法如下:

public class Application extends ContextWrapper implements ComponentCallbacks2 {
    // 在应用创建时调用
    public void onCreate();
    // 在应用销毁时调用
    public void onTerminate();
    // 在系统配置发生变化时调用
    public void onConfigurationChanged(Configuration newConfig);
    // 在系统内存不足时调用
    public void onLowMemory();
    // 在系统要求应用释放多余内存时调用
    public void onTrimMemory(int level);
}

应用中可以定义Application的子类。子类必须在AndroidManifest.xml<application/>标签中定义,这样系统才能在生成应用对象时使用子类来替换Application类。

<application/>标签在Android SDK文档中有详细的介绍,可以先简单阅读标签的属性描述,不然到后面理解ActivityManagerService的业务逻辑时可能会有障碍。

allowTaskReparenting为例,它的描述是这样的:

  • 当下一次将启动 Activity 的任务转至前台时,Activity 是否能从该任务转移至与其有相似性的任务
    • “true”表示可以转移,“false”表示仍须留在启动它的任务处。
    • 默认值为“false”。
  • 对于<activity> 标签有其自己的 allowTaskReparenting 属性,该属性可以替换此处设置的值。

剩下的属性大家可以参考官方说明。传送门:<application/>标签

Android 框架的核心-AMS服务

ActivityManagerServiceAndroid Framework的核心,它管理着Android系统的4大组件:ActivityServiceContentProviderBroadcast

前面已经介绍了,Android希望模糊进程的作用,取而代之以组件的概念,ActivityManagerService正是这一理念的实现。ActivityManagerService除了管理组件外,同时也管理和调度所有的用户进程

为了方便,ActivityManagerService后面就用AMS来表示了哈

AMSSystemServer中的启动流程

我们已经知道AMS服务运行在SystemServer进程中。

AMSSystemServer的主要流程如下:

private void startBootstrapServices() {
    // 创建 AMS 服务
    mActivityManagerService = mSystemServiceManager.startService(
            ActivityManagerService.Lifecycle.class).getService();
    ......
    // 调用 AMS 的 setSystemProcess
    mActivityManagerService.setSystemProcess();
}
private void startOtherServices() {
    // 调用 AMS 的 systemReady
    mActivityManagerService.systemReady(...);
}

上面的代码重点分为三部分:

  • 通过SystemServiceManager.startService方法进行AMS创建

    • 方法会根据传入的Class对象来创建对应类的实例对象
    • 这里传入的ActivityManagerService.Lifecycle类,类结构如下:
    public static final class Lifecycle extends SystemService {
        private final ActivityManagerService mService;
        public Lifecycle(Context context) {
            super(context);
            // 创建 ActivityManagerService 实例对象
            mService = new  ActivityManagerService(context);
        }
        ......
    }
    
    • 可以看到,在Lifecycle的构造方法中初始化了AMS对象
  • 调用AMSsetSystemProcess方法

  • 调用AMSsystemReady方法

这三部分我们详细来看下

AMS的初始化

AMS的构造方法如下:

public ActivityManagerService(Context systemContext) {
    ......
    mContext = systemContext;
    mFactoryTest = FactoryTest.getMode();
    // 获取运行在 SystemServer 中的 ActivityThread 对象
    mSystemThread = ActivityThread.currentActivityThread();
    mUiContext = mSystemThread.getSystemUiContext();
    ......
    // 创建用于处理消息的线程和Handler对象
    // 这里创建了两个消息处理线程:mHandlerThread 和 mProcStartHandlerThread
    mHandlerThread = new ServiceThread(TAG,
            THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
    mHandlerThread.start();
    mHandler = new MainHandler(mHandlerThread.getLooper());
    mUiHandler = mInjector.getUiHandler(this);

    mProcStartHandlerThread = new ServiceThread(TAG + ":procStart",
            THREAD_PRIORITY_FOREGROUND, false /* allowIo */);
    mProcStartHandlerThread.start();
    mProcStartHandler = new Handler(mProcStartHandlerThread.getLooper());
    ......
    // 创建管理广播的数据结构
    mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "foreground", BROADCAST_FG_TIMEOUT, false);
    mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
            "background", BROADCAST_BG_TIMEOUT, true);
    mBroadcastQueues[0] = mFgBroadcastQueue;
    mBroadcastQueues[1] = mBgBroadcastQueue;
    // 创建管理组件 Service 的对象
    mServices = new ActiveServices(this);
    // 创建管理组件 Provider 的对象
    mProviderMap = new ProviderMap(this);
    mAppErrors = new AppErrors(mUiContext, this);
    // 获取系统的 system 和 data 目录
    File dataDir = Environment.getDataDirectory();
    File systemDir = new File(dataDir, "system");
    systemDir.mkdirs();
    // 创建 BatteryStatsService 服务
    mBatteryStatsService = new BatteryStatsService(systemContext, systemDir, mHandler);
    ......
    // 创建 ProcessStatsService 服务
    mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
    // 创建 AppOpsService 服务
    mAppOpsService = mInjector.getAppOpsService(new File(systemDir, "appops.xml"), mHandler);
    // 打开 urigrants.xml 文件
    mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"), "uri-grants");
    // 创建 UserController ,UserController 对应的构造方法中会设置 USER_SYSTEM 用户作为第一个用户
    mUserController = new UserController(this);
    // 创建 VrController ,用来进行部分 VR 状态的监听和切换工作
    mVrController = new VrController(this);
    // 获取 opengles 的版本
    GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version",
        ConfigurationInfo.GL_ES_VERSION_UNDEFINED);
    ......
    // 创建 Activity 管理对象
    mStackSupervisor = createStackSupervisor();
    ......
    // 创建 Intent 防火墙
    mIntentFirewall = new IntentFirewall(new IntentFirewallInterface(), mHandler);
    ......
    // 创建 CPU 使用情况的统计线程
    mProcessCpuThread = new Thread("CpuTracker") {
        ...
    };
    ......
    // 将服务添加到 WatchDog 进行监听
    Watchdog.getInstance().addMonitor(this);
    Watchdog.getInstance().addThread(mHandler);
    ......
}

AMS构造方法的主要作用是创建出了4大组件ActivityServiceContentProviderBroadcast的管理对象和一些辅助服务,没有很复杂的逻辑,注释很详细啦

AMSsetSystemProcess方法

SystemServer中创建完AMS对象后,会调用它的setSystemProcess方法,代码如下:

public void setSystemProcess() {
    try {
        // =============== 将一些监测服务添加到 ServiceManager ===============
        // AMS 服务
        ServiceManager.addService(Context.ACTIVITY_SERVICE, this, /* allowIsolated= */ true,
                DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
        // ProcessStats 是可以用来dump每个进程内存使用情况的服务
        ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
        // MemBinder 是可以用来dump每个进程内存信息的服务
        ServiceManager.addService("meminfo", new MemBinder(this), /* allowIsolated= */ false,
                DUMP_FLAG_PRIORITY_HIGH);
        // GraphicsBinder 是可以用来dump每个进程图形加速状态的服务
        ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
        // DbBinder 是可以用来dump每个进程数据库状态的服务
        ServiceManager.addService("dbinfo", new DbBinder(this));
        if (MONITOR_CPU_USAGE) {
            // CPU 信息
            ServiceManager.addService("cpuinfo", new CpuBinder(this),
                    /* allowIsolated= */ false, DUMP_FLAG_PRIORITY_CRITICAL);
        }
        // PermissionController 是用来检查 Binder 调用权限的服务
        ServiceManager.addService("permission", new PermissionController(this));
        // ProcessInfoService 是可以用来获取进程相关信息的服务
        ServiceManager.addService("processinfo", new ProcessInfoService(this));
        // 获取 framework-res.apk 的 ApplicationInfo 对象
        ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
        mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
        synchronized (this) {
            // 将 SystemServer 进程本身添加到 AMS process 的管理中
            ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
            app.persistent = true;
            app.pid = MY_PID;
            app.maxAdj = ProcessList.SYSTEM_ADJ;
            app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
            synchronized (mPidsSelfLocked) {
                mPidsSelfLocked.put(app.pid, app);
            }
            updateLruProcessLocked(app, false, null);
            updateOomAdjLocked();
        }
    } catch (PackageManager.NameNotFoundException e) {
        throw new RuntimeException("Unable to find android system package", e);
    }
    // 启动应用操作监听
    mAppOpsService.startWatchingMode(......);
}

SystemServer调用setSystemProcess方法的主要工作是向ServiceManager注册一些服务。需要注意的是:

  • 通过getApplicationInfo方法获取了framework-res.apkApplicationInfo对象
  • 然后使用newProcessRecordLocked()方法将ApplicationInfo对象转化为ProcessRecord对象,并将其添加到AMS的进程管理体系中

AMSsystemReady方法

SystemServer在启动完所有服务之后,将调用AMSsystemReady方法。这个方法时Android进入用户交互阶段前最后进行的准备工作。

systemReady方法比较长,我们一点一点来分析

AMS相关服务的准备阶段

相关代码如下:

public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
    synchronized(this) {
        if (mSystemReady) {
            if (goingCallback != null) {
                goingCallback.run();
            }
            return;
        }
        ......
        // 通知 VrController 注册 VrModeStateListener
        mVrController.onSystemReady();
        // 通知 UserController 装载用户的 Profile 信息
        mUserController.onSystemReady();
        // 通知 RecentTasks 重建带有 persisitent 标记的 Task
        mRecentTasks.onSystemReadyLocked();
        // 通知 AppOpsService 启动 APP 权限监听
        mAppOpsService.systemReady();
        // 系统相关服务准备完成
        mSystemReady = true;
    }
    ......
    // 查找待清理进程
    ArrayList<ProcessRecord> procsToKill = null;
    synchronized(mPidsSelfLocked) {
        for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
            ProcessRecord proc = mPidsSelfLocked.valueAt(i);
            // 此处判断进程是否有 FLAG_PERSISTENT 标志
            if (!isAllowedWhileBooting(proc.info)){
                if (procsToKill == null) {
                    procsToKill = new ArrayList<ProcessRecord>();
                }
                // 将没有 FLAG_PERSISTENT 标志的进程添加到清理集合中
                procsToKill.add(proc);
            }
        }
    }
    // 清理集合中的进程
    synchronized(this) {
        if (procsToKill != null) {
            for (int i=procsToKill.size()-1; i>=0; i--) {
                ProcessRecord proc = procsToKill.get(i);
                removeProcessLocked(proc, true, false, "system update done");
            }
        }
        // 进程清理完成
        mProcessesReady = true;
    }
    Slog.i(TAG, "System now ready");
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis());
    ......
}

上面注释比较详细,简单总结主要流程如下:

  • 调用VrControllerUserControllerRecentTasksAppOpsService服务的*SystemReady()方法进行对应服务的初始化工作
    • 对于上面的4个服务,任何一个的知识点都可以单独写一篇博文了,这里我们先略过哈,大局为重
  • mPidsSelfLocked集合中查找非FLAG_PERSISTENT标记的进程,并将其清理
    • mPidsSelfLocked集合存放的是已经运行了的进程
    • 这些基本都是通过ActivityThread.attach()加入mPidsSelfLocked集合的
    • FLAG_PERSISTENT标记的进程说明后面很快还会启动,没有必要清理

SystemServer启动的过程会执行PMSupdatePackagesIfNeeded()方法,这个方法会发送一个ACTION_PRE_BOOT_COMPLETED的广播。

所以,如果我们的应用响应这个ACTION_PRE_BOOT_COMPLETED并且加入FLAG_PERSISTENT标志,那么就可以存活下来

FactoryMode检测阶段

相关代码如下

synchronized(this) {
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL) {
        // 处于工厂模式,则查找对应的应用程序
        ResolveInfo ri = mContext.getPackageManager()
                .resolveActivity(new Intent(Intent.ACTION_FACTORY_TEST),
                        STOCK_PM_FLAGS);
        CharSequence errorMsg = null;
        if (ri != null) {
            ActivityInfo ai = ri.activityInfo;
            ApplicationInfo app = ai.applicationInfo;
            // 判断找到的程序是否为系统应用
            if ((app.flags&ApplicationInfo.FLAG_SYSTEM) != 0) {
                mTopAction = Intent.ACTION_FACTORY_TEST;
                mTopData = null;
                mTopComponent = new ComponentName(app.packageName,
                        ai.name);
            } else {
                errorMsg = mContext.getResources().getText(
                        com.android.internal.R.string.factorytest_not_system);
            }
        } else {
            errorMsg = mContext.getResources().getText(
                    com.android.internal.R.string.factorytest_no_action);
        }
        if (errorMsg != null) {
            ......
            // 错误消息不为空,发送对应的信息
            Message msg = Message.obtain();
            msg.what = SHOW_FACTORY_ERROR_UI_MSG;
            msg.getData().putCharSequence("msg", errorMsg);
            mUiHandler.sendMessage(msg);
        }
    }
}

对于Android产品,如手机、电视等,需要进入工厂模式来执行检测程序,工厂模式下运行的程序需要响应Intent.ACTION_FACTORY_TEST

因此,当检测到处于工厂模式时,会去查找响应该Intent的程序,并放置到mTopComponent中,这样将会启动对应的测试应用。如果找不到,则发送异常Message

应用启动阶段

相关代码如下:

// 更新设置信息,包括是否支持画中画、多窗口等属性
retrieveSettings();
final int currentUserId = mUserController.getCurrentUserId();
synchronized (this) {
    readGrantedUriPermissionsLocked();
}
......
// 执行回调参数
if (goingCallback != null) goingCallback.run();
// 通知其他服务,当前用户开始运行
mSystemServiceManager.startUser(currentUserId);
synchronized (this) {
    // 启动带有 FLAG_PERSISTENT 标记的应用
    startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);
    mBooting = true;
    ......
    // 启动 Home 应用
    startHomeActivityLocked(currentUserId, "systemReady");
    ......
    long ident = Binder.clearCallingIdentity();
    try {
        // 发送 ACTION_USER_STARTED 广播
        Intent intent = new Intent(Intent.ACTION_USER_STARTED);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY
                | Intent.FLAG_RECEIVER_FOREGROUND);
        intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
        broadcastIntentLocked(null, null, intent,...);
        // 发送 ACTION_USER_STARTING 广播
        intent = new Intent(Intent.ACTION_USER_STARTING);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        intent.putExtra(Intent.EXTRA_USER_HANDLE, currentUserId);
        broadcastIntentLocked(null, null, intent,...);
    } catch (Throwable t) {
        Slog.wtf(TAG, "Failed sending first user broadcasts", t);
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
    mStackSupervisor.resumeFocusedStackTopActivityLocked();
    mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
    ......
}

这一段的代码主要作用是

  • 更新设置,判断是否支持画中画、多窗口等信息
  • 执行systemReady方法参数goingCallback的回调方法run()
    • 回调方法是在SystemServer中定义的
  • 启动带有FLAG_PERSISTENT标记的应用
    • 具体启动应用是通过addAppLocked()方法,后面详细介绍
  • 启动Home应用
    • 查找并启动能够响应android.intent.action.MAINActivity

Process管理

虽然Android在应用开发中可以弱化进程的概念,但是在AMS中,还必须管理和调度进程。AMS对进程的管理,主要体现在两个方面:

  • 动态调整进程在mLruProcesses列表的位置
    • 核心方法:updateLruProcessLocked
  • 调整进程的oom_adj的值
    • 核心方法:updateOomAdjLocked

这两项调整和Android的内存回收机制有关。当内存不足时,系统会关闭一些进程来释放内存。

进程的启动

前面介绍了,AMS中启动带有FLAG_PERSISTENT标记的应用是通过addAppLocked()方法,代码如下:

final ProcessRecord addAppLocked(ApplicationInfo info, String customProcess, boolean isolated,
        boolean disableHiddenApiChecks, String abiOverride) {
    ProcessRecord app;
    // isolated 为 true 表示要启动一个新的进程
    if (!isolated) {
        // 在已经启动的进程列表中查找
        app = getProcessRecordLocked(customProcess != null ? customProcess : info.processName,
                info.uid, true);
    } else {
        app = null;
    }
    if (app == null) {
        // 新建一个 ProcessRecord 对象
        app = newProcessRecordLocked(info, customProcess, isolated, 0);
        updateLruProcessLocked(app, false, null);
        updateOomAdjLocked();
    }
    try {
        // 将包的stopped状态设置为false
        // 如果为ture那么所有广播都无法接收,除非带有标记FLAG_INCLUDE_STOPPED_PACKAGES的广播
        // 正经的广播都不会带有这个标记
        AppGlobals.getPackageManager().setPackageStoppedState(info.packageName, false, UserHandle.getUserId(app.uid));
    } catch (RemoteException e) {
    } catch (IllegalArgumentException e) {
        ......
    }
    if ((info.flags & PERSISTENT_MASK) == PERSISTENT_MASK) {
        // 设置 persistent 标记
        app.persistent = true;
        app.maxAdj = ProcessList.PERSISTENT_PROC_ADJ;
    }
    if (app.thread == null && mPersistentStartingProcesses.indexOf(app) < 0) {
        mPersistentStartingProcesses.add(app);
        // 启动进程
        startProcessLocked(app, "added application",
                customProcess != null ? customProcess : app.processName, disableHiddenApiChecks,
                abiOverride);
    }
    return app;
}

addAppLocked()方法流程如下:

  • 先根据参数isolated来决定是否启动一个新进程
    • 如果isolatedtrue,即使系统中可能已经有一个同名的进程存在,也会通过newProcessRecordLocked()再新建一个进程
      • 通过updateLruProcessLocked()方法更新运行中的进程的状态
      • 通过updateOomAdjLocked()方法来更新进程的优先级
    • 如果isolatedfalse,会通过getProcessRecordLocked()方法在当前运行的进程列表中查找
  • 接着检查应用是否具有persistent标记
    • 如果有,设置persistentmaxAdj属性
  • 最后,通过startProcessLocked()来启动进程

updateLruProcessLocked()updateOomAdjLocked()这两个方法是进程管理的核心,等下细聊。

我们先看下startProcessLocked()的启动逻辑:

private final boolean startProcessLocked(ProcessRecord app, String hostingType,
        String hostingNameStr, boolean disableHiddenApiChecks, String abiOverride) {
    ......
    long startTime = SystemClock.elapsedRealtime();
    if (app.pid > 0 && app.pid != MY_PID) {
        synchronized (mPidsSelfLocked) {
            // 移除集合中的进程ID,避免重复
            mPidsSelfLocked.remove(app.pid);
            // 移除消息队列中的 PROC_START_TIMEOUT_MSG 消息,避免干扰
            mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
        }
    }
    ......
    mProcessesOnHold.remove(app);
    ...//省略大量的启动参数准备过程
    final String entryPoint = "android.app.ActivityThread";
    return startProcessLocked(hostingType, hostingNameStr, entryPoint, ...);
}
private boolean startProcessLocked(hostingType, hostingNameStr, entryPoint,...) {
    ......
    if (mConstants.FLAG_PROCESS_START_ASYNC) {
        // 异步启动的情况,通过 mProcStartHandler 来处理
        mProcStartHandler.post(() -> {
            ......
            // 启动应用
            final ProcessStartResult startResult = startProcess(app.hostingType, entryPoint, ...);
            synchronized (ActivityManagerService.this) {
                // 发送一个延时消息用来监听应用启动
                handleProcessStartedLocked(app, startResult, startSeq);
            }
            ......
        });
        return true;
    } else {
        // 启动应用
        final ProcessStartResult startResult = startProcess(hostingType, entryPoint, ...);
        // 发送一个延时消息用来监听应用启动
        handleProcessStartedLocked(app, startResult.pid, startResult.usingWrapper,
        startSeq, false);
        return app.pid > 0;
    }
}
private ProcessStartResult startProcess(String hostingType, String entryPoint, ...) {
    ......
    final ProcessStartResult startResult;
    if (hostingType.equals("webview_service")) {
        // startWebView 是 Process 的静态方法
        // 启动的是 WebViewZygote,应该是为了和本地应用有所区分,不先管它。
        startResult = startWebView(entryPoint,...);
    } else {
        // 通过 Process.start() 函数启动应用
        startResult = Process.start(entryPoint, ...);
    }
    ...
    return startResult;
}

注释比较清楚。对于startProcessLocked()方法,主要做了两件事:

  • 通过startProcess()方法来启动应用进程

    • startProcess()方法最后是通过Process.start()方法来完成。执行结果就是创建了一个新进程,并在其中执行ActivityThreadmain()方法
    • 关于Process.start()方法在深入Android系统(七)Zygote进程中已经介绍过了,这里就不展开啦
  • 然后通过handleProcessStartedLocked()方法来检测应用启动是否超时

handleProcessStartedLocked()方法如下:

    static final int PROC_START_TIMEOUT_WITH_WRAPPER = 1200*1000;
    static final int PROC_START_TIMEOUT = 10*1000;
    private boolean handleProcessStartedLocked(...) {
        ......
        synchronized (mPidsSelfLocked) {
            this.mPidsSelfLocked.put(pid, app);
            if (!procAttached) {
                Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
                msg.obj = app;
                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
                // 发送一个延时消息
                mHandler.sendMessageDelayed(msg, usingWrapper
                        ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
            }
        }
        return true;
    }

handleProcessStartedLocked()方法主要是发送了一个延时消息PROC_START_TIMEOUT_MSG

Android的想法应该是:当应用启动后去移除这个消息,如果消息在延时结束前没有被移除,就认为应用的启动出了问题。

那么,在哪里移除的呢??

我们已经知道,应用的启动入口在ActivityThreadmain()方法。而在main()方法的执行逻辑中会执行一个attach()方法(这个方法也很重要),方法如下:

    private void attach(boolean system, long startSeq) {
        ......
        final IActivityManager mgr = ActivityManager.getService();
        try {
            mgr.attachApplication(mAppThread, startSeq);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
        ......
    }

attach()方法中会调用AMSattachApplication() 方法,而在attachApplication()中执行了 mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

666,把Handler使用的是淋漓尽致啊

进程列表的调整-LRU

LRULeast Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。

Android中进程启动后都会在AMS的成员变量mLruProcesses中保存其ProcessRecord信息。mLruProcessesLRU顺序存储了当前运行的应用程序进程信息,mLruProcesses中的第一个元素就是最近最少使用的进程对应的ProcessRecord

AMS中会通过updateLruProcessLocked()来调整进程在mLruProcesses列表中的位置,代码如下:

final void updateLruProcessLocked(ProcessRecord app, boolean activityChange,
        ProcessRecord client) {
    // app.activities.size() > 0 说明本进程中有活动的 Activity。app.activities 元素的类型是 ActivityRecord
    // app.hasClientActivities 为 true 表示绑定了本进程Service的客户进程中有活动的 Activity
    // app.treatLikeActivity 为 true 表示 Service 启动时带有 BIND_TREAT_LIKE_ACTIVITY 标记,这个属性和键盘逻辑关系比较大
    // app.recentTasks.size() > 0 说明本进程中有活动的任务栈。app.recentTasks 元素的类型是 TaskRecord
    final boolean hasActivity = app.activities.size() > 0 || app.hasClientActivities
            || app.treatLikeActivity || app.recentTasks.size() > 0;
    final boolean hasService = false; // not impl yet. app.services.size() > 0;
    if (!activityChange && hasActivity) {
        // 如果当前进程中存在 Activity 并且 Activity 的状态没有发生过变化
        // 不做处理,直接返回
        return;
    }
    mLruSeq++; // 执行次数 +1
    final long now = SystemClock.uptimeMillis();
    app.lastActivityTime = now; // 更新 lastActivityTime 时间
    if (hasActivity) {
        // 如果 hasActivity 为 true 并且在 mLruProcesses 列表的最后一个位置
        // 不需要再进行额外的操作,直接退出
        final int N = mLruProcesses.size();
        if (N > 0 && mLruProcesses.get(N-1) == app) {
            return;
        }
    } else {
        // hasActivity 为 false,说明进程中没有 Activity
        // 如果位置位于 mLruProcessServiceStart-1 说明没有问题
        if (mLruProcessServiceStart > 0
                && mLruProcesses.get(mLruProcessServiceStart-1) == app) {
            return;
        }
    }
    // 获取当前应用在集合中的位置
    int lrui = mLruProcesses.lastIndexOf(app);
    if (app.persistent && lrui >= 0) {
        // 如果集合中已经存在并且应用属于 persistent 类型,则不需要做优化处理,直接返回
        return;
    }
    if (lrui >= 0) {
        // 如果 mLruProcesses 集合中已经存在,并且不是 persistent 类型,先将其从集合中移除
        // 并同时调整 mLruProcessActivityStart 和 mLruProcessServiceStart 的位置
        if (lrui < mLruProcessActivityStart) {
            mLruProcessActivityStart--;
        }
        if (lrui < mLruProcessServiceStart) {
            mLruProcessServiceStart--;
        }
        mLruProcesses.remove(lrui);
    }
    int nextIndex;
    if (hasActivity) {
        // 当前集合中所有的进程数量,请注意此时 app 已经不在集合中了
        final int N = mLruProcesses.size();
        if ((app.activities.size() == 0 || app.recentTasks.size() > 0)
                && mLruProcessActivityStart < (N - 1)) {
            // 进程中没有 Activity,但是它的 Service 客户进程有 Activity
            // 将进程插入到集合的尾部
            mLruProcesses.add(N - 1, app);
            // 检查集合中进程的uid,将与 app 相同uid的进程位置调整到一起
            final int uid = app.info.uid;
            for (int i = N - 2; i > mLruProcessActivityStart; i--) {
                ProcessRecord subProc = mLruProcesses.get(i);
                if (subProc.info.uid == uid) {
                    // 如果app的前一个进程的uid与其相同,检继续查前一个进程
                    if (mLruProcesses.get(i - 1).info.uid != uid) {
                        // 两个进程uid不一致进行位置互换,这里不太清楚目的
                        ProcessRecord tmp = mLruProcesses.get(i);
                        mLruProcesses.set(i, mLruProcesses.get(i - 1));
                        mLruProcesses.set(i - 1, tmp);
                        i--;
                    }
                } else {
                    // A gap, we can stop here.
                    break;
                }
            }
        } else {
            // 如果包含 Activity 直接添加到集合的尾部
            mLruProcesses.add(app);
        }
        nextIndex = mLruProcessServiceStart;
    } else if (hasService) {
        mLruProcesses.add(mLruProcessActivityStart, app);
        nextIndex = mLruProcessServiceStart;
        mLruProcessActivityStart++;
    } else  {
        // 没有 Service 的情况
        int index = mLruProcessServiceStart;
        ......
        mLruProcesses.add(index, app);
        nextIndex = index-1;
        mLruProcessActivityStart++;
        mLruProcessServiceStart++;
    }
    // 将和本进程 Service 关联的客户进程的位置调整到本进程之后
    for (int j=app.connections.size()-1; j>=0; j--) {
        ConnectionRecord cr = app.connections.valueAt(j);
        if (cr.binding != null && !cr.serviceDead && cr.binding.service != null
                && cr.binding.service.app != null
                && cr.binding.service.app.lruSeq != mLruSeq
                && !cr.binding.service.app.persistent) {
            nextIndex = updateLruProcessInternalLocked(cr.binding.service.app, now, nextIndex,
                    "service connection", cr, app);
        }
    }
    // 将和本进程 ContentProvider 关联的客户进程的位置调整到本进程之后
    for (int j=app.conProviders.size()-1; j>=0; j--) {
        ContentProviderRecord cpr = app.conProviders.get(j).provider;
        if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq && !cpr.proc.persistent) {
            nextIndex = updateLruProcessInternalLocked(cpr.proc, now, nextIndex,
                    "provider reference", cpr, app);
        }
    }
}

updateLruProcessLocked()方法中调整进程很重要的一个依据是进程中有没有活动的Activity
Activity用来显示UI,关系着用户体验,因此Android尽量不关闭运行Activity组件的进程。除了进程本身存在Activity对象外,如果和进程中运行的Service相关联的客户进程中有Activity,也算本进程拥有Activity

  • 这样做的目的是为了将来杀死进程释放内存做准备。
  • 如果一个进程的关联进程有Activity对象存在,那么它的重要性也和真正拥有Activity对象的进程相当。
  • 如果杀死它,将可能导致另一个进程出现异常

如果一个进程拥有Activity,通常会把它放入队列的最高端的位置(也就是集合索引值大的方向)
否则,只会把它放到所有没有Activity的进程的前面,也就是mLruProcessActivityStart所表示的位置。

调整完某个进程的位置之后,还需要调整和该进程相关联进程的位置
进程的关联进程有两种类型:

  • 一种是绑定了本进程Service的进程
  • 一种是连接了本进程的ContentProvider的进程

如果关联进程本身有Activity是不会调整的,需要调整的是那些没有Activity的进程,在updateLruProcessInternalLocked()方法中会进行这种调整,我们简单看下:

private int updateLruProcessInternalLocked(ProcessRecord app, long now, int index,
        String what, Object obj, ProcessRecord srcApp) {
    app.lastActivityTime = now;
    if (app.activities.size() > 0 || app.recentTasks.size() > 0) {
        // 如果待调整进程包含 Activity ,不做调整,直接返回
        return index;
    }
    int lrui = mLruProcesses.lastIndexOf(app);
    if (lrui < 0) {
        // 如果进程不在 mLruProcesses 集合中,直接返回
        return index;
    }
    if (lrui >= index) {
        // 如果进程当前位置高于要调整的位置,不作调整,直接返回
        return index;
    }
    if (lrui >= mLruProcessActivityStart) {
        // 如果进程当前位置比 Activity 进程的其实位置还要高,不做调整,直接返回
        return index;
    }
    // 把进程调整到 index-1 的位置
    mLruProcesses.remove(lrui);
    if (index > 0) {
        index--;
    }
    mLruProcesses.add(index, app);
    return index;
}

注释比较详细,不多解释了哈。

ProcessList

前面介绍了进程列表的调整逻辑,在学习oom_adj调整前我们先看下ProcessList类的内容

ProcessList类中定义了大量AMS中用到的常量,部分定义如下:

public final class ProcessList {
    // 定义进程发生 crash 的最小时间间隔,如果进程在小于这个时间内发生 crash,会被认为是异常进程
    static final int MIN_CRASH_INTERVAL = 60*1000;
    ......
    // 处于某种不可知状态的进程的 oom_adj 值
    static final int UNKNOWN_ADJ = 1001;
    // cached 进程的 oom_adj 的最大值和最小值定义
    static final int CACHED_APP_MAX_ADJ = 906;
    static final int CACHED_APP_MIN_ADJ = 900;
    // 位于 B 列表的服务进程的 oom_adj 值。位于 B 列表的都是一些旧的、过时的服务进程
    static final int SERVICE_B_ADJ = 800;
    // 当前 Activity 的前一个 Activity 所处进程的 oom_adj 值
    static final int PREVIOUS_APP_ADJ = 700;
    // Home 进程的 oom_adj 值
    static final int HOME_APP_ADJ = 600;
    // 只包含组件 Service 的进程的 oom_adj 值
    static final int SERVICE_ADJ = 500;
    // heavy-weight 进程的 oom_adj 值
    static final int HEAVY_WEIGHT_APP_ADJ = 400;
    // 正在执行 backup 任务进程的 oom_adj 值
    static final int BACKUP_APP_ADJ = 300;
    // 不在前台但是包含有用户可感知组件的进程的 oom_adj 值(例如播放音乐的后台进程)
    static final int PERCEPTIBLE_APP_ADJ = 200;
    // 仅包含 Activity 的可见进程的 oom_adj 值
    static final int VISIBLE_APP_ADJ = 100;
    ......
    // 定义用于内存回收的 oom_adj 阈值
    private final int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };
    // 定义用于低配置(HVGA或更低,或内存小于512MB)设备内存回收的内存阈值,单位:KB
    private final int[] mOomMinFreeLow = new int[] {
            12288, 18432, 24576,
            36864, 43008, 49152
    };
    // 用于定义高配置(高分辨率或内存1GB左右)设备内存回收的内存阈值,单位:KB
    private final int[] mOomMinFreeHigh = new int[] {
            73728, 92160, 110592,
            129024, 147456, 184320
    };
    ......
}

ProcessList中定义的常量有很多和进程类型相关的,像cached进程service进程等。关于Android进程的分类官方将其分为了4类,简要如下:

  • foreground process:前台进程,是用户目前执行操作所需的进程。在不同的情况下,进程可能会因为其所包含的各种应用组件而被视为前台进程
  • visible process:可见进程,正在进行用户当前知晓的任务,因此终止该进程会对用户体验造成明显的负面影响
  • service process:服务进程,包含一个已使用startService()方法启动的Service
  • cached process:缓存进程,是目前不需要的进程,因此,如果其他地方需要内存,系统可以根据需要自由地终止该进程

进程具体的类型描述及终止规则就不详细介绍了,具体大家可以参考官网:进程和应用生命周期,很详细哟

ProcessList中同样也定义了一个和oom_adj相关的方法,用来将oom_adj的数值发送给lmkd,如下:

    public static final void setOomAdj(int pid, int uid, int amt) {
        if (pid <= 0) {
            return;
        }
        if (amt == UNKNOWN_ADJ)
            return;
        long start = SystemClock.elapsedRealtime();
        ByteBuffer buf = ByteBuffer.allocate(4 * 4);
        buf.putInt(LMK_PROCPRIO);
        buf.putInt(pid);
        buf.putInt(uid);
        buf.putInt(amt);
        writeLmkd(buf);
    }

调整进程的oom_adj

Android基于各个应用进程四大组件的状态对应用进程进行重要性评估,也就是计算更新oom_adj值。并在系统内存紧张时根据重要性由低到高来选择杀死应用进程,以达到释放内存的目的

AMS中调整进程oom_adj值的方法是updateOomAdjLocked()方法,简要代码如下:

final void updateOomAdjLocked() {
    // 获取位于前台的 Activity 和它所在的进程
    final ActivityRecord TOP_ACT = resumedAppLocked();
    final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
    final long now = SystemClock.uptimeMillis();
    final long nowElapsed = SystemClock.elapsedRealtime();
    final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
    final int N = mLruProcesses.size();
    ......
    // 计算 slot 数值。这里就用到了 ProcessList 类中的一些常量
    // 根据常量数值,计算结果为:(906-900+1)/2=3
    int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
            - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
    // 计算 empty进程 的数量
    int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
    if (numEmptyProcs > cachedProcessLimit) {
        // 如果空进程的数量超过了 cache进程 的限制值,更新为限制值
        numEmptyProcs = cachedProcessLimit;
    }
    // 计算一个 slot 可以存放的empty进程数
    int emptyFactor = numEmptyProcs/numSlots;
    if (emptyFactor < 1) emptyFactor = 1;
    // 计算一个 slot 可以存放的cache进程数
    int cachedFactor = (mNumCachedHiddenProcs > 0 ? mNumCachedHiddenProcs : 1)/numSlots;
    if (cachedFactor < 1) cachedFactor = 1;
    ......
    // 初始化 adj 数值
    int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
    int nextCachedAdj = curCachedAdj+1;
    int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
    int nextEmptyAdj = curEmptyAdj+2;
    ......
    // 从 mLruProcesses 集合的后面向前处理
    for (int i=N-1; i>=0; i--) {
        ProcessRecord app = mLruProcesses.get(i);
        if (!app.killedByAm && app.thread != null) {
            app.procStateChanged = false;
            // 计算进程的 oom_adj 值
            computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
            ......
            if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
                // 计算出的 oom_adj 值大于 ProcessList.UNKNOWN_ADJ(系统定义的最大值)的情况
                switch (app.curProcState) {
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
                    case ActivityManager.PROCESS_STATE_CACHED_RECENT:
                        // 上面三种进程状态按照cached进程的参数进行调整
                        app.curRawAdj = curCachedAdj;
                        // 修正 adj 数值
                        app.curAdj = app.modifyRawOomAdj(curCachedAdj);
                        if (curCachedAdj != nextCachedAdj) {
                            stepCached++;
                            if (stepCached >= cachedFactor) { // 一个 slot 中放不下了
                                stepCached = 0;
                                curCachedAdj = nextCachedAdj;
                                nextCachedAdj += 2; // 使用一个新的 slot,即将 adj 值增加 2
                                if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                    nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
                                }
                            }
                        }
                        break;
                    default:
                        // 其他进程状态按照empty进程的参数进行设置
                        app.curRawAdj = curEmptyAdj;
                        app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
                        if (curEmptyAdj != nextEmptyAdj) {
                            stepEmpty++;
                            if (stepEmpty >= emptyFactor) {
                                stepEmpty = 0;
                                curEmptyAdj = nextEmptyAdj;
                                nextEmptyAdj += 2;
                                if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
                                    nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
                                }
                            }
                        }
                        break;
                }
            }
        }
    }
    ......
    for (int i=N-1; i>=0; i--) {
        ProcessRecord app = mLruProcesses.get(i);
        if (!app.killedByAm && app.thread != null) {
            // 更新进程的 oom_adj 值
            applyOomAdjLocked(app, true, now, nowElapsed);
            ......
        }
    }
    ......
}

updateOomAdjLocked()方法中

  • 先计算出了slot和每个slot需要容纳的进程数量,包括:cached进程empty进程
    • 这样做的目的是为了将系统中的cached进程empty进程分成不同的级别
    • 每个级别拥有相同的oom_adj
    • 级别和级别间隔的oom_adj值为2
  • 然后通过调用computeOomAdjLocked()方法来计算进程的oom_adj的值
    • oom_adj值的计算过程这里也就先简单略过啦,感兴趣的同学可以前去学习一下
  • 如果计算后该进程的curAdj仍然大于系统定义的最大oom_adj值(ProcessList.UNKNOWN_ADJ),表明该进程属于cached进程或者empty进程
    • 根据进程状态变量curProcState来区分进程类型
    • 确认进程类型后,会按照从低到高的原则把进程放到某个slot级别中
    • 如果该级别的进程数满了,就进入下一个级别(oom_adj数值+2)

我们需要注意的是,计算oom_adj值的时候是从mLruProcesses列表的尾部开始计算的
这就意味着排在后面(数组索引值大的)的进程如果变成了cahced进程empty进程,将会比它前面的同类型进程有更小的oom_adj
oom_adj值越大越可能被关闭,这也就是为什么要经常调整进程在mLruProcesses列表的位置

而对于applyOomAdjLocked()方法,最核心的地方在于执行了ProcessList.setOomAdj()方法,跟踪方法调用会发现

  • 最终是往名为lmkdSocket发送数据,数据内容为piduid及对应的adj
  • lmkdlmkd进程创建,所以后面的事情就由lmkd来处理

关于lmkd我们就不深入了,大家可以前去官网了解:低内存终止守护程序

官方简介:Android 低内存终止守护程序 (lmkd) 进程可监控运行中的 Android 系统的内存状态,并通过终止最不必要的进程来应对内存压力大的问题,使系统以可接受的性能水平运行

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值