其他分享
首页 > 其他分享> > Android 11 截图流程梳理

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