为什么我无法在Android Instant App中访问摄像头?

时间:2017-09-10 13:21:05

标签: android android-camera runtime-permissions android-instant-apps android-8.0-oreo

我正在尝试开发一款相机即时应用,但是当我通过manager.openCamera(cameraId, mStateCallback, null);打开相机时它总是会发生崩溃:

09-10 21:00:55.333 9472-9472/com.pixelslab.stickerpe I/CameraManager: Using legacy camera HAL.
09-10 21:00:55.340 1402-1914/? I/CameraService: CameraService::connect call (PID -1 "com.pixelslab.stickerpe", camera ID 0) for HAL version default and Camera API version 1
09-10 21:00:55.340 1402-1914/? W/ServiceManager: Permission failure: android.permission.CAMERA from uid=10088 pid=9472
09-10 21:00:55.340 1402-1914/? E/CameraService: Permission Denial: can't use the camera pid=9472, uid=10088
09-10 21:00:55.345 9472-9472/com.pixelslab.stickerpe D/AndroidRuntime: Shutting down VM
09-10 21:00:55.348 9472-9472/com.pixelslab.stickerpe E/AndroidRuntime: FATAL EXCEPTION: 
     main Process: com.pixelslab.stickerpe, PID: 9472
     java.lang.SecurityException: Lacking privileges to access camera service
     at android.hardware.camera2.CameraManager.throwAsPublicException(CameraManager.java:643)
     at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:340)
     at android.hardware.camera2.CameraManager.openCameraForUid(CameraManager.java:466)
     at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:430)
     at com.gomo.minivideo.camera2.Camera2VideoFragment.openCamera(Camera2VideoFragment.java:378)
     at com.gomo.minivideo.camera2.Camera2VideoFragment.access$000(Camera2VideoFragment.java:65)
     at com.gomo.minivideo.camera2.Camera2VideoFragment$1.onSurfaceTextureAvailable(Camera2VideoFragment.java:120)
     at android.view.TextureView.getHardwareLayer(TextureView.java:390)
     at android.view.TextureView.draw(TextureView.java:339)
     at android.view.View.updateDisplayListIfDirty(View.java:18069)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.updateDisplayListIfDirty(View.java:18060)
     at android.view.View.draw(View.java:18847)
     at android.view.ViewGroup.drawChild(ViewGroup.java:4214)
     at android.view.ViewGroup.dispatchDraw(ViewGroup.java:4000)
     at android.view.View.draw(View.java:19122)
     at com.android.internal.policy.DecorView.draw(DecorView.java:785)
     at android.view.View.updateDisplayListIfDirty(View.java:18069)
     at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:643)
     at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:649)
     at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:757)
     at android.view.ViewRootImpl.draw(ViewRootImpl.java:2980)
     at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2794)
     at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2347)
     at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1386)
     at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6733)
     at android.view.Choreographer$CallbackRecord.run(Choreographer.java:911)
     at android.view.Choreographer.doCallbacks(Choreographer.java:723)
     at android.view.Choreographer.doFrame(Choreographer.java:658)
     at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:897)
     at android.os.Handler.handleCallback(Handler.java:789)
     at android.os.Handler.dispatchMessage(Handler.java:98)
     at android.os.Looper.loop(Looper.java:164)
     at android.app.ActivityThread.main(ActivityThread.java:6541)
09-10 21:00:55.349 9472-9472/com.pixelslab.stickerpe E/AndroidRuntime:
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)
       Caused by: android.os.ServiceSpecificException: Lacking privileges to access camera service (code 1)
     at android.hardware.camera2.legacy.LegacyExceptionUtils.throwOnServiceError(LegacyExceptionUtils.java:132)
     at android.hardware.camera2.legacy.CameraDeviceUserShim.connectBinderShim(CameraDeviceUserShim.java:374)
     at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:317)
     ... 61 more

但是,我确信我已授予相机权限,因为相同的代码在已安装的版本中成功运行。

有人能解决这个问题吗?谢谢!

以下代码崩溃了:

// CameraFragment.java
private TextureView.SurfaceTextureListener mSurfaceTextureListener
        = new TextureView.SurfaceTextureListener() {

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
                                          int width, int height) {
        openCamera(width, height);
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture,
                                            int width, int height) {
        configureTransform(width, height);
    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
        return true;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
    }

};

