Android Launcher3各启动场景源码分析

文章目录

  • 一、概述
  • 二、开机启动Launcher
    • 2.1、开机启动Launcher流程图
    • 2.2、开机启动流程源码分析
  • 三、短压Home键启动Launcher
    • 3.1、短压Home键启动Launcher流程图
    • 3.2、短压Home键启动Launcher源码分析
  • 四、Launcher异常崩溃后的自启动
    • 4.1、Launcher异常崩溃后的自启动流程图
    • 4.2、Launcher异常崩溃后的自启动源码分析


  团队博客: 汽车电子社区


一、概述

  Launcher3是Android系统提供的默认桌面应用(Launcher),它的源码路径在**“packages/apps/Launcher3/”**。Launcher3的启动场景主要包括:
    1、开机后启动:开机时,android ams服务拉起Launcher。
    2、按键启动:比如短压home键,android wms中的PhoneWindowManager拉起Launcher。
    3、异常崩溃后启动:Launcher异常崩溃后,android ams再次拉起Launcher。
  针对这三种情况,分析一下Aosp源码如何实现。

二、开机启动Launcher

2.1、开机启动Launcher流程图

在这里插入图片描述

2.2、开机启动流程源码分析

  Launcher的开机启动由Android的AMS服务完成。AMS在SystemReady阶段会调用startHomeOnAllDisplays函数。Android支持多Display(虚拟Display或者由硬件上报的实际Display),多Display情况下一般Launcher会针对不同Display做不同的效果。

// frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public void systemReady(final Runnable goingCallback, TimingsTraceLog traceLog) {
    traceLog.traceBegin("PhaseActivityManagerReady");
    synchronized(this) {
        if (mSystemReady) {
            // If we're done calling all the receivers, run the next "boot phase" passed in
            // by the SystemServer
            if (goingCallback != null) {
                goingCallback.run();
            }
            return;
        }

        mLocalDeviceIdleController
                = LocalServices.getService(DeviceIdleController.LocalService.class);
        mActivityTaskManager.onSystemReady();
        // Make sure we have the current profile info, since it is needed for security checks.
        mUserController.onSystemReady();
        mAppOpsService.systemReady();
        mSystemReady = true;
    }

    try {
        sTheRealBuildSerial = IDeviceIdentifiersPolicyService.Stub.asInterface(
                ServiceManager.getService(Context.DEVICE_IDENTIFIERS_SERVICE))
                .getSerial();
    } catch (RemoteException e) {}

    ArrayList<ProcessRecord> procsToKill = null;
    synchronized(mPidsSelfLocked) {
        for (int i=mPidsSelfLocked.size()-1; i>=0; i--) {
            ProcessRecord proc = mPidsSelfLocked.valueAt(i);
            if (!isAllowedWhileBooting(proc.info)){
                if (procsToKill == null) {
                    procsToKill = new ArrayList<ProcessRecord>();
                }
                procsToKill.add(proc);
            }
        }
    }

    synchronized(this) {
        if (procsToKill != null) {
            for (int i=procsToKill.size()-1; i>=0; i--) {
                ProcessRecord proc = procsToKill.get(i);
                Slog.i(TAG, "Removing system update proc: " + proc);
                mProcessList.removeProcessLocked(proc, true, false, "system update done");
            }
        }

        // Now that we have cleaned up any update processes, we
        // are ready to start launching real processes and know that
        // we won't trample on them any more.
        mProcessesReady = true;
    }

    Slog.i(TAG, "System now ready");
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_AMS_READY, SystemClock.uptimeMillis());

    mAtmInternal.updateTopComponentForFactoryTest();
    mAtmInternal.getLaunchObserverRegistry().registerLaunchObserver(mActivityLaunchObserver);

    watchDeviceProvisioning(mContext);

    retrieveSettings();
    mUgmInternal.onSystemReady();

    final PowerManagerInternal pmi = LocalServices.getService(PowerManagerInternal.class);
    if (pmi != null) {
        pmi.registerLowPowerModeObserver(ServiceType.FORCE_BACKGROUND_CHECK,
                state -> updateForceBackgroundCheck(state.batterySaverEnabled));
        updateForceBackgroundCheck(
                pmi.getLowPowerState(ServiceType.FORCE_BACKGROUND_CHECK).batterySaverEnabled);
    } else {
        Slog.wtf(TAG, "PowerManagerInternal not found.");
    }

    if (goingCallback != null) goingCallback.run();
    // Check the current user here as a user can be started inside goingCallback.run() from
    // other system services.
    final int currentUserId = mUserController.getCurrentUserId();
    Slog.i(TAG, "Current user:" + currentUserId);
    if (currentUserId != UserHandle.USER_SYSTEM && !mUserController.isSystemUserStarted()) {
        // User other than system user has started. Make sure that system user is already
        // started before switching user.
        throw new RuntimeException("System user not started while current user is:"
                + currentUserId);
    }
    traceLog.traceBegin("ActivityManagerStartApps");
    mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_RUNNING_START,
            Integer.toString(currentUserId), currentUserId);
    mBatteryStatsService.noteEvent(BatteryStats.HistoryItem.EVENT_USER_FOREGROUND_START,
            Integer.toString(currentUserId), currentUserId);

    // On Automotive, at this point the system user has already been started and unlocked,
    // and some of the tasks we do here have already been done. So skip those in that case.
    // TODO(b/132262830): this workdound shouldn't be necessary once we move the
    // headless-user start logic to UserManager-land
    final boolean bootingSystemUser = currentUserId == UserHandle.USER_SYSTEM;

    if (bootingSystemUser) {
        mSystemServiceManager.startUser(currentUserId);
    }

    synchronized (this) {
        // Only start up encryption-aware persistent apps; once user is
        // unlocked we'll come back around and start unaware apps
        startPersistentApps(PackageManager.MATCH_DIRECT_BOOT_AWARE);

        // Start up initial activity.
        mBooting = true;
        // Enable home activity for system user, so that the system can always boot. We don't
        // do this when the system user is not setup since the setup wizard should be the one
        // to handle home activity in this case.
        if (UserManager.isSplitSystemUser() &&
                Settings.Secure.getInt(mContext.getContentResolver(),
                     Settings.Secure.USER_SETUP_COMPLETE, 0) != 0) {
            ComponentName cName = new ComponentName(mContext, SystemUserHomeActivity.class);
            try {
                AppGlobals.getPackageManager().setComponentEnabledSetting(cName,
                        PackageManager.COMPONENT_ENABLED_STATE_ENABLED, 0,
                        UserHandle.USER_SYSTEM);
            } catch (RemoteException e) {
                throw e.rethrowAsRuntimeException();
            }
        }

        if (bootingSystemUser) {
            // 启动Home,也就是Launcher
            mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
        }

        mAtmInternal.showSystemReadyErrorDialogsIfNeeded();

        if (bootingSystemUser) {
            final int callingUid = Binder.getCallingUid();
            final int callingPid = Binder.getCallingPid();
            long ident = Binder.clearCallingIdentity();
            try {
                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,
                        null, null, 0, null, null, null, OP_NONE,
                        null, false, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
                        currentUserId);
                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,
                        null, new IIntentReceiver.Stub() {
                            @Override
                            public void performReceive(Intent intent, int resultCode, String data,
                                    Bundle extras, boolean ordered, boolean sticky, int sendingUser)
                                    throws RemoteException {
                            }
                        }, 0, null, null,
                        new String[] {INTERACT_ACROSS_USERS}, OP_NONE,
                        null, true, false, MY_PID, SYSTEM_UID, callingUid, callingPid,
                        UserHandle.USER_ALL);
            } catch (Throwable t) {
                Slog.wtf(TAG, "Failed sending first user broadcasts", t);
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        } else {
            Slog.i(TAG, "Not sending multi-user broadcasts for non-system user "
                    + currentUserId);
        }
        mAtmInternal.resumeTopActivities(false /* scheduleIdle */);
        if (bootingSystemUser) {
            mUserController.sendUserSwitchBroadcasts(-1, currentUserId);
        }

        BinderInternal.nSetBinderProxyCountWatermarks(BINDER_PROXY_HIGH_WATERMARK,
                BINDER_PROXY_LOW_WATERMARK);
        BinderInternal.nSetBinderProxyCountEnabled(true);
        BinderInternal.setBinderProxyCountCallback(
                new BinderInternal.BinderProxyLimitListener() {
                    @Override
                    public void onLimitReached(int uid) {
                        Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid "
                                + Process.myUid());
                        BinderProxy.dumpProxyDebugInfo();
                        if (uid == Process.SYSTEM_UID) {
                            Slog.i(TAG, "Skipping kill (uid is SYSTEM)");
                        } else {
                            killUid(UserHandle.getAppId(uid), UserHandle.getUserId(uid),
                                    "Too many Binders sent to SYSTEM");
                        }
                    }
                }, mHandler);

        traceLog.traceEnd(); // ActivityManagerStartApps
        traceLog.traceEnd(); // PhaseActivityManagerReady
    }
}

  调用ActivityTaskManagerInternal类型的接口startHomeOnAllDisplays,这个接口ActivityTaskManagerService.java文件中实现。

