Android App應用啟動流程(一)

zybing發表於2021-09-09

本篇原始碼分析基於Android8.0  API 26
關於Android原始碼檢視:
你可以去看這裡: 線上檢視,也可以在Android Studio裡面關聯原始碼,也可以完全下載自己去編譯

Launcher中的App列表使用的RecyclerView

//public class AllAppsRecyclerView extends BaseRecyclerView//public abstract class BaseRecyclerView extends RecyclerView//AllAppsRecyclerView最終繼承自RecyclerView

這個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建構函式傳入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圖示點選事件先講到這。

原文出處

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/855/viewspace-2801645/,如需轉載,請註明出處,否則將追究法律責任。

相關文章