国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
Android源碼中屏幕截圖的實現(xiàn)

Android源碼中屏幕截圖的實現(xiàn)

http://my.eoe.cn/viver120/archive/5448.html

Android手機一般都自帶有手機屏幕截圖的功能:在手機任何界面(當然手機要是開機點亮狀態(tài)),通過按組合鍵,屏幕閃一下,然后咔嚓一聲,截圖的照片會保存到當前手機的圖庫中,真是一個不錯的功能!

以我手頭的測試手機為例,是同時按電源鍵+音量下鍵來實現(xiàn)截屏,蘋果手機則是電源鍵 + HOME鍵,小米手機是菜單鍵+音量下鍵,而HTC一般是按住電源鍵再按左下角的“主頁”鍵。那么Android源碼中使用組合鍵是如何實現(xiàn)屏幕截圖功能呢?前段時間由于工作的原因仔細看了一下,這兩天不忙,便把相關的知識點串聯(lián)起來整理一下,分下面兩部分簡單分析下實現(xiàn)流程:

Android源碼中對組合鍵的捕獲。

Android源碼中對按鍵的捕獲位于文件PhoneWindowManager.java(alps\frameworks\base\policy\src\com\android\internal\policy\impl)中,這個類處理所有的鍵盤輸入事件,其中函數(shù)interceptKeyBeforeQueueing()會對常用的按鍵做特殊處理。以我手頭的測試機為例,是同時按電源鍵和音量下鍵來截屏,那么在這個函數(shù)中我們會看到這么兩段代碼:

 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930
....... case KeyEvent.KEYCODE_VOLUME_DOWN:            case 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();                    }......            case KeyEvent.KEYCODE_POWER: {                result &= ~ACTION_PASS_TO_USER;                if (down) {                    if (isScreenOn && !mPowerKeyTriggered                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {                        mPowerKeyTriggered = true;                        mPowerKeyTime = event.getDownTime();                        interceptScreenshotChord();                    }......

可以看到正是在這里(響應Down事件)捕獲是否按了音量下鍵和電源鍵的,而且兩個地方都會進入函數(shù)interceptScreenshotChord()中,那么接下來看看這個函數(shù)干了什么工作:

 1 2 3 4 5 6 7 8 910111213
    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());            }        }    }

在這個函數(shù)中,用兩個布爾變量判斷是否同時按了音量下鍵和電源鍵后,再計算兩個按鍵響應Down事件之間的時間差不超過150毫秒,也就認為是同時按了這兩個鍵后,算是真正的捕獲到屏幕截屏的組合鍵。

附言:文件PhoneWindowManager.java類是攔截鍵盤消息的處理類,在此類中還有對home鍵、返回鍵等好多按鍵的處理。

Android源碼中調用屏幕截圖的接口。

捕獲到組合鍵后,我們再看看android源碼中是如何調用屏幕截圖的函數(shù)接口。在上面的函數(shù)interceptScreenshotChord中我們看到用handler判斷長按組合鍵500毫秒之后,會進入如下函數(shù):

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

在這里啟動了一個線程來完成截屏的功能,接著看函數(shù)takeScreenshot():

 1 2 3 4 5 6 7 8 910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
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);            }        }    }

可以看到這個函數(shù)使用AIDL綁定了service服務到"com.android.systemui.screenshot.TakeScreenshotService",注意在service連接成功時,對message的msg.arg1和msg.arg2兩個參數(shù)的賦值。其中在mScreenshotTimeout中對服務service做了超時處理。接著我們找到實現(xiàn)這個服務service的類TakeScreenshotService,看看其實現(xiàn)的流程:

 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132
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();    }}

在這個類中,我們主要看調用接口,用到了mScreenshot.takeScreenshot()傳遞了三個參數(shù),第一個是個runnable,第二和第三個是之前message傳遞的兩個參數(shù)msg.arg1和msg.arg2。最后我們看看這個函數(shù)takeScreenshot(),位于文件GlobalScreenshot.java中(跟之前的函數(shù)重名但是文件路徑不一樣):

 1 2 3 4 5 6 7 8 9101112131415161718192021222324252627282930313233343536373839404142434445464748
 /**     * 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 = Surface.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);            mScreenBitmap = ss;        }        // Optimizations        mScreenBitmap.setHasAlpha(false);        mScreenBitmap.prepareToDraw();        // Start the post-screenshot animation        startAnimation(finisher, mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels,                statusBarVisible, navBarVisible);    }

這段代碼的注釋比較詳細,其實看到這里,我們算是真正看到截屏的操作了,具體的工作包括對屏幕大小、旋轉角度的獲取,然后調用Surface類的screenshot方法截屏保存到bitmap中,之后把這部分位圖填充到一個畫布上,最后再啟動一個延遲的拍照動畫效果。如果再往下探究screenshot方法,發(fā)現(xiàn)已經(jīng)是一個native方法了:

1234567
    /**     * Like {@link #screenshot(int, int, int, int)} but includes all     * Surfaces in the screenshot.     *     * @hide     */    public static native Bitmap screenshot(int width, int height);

使用JNI技術調用底層的代碼,如果再往下走,會發(fā)現(xiàn)映射這這個jni函數(shù)在文件android_view_Surface.cpp中,這個真的已經(jīng)是底層c++語言了,統(tǒng)一調用的底層函數(shù)是:

 1 2 3 4 5 6 7 8 91011121314151617181920212223242526272829303132
static jobject doScreenshot(JNIEnv* env, jobject clazz, jint width, jint height,        jint minLayer, jint maxLayer, bool allLayers){    ScreenshotPixelRef* pixels = new ScreenshotPixelRef(NULL);    if (pixels->update(width, height, minLayer, maxLayer, allLayers) != NO_ERROR) {        delete pixels;        return 0;    }    uint32_t w = pixels->getWidth();    uint32_t h = pixels->getHeight();    uint32_t s = pixels->getStride();    uint32_t f = pixels->getFormat();    ssize_t bpr = s * android::bytesPerPixel(f);    SkBitmap* bitmap = new SkBitmap();    bitmap->setConfig(convertPixelFormat(f), w, h, bpr);    if (f == PIXEL_FORMAT_RGBX_8888) {        bitmap->setIsOpaque(true);    }    if (w > 0 && h > 0) {        bitmap->setPixelRef(pixels)->unref();        bitmap->lockPixels();    } else {        // be safe with an empty bitmap.        delete pixels;        bitmap->setPixels(NULL);    }    return GraphicsJNI::createBitmap(env, bitmap, false, NULL);}

由于對C++不熟,我這里就不敢多言了。其實到這里,算是對手機android源碼中通過組合鍵屏幕截圖的整個流程有個大體了解了,一般我們在改動中熟悉按鍵的捕獲原理,并且清楚調用的截屏函數(shù)接口即可,如果有興趣的,可以繼續(xù)探究更深的底層是如何實現(xiàn)的。


本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
android 按鍵聲音
linux和android端的pthread學習
Android實現(xiàn)截屏方式整理(總結)
Android 之 遠程圖片獲取和本地緩存
Android實現(xiàn)獲取本機中所有圖片(使用android-support-v4.jar)
Android實現(xiàn)獲取本機中所有圖片
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服