Android 11 截图流程梳理
作者:互联网
Android 原生截屏方式为,power键和音量下键的组合键,那么想要分析截屏流程就从按键的处理流程开始往下进行分析
1. PhoneWindowManager -- Android按键分发
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
//音量键按下
if (down) {
// Any activity on the vol down button stops the ringer toggle shortcut
cancelPendingRingerToggleChordAction();
if (interactive && !mScreenshotChordVolumeDownKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
//该值在后面的判断中会需要
mScreenshotChordVolumeDownKeyTriggered = true;
mScreenshotChordVolumeDownKeyTime = event.getDownTime();
mScreenshotChordVolumeDownKeyConsumed = false;
cancelPendingPowerKeyAction();
interceptScreenshotChord();
interceptAccessibilityShortcutChord();
}
} else {
mScreenshotChordVolumeDownKeyTriggered = false;
cancelPendingScreenshotChordAction();
cancelPendingAccessibilityShortcutAction();
}
}
break;
}
case KeyEvent.KEYCODE_POWER: {
EventLogTags.writeInterceptPower(
KeyEvent.actionToString(event.getAction()),
mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
// Any activity on the power button stops the accessibility shortcut
cancelPendingAccessibilityShortcutAction();
result &= ~ACTION_PASS_TO_USER;
isWakeKey = false; // wake-up will be handled separately
if (down) {
//power键按下事件处理
interceptPowerKeyDown(event, interactive);
} else {
interceptPowerKeyUp(event, interactive, canceled);
}
break;
}
}
在PhoneWindowManager中处理power键的事件及音量下键的事件, 截图时Power键为按下状态,所以去看power键的按下事件处理
private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
......
//锁屏电源键状态并且检测屏幕截图
if (interactive && !mScreenshotChordPowerKeyTriggered
&& (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
//power键按下的标志
mScreenshotChordPowerKeyTriggered = true;
//获取 Power 键的触发时间
mScreenshotChordPowerKeyTime = event.getDownTime();
//处理屏幕截图事件
interceptScreenshotChord();
interceptRingerToggleChord();
}
......
}
private void interceptScreenshotChord() {
/*
mScreenshotChordEnabled 其值为mContext.getResources().getBoolean(com.android.internal.R.bool.config_enableScreenshotChord);
mScreenshotChordVolumeDownKeyTriggered 音量下键按下时值为true
mScreenshotChordPowerKeyTriggered 电源键按下时值为true
mA11yShortcutChordVolumeUpKeyTriggered 音量上键抬起时为false , 按下时为true
*/
if (mScreenshotChordEnabled
&& mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
&& !mA11yShortcutChordVolumeUpKeyTriggered) {
//获取当前时间
final long now = SystemClock.uptimeMillis();
//当前时间小于 音量下键按下时间 + 150ms
//当前时间小于 power键按下时间 + 150ms
if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
&& now <= mScreenshotChordPowerKeyTime
+ SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
mScreenshotChordVolumeDownKeyConsumed = true;
cancelPendingPowerKeyAction();
//使用全屏截图--type
mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
//执行mScreenshotRunnable
mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
}
}
}
继续查看ScreenshotRunnable, 此时会一步步向下调用,最终到SystemUI
private class ScreenshotRunnable implements Runnable {
private int mScreenshotType = TAKE_SCREENSHOT_FULLSCREEN;
private int mScreenshotSource = SCREENSHOT_KEY_OTHER;
public void setScreenshotType(int screenshotType) {
mScreenshotType = screenshotType;
}
public void setScreenshotSource(int screenshotSource) {
mScreenshotSource = screenshotSource;
}
@Override
public void run() {
mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);
}
}
//向下调用至DisplayPolicy.java
public void takeScreenshot(int screenshotType, int source) {
if (mScreenshotHelper != null) {
mScreenshotHelper.takeScreenshot(screenshotType,
getStatusBar() != null && getStatusBar().isVisibleLw(),
getNavigationBar() != null && getNavigationBar().isVisibleLw(),
source, mHandler, null /* completionConsumer */);
}
}
//继续向下至 ScreenshotHelper.java
public void takeScreenshot(final int screenshotType, final boolean hasStatus,
final boolean hasNav, int source, @NonNull Handler handler,
@Nullable Consumer<Uri> completionConsumer) {
ScreenshotRequest screenshotRequest = new ScreenshotRequest(source, hasStatus, hasNav);
takeScreenshot(screenshotType, SCREENSHOT_TIMEOUT_MS, handler, screenshotRequest,
completionConsumer);
}
//到了 Binder调用环节, 此为客户端, 服务端为SystemUI中的 TakeScreenshotService
private void takeScreenshot(final int screenshotType, long timeoutMs, @NonNull Handler handler,
ScreenshotRequest screenshotRequest, @Nullable Consumer<Uri> completionConsumer) {
synchronized (mScreenshotLock) {
final Runnable mScreenshotTimeout = () -> {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
Log.e(TAG, "Timed out before getting screenshot capture response");
resetConnection();
notifyScreenshotError();
}
}
if (completionConsumer != null) {
completionConsumer.accept(null);
}
};
Message msg = Message.obtain(null, screenshotType, screenshotRequest);
Handler h = new Handler(handler.getLooper()) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SCREENSHOT_MSG_URI:
if (completionConsumer != null) {
completionConsumer.accept((Uri) msg.obj);
}
handler.removeCallbacks(mScreenshotTimeout);
break;
case SCREENSHOT_MSG_PROCESS_COMPLETE:
synchronized (mScreenshotLock) {
resetConnection();
}
break;
}
}
};
msg.replyTo = new Messenger(h);
if (mScreenshotConnection == null || mScreenshotService == null) {
//一个标准的Service连接
//config_screenshotServiceComponent == com.android.systemui/com.android.systemui.screenshot.TakeScreenshotService
final ComponentName serviceComponent = ComponentName.unflattenFromString(
mContext.getResources().getString(
com.android.internal.R.string.config_screenshotServiceComponent));
final Intent serviceIntent = new Intent();
serviceIntent.setComponent(serviceComponent);
ServiceConnection conn = new ServiceConnection() {
@Override
//当Service连接成功之后
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != this) {
return;
}
mScreenshotService = service;
Messenger messenger = new Messenger(mScreenshotService);
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) {
completionConsumer.accept(null);
}
}
}
}
@Override
//当Service断开连接时
public void onServiceDisconnected(ComponentName name) {
synchronized (mScreenshotLock) {
if (mScreenshotConnection != null) {
resetConnection();
// only log an error if we're still within the timeout period
if (handler.hasCallbacks(mScreenshotTimeout)) {
handler.removeCallbacks(mScreenshotTimeout);
notifyScreenshotError();
}
}
}
}
};
//bindService
if (mContext.bindServiceAsUser(serviceIntent, conn,
Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE,
UserHandle.CURRENT)) {
mScreenshotConnection = conn;
handler.postDelayed(mScreenshotTimeout, timeoutMs);
}
} else {
//如果已经连接则直接发送Message
Messenger messenger = new Messenger(mScreenshotService);
try {
messenger.send(msg);
} catch (RemoteException e) {
Log.e(TAG, "Couldn't take screenshot: " + e);
if (completionConsumer != null) {
completionConsumer.accept(null);
}
}
handler.postDelayed(mScreenshotTimeout, timeoutMs);
}
}
}
客户端通过向服务端发送message来将截屏任务交给service,由service处理后面的操作
TakeScreenshotService.java
private Handler mHandler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(Message msg) {
//获取客户端传的Messenger对象
final Messenger callback = msg.replyTo;
Consumer<Uri> uriConsumer = uri -> {
Message reply = Message.obtain(null, SCREENSHOT_MSG_URI, uri);
try {
//Messenger 双向通信,在服务端用远程客户端的 Messenger 对象给客户端发送信息
callback.send(reply);
} catch (RemoteException e) {
}
};
Runnable onComplete = () -> {
Message reply = Message.obtain(null, SCREENSHOT_MSG_PROCESS_COMPLETE);
try {
callback.send(reply);
} catch (RemoteException e) {
}
};
//判断用户的设备是否为解锁状态
//如果用户的存储被锁定,我们没有地方存储截图,所以跳过它,而不是显示一个误导性的动画和错误通知。
if (!mUserManager.isUserUnlocked()) {
Log.w(TAG, "Skipping screenshot because storage is locked!");
post(() -> uriConsumer.accept(null));
post(onComplete);
return;
}
ScreenshotHelper.ScreenshotRequest screenshotRequest =
(ScreenshotHelper.ScreenshotRequest) msg.obj;
mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));
switch (msg.what) {
case WindowManager.TAKE_SCREENSHOT_FULLSCREEN://全屏截图
//我们在PhoneWindowManager传入的type为全屏截图,所以需要执行全屏截图流程
mScreenshot.takeScreenshotFullscreen(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION://区域截图
mScreenshot.takeScreenshotPartial(uriConsumer, onComplete);
break;
case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
Bitmap screenshot = BitmapUtil.bundleToHardwareBitmap(
screenshotRequest.getBitmapBundle());
Rect screenBounds = screenshotRequest.getBoundsInScreen();
Insets insets = screenshotRequest.getInsets();
int taskId = screenshotRequest.getTaskId();
int userId = screenshotRequest.getUserId();
ComponentName topComponent = screenshotRequest.getTopComponent();
mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
taskId, userId, topComponent, uriConsumer, onComplete);
break;
default:
Log.d(TAG, "Invalid screenshot option: " + msg.what);
}
}
};
TakeScreenshotService调用GlobalScreenshot.java的takeScreenshotFullscreen
//GlobalScreenshot.java 目前Google GitHub上的代码和MTK AndroidR 基线代码稍有差异,总体流程一致
void takeScreenshotFullscreen(Consumer<Uri> finisher, Runnable onComplete) {
mOnCompleteRunnable = onComplete;
mDisplay.getRealMetrics(mDisplayMetrics);
takeScreenshotInternal(
finisher,
new Rect(0, 0, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
}
private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {
// copy the input Rect, since SurfaceControl.screenshot can mutate it
Rect screenRect = new Rect(crop);
int rot = mDisplay.getRotation();
int width = crop.width();
int height = crop.height();
//此处调用SurfaceControl.screenshot方法进行截屏, 此方法返回一个Bitmap
saveScreenshot(SurfaceControl.screenshot(crop, width, height, rot), finisher, screenRect,
Insets.NONE, true);
}
稍有差异,但截图流程一致
private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
Insets screenInsets, boolean showFlash) {
if (mScreenshotLayout.isAttachedToWindow()) {
// if we didn't already dismiss for another reason
if (mDismissAnimation == null || !mDismissAnimation.isRunning()) {
mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED);
}
//此方法会清除上一次的截图信息--连续截图行为
dismissScreenshot("new screenshot requested", true);
}
mScreenBitmap = screenshot;
if (mScreenBitmap == null) {
//如果没有Bitmap则报告错误信息
mNotificationsController.notifyScreenshotError(
R.string.screenshot_failed_to_capture_text);
finisher.accept(null);
mOnCompleteRunnable.run();
return;
}
if (!isUserSetupComplete()) {
//用户设置尚未完成,不应该向用户展示 分享和编辑 , 只显示一个Toast并保存图片
saveScreenshotAndToast(finisher);
return;
}
mScreenBitmap.setHasAlpha(false);
mScreenBitmap.prepareToDraw();
onConfigChanged(mContext.getResources().getConfiguration());
if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
mDismissAnimation.cancel();
}
//获取焦点
setWindowFocusable(true);
//开始截图后的动画
startAnimation(finisher, screenRect, screenInsets, showFlash);
}
private void startAnimation(final Consumer<Uri> finisher, Rect screenRect, Insets screenInsets,
boolean showFlash) {
mScreenshotHandler.post(() -> {
// mScreenshotLayout是截屏的缩略图的父View
if (!mScreenshotLayout.isAttachedToWindow()) {
mWindowManager.addView(mScreenshotLayout, mWindowLayoutParams);
}
//动画相关的View
mScreenshotAnimatedView.setImageDrawable(
createScreenDrawable(mScreenBitmap, screenInsets));
setAnimatedViewSize(screenRect.width(), screenRect.height());
//动画开始执行的时候才显示
mScreenshotAnimatedView.setVisibility(View.GONE);
//缩略图显示的View,将native层返回的Bitmap加载到此View上
mScreenshotPreview.setImageDrawable(createScreenDrawable(mScreenBitmap, screenInsets));
mScreenshotPreview.setVisibility(View.INVISIBLE);
mScreenshotHandler.post(() -> {
mScreenshotLayout.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
//创建动画
mScreenshotAnimation =
createScreenshotDropInAnimation(screenRect, showFlash);
//保存截图
saveScreenshotInWorkerThread(finisher, new ActionsReadyListener() {
@Override
void onActionsReady(SavedImageData imageData) {
showUiOnActionsReady(imageData);
}
});
//播放相机拍照时的声音--截图使用此声音
mCameraSound.play(MediaActionSound.SHUTTER_CLICK);
mScreenshotPreview.setLayerType(View.LAYER_TYPE_HARDWARE, null);
mScreenshotPreview.buildLayer();
//开始执行动画
mScreenshotAnimation.start();
});
});
}
//创建一个新的工作线程,并将截图保存到媒体存储中。
private void saveScreenshotInWorkerThread(
Consumer<Uri> finisher, @Nullable ActionsReadyListener actionsReadyListener) {
SaveImageInBackgroundData data = new SaveImageInBackgroundData();
data.image = mScreenBitmap; //native层返回的Bitmap
data.finisher = finisher;
data.mActionsReadyListener = actionsReadyListener;
if (mSaveInBgTask != null) {
// just log success/failure for the pre-existing screenshot
mSaveInBgTask.setActionsReadyListener(new ActionsReadyListener() {
@Override
void onActionsReady(SavedImageData imageData) {
logSuccessOnActionsReady(imageData);
}
});
}
//截图的一些存储信息在SaveImageInBackgroundTask中构建
mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data);
mSaveInBgTask.execute();
}
到此截图流程完毕,可以查看下截图的View的xml文件
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/global_screenshot_frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/global_screenshot_actions_background"
android:layout_height="@dimen/screenshot_bg_protection_height"
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:alpha="0.0"
android:src="@drawable/screenshot_actions_background_protection"/>
<!--截屏动画相关的View -->
<ImageView
android:id="@+id/global_screenshot_animated_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|start"
android:visibility="gone"
android:elevation="@dimen/screenshot_preview_elevation"
android:background="@drawable/screenshot_rounded_corners" />
<ImageView
android:id="@+id/global_screenshot_flash"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:elevation="@dimen/screenshot_preview_elevation"
android:src="@android:color/white"/>
<com.android.systemui.screenshot.ScreenshotSelectorView
android:id="@+id/global_screenshot_selector"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:pointerIcon="crosshair"/>
<!-- 此处包含了一个layout, 而缩略图的View就在此layout中 -->
<include layout="@layout/global_screenshot_static"/>
<!-- 截屏右上角的关闭缩略图按钮 -->
<FrameLayout
android:id="@+id/global_screenshot_dismiss_button"
android:layout_width="@dimen/screenshot_dismiss_button_tappable_size"
android:layout_height="@dimen/screenshot_dismiss_button_tappable_size"
android:elevation="7dp"
android:visibility="gone"
android:contentDescription="@string/screenshot_dismiss_ui_description">
<ImageView
android:id="@+id/global_screenshot_dismiss_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="@dimen/screenshot_dismiss_button_margin"
android:src="@drawable/screenshot_cancel"/>
</FrameLayout>
</FrameLayout>
标签:11,截图,screenshot,int,void,new,Android,null 来源: https://blog.csdn.net/a_hang_szz/article/details/120563575