为了账号安全,请及时绑定邮箱和手机立即绑定

Android App应用启动流程(一)

标签:
Android

本篇源码分析基于Android8.0  API 26
关于Android源码查看:
你可以去看这里:https://android.googlesource.com/platform/frameworks/base/ 在线查看,也可以在Android Studio里面关联源码,也可以完全下载自己去编译

前言

其实app启动就是调用了一个startActivity,但是这个startActivity方法并没有我们想像中的那么简单,这其中牵涉到了三个进程:Zygote进程,system_server进程,还有被启动的app进程,其中还牵涉到Binder通信,AIDL,Handler机制等等,如果你把这几位搞明白再去看App启动源码,也许理解的更快一些,假如这几位你不是很理解,也是可以看app启动流程的,没准你会对他们更加感兴趣。

1,桌面图标点击

当我们点击Android系统桌面的应用图标的时候,app被启动,然后进入我们的MainActivity,呈现到用户面前,这就是App被启动了。其实桌面也只不过是一个app,它的名字叫Launcher,只不过是系统把它启动起来的,这里不关心Launcher的启动流程,只关心它里面的图标点击事件。

这里需要去看Launcher的源码,感兴趣的可以去看这里 https://android.googlesource.com/platform/packages/apps/

Launcher中的App列表使用的RecyclerView

//public class AllAppsRecyclerView extends BaseRecyclerView//public abstract class BaseRecyclerView extends RecyclerView//AllAppsRecyclerView最终继承自RecyclerView

<com.android.launcher3.allapps.AllAppsRecyclerView            android:id="@+id/apps_list_view"
            android:layout_below="@id/search_container_all_apps"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_horizontal|top"
            android:clipToPadding="false"
            android:overScrollMode="never"
            android:descendantFocusability="afterDescendants"
            android:focusable="true" />

这个Activity叫做Launcher.java,既然是RecyclerView,我们很自然的想到了item点击事件,item点击事件其实是对item设置的View.OnClickListener事件,恰巧Launcher实现了这个事件

/**
 * Default launcher application.
 */public class Launcher extends BaseActivity
        implements LauncherExterns, View.OnClickListener, OnLongClickListener,                   LauncherModel.Callbacks, View.OnTouchListener, LauncherProviderChangeListener,                   AccessibilityManager.AccessibilityStateChangeListener,                   WallpaperColorInfo.OnThemeChangeListener {

那么是不是这里就是用来处理RecyclerView应用图标的点击事件呢?答案是yes
给AllAppsRecyclerView设置adapter被封装到了AllAppsContainerView里面,然后AllAppsContainerView里面

mAdapter = new AllAppsGridAdapter(mLauncher, mApps, mLauncher, this);

AllAppsGridAdapter

/**
 * The grid view adapter of all the apps.
 */
public class AllAppsGridAdapter extends RecyclerView.Adapter<AllAppsGridAdapter.ViewHolder> {    ...
    //AllAppsGridAdapter构造函数传入iconClickListener
    public AllAppsGridAdapter(Launcher launcher, AlphabeticalAppsList apps, View.OnClickListener
            iconClickListener, View.OnLongClickListener iconLongClickListener) {
        Resources res = launcher.getResources();
        mLauncher = launcher;
        mApps = apps;
        mEmptySearchMessage = res.getString(R.string.all_apps_loading_message);
        mGridSizer = new GridSpanSizer();
        mGridLayoutMgr = new AppsGridLayoutManager(launcher);
        mGridLayoutMgr.setSpanSizeLookup(mGridSizer);
        mLayoutInflater = LayoutInflater.from(launcher);
        mIconClickListener = iconClickListener;
        mIconLongClickListener = iconLongClickListener;        if (FeatureFlags.LAUNCHER3_PHYSICS) {
            mSpringAnimationHandler = new SpringAnimationHandler<>(
                    SpringAnimationHandler.Y_DIRECTION, new AllAppsSpringAnimationFactory());
        }
    }    ...
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        switch (viewType) {
            case VIEW_TYPE_ICON:
            case VIEW_TYPE_PREDICTION_ICON:
                //icon点击事件
                BubbleTextView icon = (BubbleTextView) mLayoutInflater.inflate(
                        R.layout.all_apps_icon, parent, false);
                icon.setOnClickListener(mIconClickListener);
                icon.setOnLongClickListener(mIconLongClickListener);
                icon.setLongPressTimeout(ViewConfiguration.getLongPressTimeout());
                icon.setOnFocusChangeListener(mIconFocusListener);
                // Ensure the all apps icon height matches the workspace icons in portrait mode.
                icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;                return new ViewHolder(icon);            ...
        }
    }    ...}

可见这个点击事件被设置上了,现在我们回去看看Launcher.java这个Activity里面的onClick事件

