Android 7.1 SystemUI近期任务打开应用流程

简介: Android 7.1 SystemUI近期任务打开应用流程

平台


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

如图:

image.png


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


相关文章
|
2月前
|
开发框架 前端开发 Android开发
Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势
本文深入探讨了 Flutter 与原生模块(Android 和 iOS)之间的通信机制,包括方法调用、事件传递等,分析了通信的必要性、主要方式、数据传递、性能优化及错误处理,并通过实际案例展示了其应用效果,展望了未来的发展趋势。这对于实现高效的跨平台移动应用开发具有重要指导意义。
264 4
|
4天前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
1月前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
70 14
|
1月前
|
Java Linux 数据库
探索安卓开发:打造你的第一款应用
在数字时代的浪潮中,每个人都有机会成为创意的实现者。本文将带你走进安卓开发的奇妙世界,通过浅显易懂的语言和实际代码示例,引导你从零开始构建自己的第一款安卓应用。无论你是编程新手还是希望拓展技术的开发者,这篇文章都将为你打开一扇门,让你的创意和技术一起飞扬。
|
1月前
|
搜索推荐 前端开发 测试技术
打造个性化安卓应用:从设计到开发的全面指南
在这个数字时代,拥有一个定制的移动应用不仅是一种趋势,更是个人或企业品牌的重要延伸。本文将引导你通过一系列简单易懂的步骤,从构思你的应用理念开始,直至实现一个功能齐全的安卓应用。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你提供必要的工具和知识,帮助你将创意转化为现实。
|
2月前
|
Linux Android开发 iOS开发
深入探索Android与iOS的多任务处理机制
在移动操作系统领域,Android和iOS各有千秋,尤其在多任务处理上展现出不同的设计理念和技术实现。本文将深入剖析两大平台在后台管理、资源分配及用户体验方面的策略差异,揭示它们如何平衡性能与电池寿命,为用户带来流畅而高效的操作体验。通过对比分析,我们不仅能够更好地理解各自系统的工作机制,还能为开发者优化应用提供参考。
|
2月前
|
算法 Linux 调度
深入探索安卓系统的多任务处理机制
【10月更文挑战第21天】 本文旨在为读者提供一个关于Android系统多任务处理机制的全面解析。我们将从Android操作系统的核心架构出发,探讨其如何管理多个应用程序的同时运行,包括进程调度、内存管理和电量优化等方面。通过深入分析,本文揭示了Android在处理多任务时所面临的挑战以及它如何通过创新的解决方案来提高用户体验和设备性能。
73 1
|
1月前
|
Java Android开发 开发者
探索安卓开发:构建你的第一个“Hello World”应用
在安卓开发的浩瀚海洋中,每个新手都渴望扬帆起航。本文将作为你的指南针,引领你通过创建一个简单的“Hello World”应用,迈出安卓开发的第一步。我们将一起搭建开发环境、了解基本概念,并编写第一行代码。就像印度圣雄甘地所说:“你必须成为你希望在世界上看到的改变。”让我们一起开始这段旅程,成为我们想要见到的开发者吧!
46 0
|
2月前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
2月前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。

热门文章

最新文章