平台
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的了…