/**
 * Launches the intent referred by the clicked shortcut.
 *
 * @param v The view representing the clicked shortcut.
 */public void onClick(View v) {
    ...

    Object tag = v.getTag();    if (tag instanceof ShortcutInfo) {
        onClickAppShortcut(v);
    } else if (tag instanceof FolderInfo) {        if (v instanceof FolderIcon) {
            onClickFolderIcon(v);
        }
    } else if ((v instanceof PageIndicator) ||
        (v == mAllAppsButton && mAllAppsButton != null)) {
        onClickAllAppsButton(v);
    } else if (tag instanceof AppInfo) {        //执行这里
        startAppShortcutOrInfoActivity(v);
    } else if (tag instanceof LauncherAppWidgetInfo) {        if (v instanceof PendingAppWidgetHostView) {
            onClickPendingWidget((PendingAppWidgetHostView) v);
        }
    }
}//拿到应用信息,执行startActivitySafely(v, intent, item)private void startAppShortcutOrInfoActivity(View v) {
    ItemInfo item = (ItemInfo) v.getTag();
    Intent intent;    if (item instanceof PromiseAppInfo) {
        PromiseAppInfo promiseAppInfo = (PromiseAppInfo) item;
        intent = promiseAppInfo.getMarketIntent();
    } else {
        intent = item.getIntent();
    }    if (intent == null) {        throw new IllegalArgumentException("Input must have a valid intent");
    }    boolean success = startActivitySafely(v, intent, item);
    getUserEventDispatcher().logAppLaunch(v, intent); // TODO for discovered apps b/35802115
    if (success && v instanceof BubbleTextView) {
        mWaitingForResume = (BubbleTextView) v;
        mWaitingForResume.setStayPressed(true);
    }
}public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
    ...    // Only launch using the new animation if the shortcut has not opted out (this is a
    // private contract between launcher and may be ignored in the future).
    boolean useLaunchAnimation = (v != null) &&
            !intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
    Bundle optsBundle = useLaunchAnimation ? getActivityLaunchOptions(v) : null;
    UserHandle user = item == null ? null : item.user;    // Prepare intent
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    if (v != null) {
        intent.setSourceBounds(getViewBounds(v));
    }    try {        if (Utilities.ATLEAST_MARSHMALLOW
                && (item instanceof ShortcutInfo)
                && (item.itemType == Favorites.ITEM_TYPE_SHORTCUT
                 || item.itemType == Favorites.ITEM_TYPE_DEEP_SHORTCUT)
                && !((ShortcutInfo) item).isPromise()) {            // Shortcuts need some special checks due to legacy reasons.
            startShortcutIntentSafely(intent, optsBundle, item);
        } else if (user == null || user.equals(Process.myUserHandle())) {            // Could be launching some bookkeeping activity
            startActivity(intent, optsBundle);
        } else {
            LauncherAppsCompat.getInstance(this).startActivityForProfile(
                    intent.getComponent(), user, intent.getSourceBounds(), optsBundle);
        }        return true;
    } catch (ActivityNotFoundException|SecurityException e) {
        Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
        Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
    }    return false;
}

可以看到最后执行的是startActivity方法,startActivity是Activity的方法,关于app图标点击事件先讲到这。

app启动

从上面桌面的图标点击事件,来到了Activity中的startActivity方法,这里第二个参数 Bundle options我也不知道是空还是非空,但是,没关系,他们最终执行的是startActivityForResult方法

/**
 * @param intent The intent to start.
 * @param options 给Activity的启动设置一些额外的属性
 * See {@link android.content.Context#startActivity(Intent, Bundle)}
 * Context.startActivity(Intent, Bundle)} for more details.
 */@Overridepublic void startActivity(Intent intent, @Nullable Bundle options) {    if (options != null) {
        startActivityForResult(intent, -1, options);
    } else {        // Note we want to go through this call for compatibility with
        // applications that may have overridden the method.
        startActivityForResult(intent, -1);
    }
}public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
        @Nullable Bundle options) {    if (mParent == null) {
        options = transferSpringboardActivityOptions(options);
        Instrumentation.ActivityResult ar =
            mInstrumentation.execStartActivity(                this, mMainThread.getApplicationThread(), mToken, this,
                intent, requestCode, options);        if (ar != null) {
            mMainThread.sendActivityResult(
                mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                ar.getResultData());
        }        if (requestCode >= 0) {            // If this start is requesting a result, we can avoid making
            // the activity visible until the result is received.  Setting
            // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
            // activity hidden during this time, to avoid flickering.
            // This can only be done when a result is requested because
            // that guarantees we will get information back when the
            // activity is finished, no matter what happens to it.
            mStartedActivity = true;
        }

        cancelInputsAndStartExitTransition(options);        // TODO Consider clearing/flushing other event sources and events for child windows.
    } else {        if (options != null) {
            mParent.startActivityFromChild(this, intent, requestCode, options);
        } else {            // Note we want to go through this method for compatibility with
            // existing applications that may have overridden it.
            mParent.startActivityFromChild(this, intent, requestCode);
        }
    }
}