// android/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
final class LocalService extends ActivityTaskManagerInternal {
    @Override
    public boolean startHomeOnAllDisplays(int userId, String reason) {
        synchronized (mGlobalLock) {
            // 这个对象是RootActivityContainer类型
            return mRootActivityContainer.startHomeOnAllDisplays(userId, reason);
        }
    }
}

  接下来调用RootActivityContainer的接口startHomeOnAllDisplays,第二个参数reson的值为“systemReady”。

// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
boolean startHomeOnAllDisplays(int userId, String reason) {
    boolean homeStarted = false;
    for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
        final int displayId = mActivityDisplays.get(i).mDisplayId;
        homeStarted |= startHomeOnDisplay(userId, reason, displayId);
    }
    return homeStarted;
}

  遍历mActivityDisplays对于所有处于Active状态的Display调用startHomeOnDisplay,在每个Display上都启动Home(Launcher)。

boolean startHomeOnDisplay(int userId, String reason, int displayId) {
    // allowInstrumenting :false
    // fromHomeKey : false
    return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */,
            false /* fromHomeKey */);
}
    
boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
        boolean fromHomeKey) {
    // 如果DisplayID是非法的,使用当前处于顶层焦点的 Display
    // Fallback to top focused display if the displayId is invalid.
    if (displayId == INVALID_DISPLAY) {
        displayId = getTopDisplayFocusedStack().mDisplayId;
    }
    
    // 构建一个Intent
    Intent homeIntent = null;
    ActivityInfo aInfo = null;
    if (displayId == DEFAULT_DISPLAY) {
        // 如果DisplayID是默认的Display(一般是主屏)
        // 调用ActivityTaskManagerService的getHomeIntent,拿到用来启动Home的Intent
        homeIntent = mService.getHomeIntent();
        // 查找当前系统里包含 android.intent.category.HOME的Activity。
        // 择优选择使用哪个Activity
        aInfo = resolveHomeActivity(userId, homeIntent);
    } else if (shouldPlaceSecondaryHomeOnDisplay(displayId)) {
        // 如果不是默认Display屏幕
        Pair<ActivityInfo, Intent> info = resolveSecondaryHomeActivity(userId, displayId);
        aInfo = info.first;
        homeIntent = info.second;
    }
    if (aInfo == null || homeIntent == null) {
        return false;
    }
    
    // 判断是否运行启动Home
    if (!canStartHomeOnDisplay(aInfo, displayId, allowInstrumenting)) {
        return false;
    }

    // 更新Home Intent
    // Updates the home component of the intent.
    homeIntent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
    homeIntent.setFlags(homeIntent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
    // 如果是从HomeKey启动的,添加额外参数、
    // Updates the extra information of the intent.
    if (fromHomeKey) {
        homeIntent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, true);
    }
    // Update the reason for ANR debugging to verify if the user activity is the one that
    // actually launched.
    final String myReason = reason + ":" + userId + ":" + UserHandle.getUserId(
            aInfo.applicationInfo.uid) + ":" + displayId;
    // 启动Home
    mService.getActivityStartController().startHomeActivity(homeIntent, aInfo, myReason,
            displayId);
    return true;
}

  startHomeOnDisplay函数中查询当前系统中包含 android.intent.category.HOME信息的Activity(resolveHomeActivity),如果找到了多个Activity则选择高优先级的。根据查找的Activity信息构建Intent,使用ActivityTaskManagerService的ActivityStartController启动Home对应的Activity。
  getHomeIntent函数在ActivityTaskManagerService中实现。

// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
Intent getHomeIntent() {
        // String mTopAction = Intent.ACTION_MAIN;
    Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
    intent.setComponent(mTopComponent);
    intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
            // 非FactoryTest模式下走这里。
            // android.intent.category.HOME
        intent.addCategory(Intent.CATEGORY_HOME);
    }
    return intent;
}

  resolveHomeActivity函数用于查找包含android.intent.category.HOME信息的Activity。实现上是通过PMS完成的。

// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
/**
 * This resolves the home activity info.
 * @return the home activity info if any.
 */
@VisibleForTesting
ActivityInfo resolveHomeActivity(int userId, Intent homeIntent) {
        final ComponentName comp = homeIntent.getComponent();
        try {
                if (comp != null) {
                        // Factory test.
                } else {
                        final String resolvedType =
                                        homeIntent.resolveTypeIfNeeded(mService.mContext.getContentResolver());
                        // 调用PMS查找信息
                        final ResolveInfo info = AppGlobals.getPackageManager()
                                        .resolveIntent(homeIntent, resolvedType, flags, userId);
                        if (info != null) {
                                aInfo = info.activityInfo;
                        }
                }
        } catch (RemoteException e) {
                // ignore
        }

        aInfo = new ActivityInfo(aInfo);
        aInfo.applicationInfo = mService.getAppInfoForUser(aInfo.applicationInfo, userId);
        return aInfo;
}

  当找到所需信息后,调用ActivityStartController的startHomeActivity启动Home。该接口与AMS的startActivity和startActivityAsUser实现上基本原理一样,都是通过Intent启动Activity(fork一个进程出来)。

// frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason, int displayId) {
        final ActivityOptions options = ActivityOptions.makeBasic();
        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN);
        if (!ActivityRecord.isResolverActivity(aInfo.name)) {
                // The resolver activity shouldn't be put in home stack because when the foreground is
                // standard type activity, the resolver activity should be put on the top of current
                // foreground instead of bring home stack to front.
                options.setLaunchActivityType(ACTIVITY_TYPE_HOME);
        }
        options.setLaunchDisplayId(displayId);
        // 启动Activity
        mLastHomeActivityStartResult = obtainStarter(intent, "startHomeActivity: " + reason)
                        .setOutActivity(tmpOutRecord)
                        .setCallingUid(0)
                        .setActivityInfo(aInfo)
                        .setActivityOptions(options.toBundle())
                        .execute();
        mLastHomeActivityStartRecord = tmpOutRecord[0];
        final ActivityDisplay display =
                        mService.mRootActivityContainer.getActivityDisplay(displayId);
        final ActivityStack homeStack = display != null ? display.getHomeStack() : null;
        if (homeStack != null && homeStack.mInResumeTopActivity) {
                // If we are in resume section already, home activity will be initialized, but not
                // resumed (to avoid recursive resume) and will stay that way until something pokes it
                // again. We need to schedule another resume.
                mSupervisor.scheduleResumeTopActivities();
        }
}

