Android 系统应用 - Launcher 的启动过程

相关文章链接:

1. Android Framework - 学习启动篇
2. Android 系统服务 - AMS 的启动过程
3. Android 系统服务 - PMS 的启动过程
4. Android Framework - 开机启动 Init 进程

相关源码文件:

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
/apps/Launcher3/src/com/android/launcher3/launcher3.java
/apps/Launcher3/src/com/android/launcher3/LauncherModel.java
/apps/Launcher3/src/com/android/launcher3/LauncherAppsCompatV16.java

/frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
/frameworks/base/cmds/bootanimation/bootanimation_main.cpp 

1. 启动入口分析

    public void systemReady(final Runnable goingCallback) {   
      synchronized (this) {
        // 启动 Launcher
        startHomeActivityLocked(mCurrentUserId, "systemReady");
      }
    }

    boolean startHomeActivityLocked(int userId, String reason) {
        // 获取 Launcher 的启动意图
        Intent intent = getHomeIntent();
        // 通过意图解析到 ActivityInfo 
        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
        if (aInfo != null) {
            intent.setComponent(new ComponentName(
                    aInfo.applicationInfo.packageName, aInfo.name));
            aInfo = new ActivityInfo(aInfo);
            aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
            // 通过进程名和uid 查询进程信息
            ProcessRecord app = getProcessRecordLocked(aInfo.processName,
                    aInfo.applicationInfo.uid, true);
            // 这里进程还没启动 app 为 null
            if (app == null || app.instrumentationClass == null) {
                intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK);
                mStackSupervisor.startHomeActivity(intent, aInfo, reason);
            }
        }
        return true;
    }

    Intent getHomeIntent() {
        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
        intent.setComponent(mTopComponent);
        if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            // 用 CATEGORY_HOME 去查询
            intent.addCategory(Intent.CATEGORY_HOME);
        }
        return intent;
    }

Launcher 启动的入口方法在 AMS 的 systemReady 方法中,首先会通过意图向 PMS 发起解析请求,PMS 查询返回 ActivityInfo 对象,注意这里的 category 是 CATEGORY_HOME ;然后通过进程的名字和 uid 去查询是否启动了进程,目前 Launcher 进程的 ProcessRecord 肯定是空;最后调用 startHomeActivity 方法去启动和创建 Launcher 。关于 Launcher 进程的创建和 Launcher Activity 的启动流程这里先不讲,在后面分析四大组件的启动过程时会详细讲到。

2. 查询解析填充所有 App 信息

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化 Model
        LauncherAppState app = LauncherAppState.getInstance();
        mModel = app.setLauncher(this);
        setContentView(R.layout.launcher);
        if (!mRestoring) {
            if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
                // If the user leaves launcher, then we should just load items asynchronously when
                // they return.
                mModel.startLoader(PagedView.INVALID_RESTORE_PAGE);
            } else {
                // We only load the page synchronously if the user rotates (or triggers a
                // configuration change) while launcher is in the foreground
                mModel.startLoader(mWorkspace.getRestorePage());
            }
        }
    }


    public void startLoader(int synchronousBindPage, int loadFlags) {
        synchronized (mLock) {
            if (mCallbacks != null && mCallbacks.get() != null) {
                mLoaderTask = new LoaderTask(mApp.getContext(), loadFlags);
                if (synchronousBindPage != PagedView.INVALID_RESTORE_PAGE
                        && mAllAppsLoaded && mWorkspaceLoaded && !mIsLoaderTaskRunning) {
                    mLoaderTask.runBindSynchronousPage(synchronousBindPage);
                } else {
                    sWorkerThread.setPriority(Thread.NORM_PRIORITY);
                    sWorker.post(mLoaderTask);
                }
            }
        }
    }

    public void run() {
      loadAndBindAllApps();
    }

    private void loadAllApps() {
      final List<LauncherActivityInfoCompat> apps = mLauncherApps.getActivityList(null, user);
      for (int i = 0; i < apps.size(); i++) {
        LauncherActivityInfoCompat app = apps.get(i);
        mBgAllAppsList.add(new AppInfo(mContext, app, user, mIconCache));
      }
           
      mHandler.post(new Runnable() {
        public void run() {
          final Callbacks callbacks = tryGetCallbacks(oldCallbacks);
          if (callbacks != null) {
            callbacks.bindAllApplications(added);
          } else {
            ...     
          }
      }});
    }

    public List<LauncherActivityInfoCompat> getActivityList(String packageName, UserHandleCompat user) {
        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
        mainIntent.setPackage(packageName);
        // 像 PMS 发起查询所有 ResolveInfo 信息
        List<ResolveInfo> infos = mPm.queryIntentActivities(mainIntent, 0);
        List<LauncherActivityInfoCompat> list = new ArrayList<LauncherActivityInfoCompat>(infos.size());
        for (ResolveInfo info : infos) {
            list.add(new LauncherActivityInfoCompatV16(mContext, info));
        }
        return list;
    }

