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 更改时连接到相机服务的主要内容,如果未能解决你的问题,请参考以下文章
v11。+请通过更新google-services插件的版本来修复版本冲突
为啥 AndroidManifest.xml 的 targetSdkVersion 无效?