android:无法在 targetSdkVersion 更改时连接到相机服务

Posted

技术标签:

【中文标题】android:无法在 targetSdkVersion 更改时连接到相机服务【英文标题】:android: Fail to connect to camera service on targetSdkVersion change 【发布时间】:2012-07-07 14:07:44 【问题描述】:

我的相机应用 manifest.xml 中有以下设置:

<uses-sdk
    android:minSdkVersion="7"
    android:targetSdkVersion="8" />

现在我改成这样了:

<uses-sdk
    android:minSdkVersion="7"
    android:targetSdkVersion="13" />

问题是:我的应用程序无法再打开相机了。此行抛出错误:

this.camera = Camera.open();

错误信息:

连接相机服务失败

顺便说一句,奇怪的事情:使用新设置启动应用程序后,我不再让相机设备运行(所有其他照片应用程序也在启动时崩溃)。我必须重新启动设备才能再次使用相机。

有人可以帮我吗?

我的 Preview.java 类:

package net.mt.lib.cc;

import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.List;

import net.mt.lib.Tools;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityManager.MemoryInfo;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.ErrorCallback;
import android.hardware.Camera.Size;
import android.os.Build;
import android.os.Debug;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.crittercism.app.Crittercism;
import com.flurry.android.FlurryAgent;

/**
 *
 * openCamera()
 * onSizeChanged()
 * onLayout()
 * onLayout()
 * surfaceCreated()
 * surfaceChanged()
 * onLayout()
 */