Launcher 应用的 onCreate 方法中会调用 mModel 的 startLoader 方法去查询所有的 App 应用信息,该方法的内部实际调用的是 PKMS 的 queryIntentActivities 方法。并且会将所有查询到的 Apk 信息,通过回调的方式通知 Launcher 去填充我们桌面的 RecyclerView 界面。注意这里查询的 Action 和 Category 分别是 ACTION_MAIN 和 CATEGORY_LAUNCHER ,因为我们在 AndroidMnifest.xml 中一般都会有如下配置。

  <activity android:name=".MainActivity" android:launchMode="singleTask">
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
  </activity>

3. 启动和关闭开机画面

启动完 Launcher 后系统会帮我们关闭开机启动的画面,所以这里我们顺便讲一下开机画面的启动和关闭。我们首先来分析一下开机动画的启动,开机动画其实是 init 进程中解析到的一个服务,只不过 init.rc 脚本中的配置是 disable 。也就是说 init 进程启动的时候只会解析到,而不会启动开机动画的进程,因为开机动画进程需要处理渲染,因此必须要依赖 SurfaceFlinger 进程初始化完毕。

service bootanim /system/bin/bootanimation
  class core
  user graphics
  group graphics audio
  disabled
  oneshot

void SurfaceFlinger::init() {
    // start boot animation
    startBootAnim();
}

void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");
    property_set("ctl.start", "bootanim");
}

SurfaceFlinger 初始化完毕后会通过 property_set 这种方式去通知 init 进程启动 bootanim 进程,因此我们只需要找到 bootanim 进程的启动源码即可。

int main()
{
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.nobootanimation", value, "0");
    int noBootAnimation = atoi(value);
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {
        // 打开 binder 驱动
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation();

        IPCThreadState::self()->joinThreadPool();

    }
    return 0;
}

void BootAnimation::onFirstRef() {
    status_t err = mSession->linkToComposerDeath(this);
    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
    if (err == NO_ERROR) {
        run("BootAnimation", PRIORITY_DISPLAY);
    }
}

status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();
    // 获取初始化 layer
    ...
    // 初始化 opengl and egl
    ...
    // 初始化打开开机启动 zip 包
    ...
    return NO_ERROR;
}

bool BootAnimation::threadLoop()
{
    bool r;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZip == NULL) {
        ...
    } else {
        r = movie();
    }
    // 销毁 opengl 和 egl
    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    IPCThreadState::self()->stopProcess();
    return r;
}

bool BootAnimation::movie()
{
    String8 desString;
    // 读取 desc.txt 配置文件
    if (!readFile("desc.txt", desString)) {
        return false;
    }
    char const* s = desString.string();

    // 解析描述文件
    for (;;) {
        ...
    }

    for (size_t i=0 ; i<pcount ; i++) {
        for (int r=0 ; !part.count || r<part.count ; r++) {
            // opengl 绘制操作
            glClearColor(
                    part.backgroundColor[0],
                    part.backgroundColor[1],
                    part.backgroundColor[2],
                    1.0f);
            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                const Animation::Frame& frame(part.frames[j]);
                nsecs_t lastFrame = systemTime();
                ...
                if (r > 0) {
                    glBindTexture(GL_TEXTURE_2D, frame.tid);
                } else {
                    ...
                    initTexture(frame);
                }
                
                // specify the y center as ceiling((mHeight - animation.height) / 2)
                // which is equivalent to mHeight - (yc + animation.height)
                glDrawTexiOES(xc, mHeight - (yc + animation.height),
                              0, animation.width, animation.height);
                eglSwapBuffers(mDisplay, mSurface);

                // 不断绘制时检测是否需要退出
                checkExit();
            }
            // 如果退出了就跳出结束绘制
            if(exitPending() && !part.count)
                break;
        }

        // free the textures for this part
        if (part.count != 1) {
            for (size_t j=0 ; j<fcount ; j++) {
                const Animation::Frame& frame(part.frames[j]);
                glDeleteTextures(1, &frame.tid);
            }
        }
    }
    return false;
}

