目录

前言

Android 框架

Android系统启动流程

        装载机层

内核层

原生API层

框架层(Framework)

应用层

Android应用程序打开流程

重要声明

参考鸣谢


前言

如果你和我一样刚刚接触安卓不久,又恰巧学过其他编程,那么你多半会有个和我一样的疑问缠绕心头:Android 程序的入口到底在哪里?

我们知道,Android开发采用的编程语言是 Java Kotlin,由于目前使用的是 Java ,这里仅说 Java。以往我们写 Java程序都会有一个入口函数main;而我们之前写的 Demo,程序都是从 Activity onCreate( ) 方法中开始执行,那么它就我们Anroid APP程序的入口吗 ?

答案是 否定的

我们都知道我们 Android 应用程序是运行在 Android系统 之上的,即先有 Android系统,才能运行我们的 app

下面我们先看了解下 Android 的整体框架及 Android 系统的启动流程:

Android 框架

【23】Android 应用程序入口探究_第1张图片

Android系统启动流程

【23】Android 应用程序入口探究_第2张图片

装载机层

  • Boot Rom:当手机处于关机状态时,长按开机键开机,会引导芯片开始从固化在 ROM 里的代码开始执行,然后加载引导程序到RAM
  • Boot Loader:启动 Android 系统之前的引导程序,主要是检查 RAM,初始化参数等

内核层

Kernel层指的就是 Android 的 Linux内核层,其启动流程如下:

  • 启动 swapper进程PID = 0),这是系统初始化过程内核创建的第一个进程,用于初始化进程管理,内存管理,加载DisplayCameraBinder等驱动相关工作
  • 启动 kthreadd 进程PID = 2),它会创建内核工作 线程 kworkder,软中断 线程ksoftirqdthermal 等内核守护进程。

       kthreadd 是所有内核进程的鼻祖。

原生API层

这里 Native层 主要包括 init进程PID = 1) 孵化的用户空间的守护进程,HAL 层以及开机动画等。

  • init 进程会孵化出 ueventdlogdhealthdinstalldadbdlmkd 等用户守护进程;
  • init 进度即将启动 ServiceManager(Binder 服务管家),bootanim(开机动画)等重要服务。
  • init 进程孵化出 Zygote进程Zygote进程是 Android 系统第一个 Java 进程(虚拟机进程)。

        Init 是所有用户空间进程的鼻祖。

        Zygote进程是所有 Java 进程的父进程。

框架层(Framework)

Zygote 进程,是由 init 进程通过解析 init.rc 文件后 fork( ) 生成的,它主要包括以下内

  • 加载 ZygoteInit 类,注册 Zygote Socket 服务端套接字
  • 加载虚拟机
  • 提前加载类 preloadClasses
  • 提前加载资源 preloadResouces

System Server 进程,是由 Zygote 进程 fork( ) 而来

  • System Server Zygote 孵化的第一个进程。
  • System Server 负责启动和管理整个 Java framework,包含 ActivityManagerWindowManagerPackageManagerPowerManager 等服务。

Media Server 进程,是由 init 进程 fork( )  而来

负责启动和管理整个 C++ framework,包含 AudioFlingerCamera Service 等服务。

应用层

  • Zygote 孵化进程第的一个 App进程 Launcher进程,也就是我们的桌面进程。
  • Zygote进程 也会创建 BrowserPhoneEmail等应用进程。也就是说所有的应用都是进程由 Zygote 进程 fork() 生成的。而且上层的进程全部由下层的进程进行管理,包括但不限于界面的注册,跳转,消息的传递。

Android应用程序打开流程

【23】Android 应用程序入口探究_第3张图片

重要声明

本次Android源码阅读的版本是 2014-09-06- 索引:棒棒糖 - 5.0.0_r2

为什么不看最新的源码呢,因为资料少..目前还真啃不动

主界面 Launcher 其实也是一个 Activity,它存在击 icon 的事件OnClick()