class Preview extends ViewGroup implements SurfaceHolder.Callback  // <1>
    private static final String TAG = "Preview";

    SurfaceHolder mHolder; // <2>
    public Camera camera; // <3>
    SurfaceView mSurfaceView;
    BaseCameraActivity cameraActivity;
    int l2 = 0, t2 = 0, r2 = 0, b2 = 0;
    int padding = 20;
    PreviewCallback cb;
    private double downscalingFactor = 1;
    // the size of this view. gets set in onMeasure()
    int fullWidth, fullHeight;
    Size bestPictureSize = null;
    Size bestPreviewSize = null;

    private String allResolutions;

    private Context context;

    public Preview(Context context, PreviewCallback callback) 
        super(context);
        this.cb = callback;
        init(context);
    



    private void init(Context context) 
        setKeepScreenOn(true);
        cameraActivity = (BaseCameraActivity) context;
        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);
        this.context = context;
        mHolder = mSurfaceView.getHolder(); // <4>
        mHolder.addCallback(this); // <5>
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // <6>
    


    public void openCamera() 
        Log.d(TAG,"openCamera()");
        if (this.camera == null) 
            try
                this.camera = Camera.open();
                this.camera.setErrorCallback(new ErrorCallback() 
                    @Override
                    public void onError(int error, Camera camera) 
                        Log.e(TAG, "error! code:"+error);
                        Toast.makeText(cameraActivity, "Camera error occured: "+error, 8000).show();
                    
                );
                requestLayout(); // -> onSizeChanged() -> onLayout()
            catch (Exception e) 
                String errorMessage = "Manufacturer: "+Build.MANUFACTURER+"; Model:"+Build.MODEL+"; Camera: "+this.camera+"; Stacktrace:"+Tools.exception2String(e);
                Log.d(TAG,"error occured: "+errorMessage);
                FlurryAgent.onError("1", errorMessage, "Preview.openCamera()");
                Toast.makeText(cameraActivity, "Uuups, I am sorry! Could not connect to the camera device. Please restart me or your phone.", 8000).show();
                Crittercism.logHandledException(e);
            
        
    



    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
        Log.d(TAG,"onSizeChanged() "+w+" "+h);
        fullWidth = w;
        fullHeight = h;
        if(fullWidth < fullHeight) 
            int tmp = fullHeight;
            fullHeight = fullWidth;
            fullWidth = tmp;
            Log.d(TAG,"switched:"+fullWidth+"x"+fullHeight);
        else
            Log.d(TAG,"fullSize:"+fullWidth+"x"+fullHeight);
        
        if(this.camera != null)
            this.setCameraPreviewSize();
            this.setCameraPictureSize();
            if (getChildCount() > 0) 
                final View child = getChildAt(0);
                Log.d(TAG,"r:"+this.getPreviewRight()+" l:"+this.getPreviewLeft()+" b:"+this.getPreviewBottom()+" t:"+this.getPreviewTop());
                child.layout(this.getPreviewLeft(), this.getPreviewTop(), this.getPreviewRight(), this.getPreviewBottom());
                cb.previewReady(getPreviewLeft(), getPreviewTop(), getPreviewRight()-getPreviewLeft(), getPreviewBottom() - getPreviewTop(), getBestPictureSize().width, getBestPictureSize().height, (int) downscalingFactor, allResolutions);
            
        

        super.onSizeChanged(w, h, oldw, oldh);
    

    private void calcScaledPreviewSize()       
        int previewWidth = getBestPreviewSize().width;
        int previewHeight = getBestPreviewSize().height;
        float scaledWidth;
        float scaledHeight;

        Log.d(TAG,"preview width: "+previewWidth+", preview height: "+previewHeight);
        Log.d(TAG,"display width: "+fullWidth+", display height: "+fullHeight);
        float previewRatio = (float) previewWidth / (float) previewHeight;
        float displayRatio = (float) fullWidth / (float) fullHeight;

        if(displayRatio >= previewRatio) // the display is wider then the preview image
            scaledHeight = fullHeight - 2*padding;
            scaledWidth = scaledHeight * previewRatio;
            l2 = (int) (fullWidth - scaledWidth) / 2;
            t2 = padding;
            r2 = (int) (fullWidth + scaledWidth) / 2;
            b2 = (int) scaledHeight + padding;

        else
            scaledWidth = fullWidth - 2*padding;
            scaledHeight = scaledWidth / previewRatio;
            l2 = padding;
            t2 = (int) (fullHeight - scaledHeight) / 2;
            r2 = (int) scaledWidth + padding;
            b2 = (int) (fullHeight + scaledHeight) / 2;     
        
    

    public int getPreviewTop() 
        if(this.t2 == 0)
            this.calcScaledPreviewSize();
        
        return t2;
    
    public int getPreviewBottom() 
        if(this.b2 == 0)
            this.calcScaledPreviewSize();
        
        return b2;
    
    public int getPreviewLeft() 
        if(this.l2 == 0)
            this.calcScaledPreviewSize();
        
        return l2;
    
    public int getPreviewRight() 
        if(this.r2 == 0)
            this.calcScaledPreviewSize();
        
        return r2;
    
    public int getPreviewWidth() 
        return this.getPreviewRight()-this.getPreviewLeft();
    
    public int getPreviewHeight() 
        return this.getPreviewBottom()-this.getPreviewTop();
    

    private void setCameraPreviewSize() 
        Camera.Parameters parameters = camera.getParameters();
        if(parameters.getPreviewSize() != this.getBestPreviewSize())
            parameters.setPreviewSize(this.getBestPreviewSize().width, this.getBestPreviewSize().height);
            this.camera.setParameters(parameters);
        
    

    private void setCameraPictureSize() 
        Camera.Parameters parameters = this.camera.getParameters();
        if(parameters.getPictureSize() != this.getBestPictureSize())
            parameters.setPictureSize(getBestPictureSize().width, getBestPictureSize().height);
            this.camera.setParameters(parameters);
        
    

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) 
        Log.d(TAG,"onLayout()");
        /*
        if (changed && getChildCount() > 0 && this.camera != null) 
            final View child = getChildAt(0);
            Log.d(TAG,"r:"+this.getPreviewRight()+" l:"+this.getPreviewLeft()+" b:"+this.getPreviewBottom()+" t:"+this.getPreviewTop());
            child.layout(this.getPreviewLeft(), this.getPreviewTop(), this.getPreviewRight(), this.getPreviewBottom());
            cameraActivity.initOverlay(this.getPreviewLeft(),this.getPreviewTop(),this.getPreviewRight(),this.getPreviewBottom());
        */
    
    public Size getBestPictureSize() 
        if(this.bestPictureSize == null)
            this.calculateOptimalPictureAndPreviewSizes();
        
        return bestPictureSize;
    

    public Size getBestPreviewSize() 
        if(this.bestPreviewSize == null)
            this.calculateOptimalPictureAndPreviewSizes();
        
        return bestPreviewSize;
    



    // Called once the holder is ready
    public void surfaceCreated(SurfaceHolder holder)  // <7>
        // The Surface has been created, acquire the camera and tell it where
        // to draw.
        Log.d(TAG,"surfaceCreated()");
        try 
            if (this.camera != null) 
                this.camera.setPreviewDisplay(holder);
            
         catch (IOException exception) 
            Log.e(TAG, "IOException caused by setPreviewDisplay()", exception);

        
    

    public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) 
        Log.d(TAG,"surfaceChanged()");
        if (camera != null) 

            Camera.Parameters parameters = camera.getParameters();
            parameters.setPreviewSize(getBestPreviewSize().width, getBestPreviewSize().height);
            camera.setParameters(parameters);
            camera.startPreview();
            cb.onPreviewStart();
            requestLayout();
        
    

    public void surfaceDestroyed(SurfaceHolder holder)  // <14>
        Log.d(TAG,"surfaceDestroyed()");
        if(this.camera != null)
            camera.stopPreview();
            camera.lock();
            camera.release();
            this.camera = null;
        
    

    public void releaseCamera()
        Log.d(TAG,"releaseCamera()");
        if (camera != null) 
            camera.stopPreview();
            camera.setPreviewCallback(null);
            camera.release();
            camera = null;
        
    