private void openCamera(int width, int height) {
    final Activity activity = getActivity();
    if (null == activity || activity.isFinishing()) {
        return;
    }
    CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
    try {
        Log.d(TAG, "tryAcquire");
        if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
            throw new RuntimeException("Time out waiting to lock camera opening.");
        }
        String cameraId = manager.getCameraIdList()[0];

        CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
        StreamConfigurationMap map = characteristics
                .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
        mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
        if (map == null) {
            throw new RuntimeException("Cannot get available preview/video sizes");
        }
        mVideoSize = chooseVideoSize(map.getOutputSizes(MediaRecorder.class));
        mPreviewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture.class),
                width, height, mVideoSize);

        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
            mTextureView.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        } else {
            mTextureView.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
        }
        configureTransform(width, height);
        mMediaRecorder = new MediaRecorder();
        manager.openCamera(cameraId, mStateCallback, null); // here will crash!!!
    } catch (CameraAccessException e) {
        Toast.makeText(activity, "Cannot access the camera.", Toast.LENGTH_SHORT).show();
        activity.finish();
    } catch (NullPointerException e) {
        ErrorDialog.newInstance(getString(R.string.camera_error))
                .show(getChildFragmentManager(), FRAGMENT_DIALOG);
    } catch (InterruptedException e) {
        throw new RuntimeException("Interrupted while trying to lock camera opening.");
    }
}

以下是我获得相机许可的方式:

// MainActivity.java
private void requestCameraPermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.CAMERA)) {
        Snackbar.make(mRootView, R.string.permission_camera_rationale,
                Snackbar.LENGTH_INDEFINITE)
                .setAction(R.string.ok, new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA);
                    }
                })
                .show();
    } else {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA,
                Manifest.permission.INTERNET, Manifest.permission.RECORD_AUDIO}, REQUEST_CAMERA);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_CAMERA) {
        if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            getSupportFragmentManager().beginTransaction().replace(R.id.content_layout, Camera2VideoFragment.newInstance()).commitAllowingStateLoss();
            //getSupportFragmentManager().beginTransaction().replace(R.id.content_layout, new CameraFragment()).commitAllowingStateLoss();
        }
    }
}

然后这是我的完整清单文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pixelslab.stickerpe">

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

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.RECORD_VIDEO" />
    <uses-permission android:name="android.permission.CAMERA" />

    <application
        android:name="com.gomo.minivideo.CameraApp"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/CameraTheme">
        <activity
            android:name="com.gomo.minivideo.MainActivity"
            android:configChanges="orientation|screenSize|keyboardHidden"
            android:launchMode="singleTop"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data
                android:name="default-url"
                android:value="https://hugo775128583.github.io/main" />

            <intent-filter android:order="1" android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="https"
                    android:host="hugo775128583.github.io"
                    android:pathPrefix="/main" />
            </intent-filter>
            <intent-filter android:order="1" android:autoVerify="true">
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />

                <data
                    android:scheme="http"
                    android:host="hugo775128583.github.io"
                    android:pathPrefix="/main" />
            </intent-filter>
        </activity>

        <service android:name="com.jb.zcamera.camera.ProcessVideoService" />

        <activity android:name="com.gomo.minivideo.camera.ShareActivity" />
        <activity android:name="com.gomo.minivideo.camera.VideoViewActivity" />
    </application>

</manifest>

3 个答案:

答案 0 :(得分:2)

这看起来像是在Android O上可以重现的错误。它也可以通过适用于Instant App的示例Camera2Basic项目进行复制:
https://github.com/Idolon-V/InstantApp-Camera

我在Google跟踪器上提交了一个错误:https://issuetracker.google.com/issues/66942980

答案 1 :(得分:0)

在清单文件中添加此行

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

答案 2 :(得分:0)

参考Google issue tracker

此问题已经修复并已在8.1设备上推出。 我们正在与合作伙伴合作,将修复程序推广到8.0设备的更新中。 我们预计这些更新将在未来几个月推出。

如果问题仍然存在,请在Google issue tracker报告,然后重新审核。