三、短压Home键启动Launcher

3.1、短压Home键启动Launcher流程图

在这里插入图片描述

3.2、短压Home键启动Launcher源码分析

  短压与长按区分,点一下Home键就属于短压。短压Home键后,Android会启动Home。

// frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

// Native callback.
private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
        // native层的 inputservice,通过这个接口上报回调
    return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
// frameworks/base/services/core/java/com/android/server/wm/InputManagerCallback.java
/**
Provides an opportunity for the window manager policy to process a key before
ordinary dispatch.
*/
@Override
public long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
         WindowState windowState = mService.windowForClientLocked(null, focus, false);
         return mService.mPolicy.interceptKeyBeforeDispatching(windowState, event, policyFlags);
}

  mService是WindowManagerService类型,WindowManagerService中的mPolicy是PhoneWindowManager。PhoneWindowManager作为WMS的Policy配置文件,专门用来处理与UI行为有关的事件。PhoneWindowManager会拦截HomeKey事件进行相应处理后选择不再派发Home(PhoneWindowManager处理完就不需要其他人处理了),或者继续派发HomeKey给当前焦点View。

// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
private long interceptKeyBeforeDispatchingInner(WindowState win, KeyEvent event,
            int policyFlags) {
    final boolean keyguardOn = keyguardOn();
    final int keyCode = event.getKeyCode();
    final int repeatCount = event.getRepeatCount();
    final int metaState = event.getMetaState();
    final int flags = event.getFlags();
    final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
    final boolean canceled = event.isCanceled();
    final int displayId = event.getDisplayId();

    // First we always handle the home key here, so applications
    // can never break it, although if keyguard is on, we do let
    // it handle it, because that gives us the correct 5 second
    // timeout.
    if (keyCode == KeyEvent.KEYCODE_HOME) {
            // 处理HomeKey
            DisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);
            if (handler == null) {
                    handler = new DisplayHomeButtonHandler(displayId);
                    mDisplayHomeButtonHandlers.put(displayId, handler);
            }
            return handler.handleHomeButton(win, event);
    } else if (keyCode == KeyEvent.KEYCODE_MENU) {
    // 省略
    }
}

/** A handler to handle home keys per display */
private class DisplayHomeButtonHandler {
    int handleHomeButton(WindowState win, KeyEvent event) {
        final boolean keyguardOn = keyguardOn();
        final int repeatCount = event.getRepeatCount();
        final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
        final boolean canceled = event.isCanceled();
        // If we have released the home key, and didn't do anything else
        // while it was pressed, then it is time to go home!
        if (!down) {
                // 省略
                // Post to main thread to avoid blocking input pipeline.
                // 处理短压Home
                mHandler.post(() -> handleShortPressOnHome(mDisplayId));
                return -1;
        }
        // 省略
        return -1;
    }
}

private void handleShortPressOnHome(int displayId) {
    // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
    final HdmiControl hdmiControl = getHdmiControl();
    if (hdmiControl != null) {
            hdmiControl.turnOnTv();
    }

    // If there's a dream running then use home to escape the dream
    // but don't actually go home.
    if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {
            mDreamManagerInternal.stopDream(false /*immediate*/);
            return;
    }

    // 启动Home
    // Go home!
    launchHomeFromHotKey(displayId);
}

  PhoneWindowManager针对每个Display创建一个DisplayHomeButtonHandler ,通过它处理HomeKey。在启动Home期间如果开始了dream模式(类似于屏保),会先退出dream。最后调用launchHomeFromHotKey来启动Home,后续流程基本上与Home开机启动一致了。

四、Launcher异常崩溃后的自启动

4.1、Launcher异常崩溃后的自启动流程图