// 代码节选:// Launcher.java   --- 2428    /**     * Launches the intent referred by the clicked shortcut.     *     * @param v The view representing the clicked shortcut.     */    public void onClick(View v) {        // Make sure that rogue clicks don't get through while allapps is launching, or after the        // view has detached (it's possible for this to happen if the view is removed mid touch).        if (v.getWindowToken() == null) {            return;        }        if (!mWorkspace.isFinishedSwitchingState()) {            return;        }        if (v instanceof Workspace) {            if (mWorkspace.isInOverviewMode()) {                mWorkspace.exitOverviewMode(true);            }            return;        }        if (v instanceof CellLayout) {            if (mWorkspace.isInOverviewMode()) {                mWorkspace.exitOverviewMode(mWorkspace.indexOfChild(v), true);            }        }        Object tag = v.getTag();        if (tag instanceof ShortcutInfo) {            onClickAppShortcut(v);        } else if (tag instanceof FolderInfo) {            if (v instanceof FolderIcon) {                onClickFolderIcon(v);            }        } else if (v == mAllAppsButton) {            onClickAllAppsButton(v);        } else if (tag instanceof AppInfo) {            startAppShortcutOrInfoActivity(v);        } else if (tag instanceof LauncherAppWidgetInfo) {            if (v instanceof PendingAppWidgetHostView) {                onClickPendingWidget((PendingAppWidgetHostView) v);            }        }    }

OnClick 事件会调用 startAppShortcutOrInfoActivity ()

// 代码节选:// Launcher.java   --- 2629private void startAppShortcutOrInfoActivity(View v) {        Object tag = v.getTag();        final ShortcutInfo shortcut;        final Intent intent;        if (tag instanceof ShortcutInfo) {            shortcut = (ShortcutInfo) tag;            intent = shortcut.intent;            int[] pos = new int[2];            v.getLocationOnScreen(pos);            intent.setSourceBounds(new Rect(pos[0], pos[1],                    pos[0] + v.getWidth(), pos[1] + v.getHeight()));        } else if (tag instanceof AppInfo) {            shortcut = null;            intent = ((AppInfo) tag).intent;        } else {            throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");        }        boolean success = startActivitySafely(v, intent, tag);        mStats.recordLaunch(intent, shortcut);        if (success && v instanceof BubbleTextView) {            mWaitingForResume = (BubbleTextView) v;            mWaitingForResume.setStayPressed(true);        }    }

这里调用了 startActivitySafely() 并通过 Binder IPC 机制 调用 ActivityManagerService.java 中的 startActivity (Intent)

// 代码节选:// ActivityManagerService.java   --- 3589    @Override    public final int startActivity(IApplicationThread caller, String callingPackage,            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,            int startFlags, ProfilerInfo profilerInfo, Bundle options) {        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,            resultWho, requestCode, startFlags, profilerInfo, options,            UserHandle.getCallingUserId());    }

ActivityManagerService 会执行如下操作:

  • 通过 PackageManager resolveIntent () 收集这个 intent 对象的指向信息,并将指向信息被存储在一个 intent 对象中。
// 代码节选:// ActivityManagerService.java   --- 3341ResolveInfo info = AppGlobals.getPackageManager().resolveIntent(                        intent,                        intent.resolveTypeIfNeeded(mContext.getContentResolver()),                            flags, userId);
  • 通过 grantUriPermissionLocked () 方法来验证用户是否有足够的权限去调用该 intent 对象指向的 Activity.
// 代码节选:// ActivityManagerService.java   --- 7538// grantUriPermissionLocked () 在 7388 处/**     * @param uri This uri must NOT contain an embedded userId.     * @param userId The userId in which the uri is to be resolved.     */    @Override    public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri,            final int modeFlags, int userId) {        enforceNotIsolatedCaller("grantUriPermission");        GrantUri grantUri = new GrantUri(userId, uri, false);        synchronized(this) {            final ProcessRecord r = getRecordForAppLocked(caller);            if (r == null) {                throw new SecurityException("Unable to find app for caller "                        + caller                        + " when granting permission to uri " + grantUri);            }            if (targetPkg == null) {                throw new IllegalArgumentException("null target");            }            if (grantUri == null) {                throw new IllegalArgumentException("null uri");            }            Preconditions.checkFlagsArgument(modeFlags, Intent.FLAG_GRANT_READ_URI_PERMISSION                    | Intent.FLAG_GRANT_WRITE_URI_PERMISSION                    | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION                    | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);            grantUriPermissionLocked(r.uid, targetPkg, grantUri, modeFlags, null,                    UserHandle.getUserId(r.uid));        }    }
  • 如果有权限,ActivityManagerService 会检查并在新的 task 中启动目标 activity
    这时需要先检查这个进程的 ProcessRecord 是否存在了。(ProcessRecord 是用于描述进程的数据结构对象)
    如果 ProcessRecord null, ActivityManagerService 会创建新的进程来实例化目标 activity.

为了启动 App 并使其 Activity 进入 resumed 状态,需要先使当前运行的 Activity(也就是 Launcher) 进入 Paused 状态

ActivityManagerService 最终调用 overridePendingTransition( ) 执行(未严格验证)

