为啥我无法在 Android Instant App 中访问相机?

Posted

技术标签:

【中文标题】为啥我无法在 Android Instant App 中访问相机?【英文标题】:Why can't I access camera in Android Instant App?为什么我无法在 Android Instant App 中访问相机? 【发布时间】:2018-02-18 20:04:38 【问题描述】:

我正在尝试开发一个相机即时应用程序,但是当我通过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>

【问题讨论】:

我们也需要密码!没有代码无法判断发生了什么。 感谢您的建议。我刚刚发布了代码。 @JavenRuan 因此,请求权限对话框出现在您的免安装应用中,并且在您授予相机权限后,片段尝试打开相机并崩溃,但出现上述异常。那正确吗?您能否也发布您的清单文件? @Idolon,是的,你完全正确!我发布了我的清单文件,请检查。 @philo 我提交了一个错误:issuetracker.google.com/issues/66942980 【参考方案1】:

参考Google issue tracker,

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

如果任何问题仍然存在,请通过Google issue tracker 报告,他们将重新打开进行检查。

【讨论】:

【参考方案2】:

在清单文件中添加这一行

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

【讨论】:

【参考方案3】:

这看起来像是一个可以在 Android O 上重现的错误。它也可以通过将示例 Camera2Basic 项目改编为 Instant App 来重现:https://github.com/Idolon-V/InstantApp-Camera

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

【讨论】:

以上是关于为啥我无法在 Android Instant App 中访问相机?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Instant 不支持 ChronoUnit.YEARS 的操作?

为啥 Instant 在彼此之后打印时显示不同的值? [关闭]

Wireless-Aruba_AP通过DHCP获取Controller地址

Wireless-Aruba_AP通过DHCP获取Controller地址

不同项目/分支中的 Android Instant App

如何为 Android Studio 3.0 禁用 Instant Run