在这里插入图片描述

4.2、Launcher异常崩溃后的自启动源码分析

  Launcher意外退出(比如崩溃了)时,会触发AMS的forceStopPackage。AMS会再次将Home拉起。

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override
public void forceStopPackage(final String packageName, int userId) {
    try {
        IPackageManager pm = AppGlobals.getPackageManager();
        synchronized(this) {
            int[] users = userId == UserHandle.USER_ALL
                            ? mUserController.getUsers() : new int[] { userId };
            for (int user : users) {
                if (mUserController.isUserRunning(user, 0)) {
                    // 对每个运行的User,停掉 packageName对应的应用
                    forceStopPackageLocked(packageName, pkgUid, "from pid " + callingPid);
                    finishForceStopPackageLocked(packageName, pkgUid);
                }
            }
        }
    } finally {
        Binder.restoreCallingIdentity(callingId);
    }
}

  forceStopPackageLocked函数中,会先Kill掉应用对应的进程。然后 resume focused app,在resume的过程中会拉起Home。

//frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@GuardedBy("this")
final boolean forceStopPackageLocked(String packageName, int appId,
        boolean callerWillRestart, boolean purgeCache, boolean doit,
        boolean evenPersistent, boolean uninstalling, int userId, String reason) {
    // kill进程
    boolean didSomething = mProcessList.killPackageProcessesLocked(packageName, appId, userId,
    ProcessList.INVALID_ADJ, callerWillRestart, true /* allowRestart /, doit,
*                        evenPersistent, true /* setRemoved */,
    packageName == null ? ("stop user " + userId) : ("stop " + packageName));

    if (doit) {
        if (purgeCache && packageName != null) {
            AttributeCache ac = AttributeCache.instance();
            if (ac != null) {
                    ac.removePackage(packageName);
            }
        }
        if (mBooted) {
            // resume focused app
            // 通过这个函数重新拉起Home
            mAtmInternal.resumeTopActivities(true /* scheduleIdle */);
        }
    }

    return didSomething;
}

  调用ActivityTaskManagerServiced的resumeTopActivities函数。在 Home崩溃的情况下,调用这个函数,可以保证Home重新被拉起(这个函数最终会调用到RootActivityContainer 的resumeHomeActivity函数。感兴趣的可以继续顺着代码往下看)

//frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@Override
public void resumeTopActivities(boolean scheduleIdle) {
    synchronized (mGlobalLock) {
        mRootActivityContainer.resumeFocusedStacksTopActivities();
        if (scheduleIdle) {
            mStackSupervisor.scheduleIdleLocked();
        }
    }
}
// frameworks/base/services/core/java/com/android/server/wm/RootActivityContainer.java
boolean resumeFocusedStacksTopActivities(
            ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
    for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
        // 省略
        if (!resumedOnDisplay) {
            // In cases when there are no valid activities (e.g. device just booted or launcher
            // crashed) it's possible that nothing was resumed on a display. Requesting resume
            // of top activity in focused stack explicitly will make sure that at least home
            // activity is started and resumed, and no recursion occurs.
            final ActivityStack focusedStack = display.getFocusedStack();
            if (focusedStack != null) {
                    focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
            }
        }
    }

    return result;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/332347.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言总结十三:程序环境和预处理详细总结

了解程序的运行环境可以让我们更加清楚的程序的底层运行的每一个步骤和过程&#xff0c;做到心中有数&#xff0c;预处理阶段是在预编译阶段完成&#xff0c;掌握常用的预处理命令语法&#xff0c;可以让我们正确的使用预处理命令&#xff0c;从而提高代码的开发能力和阅读别人…

在线扒站网PHP源码-在线扒站工具网站源码

源码介绍 这是一款在线的网站模板下载程序&#xff0c;也就是我们常说的扒站工具&#xff0c;利用它我们可以很轻松的将别人的网站模板样式下载下来&#xff0c;这样就可以大大提高我们编写前端的速度了&#xff01;注&#xff1a;扒取的任何站点不得用于商业、违法用途&#…