// 代码摘取:// ActivityManagerService .java  ---- 4700    @Override    public void overridePendingTransition(IBinder token, String packageName,            int enterAnim, int exitAnim) {        synchronized(this) {            ActivityRecord self = ActivityRecord.isInStackLocked(token);            if (self == null) {                return;            }            final long origId = Binder.clearCallingIdentity();            if (self.state == ActivityState.RESUMED                    || self.state == ActivityState.PAUSING) {                mWindowManager.overridePendingAppTransition(packageName,                        enterAnim, exitAnim, null);            }            Binder.restoreCallingIdentity(origId);        }    }

之后又通过 Binder 通信 调用到 ApplicationThread 的 schedulePauseActivity( )向本地的消息循环中加入一个 H.PAUSE_ACTIVITY 的消息。

// 代码摘取:// ApplicationThread.java  ---- 565        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);        }

之后会通过 Handler 的子类  - H 发送消息,处理消息的逻辑在 ActivityThread.java 的 handleMessage( ) 

// 代码摘取:// ActivityThread.java  ---- 1269        public void handleMessage(Message msg) {            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));            switch (msg.what) {                case LAUNCH_ACTIVITY: {                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");                    final 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;                //...            }            if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));        }

最终会调用 Activity performPause () 方法

// 代码摘取:// Activity.java  ---- 6040    final void performPause() {        mDoReportFullyDrawn = false;        mFragments.dispatchPause();        mCalled = false;        onPause();        mResumed = false;        if (!mCalled && getApplicationInfo().targetSdkVersion                >= android.os.Build.VERSION_CODES.GINGERBREAD) {            throw new SuperNotCalledException(                    "Activity " + mComponent.toShortString() +                    " did not call through to super.onPause()");        }        mResumed = false;    }

终于出现一个比较熟悉的身影了   onPause( )

之后在 handlePauseActivity ( ) 中调用 ActivityManagerNative.getDefault().activityPaused(token);

// 代码摘取:// Activitythread.java  ---- 3149    private void handlePauseActivity(IBinder token, boolean finished,            boolean userLeaving, int configChanges, boolean dontReport) {        ActivityClientRecord r = mActivities.get(token);        if (r != null) {            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);            if (userLeaving) {                performUserLeavingActivity(r);            }            r.activity.mConfigChangeFlags |= configChanges;            performPauseActivity(token, finished, r.isPreHoneycomb());            // Make sure any pending writes are now committed.            if (r.isPreHoneycomb()) {                QueuedWork.waitToFinish();            }            // Tell the activity manager we have paused.            if (!dontReport) {                try {                    ActivityManagerNative.getDefault().activityPaused(token);                } catch (RemoteException ex) {                }            }            mSomeActivitiesChanged = true;        }    }

通过 Binder 机制调用 ActivityManagerService 中的 activityPaused ( ) 方法,如下:

// 代码摘录://ActivityManagerService.java   ---- 6435    @Override    public final void activityPaused(IBinder token) {        final long origId = Binder.clearCallingIdentity();        synchronized(this) {            ActivityStack stack = ActivityRecord.getStackLocked(token);            if (stack != null) {                stack.activityPausedLocked(token, false);            }        }        Binder.restoreCallingIdentity(origId);    }

最终调用到 startProcessLocked ( )

该方法用以创建新的进程,通过 socket 通道传递参数给 Zygote 进程。Zygote 孵化自身,并调用 ZygoteInit.main () 方法来实例化 ActivityThread 对象并最终返回新进程的 pid。具体过程如下:

// 代码摘录:// ActivityManagerService.java   ---- 2947    final ProcessRecord startProcessLocked(String processName,            ApplicationInfo info, boolean knownToBeDead, int intentFlags,            String hostingType, ComponentName hostingName, boolean allowWhileBooting,            boolean isolated, boolean keepIfLarge) {        return startProcessLocked(processName, info, knownToBeDead, intentFlags, hostingType,                hostingName, allowWhileBooting, isolated, 0 /* isolatedUid */, keepIfLarge,                null /* ABI override */, null /* entryPoint */, null /* entryPointArgs */,                null /* crashHandler */);    }

接着会调用startProcessLocked ( )的重载方法:

// 代码摘录://ActivityManagerService.java   ---- 3189    private final void startProcessLocked(ProcessRecord app, String hostingType,            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {                        //...            // Start the process.  It will either succeed and return a result containing            // the PID of the new process, or else throw a RuntimeException.            boolean isActivityProcess = (entryPoint == null);            if (entryPoint == null) entryPoint = "android.app.ActivityThread";            checkTime(startTime, "startProcess: asking zygote to start proc");            Process.ProcessStartResult startResult = Process.start(entryPoint,                    app.processName, uid, uid, gids, debugFlags, mountExternal,                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,                    app.info.dataDir, entryPointArgs);            checkTime(startTime, "startProcess: returned from zygote!");                        //...    }

