平台
RK3288 + Android 7.1
概述
从Launcher中启动应用和从Recent列表中启动应用是否是一致的?
有以下几种情况:
应用未启动过
|-launcher启动:
2017-01-01 21:12:56.490 system_process I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.calculator2/.Calculator bnds=[960,678][1235,834] (has extras)} from uid 10036 on display 0 2017-01-01 21:12:56.512 system_process I/ActivityManager: Start proc 1719:com.android.calculator2/u0a32 for activity com.android.calculator2/.Calculator 2017-01-01 21:12:57.070 system_process I/ActivityManager: Displayed com.android.calculator2/.Calculator: +569ms |-Recent启动: 2017-01-01 21:15:11.067 system_process I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10300000 cmp=com.android.calculator2/.Calculator} from uid 10036 on display 0 2017-01-01 21:15:11.108 system_process I/ActivityManager: Start proc 1394:com.android.calculator2/u0a32 for activity com.android.calculator2/.Calculator 2017-01-01 21:15:11.578 system_process I/ActivityManager: Displayed com.android.calculator2/.Calculator: +492ms
应用已启动, 但使用HOME键切换到了后台
|-Launcher
2017-01-01 21:21:25.143 system_process I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.calculator2/.Calculator bnds=[960,678][1235,834] (has extras)} from uid 10036 on display 0 |-Recent 2017-01-01 21:22:17.313 system_process E/ActivityManager: applyOptionsLocked: Unknown animationType=0
应用已启动, 但已通过BACK键退出.
|-Launcher
2017-01-01 21:17:57.950 system_process I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.android.calculator2/.Calculator bnds=[960,678][1235,834] (has extras)} from uid 10036 on display 0 2017-01-01 21:17:58.284 system_process I/ActivityManager: Displayed com.android.calculator2/.Calculator: +325ms |-Recent 2017-01-01 21:18:57.879 system_process E/ActivityManager: applyOptionsLocked: Unknown animationType=0
LOG并不详尽, 仅供参考;
从上面中可以看出启动过程中除了第一种情况, 后面两种都存在差区别, 暂时没有深入研究的打算.
分析
先看layout布局, AndroidStudio > Tools > Layout Inspector
如图:
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@Override public void onClick(final View v) { if (mIsDisabledInSafeMode) { Context context = getContext(); String msg = context.getString(R.string.recents_launch_disabled_message, mTask.title); if (mDisabledAppToast != null) { mDisabledAppToast.cancel(); } mDisabledAppToast = Toast.makeText(context, msg, Toast.LENGTH_SHORT); mDisabledAppToast.show(); return; } boolean screenPinningRequested = false; if (v == mActionButtonView) { // Reset the translation of the action button before we animate it out mActionButtonView.setTranslationZ(0f); screenPinningRequested = true; } EventBus.getDefault().send(new LaunchTaskEvent(this, mTask, null, INVALID_STACK_ID, screenPinningRequested)); MetricsLogger.action(v.getContext(), MetricsEvent.ACTION_OVERVIEW_SELECT, mTask.key.getComponent().toString()); }
//LaunchTaskEvent, 保存任务信息. |-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/events/activity/LaunchTaskEvent.java public class LaunchTaskEvent extends EventBus.Event { public final TaskView taskView; public final Task task; public final Rect targetTaskBounds; public final int targetTaskStack; public final boolean screenPinningRequested; public LaunchTaskEvent(TaskView taskView, Task task, Rect targetTaskBounds, int targetTaskStack, boolean screenPinningRequested) { this.taskView = taskView; this.task = task; this.targetTaskBounds = targetTaskBounds; this.targetTaskStack = targetTaskStack; this.screenPinningRequested = screenPinningRequested; } }
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
public class EventBus extends BroadcastReceiver { private static final String TAG = "EventBus"; private static final boolean DEBUG_TRACE_ALL = false; //... /** * An event super class that allows us to track internal event state across subscriber * invocations. * * Events should not be edited by subscribers. */ public static class Event implements Cloneable { // Indicates that this event's dispatch should be traced and logged to logcat boolean trace; // Indicates that this event must be posted on the EventBus's looper thread before invocation boolean requiresPost; // Not currently exposed, allows a subscriber to cancel further dispatch of this event boolean cancelled; // Only accessible from derived events protected Event() {} /** * Called by the EventBus prior to dispatching this event to any subscriber of this event. */ void onPreDispatch() { // Do nothing } /** * Called by the EventBus after dispatching this event to every subscriber of this event. */ void onPostDispatch() { // Do nothing } @Override protected Object clone() throws CloneNotSupportedException { Event evt = (Event) super.clone(); // When cloning an event, reset the cancelled-dispatch state evt.cancelled = false; return evt; } } //--------------真正干活的:--------------------- //... //--------------------单例, 没什么特别的------------- /** * @return the default event bus for the application's main thread. */ public static EventBus getDefault() { if (sDefaultBus == null) synchronized (sLock) { if (sDefaultBus == null) { if (DEBUG_TRACE_ALL) { logWithPid("New EventBus"); } sDefaultBus = new EventBus(Looper.getMainLooper()); } } return sDefaultBus; } /** * Sends an event to the subscribers of the given event type immediately. This can only be * called from the same thread as the EventBus's looper thread (for the default EventBus, this * is the main application thread). */ public void send(Event event) { // Fail immediately if we are being called from the non-main thread long callingThreadId = Thread.currentThread().getId(); if (callingThreadId != mHandler.getLooper().getThread().getId()) { throw new RuntimeException("Can not send() a message from a non-main thread."); } if (DEBUG_TRACE_ALL) { logWithPid("send(" + event.getClass().getSimpleName() + ")"); } // Reset the event's cancelled state event.requiresPost = false; event.cancelled = false; queueEvent(event); } /** * Adds a new message. */ private void queueEvent(final Event event) { ArrayList<EventHandler> eventHandlers = mEventTypeMap.get(event.getClass()); if (eventHandlers == null) { return; } // Prepare this event boolean hasPostedEvent = false; event.onPreDispatch(); // We need to clone the list in case a subscriber unregisters itself during traversal // TODO: Investigate whether we can skip the object creation here eventHandlers = (ArrayList<EventHandler>) eventHandlers.clone(); int eventHandlerCount = eventHandlers.size(); for (int i = 0; i < eventHandlerCount; i++) { final EventHandler eventHandler = eventHandlers.get(i); if (eventHandler.subscriber.getReference() != null) { if (event.requiresPost) { mHandler.post(new Runnable() { @Override public void run() { processEvent(eventHandler, event); } }); hasPostedEvent = true; } else { processEvent(eventHandler, event); } } } // Clean up after this event, deferring until all subscribers have been called if (hasPostedEvent) { mHandler.post(new Runnable() { @Override public void run() { event.onPostDispatch(); } }); } else { event.onPostDispatch(); } } /** * Processes and dispatches the given event to the given event handler, on the thread of whoever * calls this method. */ private void processEvent(final EventHandler eventHandler, final Event event) { // Skip if the event was already cancelled if (event.cancelled) { if (event.trace || DEBUG_TRACE_ALL) { logWithPid("Event dispatch cancelled"); } return; } try { if (event.trace || DEBUG_TRACE_ALL) { logWithPid(" -> " + eventHandler.toString()); } Object sub = eventHandler.subscriber.getReference(); if (sub != null) { long t1 = 0; if (DEBUG_TRACE_ALL) { t1 = SystemClock.currentTimeMicro(); } eventHandler.method.invoke(sub, event); if (DEBUG_TRACE_ALL) { long duration = (SystemClock.currentTimeMicro() - t1); mCallDurationMicros += duration; mCallCount++; logWithPid(eventHandler.method.toString() + " duration: " + duration + " microseconds, avg: " + (mCallDurationMicros / mCallCount)); } } else { Log.e(TAG, "Failed to deliver event to null subscriber"); } } catch (IllegalAccessException e) { Log.e(TAG, "Failed to invoke method", e.getCause()); } catch (InvocationTargetException e) { throw new RuntimeException(e.getCause()); } }
eventHandler.method.invoke(sub, event);
首先, 从mEventTypeMap拿出eventHandlers:
ArrayList eventHandlers = mEventTypeMap.get(event.getClass());
再调用eventHandlers所有的元素传入processEvent.
mEventTypeMap
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/events/EventBus.java
/** * Registers a new subscriber. */ private void registerSubscriber(Object subscriber, int priority, MutableBoolean hasInterprocessEventsChangedOut) { // Fail immediately if we are being called from the non-main thread //... if (subscriberMethods != null) { if (DEBUG_TRACE_ALL) { logWithPid("Subscriber class type already registered"); } // If we've parsed this subscriber type before, just add to the set for all the known // events for (EventHandlerMethod method : subscriberMethods) { ArrayList<EventHandler> eventTypeHandlers = mEventTypeMap.get(method.eventType); eventTypeHandlers.add(new EventHandler(sub, method, priority)); sortEventHandlersByPriority(eventTypeHandlers); } mSubscribers.add(sub); return; } else { if (DEBUG_TRACE_ALL) { logWithPid("Subscriber class type requires registration"); } // If we are parsing this type from scratch, ensure we add it to the subscriber type // map, and pull out he handler methods below subscriberMethods = new ArrayList<>(); mSubscriberTypeMap.put(subscriberType, subscriberMethods); mSubscribers.add(sub); } //...接着后面有一系列反射赋值, 很重要.... //主要是找出对应的这个函数名. //private static final String METHOD_PREFIX = "onBusEvent"; }
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@Override protected void onAttachedToWindow() { EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1); EventBus.getDefault().register(mTouchHandler, RecentsActivity.EVENT_BUS_PRIORITY + 2); super.onAttachedToWindow(); } public final void onBusEvent(LaunchTaskEvent event) { new Exception("ALog.onBusEvent").printStackTrace(); mLastTaskLaunchedWasFreeform = event.task.isFreeformTask(); mTransitionHelper.launchTaskFromRecents(getStack(), event.task, mTaskStackView, event.taskView, event.screenPinningRequested, event.targetTaskBounds, event.targetTaskStack); }
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
/** * Launches the specified {@link Task}. */ public void launchTaskFromRecents(final TaskStack stack, @Nullable final Task task, final TaskStackView stackView, final TaskView taskView, final boolean screenPinningRequested, final Rect bounds, final int destinationStack) { new Exception("ALog.launchTaskFromRecents").printStackTrace(); final ActivityOptions opts = ActivityOptions.makeBasic(); if (bounds != null) { opts.setLaunchBounds(bounds.isEmpty() ? null : bounds); } final ActivityOptions.OnAnimationStartedListener animStartedListener; final IAppTransitionAnimationSpecsFuture transitionFuture; if (taskView != null) { transitionFuture = getAppTransitionFuture(new AnimationSpecComposer() { @Override public List<AppTransitionAnimationSpec> composeSpecs() { return composeAnimationSpecs(task, stackView, destinationStack); } }); animStartedListener = new ActivityOptions.OnAnimationStartedListener() { @Override public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); stackView.cancelAllTaskViewAnimations(); if (screenPinningRequested) { // Request screen pinning after the animation runs mStartScreenPinningRunnable.taskId = task.key.id; mHandler.postDelayed(mStartScreenPinningRunnable, 350); } } }; } else { // This is only the case if the task is not on screen (scrolled offscreen for example) transitionFuture = null; animStartedListener = new ActivityOptions.OnAnimationStartedListener() { @Override public void onAnimationStarted() { // If we are launching into another task, cancel the previous task's // window transition EventBus.getDefault().send(new CancelEnterRecentsWindowAnimationEvent(task)); EventBus.getDefault().send(new ExitRecentsWindowFirstAnimationFrameEvent()); stackView.cancelAllTaskViewAnimations(); } }; } if (taskView == null) { // If there is no task view, then we do not need to worry about animating out occluding // task views, and we can launch immediately startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener); } else { LaunchTaskStartedEvent launchStartedEvent = new LaunchTaskStartedEvent(taskView, screenPinningRequested); if (task.group != null && !task.group.isFrontMostTask(task)) { launchStartedEvent.addPostAnimationCallback(new Runnable() { @Override public void run() { startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener); } }); EventBus.getDefault().send(launchStartedEvent); } else { EventBus.getDefault().send(launchStartedEvent); startTaskActivity(stack, task, taskView, opts, transitionFuture, animStartedListener); } } Recents.getSystemServices().sendCloseSystemWindows( BaseStatusBar.SYSTEM_DIALOG_REASON_HOME_KEY); } /** * Starts the activity for the launch task. * * @param taskView this is the {@link TaskView} that we are launching from. This can be null if * we are toggling recents and the launch-to task is now offscreen. */ private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskView taskView, ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture, final ActivityOptions.OnAnimationStartedListener animStartedListener) { SystemServicesProxy ssp = Recents.getSystemServices(); if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts)) { // Keep track of the index of the task launch int taskIndexFromFront = 0; int taskIndex = stack.indexOfStackTask(task); if (taskIndex > -1) { taskIndexFromFront = stack.getTaskCount() - taskIndex - 1; } EventBus.getDefault().send(new LaunchTaskSucceededEvent(taskIndexFromFront)); } else { // Dismiss the task if we fail to launch it if (taskView != null) { taskView.dismissTask(); } // Keep track of failed launches EventBus.getDefault().send(new LaunchTaskFailedEvent()); } if (transitionFuture != null) { ssp.overridePendingAppTransitionMultiThumbFuture(transitionFuture, wrapStartedListener(animStartedListener), true /* scaleUp */); } }
|-- frameworks/base/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
/** Starts an activity from recents. */ public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, ActivityOptions options) { if (mIam != null) { try { if (taskKey.stackId == DOCKED_STACK_ID) { // We show non-visible docked tasks in Recents, but we always want to launch // them in the fullscreen stack. if (options == null) { options = ActivityOptions.makeBasic(); } options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID); } mIam.startActivityFromRecents( taskKey.id, options == null ? null : options.toBundle()); return true; } catch (Exception e) { Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); } } return false; }
|-- frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
@Override public final int startActivityFromRecents(int taskId, Bundle bOptions) { if (checkCallingPermission(START_TASKS_FROM_RECENTS) != PackageManager.PERMISSION_GRANTED) { String msg = "Permission Denial: startActivityFromRecents called without " + START_TASKS_FROM_RECENTS; Slog.w(TAG, msg); throw new SecurityException(msg); } final long origId = Binder.clearCallingIdentity(); try { synchronized (this) { return mStackSupervisor.startActivityFromRecentsInner(taskId, bOptions); } } finally { Binder.restoreCallingIdentity(origId); } }
剩下的就看ActivityManagerService的了…