如何在CentOS 7 中基于OpenSSL 3.0 搭建Python 3.0 环境

1、OpenSSL 1.1 原因 [rootlocalhost ~]# openssl version OpenSSL 1.0.2k-fips 26 Jan 2017 [rootlocalhost ~]#通过执行openssl version可知Linux系统已经安装了OpenSSL&#xff0c;但该版本较低&#xff1b;Python 3 要求 OpenSSL版本不能低于1.1.1&#xff0c;否则安装P…

【02】mapbox js api加载arcgis切片服务

需求&#xff1a; 第三方的mapbox js api加载arcgis切片服务&#xff0c;同时叠加在mapbox自带底图上 效果图&#xff1a; 形如这种地址去加载&#xff1a; http://zjq2022.gis.com:8080/demo/loadmapbox.html arcgis切片服务参考链接思路&#xff1a;【01】mapbox js api加…

【Copula】最可能场景详解

基于Copula联合分布的最可能场景详解 最可能场景&#xff08;The most-likely scenario&#xff09;实例探讨参考 最可能场景&#xff08;The most-likely scenario&#xff09; 相应英文介绍原理介绍如下&#xff1a;&#xff08;出自论文J2020-Drought hazard transferabilit…

RTC讲解

RTC&#xff08;Real Time Clock&#xff09;实时时钟 RTC实时时钟本质上是一个独立的定时器。RTC模块拥有一组连续计数的32位无符号计数器&#xff0c;在相应软件配置下&#xff0c;可提供时钟日历的功能。修改计数器的值可以重新设置系统当前的时间和日期。 RTC模块和时钟配…

基于XG24-EK2703A的BLE HID蓝牙键盘+鼠标复合设备功能开发(BLE+HID+FreeRTOS+Gecko SDK)

目录 项目介绍硬件介绍项目设计开发环境及工程参考总体流程图硬件基本配置应用初始化按键中断回调定时器回调按键响应任务蓝牙事件回调BLE HIDReport Map及报文键盘设备鼠标设备复合设备 发送字符串上/下滚动 功能展示项目总结 &#x1f449; 【Funpack3-1】基于XG24-EK2703A的…

网络端口映射和端口转发的区别和联系

目 录 一、端口映射技术 1.1 原理 1.2 应用场景 1、远程访问 2、游戏主机 3、文件共享 4、监控视频共享 二、端口转发技术 2.1 原理 2.2 应用场景 1、网络负载均衡 2、网络安全 3、网络代理 三、端口映射和转发的实现方法 3.1 路由器配置 3.2 网络防火墙 …

C Primer Plus 第6版 编程练习 chapter 16

文章目录 1. 第1题1.1 题目描述1.2 编程源码1.3 结果显示 2. 第2题2.1 题目描述2.2 编程源码2.3 结果显示 3. 第3题3.1 题目描述3.2 编程源码3.3 结果显示 4. 第4题4.1 题目描述4.2 编程源码4.3 结果显示 5. 第5题5.1 题目描述5.2 编程源码5.3 结果显示 6. 第6题6.1 题目描述6.…

linux|操作系统|centos7物理机安装网卡驱动8188gu(内核升级,firmware固件,USB设备管理,module管理)

前言&#xff1a; 目前服务器领域centos7基本是主流的操作系统&#xff0c;而linux相对于Windows来说&#xff0c;软硬件方面的支持是差很多的&#xff0c;在硬件方面来说&#xff0c;以一个免驱的网卡为例&#xff0c;window xp可能不会自动识别到&#xff0c;但Windows10基本…

问题:Feem无法发送信息OR无法连接(手机端无法发给电脑端)