Process.start ( ) 实际上是调用 Zygote fork( ) 一个新的进程,并且在最后会调用 ActivityThread main () 方法。ActivityManagerService 所在的进程和 Zygote 所在的进程通过 Socket 通信,ZygoteInit 中循环侦听 Socket 连接:

// 代码摘录// ZygoteInit.java    ---- 733/**     * Runs the zygote process's select loop. Accepts new connections as     * they happen, and reads commands from connections one spawn-request's     * worth at a time.     *     * @throws MethodAndArgsCaller in a child process when a main() should     * be executed.     */    private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {        ArrayList fds = new ArrayList();        ArrayList peers = new ArrayList();        FileDescriptor[] fdArray = new FileDescriptor[4];        fds.add(sServerSocket.getFileDescriptor());        peers.add(null);        int loopCount = GC_LOOP_COUNT;        while (true) {            int index;            /*             * Call gc() before we block in select().             * It's work that has to be done anyway, and it's better             * to avoid making every child do it.  It will also             * madvise() any free memory as a side-effect.             *             * Don't call it every time, because walking the entire             * heap is a lot of overhead to free a few hundred bytes.             */            if (loopCount <= 0) {                gc();                loopCount = GC_LOOP_COUNT;            } else {                loopCount--;            }            try {                fdArray = fds.toArray(fdArray);                index = selectReadable(fdArray);            } catch (IOException ex) {                throw new RuntimeException("Error in select()", ex);            }            if (index < 0) {                throw new RuntimeException("Error in select()");            } else if (index == 0) {                ZygoteConnection newPeer = acceptCommandPeer(abiList);                peers.add(newPeer);                fds.add(newPeer.getFileDescriptor());            } else {                boolean done;                done = peers.get(index).runOnce();                if (done) {                    peers.remove(index);                    fds.remove(index);                }            }        }    }

可发现:

一旦建立 ZygoteConnection 即调用 runOnce( )

//代码摘录://ZygoteConnection.java    ---- 142boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {            //...            checkTime(startTime, "zygoteConnection.runOnce: preForkAndSpecialize");            pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,                    parsedArgs.appDataDir);        //...        try {            if (pid == 0) {                // in child                IoUtils.closeQuietly(serverPipeFd);                serverPipeFd = null;                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);                // should never get here, the child is expected to either                // throw ZygoteInit.MethodAndArgsCaller or exec().                return true;            } else {                // in parent...pid of < 0 means failure                IoUtils.closeQuietly(childPipeFd);                childPipeFd = null;                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);            }            //...}

我们来看看 forkAndSpecialize ( )

//代码摘录://Zygote.java    ---- 87    public static int forkAndSpecialize(int uid, int gid, int[] gids, int debugFlags,          int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,          String instructionSet, String appDataDir) {        long startTime = SystemClock.elapsedRealtime();        VM_HOOKS.preFork();        checkTime(startTime, "Zygote.preFork");        int pid = nativeForkAndSpecialize(                  uid, gid, gids, debugFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,                  instructionSet, appDataDir);        checkTime(startTime, "Zygote.nativeForkAndSpecialize");        VM_HOOKS.postForkCommon();        checkTime(startTime, "Zygote.postForkCommon");        return pid;    }

最终会去 fork( ) 进程,在子进程 (即 App 进程) 中执行 handleChildProc ( ) 方法