预览是这样初始化的(来自主活动):

@Override
    protected void onResume() 
        super.onResume();
        Log.d(TAG,"onResume()");

        previewLayout.removeAllViews();
        preview = new Preview(this, this);
        previewLayout.addView(preview, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT));
        preview.openCamera();

    

和清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.mc.ccPro"
    android:versionCode="35"
    android:versionName="3.5" >

    <application
        android:hardwareAccelerated="true"
        android:icon="@drawable/icon"
        android:label="@string/app_name"
        android:largeHeap="true" >
        <activity
            android:name=".ccProActivity"
            android:configChanges="keyboard|orientation|keyboardHidden"
            android:label="@string/app_name"
            android:windowSoftInputMode="adjustPan" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.media.action.IMAGE_CAPTURE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
            android:name="net.mc.lib.cc.PreferenceActivity"
            android:label="@string/set_preferences" >
        </activity>

    </application>

    <supports-screens 
           android:largeScreens="true" 
           android:normalScreens="true" 
           android:smallScreens="true" 
           android:resizeable="true" 
           android:anyDensity="true" />


    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="8" />

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <uses-library android:name="net.mc.lib.cc" />
    <uses-feature
        android:name="android.hardware.camera.autofocus"
        android:required="false" />

    <supports-screens
        android:largeScreens="true"
        android:normalScreens="true"
        android:smallScreens="true"
        android:xlargeScreens="true" />

</manifest>

【问题讨论】:

这通常意味着相机资源没有被充分释放。 我知道。但是为什么它可以在 targetSdkVersion=8 下正常工作呢? 您还必须发布您正在使用的其余代码。 应用权限呢?您可以发布清单中的相关部分吗? 相机 API 可能在平台版本之间发生了变化。尝试构建为新平台版本提供的 API Demos 应用程序,看看它是否工作,如果它确实查看它的代码(特别是将它与为 old SDK 版本提供的版本进行比较)。还可以考虑查看 android bugs 数据库,看看是否存在已知问题。 【参考方案1】:

使用此代码初始化相机:

        Camera camera;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) 
            int numberOfCameras = Camera.getNumberOfCameras();
            CameraInfo cameraInfo = new CameraInfo();
            for (int i = 0; i < numberOfCameras; i++) 
                Camera.getCameraInfo(i, cameraInfo);
                if (cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK)
                    camera = Camera.open(i);
            
        
        if (camera == null)
            camera = Camera.open();
        ...

【讨论】:

【参考方案2】:

尝试将此行添加到您的 AndroidManifest 中:

<uses-feature android:name="android.hardware.camera" />

【讨论】:

以上是关于android:无法在 targetSdkVersion 更改时连接到相机服务的主要内容,如果未能解决你的问题,请参考以下文章

Android 开发中,都有哪些坑需要注意

v11。+请通过更新google-services插件的版本来修复版本冲突

棒棒糖的 SDK 级别

为啥 AndroidManifest.xml 的 targetSdkVersion 无效?

无法导入“android”模块。无法删除 android presplash

数据库小组与UI小组第一次对接