【android4.3】記一次完整的android源碼截屏事件的捕獲(不同於網上的老版本)

來源:互聯網
上載者:User

(轉載請註明出處:http://blog.csdn.net/buptgshengod)1.背景

       我們知道android提供了一個系統截屏功能,就是按住電源鍵和音量減的按鍵0.5秒,系統將執行截屏功能。所以要實現系統截屏的功能,就是要捕獲系統的這兩個按鍵組合下面的函數,然後一層一層的向下挖掘。現在網上找到的版本是在Surface.java檔案下存在ScreenShot()函數,是@hide的。但是這是之前版本的辦法,在android4.3之後已經是不適用的,因為在/frameworks/base/core/java/android/view/的Surface.java下並沒有ScreenShot()函數,我猜google不會這麼絕情,一定會在framework層給開發人員留了介面,只不過寫到了別的地方。所以博主按照前任的思路自行挖掘,最後找到了新版本的ScreenShot函數,在這與大家分享。

2.挖掘過程  (1)Android源碼中對按鍵的捕獲位於檔案PhoneWindowManager.java(\frameworks\base\policy\src\com\android\internal\policy\impl)中    

ase KeyEvent.KEYCODE_VOLUME_UP:            case KeyEvent.KEYCODE_VOLUME_MUTE: {                if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {                    if (down) {                        if (isScreenOn && !mVolumeDownKeyTriggered                                && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                            mVolumeDownKeyTriggered = true;                            mVolumeDownKeyTime = event.getDownTime();                            mVolumeDownKeyConsumedByScreenshotChord = false;                            cancelPendingPowerKeyAction();                            interceptScreenshotChord();                        }                    } else {                        mVolumeDownKeyTriggered = false;                        cancelPendingScreenshotChordAction();                    }

我們看到了,如果同步選取電源鍵與音量減會啟動函數,

interceptScreenshotChord();

我們來看下著個函數

private void interceptScreenshotChord() {        if (mVolumeDownKeyTriggered && mPowerKeyTriggered && !mVolumeUpKeyTriggered) {            final long now = SystemClock.uptimeMillis();            if (now <= mVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS                    && now <= mPowerKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {                mVolumeDownKeyConsumedByScreenshotChord = true;                cancelPendingPowerKeyAction();                mHandler.postDelayed(mScreenshotChordLongPress,                        ViewConfiguration.getGlobalActionKeyTimeout());            }        }    }

我們看到handle中的這個函數,應該就是我們要執行的截屏功能

mScreenshotChordLongPress

我們進入這個函數

private final Runnable mScreenshotChordLongPress = new Runnable() {        public void run() {            takeScreenshot();        }    };

終於讓我們找到了takeScreenShot,不過別急,我們轉到這個函數

private void takeScreenshot() {        synchronized (mScreenshotLock) {            if (mScreenshotConnection != null) {                return;            }            ComponentName cn = new ComponentName("com.android.systemui",                    "com.android.systemui.screenshot.TakeScreenshotService");            Intent intent = new Intent();            intent.setComponent(cn);            ServiceConnection conn = new ServiceConnection() {                @Override                public void onServiceConnected(ComponentName name, IBinder service) {                    synchronized (mScreenshotLock) {                        if (mScreenshotConnection != this) {                            return;                        }                        Messenger messenger = new Messenger(service);                        Message msg = Message.obtain(null, 1);                        final ServiceConnection myConn = this;                        Handler h = new Handler(mHandler.getLooper()) {                            @Override                            public void handleMessage(Message msg) {                                synchronized (mScreenshotLock) {                                    if (mScreenshotConnection == myConn) {                                        mContext.unbindService(mScreenshotConnection);                                        mScreenshotConnection = null;                                        mHandler.removeCallbacks(mScreenshotTimeout);                                    }                                }                            }                        };                        msg.replyTo = new Messenger(h);                        msg.arg1 = msg.arg2 = 0;                        if (mStatusBar != null && mStatusBar.isVisibleLw())                            msg.arg1 = 1;                        if (mNavigationBar != null && mNavigationBar.isVisibleLw())                            msg.arg2 = 1;                        try {                            messenger.send(msg);                        } catch (RemoteException e) {                        }                    }                }                @Override                public void onServiceDisconnected(ComponentName name) {}            };            if (mContext.bindService(intent, conn, Context.BIND_AUTO_CREATE)) {                mScreenshotConnection = conn;                mHandler.postDelayed(mScreenshotTimeout, 10000);            }        }    }

我們看到它啟動了一個的service( "com.android.systemui.screenshot.TakeScreenshotService")。 (這也印證了我一個猜想,android的一切功能都是handle通過sendmessage然後通過service實現的)


(2)找到那個service

public class TakeScreenshotService extends Service {    private static final String TAG = "TakeScreenshotService";    private static GlobalScreenshot mScreenshot;    private Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case 1:                    final Messenger callback = msg.replyTo;                    if (mScreenshot == null) {                        mScreenshot = new GlobalScreenshot(TakeScreenshotService.this);                    }                    mScreenshot.takeScreenshot(new Runnable() {                        @Override public void run() {                            Message reply = Message.obtain(null, 1);                            try {                                callback.send(reply);                            } catch (RemoteException e) {                            }                        }                    }, msg.arg1 > 0, msg.arg2 > 0);            }        }    };    @Override    public IBinder onBind(Intent intent) {        return new Messenger(mHandler).getBinder();    }}

我們看到類GlobalScreenshot的對象執行了的功能。


(3)開啟GlobalScreenshot

 /**     * Takes a screenshot of the current display and shows an animation.     */    void takeScreenshot(Runnable finisher, boolean statusBarVisible, boolean navBarVisible) {        // We need to orient the screenshot correctly (and the Surface api seems to take screenshots        // only in the natural orientation of the device :!)        mDisplay.getRealMetrics(mDisplayMetrics);        float[] dims = {mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels};        float degrees = getDegreesForRotation(mDisplay.getRotation());        boolean requiresRotation = (degrees > 0);        if (requiresRotation) {            // Get the dimensions of the device in its native orientation            mDisplayMatrix.reset();            mDisplayMatrix.preRotate(-degrees);            mDisplayMatrix.mapPoints(dims);            dims[0] = Math.abs(dims[0]);            dims[1] = Math.abs(dims[1]);        }        // Take the screenshot        mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);        if (mScreenBitmap == null) {            notifyScreenshotError(mContext, mNotificationManager);            finisher.run();            return;        }        if (requiresRotation) {            // Rotate the screenshot to the current orientation            Bitmap ss = Bitmap.createBitmap(mDisplayMetrics.widthPixels,                    mDisplayMetrics.heightPixels, Bitmap.Config.ARGB_8888);            Canvas c = new Canvas(ss);            c.translate(ss.getWidth() / 2, ss.getHeight() / 2);            c.rotate(degrees);            c.translate(-dims[0] / 2, -dims[1] / 2);            c.drawBitmap(mScreenBitmap, 0, 0, null);            c.setBitmap(null);            // Recycle the previous bitmap            mScreenBitmap.recycle();            mScreenBitmap = ss;        }        // Optimizations        mScreenBitmap.setHasAlpha(false);        mScreenBitmap.prepareToDraw();        // Start the post-screenshot animation        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,                statusBarVisible, navBarVisible);    }

我們主要看這句,我們終於找到了screenshot(),這個地方跟低版本的android源碼是有改動的,之前的surface操作是寫到surface類裡,現在增加了這個surfacecontrol類來控制surface。

// Take the screenshot        mScreenBitmap = SurfaceControl.screenshot((int) dims[0], (int) dims[1]);

(4)現在我們開啟surfacecontrol類,位於/frameworks/base/core/java/android/view

代碼開頭我們可以看到,這個類是被google隱藏了,所以不能直接調用,若是想用截屏功能要在源碼中編譯才行,至於如何調用這個功能以後會講,have fun!

/** * SurfaceControl *  @hide */


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.