首先看一下官方的注释(基于Android12.0.0_r3版本的代码分析):
View that can display a task
是一个可以展示Task的视图。那有什么用呢?简单来说就是跨进程来展示UI。就比如我有两个进程,进程A和进程B。我想在进程A中启动进程B的Activity,这时候就可以使用TaskView来实现这个功能。
接着再来说TaskView是什么?
TaskView 是SurfaceView的子类,并且实现了
SurfaceHolder.Callback, ShellTaskOrganizer.TaskListener, ViewTreeObserver.OnComputeInternalInsetsListener这三个接口
首先说一下SurfaceView,这个用过的人都有些印象,比如摄像头的录制展示可以用SurfaceView来展示或者是一些需要即时更新的UI可以通过SurfaceView来进行展示。换大多数人的说法就是挖了个洞,也确实是,如果要是保证效率,没必要做那么多没用的操作,有个捷径何尝不是个好事情~。
接着是 SurfaceHolder.Callback,注册了这个Callback,就能够接受到Surface改变的回调,经典的三个方法surfaceCreated
,surfaceChanged
和surfaceDestroyed
,一般是和SurfaceView配合使用的。
说到这,可以把TaskView理解为一个SurfaceView。
接着说一下 ShellTaskOrganizer.TaskListener
这个接口,这个就属于是WindowManager/Shell
这一套的相关接口了。在系统的任务更改的时候,就能在这个监听中收到回调。在这里,TaskView是这个接口的实现类,那就说明,ShellTaskOrganizer
对TaskView进行管理,当有消息来时,就会去通知给TaskView。
最后说一下ViewTreeObserver.OnComputeInternalInsetsListener
这个接口,这个接口在源码中搜索了下,发现了一个熟悉的类VoiceInteractionSession
,接触到的项目对于语音助手的使用就是基于这个类的相关交互的,往下翻,发现熟悉的onShow
,onHide
方法等等。。。有点跑题了。布局完成的时候,如果实现了这个接口,就能够onComputeInternalInsets
接受到回调,语音助手收到后回去进行自己的UI绘制。
创建还不好说么,直接new一个,开句玩笑。具体如何创建,还得看谷歌源生代码是怎么使用的。
/** Interface to create TaskView. */
@ExternalThread
public interface TaskViewFactory {
/** Creates an {@link TaskView} */
void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate);
}
搜索TaskView的构造方法,发现TaskViewFactory
,听名字就是一个TaskView的创建类,查看注释,更加确定就是通过这个接口来创建TaskView了。终归是一个接口,去看下具体实现。
/** Factory controller which can create {@link TaskView} */
public class TaskViewFactoryController {
private final ShellTaskOrganizer mTaskOrganizer;
private final ShellExecutor mShellExecutor;
private final TaskViewFactory mImpl = new TaskViewFactoryImpl();
public TaskViewFactoryController(ShellTaskOrganizer taskOrganizer,
ShellExecutor shellExecutor) {
mTaskOrganizer = taskOrganizer;
mShellExecutor = shellExecutor;
}
public TaskViewFactory asTaskViewFactory() {
return mImpl;
}
/** Creates an {@link TaskView} */
public void create(@UiContext Context context, Executor executor, Consumer<TaskView> onCreate) {
TaskView taskView = new TaskView(context, mTaskOrganizer);
executor.execute(() -> {
onCreate.accept(taskView);
});
}
private class TaskViewFactoryImpl implements TaskViewFactory {
@ExternalThread
public void create(@UiContext Context context,
Executor executor, Consumer<TaskView> onCreate) {
mShellExecutor.execute(() -> {
TaskViewFactoryController.this.create(context, executor, onCreate);
});
}
}
}
可以看到TaskViewFactoryController
的内部类TaskViewFactoryImpl
实现了TaskViewFactory
这个接口,并且交给TaskViewFactoryController
来进行TaskView的创建。到这里大概就知道了,创建TaskView时需要创建一个ShellTaskOrganizer
和一个ShellExecutor
对象,接着再来创建一个TaskView对象。这里留个猜测,等到TaskView启动的Activity的onCreate()执行后,才去创建TaskView对象。
再来看一下TaskView的构造函数干了些什么事情。
public TaskView(Context context, ShellTaskOrganizer organizer) {
super(context, null, 0, 0, true /* disableBackgroundLayer */);
mTaskOrganizer = organizer;
mShellExecutor = organizer.getExecutor();
setUseAlpha();
getHolder().addCallback(this);
// 用来打印警告的log,以及告诉关闭的方法名。要成对使用
mGuard.open("release");
}
首先mTaskOrganizer
和mShellExecutor
在上面可以看到,都是在构造的时候需要的参数,接着setUseAlpha()
方法是用来设置透明度的。下面getHolder().addCallback(this)
是向其父类SurfaceView
注册SurfaceHolder.Callback
监听的。
到此为止,TaskView的创建暂时来个中场休息~
对于TaskView的使用,去启动界面使用startActivity
方法即可,向其传递一个PendingIntent对象,就能够让其在TaskView中去启动一个其他的Activity。但是这里先说一下另一个比较重要的点,startActivity
随后再说。这里说一下要进行注册的监听,也就是TaskView.Listener
,该监听是需要TaskView的setListener
方法去进行注册的。
为什么要先说这个的?首先把状态的监听注册好,能够很好的去监听我们通过TaskView展示其他进程的Activity的状态,至少能够知道执行到了TaskView的哪一步,如果出现问题方便我们去很快的定位。
先看看这个监听都包含了哪些方法:
/** Callback for listening task state. */
public interface Listener {
/** Called when the container is ready for launching activities. */
default void onInitialized() {}
/** Called when the container can no longer launch activities. */
default void onReleased() {}
/** Called when a task is created inside the container. */
default void onTaskCreated(int taskId, ComponentName name) {}
/** Called when a task visibility changes. */
default void onTaskVisibilityChanged(int taskId, boolean visible) {}
/** Called when a task is about to be removed from the stack inside the container. */
default void onTaskRemovalStarted(int taskId) {}
/** Called when a task is created inside the container. */
default void onBackPressedOnTaskRoot(int taskId) {}
}
看一下官方的注释,这个监听就是用来监听Task的状态的。
onInitialized
这个方法说明当前的容器已经准备好去启动Activity了,当TaskView
收到了surfaceCreated
【SurfaceHolder.Callback】的回调之后,便会去给注册的监听发送onInitialized
的消息。
onTaskCreated
这个方法在Task已经被创建并且放入容器时,回去收到这个回调,当TaskView收到onTaskAppeared
【ShellTaskOrganizer.TaskListener】的回调后,会去告知Listener的onTaskCreated
onTaskVisibilityChanged
这个方法在调用updateTaskVisibility()
的方法时回去通知监听的可见性变化,也即surfaceCreated
,surfaceDestroyed
和onTaskAppeared
时会去更新可见性变化。
onReleased
这个方法在容器不在启动Activity的时候进行调用,一般我们在使用完后可以手动去release()
。
onTaskRemovalStarted
这个方法在Task在栈中即将移除时调用,即在onTaskVanished
【ShellTaskOrganizer.TaskListener】回调后去执行。
onBackPressedOnTaskRoot
当Task被创建并且放入容器时进行调用,即在onBackPressedOnTaskRoot
【ShellTaskOrganizer.TaskListener】回调后去执行。
上面提到的 【ShellTaskOrganizer.TaskListener】 都是在ShellTaskOrganizer这个类分发下来的,之后也会再继续整理整体的流程。而实现SurfaceHolder.Callback
的三个方法,是在其父类另一个父类SurfaceView
进行可见性调整的时候,会向其下发对应的回调。
这里先简单记录一下和onTaskCreated()
方法的相关流程。
TaskView.Listener(onTaskCreated) -> ShellTaskOrganizer.TaskListener(onTaskAppeared) -> TaskOrganizer(onTaskAppeared) -> ITaskOrganizer(onTaskAppeared) -> 【跨进程】TaskOrganizerController.TaskOrganizerCallbacks(onTaskAppeared) -> TaskOrganizerController.TaskOrganizerState(onTaskAppeared) -> TaskOrganizerController(onTaskAppeared) -> Task(sendTaskAppeared) -> …
先分析到这吧~ 有点小困,所谓的TaskView,现在终于算是到了Task了,到我现在分析的这个位置,Task类持有一个ActivityTaskManagerService对象,ActivityTaskManagerService又会持有一个 TaskOrganizerController对象,随后去进行跨进程通信。
TaskView归根到底,还是去起一个Activity,只不过展示的位置变了。先看下源码:
/**
* Launch a new activity.
*
* @param pendingIntent Intent used to launch an activity.
* @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
* @param options options for the activity.
* @param launchBounds the bounds (window size and position) that the activity should be
* launched in, in pixels and in screen coordinates.
*/
public void startActivity(@NonNull PendingIntent pendingIntent, @Nullable Intent fillInIntent,
@NonNull ActivityOptions options, @Nullable Rect launchBounds) {
prepareActivityOptions(options, launchBounds);
try {
pendingIntent.send(mContext, 0 /* code */, fillInIntent,
null /* onFinished */, null /* handler */, null /* requiredPermission */,
options.toBundle());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
需要传入四个参数,分别是一个PendingIntent,Intent,ActivityOptions和Rect。第一个参数是即将启动的Activity的Intent信息,第二个参数是Intent的补充信息,第三个参数是Activity的一些参数,最后一个参数是展示的大小以及位置。
而prepareActivityOptions
会将Rect数据存储到ActivityOptions当中去并且向ShellTaskOrganizer
注册自身【ShellTaskOrganizer.TaskListener】。