截屏流程

370 阅读2分钟

1. 监听按键手势

[frameworks](http://aospxref.com/android-12.0.0_r3/xref/frameworks/)/[base](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/)/[services](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/)/[core](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/)/[java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/)/[com](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/)/[android](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/)/[server](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/)/[policy](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/policy/)/[PhoneWindowManager.java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java)
private void initKeyCombinationRules() {  
          mKeyCombinationManager = new KeyCombinationManager();  
          final boolean screenshotChordEnabled = mContext.getResources().getBoolean(
          com.android.internal.R.bool.config_enableScreenshotChord);  
   
          // 监听音量下键与power键是否同时按下  
          if (screenshotChordEnabled) {  
              mKeyCombinationManager.addRule(  
                      new TwoKeysCombinationRule(KEYCODE_VOLUME_DOWN, KEYCODE_POWER) {  
                          @Override  
                          void execute() {  
                              mPowerKeyHandled = true;  
                              interceptScreenshotChord();  
                          }  
                          @Override  
                          void cancel() {  
                              cancelPendingScreenshotChordAction();  
                          }  
                      });  
          }  
          ........  
}  
  
.....  
**private void interceptScreenshotChord() {  
    mHandler.removeCallbacks(mScreenshotRunnable);  
    //** **使用全屏截图  
    mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);  
    mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);  
    mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());  
}**

2. 当截屏动作被监听到后,调用 PhoneWindowManager.java 中的内部类 ScreenshotRunnable

[frameworks](http://aospxref.com/android-12.0.0_r3/xref/frameworks/)/[base](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/)/[services](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/)/[core](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/)/[java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/)/[com](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/)/[android](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/)/[server](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/)/[policy](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/policy/)/[PhoneWindowManager.java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java)  
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;  
          }  
  
          // 向下调用至DisplayPolicy.java  
          @Override  
          public void run() {  
              mDefaultDisplayPolicy.takeScreenshot(mScreenshotType, mScreenshotSource);  
          }  
}

3. 向下调用至 DisplayPolicy.java

[frameworks](http://aospxref.com/android-12.0.0_r3/xref/frameworks/)/[base](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/)/[services](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/)/[core](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/)/[java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/)/[com](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/)/[android](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/)/[server](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/)/[wm](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/wm/)/[DisplayPolicy.java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java)  
 public void takeScreenshot(int screenshotType, int source) {  
          if (mScreenshotHelper != null) {  
              mScreenshotHelper.takeScreenshot(screenshotType,  
                      getStatusBar() != null && getStatusBar().isVisible(),  
                      getNavigationBar() != null && getNavigationBar().isVisible(),  
                      source, mHandler, null /* completionConsumer */);  
          }  
}

4. 继续向下至 ScreenshotHelper.java

[frameworks](http://aospxref.com/android-12.0.0_r3/xref/frameworks/)/[base](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/)/[core](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/)/[java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/)/[com](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/com/)/[android](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/com/android/)/[internal](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/com/android/internal/)/[util](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/com/android/internal/util/)/[ScreenshotHelper.java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/core/java/com/android/internal/util/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);  
}

5. 发送截屏指令

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();  
                     }  
                 }

6.TakeScreenshotService.java 接收 msg 信息并开始处理截屏

[frameworks](http://aospxref.com/android-12.0.0_r3/xref/frameworks/)/[base](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/)/[packages](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/)/[SystemUI](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/)/[src](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/)/[com](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/)/[android](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/)/[systemui](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/)/[screenshot](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/)/[TakeScreenshotService.java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java)  
@MainThread  
    private boolean handleMessage(Message msg) {  
        final Messenger replyTo = msg.replyTo;  
        final Consumer<Uri> uriConsumer = (uri) -> reportUri(replyTo, uri);  
        RequestCallback requestCallback = new RequestCallbackImpl(replyTo);  
  
        // 判断用户设备是否解锁,如未解锁,跳出截屏  
        if (!mUserManager.isUserUnlocked()) {  
            Log.w(TAG, "Skipping screenshot because storage is locked!");  
            mNotificationsController.notifyScreenshotError(  
                    R.string.screenshot_failed_to_save_user_locked_text);  
            requestCallback.reportError();  
            return true;  
        }  
  
        ScreenshotHelper.ScreenshotRequest screenshotRequest =  
                (ScreenshotHelper.ScreenshotRequest) msg.obj;  
  
        mUiEventLogger.log(ScreenshotEvent.getScreenshotSource(screenshotRequest.getSource()));  
  
        switch (msg.what) {  
            // 全屏截屏  
            case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:  
                if (DEBUG_SERVICE) {  
                    Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");  
                }  
                mScreenshot.takeScreenshotFullscreen(uriConsumer, requestCallback);  
                break;  
            case WindowManager.TAKE_SCREENSHOT_SELECTED_REGION:  
                if (DEBUG_SERVICE) {  
                    Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_SELECTED_REGION");  
                }  
                mScreenshot.takeScreenshotPartial(uriConsumer, requestCallback);  
                break;  
            case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:  
                if (DEBUG_SERVICE) {  
                    Log.d(TAG, "handleMessage: 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();  
  
                if (screenshot == null) {  
                    Log.e(TAG, "Got null bitmap from screenshot message");  
                    mNotificationsController.notifyScreenshotError(  
                            R.string.screenshot_failed_to_capture_text);  
                    requestCallback.reportError();  
                } else {  
                    mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,  
                            taskId, userId, topComponent, uriConsumer, requestCallback);  
                }  
                break;  
            default:  
                Log.w(TAG, "Invalid screenshot option: " + msg.what);  
                return false;  
        }  
        return true;  
    };

7.TakeScreenshotService.java 调用ScreenshotController.java来完成截屏

[frameworks](http://aospxref.com/android-12.0.0_r3/xref/frameworks/)/[base](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/)/[packages](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/)/[SystemUI](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/)/[src](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/)/[com](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/)/[android](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/)/[systemui](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/)/[screenshot](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/)/[ScreenshotController.java](http://aospxref.com/android-12.0.0_r3/xref/frameworks/base/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java)  
void takeScreenshotFullscreen(Consumer<Uri> finisher, RequestCallback requestCallback) {  
        mCurrentRequestCallback = requestCallback;  
        DisplayMetrics displayMetrics = new DisplayMetrics();  
        getDefaultDisplay().getRealMetrics(displayMetrics);  
        takeScreenshotInternal(  
                finisher,  
                new Rect(0, 0, displayMetrics.widthPixels, displayMetrics.heightPixels));  
    }  
  
private void takeScreenshotInternal(Consumer<Uri> finisher, Rect crop) {  
        mScreenshotTakenInPortrait =  
                mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;  
  
        // copy the input Rect, since SurfaceControl.screenshot can mutate it  
        Rect screenRect = new Rect(crop);  
        Bitmap screenshot = captureScreenshot(crop);  
  
        if (screenshot == null) {  
            Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");  
            mNotificationsController.notifyScreenshotError(  
                    R.string.screenshot_failed_to_capture_text);  
            if (mCurrentRequestCallback != null) {  
                mCurrentRequestCallback.reportError();  
            }  
            return;  
        }  
        // 调用saveScreenshot完成截屏  
        // showFlag控制截屏动画  
        saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, true);  
    }