startActivityForResult方法先判断if (mParent == null),这里的mParent是指的Activity的父Activity,一般的Activity中mParent都是空的。所以执行的是往下执行的是execStartActivity方法,注意到这里execStartActivity的第二个参数传入的是mMainThread.getApplicationThread(),mMainThread是ActivityThread,mMainThread.getApplicationThread()获取的是ApplicationThread,这两个东西都很重要,后面回讲到。继续往下看

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    IApplicationThread whoThread = (IApplicationThread) contextThread;
    Uri referrer = target != null ? target.onProvideReferrer() : null;    if (referrer != null) {
        intent.putExtra(Intent.EXTRA_REFERRER, referrer);
    }    if (mActivityMonitors != null) {        synchronized (mSync) {            final int N = mActivityMonitors.size();            for (int i=0; i<N; i++) {                final ActivityMonitor am = mActivityMonitors.get(i);
                ActivityResult result = null;                if (am.ignoreMatchingSpecificIntents()) {
                    result = am.onStartActivity(intent);
                }                if (result != null) {
                    am.mHits++;                    return result;
                } else if (am.match(who, null, intent)) {
                    am.mHits++;                    if (am.isBlocking()) {                        return requestCode >= 0 ? am.getResult() : null;
                    }                    break;
                }
            }
        }
    }    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);        int result = ActivityManager.getService()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);
        checkStartActivityResult(result, intent);
    } catch (RemoteException e) {        throw new RuntimeException("Failure from system", e);
    }    return null;
}

你如果在网上看以前别人的源码ActivityManager.getService().startActivity这个位置是ActivityManagerNative.getDefault().startActivity,在Android API26以后源码发生了改变,不使用以前的那种形式了。不过不影响,原理都是相通的。ActivityManager.getService()拿到的是IActivityManager,IActivityManager是一个aidl文件,现在用到aidl了。ActivityManagerService圈内简称AMS,是一个很重要的系统服务类,存在于system_server进程,ActivityManagerService继承自IActivityManager.Stub,所以上面ActivityManager.getService().startActivity调用的其实是ActivityManagerService里面的方法

/**
 * @hide
 */public static IActivityManager getService() {    return IActivityManagerSingleton.get();
}public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {

根据参数对号入座

@Overridepublic final int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {    return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, bOptions,
            UserHandle.getCallingUserId());
}@Overridepublic final int startActivityAsUser(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId) {
    enforceNotIsolatedCaller("startActivity");
    userId = mUserController.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
            userId, false, ALLOW_FULL_ONLY, "startActivity", null);    // TODO: Switch to user app stacks here.
    return mActivityStarter.startActivityMayWait(caller, -1, callingPackage, intent,
            resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
            profilerInfo, null, null, bOptions, false, userId, null, null,            "startActivityAsUser");
}

下面就是各种调用,我翻过一道道山川,却迷失在你的芦苇荡。这里面有非常多的调用,注意不要跟丢

ActivityStarter.java
-->startActivityMayWait-->startActivityLocked-->startActivity-->startActivity-->startActivityUncheckedActivityStackSupervisor.java
-->resumeFocusedStackTopActivityLockedActivityStack.java
-->resumeTopActivityUncheckedLocked-->resumeTopActivityInnerLockedActivityStackSupervisor.java
-->startSpecificActivityLocked

下面看一下startSpecificActivityLocked这个方法,ProcessRecord app = mService.getProcessRecordLocked(r.processName,r.info.applicationInfo.uid, true);拿到这个app进程纪录。先判断这个进程是不是已经启动过,假如启动过执行realStartActivityLocked,假如没有启动过,执行 mService.startProcessLocked启动新的进程。

void startSpecificActivityLocked(ActivityRecord r,        boolean andResume, boolean checkConfig) {    // Is this activity's application already running?
    ProcessRecord app = mService.getProcessRecordLocked(r.processName,
            r.info.applicationInfo.uid, true);

    r.getStack().setLaunchTime(r);    if (app != null && app.thread != null) {    //假如app进程已经在运行,直接去启动
        try {            if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                    || !"android".equals(r.info.packageName)) {                // Don't add this if it is a platform component that is marked
                // to run in multiple processes, because this is actually
                // part of the framework so doesn't make sense to track as a
                // separate apk in the process.
                app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                        mService.mProcessStats);
            }
            realStartActivityLocked(r, app, andResume, checkConfig);            return;
        } catch (RemoteException e) {
            Slog.w(TAG, "Exception when starting activity "
                    + r.intent.getComponent().flattenToShortString(), e);
        }        // If a dead object exception was thrown -- fall through to
        // restart the application.
    }    //否则开进程启动
    mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,            "activity", r.intent.getComponent(), false, false, true);
}

由于代码太多,关于app应用启动流程分为两篇,第一篇先到这里,下一篇,接着这一篇,分开讲讲

  1. 进程已经启动过,并且还存在没被杀死,那么去打开Activity.

  2. 进程不存在,启动新的进程。

原文出处

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消