// 读取 service.bootanim.exit 值是否是 1 
#define EXIT_PROP_NAME "service.bootanim.exit"
void BootAnimation::checkExit() {
    // Allow surface flinger to gracefully request shutdown
    char value[PROPERTY_VALUE_MAX];
    property_get(EXIT_PROP_NAME, value, "0");
    int exitnow = atoi(value);
    if (exitnow) {
        requestExit();
        if (mAudioPlayer != NULL) {
            mAudioPlayer->requestExit();
        }
    }
}

启动动画底层采用的是 opengles 的方式来渲染绘制的,绘制的内容是本地的一个启动动画资源包,在绘制的过程中会不断的判断是否需要退出,读取的字段是 service.bootanim.exit ,为 1 代表需要 break 退出循环绘制。因此我们只需要找到 service.bootanim.exit 在哪里设置为 1 的,便可找到退出启动动画的入口。关闭动画的入口还是在 SurfaceFlinger 中只是这个调用流程比较复杂而已:

    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        if (r != null) {
            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                // 添加了一个 IdleHandler 消息
                Looper.myQueue().addIdleHandler(new Idler());
            }
        } else {
            ...
        }
    }

    private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            ActivityClientRecord a = mNewActivities;
            if (a != null) {
                mNewActivities = null;
                IActivityManager am = ActivityManagerNative.getDefault();
                ActivityClientRecord prev;
                do {
                    if (a.activity != null && !a.activity.mFinished) {
                        try {
                            // 调用 AMS 的 activityIdle
                            am.activityIdle(a.token, a.createdConfig, stopProfiling);
                        } catch (RemoteException ex) {
                            // Ignore
                        }
                    }
                } while (a != null);
            }
            return false;
        }
    }


    @Override
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        synchronized (this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token, false, config);
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

    // Checked.
    final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
            Configuration config) {
        ActivityRecord r = ActivityRecord.forTokenLocked(token);
        if (r != null) {
            ...
            if (isFrontStack(r.task.stack) || fromTimeout) {
                booting = checkFinishBootingLocked();
            }
        }
        ...
        return r;
    }

    private boolean checkFinishBootingLocked() {
        final boolean booting = mService.mBooting;
        boolean enableScreen = false;
        mService.mBooting = false;
        if (!mService.mBooted) {
            mService.mBooted = true;
            enableScreen = true;
        }
        if (booting || enableScreen) {
            mService.postFinishBooting(booting, enableScreen);
        }
        return booting;
    }

    void enableScreenAfterBoot() {
        mWindowManager.enableScreenAfterBoot();
        synchronized (this) {
            updateEventDispatchingLocked();
        }
    }

    public void performEnableScreen() {
        synchronized(mWindowMap) {
            if (!mBootAnimationStopped) {
                // 像 SurfaceFlinger 进程发起关闭开机界面的消息
                try {
                    IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                    if (surfaceFlinger != null) {
                        Parcel data = Parcel.obtain();
                        data.writeInterfaceToken("android.ui.ISurfaceComposer");
                        surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                                data, null, 0);
                        data.recycle();
                    }
                } catch (RemoteException ex) {
                    ...
                }
                mBootAnimationStopped = true;
            }
            ...
          }
        }
    }

    void SurfaceFlinger::bootFinished() {
      ...
      // 把 service.bootanim.exit 属性设置为 1 ,bootanim 进程读到 1 时就会退出开机启动动画
      property_set("service.bootanim.exit", "1");
    }

关闭开机启动动画的流程还是比较复杂的,我们来缕一缕整个逻辑,我们的 Launcher 进程启动后会启动我们 Launcher Activity 界面,而 Activity 的生命周期调用都是由 ActivityThread 来执行的,其中就会执行到 handleResumeActivity 方法,在该方法中会添加一个 IdleHandler 消息,会调用到 AMS 的 activityIdle 方法,AMS 会调用 WMS 的 enableScreenAfterBoot 方法,WMS 会跨进程通知 SurfaceFlinger 去关闭我们的开机启动动画。

视频地址:https://pan.baidu.com/s/1LZ-kHXQyxa9995I_P1ajfg
视频密码:o71f

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351