android - "RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap"尝试将位图从 Activity 传递到服务

标签 android android-activity android-service android-bitmap

我正在 Android Studio 中编写一个小型 Android 程序,它使用 MediaProjection 来抓取整个屏幕的屏幕截图,然后我想将该屏幕截图作为位图传递到我的系统叠加层(基于聊天头示例)。当 MediaProjection 运行并且 Imagereader 创建位图时,我将位图传递到类全局变量中,以便我可以将其传递到我的系统覆盖服务中并显示它。我遇到一个问题,当服务内的 Imageview 尝试读取 bmp 时,出现如下错误:

java.lang.RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap@347a4d5

根据我的理解,垃圾收集器很快就会吃掉传递的位图,当系统覆盖层尝试抓取它时,数​​据就消失了。有人能指出我如何保留位图的正确方向吗?

主要 Activity

package com.example.chatheads;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import android.graphics.PixelFormat;
import android.graphics.Point;
import android.media.ImageReader;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.Image;
import android.media.ImageReader;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

public class MainActivity extends Activity {
    Button startService,stopService;


    private static final String TAG = MainActivity.class.getName();
    private static final int REQUEST_CODE = 100;
    private static String STORE_DIRECTORY;
    private static int IMAGES_PRODUCED;
    private static final String SCREENCAP_NAME = "screencap";
    private static final int VIRTUAL_DISPLAY_FLAGS = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
    private static MediaProjection sMediaProjection;

    private MediaProjectionManager mProjectionManager;
    private ImageReader mImageReader;
    private Handler mHandler;
    private Display mDisplay;
    private VirtualDisplay mVirtualDisplay;
    private int mDensity;
    private int mWidth;
    private int mHeight;
    private int mRotation;
    private OrientationChangeCallback mOrientationChangeCallback;

    private Image image = null;
    private Bitmap bitmap = null;

    Globals sharedData = Globals.getInstance();

    public Bitmap getObjectContainer() {
        return bitmap;
    }

    private class ImageAvailableListener implements ImageReader.OnImageAvailableListener {
        @Override
        public void onImageAvailable(ImageReader reader) {

            FileOutputStream fos = null;


            try {
                image = mImageReader.acquireLatestImage();
                if (image != null) {
                    Image.Plane[] planes = image.getPlanes();
                    ByteBuffer buffer = planes[0].getBuffer();
                    int pixelStride = planes[0].getPixelStride();
                    int rowStride = planes[0].getRowStride();
                    int rowPadding = rowStride - pixelStride * mWidth;

                    // create bitmap
                    bitmap = Bitmap.createBitmap(mWidth + rowPadding / pixelStride, mHeight, Bitmap.Config.ARGB_8888);
                    bitmap.copyPixelsFromBuffer(buffer);
                    sharedData.setScreenshot(bitmap);

                    // write bitmap to a file
                   // fos = new FileOutputStream(STORE_DIRECTORY + "/myscreen_" + IMAGES_PRODUCED + ".png");
                    //bitmap.compress(CompressFormat.JPEG, 100, fos);

                    IMAGES_PRODUCED++;
                    //Log.e(TAG, "captured image: " + IMAGES_PRODUCED);
                    String s = ("captured image: " + String.valueOf(IMAGES_PRODUCED));
                    Toast toast1 = Toast.makeText(getBaseContext(),s, Toast.LENGTH_SHORT);
                    toast1.show();
                }

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (fos!=null) {
                    try {
                        fos.close();
                    } catch (IOException ioe) {
                        ioe.printStackTrace();
                    }
                }

                if (bitmap!=null) {
                    bitmap.recycle();
                }

                if (image!=null) {
                    image.close();
                }
            }
        }
    }