//代码摘录://ZygoteConnection.java    ---- 855    /**     * Handles post-fork setup of child proc, closing sockets as appropriate,     * reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller     * if successful or returning if failed.     *     * @param parsedArgs non-null; zygote args     * @param descriptors null-ok; new file descriptors for stdio if available.     * @param pipeFd null-ok; pipe for communication back to Zygote.     * @param newStderr null-ok; stream to use for stderr until stdio     * is reopened.     *     * @throws ZygoteInit.MethodAndArgsCaller on success to     * trampoline to code that invokes static main.     */    private void handleChildProc(Arguments parsedArgs,            FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)            throws ZygoteInit.MethodAndArgsCaller {        /**         * By the time we get here, the native code has closed the two actual Zygote         * socket connections, and substituted /dev/null in their place.  The LocalSocket         * objects still need to be closed properly.         */        closeSocket();        ZygoteInit.closeServerSocket();        if (descriptors != null) {            try {                ZygoteInit.reopenStdio(descriptors[0],                        descriptors[1], descriptors[2]);                for (FileDescriptor fd: descriptors) {                    IoUtils.closeQuietly(fd);                }                newStderr = System.err;            } catch (IOException ex) {                Log.e(TAG, "Error reopening stdio", ex);            }        }        if (parsedArgs.niceName != null) {            Process.setArgV0(parsedArgs.niceName);        }        if (parsedArgs.runtimeInit) {            if (parsedArgs.invokeWith != null) {                WrapperInit.execApplication(parsedArgs.invokeWith,                        parsedArgs.niceName, parsedArgs.targetSdkVersion,                        pipeFd, parsedArgs.remainingArgs);            } else {                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,                        parsedArgs.remainingArgs, null /* classLoader */);            }        } else {            String className;            try {                className = parsedArgs.remainingArgs[0];            } catch (ArrayIndexOutOfBoundsException ex) {                logAndPrintError(newStderr,                        "Missing required class name argument", null);                return;            }            String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];            System.arraycopy(parsedArgs.remainingArgs, 1,                    mainArgs, 0, mainArgs.length);            if (parsedArgs.invokeWith != null) {                WrapperInit.execStandalone(parsedArgs.invokeWith,                        parsedArgs.classpath, className, mainArgs);            } else {                ClassLoader cloader;                if (parsedArgs.classpath != null) {                    cloader = new PathClassLoader(parsedArgs.classpath,                            ClassLoader.getSystemClassLoader());                } else {                    cloader = ClassLoader.getSystemClassLoader();                }                try {                    ZygoteInit.invokeStaticMain(cloader, className, mainArgs);                } catch (RuntimeException ex) {                    logAndPrintError(newStderr, "Error starting.", ex);                }            }        }    }

注意到后面调用了 ZygoteInit.invokeStaticMain(cloader, className, mainArgs);

本处即调用 ActivityThread main () 方法

//代码摘录://ActivityThread.java    ---- 5184        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);        //初始化当前UserID        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        Security.addProvider(new AndroidKeyStoreProvider());        // Make sure TrustedCertificateStore looks in the right place for CA certificates        //设置当前用户CA证书的保存位置        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());        TrustedCertificateStore.setDefaultUserDirectory(configDir);        //设置进程名        Process.setArgV0("");        //为主进程创建Looper        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        //发出创建ActivityThread的消息        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        AsyncTask.init();        if (false) {            Looper.myLooper().setMessageLogging(new                    LogPrinter(Log.DEBUG, "ActivityThread"));        }        //主线程进入无限循环状态,等待接收消息        Looper.loop();        throw new RuntimeException("Main thread loop unexpectedly exited");    }}

从这里开始,准备主线程的消息队列,并为 App 创建并配置一个 ActivityThread, 再通过 thread.attach ( ) ActivityThead, ApplicationThread ActivityManagerService 关联起来。

ActivityThread 随后依次调用 Looper.prepareLoop () Looper.loop () 来开启消息循环

ActivityManagerService 通过 Binder 通知 ActivityThread 进行 App 中主线程的调度工作。

ActivityThread main( )逻辑如下:

大佬原图,可点击查看大图(ps:转自  3分钟看懂 Activity 启动流程

【23】Android 应用程序入口探究_第4张图片

参考鸣谢

Where is main() in Android?

Android 应用的真正入口是哪里?Seasoninthesun - 回答(强烈推荐)

Android系统架构开篇(强烈推荐)

3 分钟看懂 Activity 启动流程(强烈推荐)

Android 应用程序进程启动过程(强烈推荐)

Android 应用程序入口源码解析

ActivityThread的main方法究竟做了什么

 

资源下载

    链接    提取码: b2cg 

更多相关文章

  1. Android多进程app中Application回调onCreate()方法被执行多次分
  2. 如何降低android应用程序的耗电量
  3. 《Android系统源代码情景分析》连载回忆录:灵感之源
  4. android获取正在运行的应用程序

随机推荐

  1. 用Eclipse开发Android项目
  2. Android dumpsys命令详细使用
  3. android中的显示单位(px,dip/dp,sp)
  4. Android中使用ALSA声卡及alsa.conf asoun
  5. Android之ActivityManagerService启动详
  6. Android中Context简介
  7. 关于android软键盘enter键的替换与事件监
  8. Windows环境下Android(安卓)Sdk源码下载
  9. 传智播客Android视频教程——第九天
  10. Android Linker浅析