目录 前言 问题分析 资源、链接 其他问题 前言 需要在小米手机、华为平板、Dell电脑之间传输文件&#xff0c;试过安装破解的华为电脑管家、小米的MIUI文件传输等&#xff0c;均无果。&#xff08;小米“远程管理”ftp传输倒是可以&#xff0c;但速度太慢了&#xff0c;且…

【Ant Design of Vue】Modal.confirm无法关闭的bug

一、问题 在使用 Ant Design Vue 的 Modal.confirm 确认框时&#xff0c;出现了点击取消和确定后 Modal.confirm 确认框无法关闭的问题 二、代码 代码完全是 copy 的官网的代码&#xff0c;但是 copy 到本地后就会出现上述问题 <template><a-button click"sho…

鸿蒙开发(五)鸿蒙UI开发概览

从用户角度来讲&#xff0c;一个软件拥有好看的UI&#xff0c;那是锦上添花的事情。再精确的算法&#xff0c;再厉害的策略&#xff0c;最终都得通过UI展现给用户并且跟用户交互。那么&#xff0c;本篇一起学习下鸿蒙开发UI基础知识&#xff0c;认识下各种基本控件以及使用方式…

UE5 C++ 学习笔记 UBT UHT 和 一些头文件

总结一些似懂非懂的知识点&#xff0c;从头慢慢梳理。 任何一个项目都有创建这些三个.cs。 这个是蓝图转C 这个是本身就是C项目,应该就是多了一个GameModeBase类 Build.cs包含了每个模块的信息&#xff0c;表明了这个项目用到了哪一些模块。该文件里的using UnrealBuilTool 是…

为数字取证和 OSINT 调查定制用户体验

Tsurugi Linux 是一个高度定制的开源发行版&#xff0c;专注于支持 DFIR 调查。 该项目主要侧重于实时取证分析、事后分析和数字证据获取。用户还可以执行恶意软件分析、OSINT(开源情报)和计算机视觉活动。 我们精心打造了用户友好的体验&#xff0c;按照逻辑取证分析顺序组织…

【JS逆向学习】36kr登陆逆向案例(webpack)

在开始讲解实际案例之前&#xff0c;大家先了解下webpack的相关知识 WebPack打包 webpack是一个基于模块化的打包&#xff08;构建&#xff09;工具, 它把一切都视作模块 webpack数组形式&#xff0c;通过下标取值 !function(e) {var t {};// 加载器 所有的模块都是从这个…

Ps:使用钢笔工具快速精准抠图的技巧

众所周知&#xff0c;钢笔工具是 Photoshop 中最精准的、适用于硬边缘&#xff08;清晰轮廓&#xff09;对象的抠图工具。但是&#xff0c;如果从头开始一个锚点一个锚点的勾勒&#xff0c;既费时又费眼。 我们可以先用选区工具或选区命令做一个基础选区&#xff0c;然后将选区…

IPv6自动隧道---6to4中继

6to4中继 普通IPv6网络需要与6to4网络通过IPv4网络互通,这可以通过6to4中继路由器方式实现。所谓6to4中继,就是通过6to4隧道转发的IPv6报文的目的地址不是6to4地址,但转发的下一跳是6to4地址,该下一跳为路由器我们称之为6to4中继。隧道的IPv4目的地址依然从下一跳的6to4地…

力扣每日一练(24-1-19)

我的思路&#xff1a; def maxProfit(self, prices: List[int]) -> int:if not len(prices):return 0 max_profit 0for p in range(len(prices) - 1):prices[p] prices[p 1] - prices[p]prices[p] max(0, prices[p])max_profit sum(prices[:-1])return max_profit 其实…

百度云网盘下载速度如何提升到正常速度

引入问题 我们在下载代码学习资料的时候大多数都是百度云网盘&#xff0c;但是限速&#xff01;下载的十分的慢&#xff0c;有什么办法能让我们不开通会员就能享受正常速度呢&#xff1f; 当然有&#xff01; 解决百度云网盘下载速度过慢&#xff0c;提高到正常速度 点击右…