    private class OrientationChangeCallback extends OrientationEventListener {
        public OrientationChangeCallback(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            synchronized (this) {
                final int rotation = mDisplay.getRotation();
                if (rotation != mRotation) {
                    mRotation = rotation;
                    try {
                        // clean up
                        if(mVirtualDisplay != null) mVirtualDisplay.release();
                        if(mImageReader != null) mImageReader.setOnImageAvailableListener(null, null);

                        // re-create virtual display depending on device width / height
                        createVirtualDisplay();
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    private class MediaProjectionStopCallback extends MediaProjection.Callback {
        @Override
        public void onStop() {
            Log.e("ScreenCapture", "stopping projection.");
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    if(mVirtualDisplay != null) mVirtualDisplay.release();
                    if(mImageReader != null) mImageReader.setOnImageAvailableListener(null, null);
                    if(mOrientationChangeCallback != null) mOrientationChangeCallback.disable();
                    sMediaProjection.unregisterCallback(MediaProjectionStopCallback.this);
                }
            });
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        sharedData.setValue(1);

        // call for the projection manager
        mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);

        // start projection
        Button startButton = (Button)findViewById(R.id.startButton);
        startButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                startProjection();
            }
        });

        // stop projection
        Button stopButton = (Button)findViewById(R.id.stopButton);
        stopButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                stopProjection();
            }
        });

        // start capture handling thread
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                mHandler = new Handler();
                Looper.loop();
            }
        }.start();


        startService=(Button)findViewById(R.id.startService);
        stopService=(Button)findViewById(R.id.stopService);

        startService.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                startService(new Intent(getApplication(), ChatHeadService.class));

            }
        });
        stopService.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                stopService(new Intent(getApplication(), ChatHeadService.class));

            }
        });
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE) {
            sMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);

            if (sMediaProjection != null) {
                File externalFilesDir = getExternalFilesDir(null);
                if (externalFilesDir != null) {
                    STORE_DIRECTORY = externalFilesDir.getAbsolutePath() + "/screenshots/";
                    File storeDirectory = new File(STORE_DIRECTORY);
                    if (!storeDirectory.exists()) {
                        boolean success = storeDirectory.mkdirs();
                        if (!success) {
                            Log.e(TAG, "failed to create file storage directory.");
                            return;
                        }
                    }
                } else {
                    Log.e(TAG, "failed to create file storage directory, getExternalFilesDir is null.");
                    return;
                }

                // display metrics
                DisplayMetrics metrics = getResources().getDisplayMetrics();
                mDensity = metrics.densityDpi;
                mDisplay = getWindowManager().getDefaultDisplay();

                // create virtual display depending on device width / height
                createVirtualDisplay();

                // register orientation change callback
                mOrientationChangeCallback = new OrientationChangeCallback(this);
                if (mOrientationChangeCallback.canDetectOrientation()) {
                    mOrientationChangeCallback.enable();
                }

                // register media projection stop callback
                sMediaProjection.registerCallback(new MediaProjectionStopCallback(), mHandler);
            }
        }
    }

    /****************************************** UI Widget Callbacks *******************************/
    private void startProjection() {
        startActivityForResult(mProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
    }

    private void stopProjection() {
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (sMediaProjection != null) {
                    sMediaProjection.stop();
                }
            }
        });
    }

    /****************************************** Factoring Virtual Display creation ****************/
    private void createVirtualDisplay() {
        // get width and height
        Point size = new Point();
        mDisplay.getSize(size);
        mWidth = size.x;
        mHeight = size.y;

        // start capture reader
        mImageReader = ImageReader.newInstance(mWidth, mHeight, PixelFormat.RGBA_8888, 2);
        mVirtualDisplay = sMediaProjection.createVirtualDisplay(SCREENCAP_NAME, mWidth, mHeight, mDensity, VIRTUAL_DISPLAY_FLAGS, mImageReader.getSurface(), null, mHandler);
        mImageReader.setOnImageAvailableListener(new ImageAvailableListener(), mHandler);
    }
}

服务

package com.example.chatheads;    
import android.app.Service;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.view.View;
import android.widget.Toast;


public class ChatHeadService extends Service {

    private WindowManager windowManager;
    private ImageView chatHead;
    WindowManager.LayoutParams params;

    Globals sharedData = Globals.getInstance();



    @Override
    public void onCreate() {
        super.onCreate();

        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);

        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);

        int h = displayMetrics.heightPixels, w = displayMetrics.widthPixels;

        Bitmap.Config conf = Bitmap.Config.ARGB_8888; // see other conf types
        final Bitmap bmp = Bitmap.createBitmap(w, h, conf); // this creates a MUTABLE bitmap
        Canvas canvas = new Canvas(bmp);
        Paint paint = new Paint();
        paint.setColor(Color.BLACK);
        paint.setStrokeWidth(5);
        paint.setAlpha(60);
        Rect rect=new Rect(0, 0, bmp.getWidth(), bmp.getHeight());
        canvas.drawRect(rect,paint);
        paint.setColor(Color.GREEN);
        canvas.drawLine(0, 0, 0, h, paint);
        canvas.drawLine(0, 0, w, 0, paint);
        canvas.drawLine(w, 0, h, w, paint);
        canvas.drawLine(0, 0, 0, w, paint);


        chatHead = new ImageView(this);
        chatHead.setImageResource(R.drawable.face1);

        params= new WindowManager.LayoutParams(
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.WRAP_CONTENT,
                WindowManager.LayoutParams.TYPE_PHONE,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                PixelFormat.TRANSLUCENT);

        params.gravity = Gravity.TOP | Gravity.LEFT;
        params.x = 0;
        params.y = 1000;







        //this code is for dragging the chat head
        chatHead.setOnTouchListener(new View.OnTouchListener() {
            private int initialX;
            private int initialY;
            private float initialTouchX;
            private float initialTouchY;
            boolean isExpanded = false;

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    initialX = params.x;
                    initialY = params.y;
                    initialTouchX = event.getRawX();
                    initialTouchY = event.getRawY();

                    int n = sharedData.getValue();
                    //Toast toast1 = Toast.makeText(getBaseContext(),String.valueOf(n), Toast.LENGTH_SHORT);
                    //toast1.show();
                    if (isExpanded == true){
                        chatHead.setImageResource(R.drawable.face1);
                        isExpanded=false;
                        params.x = 0;
                        params.y = 1000;
                        windowManager.updateViewLayout(chatHead, params);
                    }
                    else{
                        //chatHead.setImageResource(R.drawable.clear);
                        Bitmap bmp2 = sharedData.getScreenshot();

                        chatHead.setImageBitmap(bmp2);
                        isExpanded = true;
                        params.x = 0;
                        params.y = 0;
                        windowManager.updateViewLayout(chatHead, params);
                    }
                    return true;
                case MotionEvent.ACTION_UP:
                    //chatHead.setImageResource(R.drawable.test);
                    return true;
                /*case MotionEvent.ACTION_MOVE:
                    params.x = initialX
                            + (int) (event.getRawX() - initialTouchX);
                    params.y = initialY
                            + (int) (event.getRawY() - initialTouchY);
                    windowManager.updateViewLayout(chatHead, params);
                    return true;*/
                }
                return false;
            }
        });
        windowManager.addView(chatHead, params);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (chatHead != null)
            windowManager.removeView(chatHead);
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }
}

全局

package com.example.chatheads;

import android.graphics.Bitmap;

public class Globals {

    private static Globals instance = new Globals();
    // Getter-Setters
    public static Globals getInstance() {
        return instance;
    }

    public static void setInstance(Globals instance) {
        Globals.instance = instance;
    }

    private Globals() {

    }

    private int testi;
    private Bitmap bmpscreenshot;

    public Bitmap getScreenshot(){
        return bmpscreenshot;
    }

    public void setScreenshot(Bitmap bmp){
        this.bmpscreenshot = bmp;
    }

    public int getValue() {
        return testi;
    }

    public void setValue(int testi) {
        this.testi = testi;
    }

}

最佳答案

您似乎太早调用了bitmap.recycle()。在服务实际处理数据后调用它。

关于android - "RuntimeException: Canvas: trying to use a recycled bitmap android.graphics.Bitmap"尝试将位图从 Activity 传递到服务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51192609/

相关文章:

android - Google翻译手机应用程序是否使用Tesseract在本地进行OCR?

java - 在哪里停止/销毁 Android Service 类中的线程?

android - 使用 NestedScrollView 折叠工具栏布局不会平滑滚动

android - 我需要在 Android 中显式完成 Activity 吗?

Android:由同一 Intent 启动的多个 Activity 实例。把一个独特的带到前台?

android - 如何在Android中调试两个进程?

java - 安卓,org.w3c.dom : No validating DocumentBuilder implementation available

安卓错误 : "commit already called" when remove a fragment

Android IntentService 从未被调用

android - 从网络服务读取数据、解析数据并将其插入 SQLite 数据库的推荐方式/顺序