5

Android アプリでのカメラの利用に関するチュートリアルに従っています。エミュレーターと物理デバイスの両方でデバッグを実行すると、「アクセス許可の拒否: カメラを使用できません」というエラーが表示されます。マニフェスト ファイルでさまざまな権限を試しました。このエラーが発生したほとんどの人は、タイプミス、アクセス許可の欠落、またはマニフェストの適切な場所にアクセス許可がありませんでした。

これが私のマニフェストファイルです:

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

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.FLASHLIGHT" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera2" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".DBZHome"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".DBZStartPowerUp"
            android:label="@string/title_activity_dbzstart_power_up" >
        </activity>
    </application>

</manifest>

これが私の活動です:

package com.example.karudo.dbzrealpowerup;

import android.app.Activity;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.Bundle;
import android.util.Size;
import android.view.Menu;
import android.view.MenuItem;
import android.view.TextureView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class DBZStartPowerUp extends Activity {

    private Size mPreviewSize;
    private String mCameraId;
    private TextureView mTextureView;
    private TextureView.SurfaceTextureListener mSurfaceTextureListener =
            new TextureView.SurfaceTextureListener() {

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

                @Override
                public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

                }

                @Override
                public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
                    return false;
                }

                @Override
                public void onSurfaceTextureUpdated(SurfaceTexture surface) {

                }
            };

    private CameraDevice mCameraDevice;
    private CameraDevice.StateCallback mCameraDeviceStateCallback
            = new CameraDevice.StateCallback() {

        @Override
        public void onOpened(CameraDevice camera) {
            mCameraDevice = camera;
            Toast.makeText(getApplicationContext(), "Camera Opened!", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onDisconnected(CameraDevice camera) {
            camera.close();
            mCameraDevice = null;
        }

        @Override
        public void onError(CameraDevice camera, int error) {
            camera.close();
            mCameraDevice = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dbzstartpowerup);
        mTextureView = (TextureView) findViewById(R.id.dbzCameraPreview);
    }

    @Override
    public void onResume() {
        super.onResume();
        if(mTextureView.isAvailable()) {

        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_dbzstartpowerup, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void setupCamera(int width, int height) {
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            for(String cameraId : cameraManager.getCameraIdList()) {
                CameraCharacteristics cameraCharacteristics = cameraManager.getCameraCharacteristics(cameraId);
                if (cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
                        CameraCharacteristics.LENS_FACING_FRONT) {
                    continue;
                }
                StreamConfigurationMap map = cameraCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), width, height);
                mCameraId = cameraId;
                return;
            }
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }

    private Size getPreferredPreviewSize(Size[] mapSizes, int width, int height) {
        List<Size> collectorSizes = new ArrayList<>();
        for(Size option : mapSizes) {
            if(width > height) {
                if(option.getWidth() > width &&
                        option.getHeight() > height) {
                    collectorSizes.add(option);
                }
            } else {
                if(option.getWidth() > height &&
                        option.getHeight() > width) {
                    collectorSizes.add(option);
                }
            }
        }
        if(collectorSizes.size() > 0) {
            return Collections.min(collectorSizes, new Comparator<Size>() {
                @Override
                public int compare(Size lhs, Size rhs) {
                    return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getWidth() * rhs.getHeight());
                }
            });
        }
        return mapSizes[0];
    }

    private void openCamera() {
        CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
        try {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
        } catch (CameraAccessException e) {
            e.printStackTrace();
        }
    }
}

そして私のlogcatのエラー:

10-04 03:15:02.740     961-8780/? E/CameraService﹕ Permission Denial: can't use the camera pid=20601, uid=10059
10-04 03:15:02.741  20601-20601/com.example.karudo.dbzrealpowerup E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.karudo.dbzrealpowerup, PID: 20601
    java.lang.SecurityException: Lacking privileges to access camera service
            at android.hardware.camera2.utils.CameraBinderDecorator.throwOnError(CameraBinderDecorator.java:108)
            at android.hardware.camera2.legacy.CameraDeviceUserShim.connectBinderShim(CameraDeviceUserShim.java:336)
            at android.hardware.camera2.CameraManager.openCameraDeviceUserAsync(CameraManager.java:324)
            at android.hardware.camera2.CameraManager.openCamera(CameraManager.java:454)
            at com.example.karudo.dbzrealpowerup.DBZStartPowerUp.openCamera(DBZStartPowerUp.java:163)
            at com.example.karudo.dbzrealpowerup.DBZStartPowerUp.access$100(DBZStartPowerUp.java:23)
            at com.example.karudo.dbzrealpowerup.DBZStartPowerUp$1.onSurfaceTextureAvailable(DBZStartPowerUp.java:34)
            at android.view.TextureView.getHardwareLayer(TextureView.java:368)
            at android.view.View.updateDisplayListIfDirty(View.java:15151)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.updateDisplayListIfDirty(View.java:15169)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.updateDisplayListIfDirty(View.java:15169)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.updateDisplayListIfDirty(View.java:15169)
            at android.view.View.draw(View.java:15948)
            at android.view.ViewGroup.drawChild(ViewGroup.java:3609)
            at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3399)
            at android.view.View.draw(View.java:16181)
            at com.android.internal.policy.PhoneWindow$DecorView.draw(PhoneWindow.java:2690)
            at android.view.View.updateDisplayListIfDirty(View.java:15174)
            at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:281)
            at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:287)
            at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:322)
            at android.view.ViewRootImpl.draw(ViewRootImpl.java:2615)
            at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2434)
            at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2067)
            at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1107)
            at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6013)
            at android.view.Choreographer$CallbackRecord.run(Choreographer.java:858)
            at android.view.Choreographer.doCallbacks(Choreographer.java:670)
            at android.view.Choreographer.doFrame(Choreographer.java:606)
            at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:844)
            at android.os.Handler.handleCallback(Handler.java:739)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:148)
            at android.app.ActivityThread.main(ActivityThread.java:5417)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

私はアプリ開発に不慣れで、確かにデバッグは得意ではありませんが、他の人のファイル (およびわずか 4 か月前のチュートリアル) で見たものから、私のマニフェストのアクセス許可は正しいようです。

誰が私が間違ったことを教えてもらえますか?

乾杯、リー。

更新:デバッグ中に、このメソッドに到達するとすぐにクラッシュすることがわかりました...

private void openCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }

}

... 具体的には、try ステートメントを実行するとすぐに。

更新 2: try/catch ステートメントをコメントアウトすると、アプリはクラッシュしませんが、期待される結果 (「Camera Opened!」を出力する) は発生しません。何か案は?

更新 3: 申し訳ありませんが、上記の編集がいかに愚かであるかに気付きました。クラッシュしなくなった理由は明らかですが、少なくともcameraManager.openCameraパラメーターをデバッグする必要があることがわかりました。誰かが見ることができるなら、コードはそこにあります:)

4

5 に答える 5

3

(Android 6.0.0)を搭載したデバイスまたは(API 23)を搭載したエミュレータでアプリを試したときにのみ、同じエラーが発生しました。しかし、他のものではうまく機能します。

これは、M を超える Android がランタイム アプリケーションで付与されたアクセス許可にいくつかの変更を加えるためです。

それを行うには、次のいくつかの手順に従ってください。

最初: この静的変数を追加します。

private static final int REQUEST_CAMERA_RESULT = 1;

次に 、openCamera メソッドを変更します

private void openCamera() {
    CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
    try {
        Log.v("CAMERA", mCameraId + " " + mCameraDeviceStateCallback);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            if(ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA)
                    == PackageManager.PERMISSION_GRANTED){
                cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback,mBackgroundHandler);
            }
            else {
                if (shouldShowRequestPermissionRationale(android.Manifest.permission.CAMERA)){
                    Toast.makeText(this,"No Permission to use the Camera services", Toast.LENGTH_SHORT).show();
                }
                requestPermissions(new String[] {android.Manifest.permission.CAMERA},REQUEST_CAMERA_RESULT);
            }
        }
        else {
            cameraManager.openCamera(mCameraId, mCameraDeviceStateCallback, mBackgroundHandler);
        }
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

mBackgroundHandler を null に自由に変更してください。バックグラウンド スレッドでカメラの作業を処理するようにしました。

そして、このメソッドをオーバーライドします

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode){
        case  REQUEST_CAMERA_RESULT:
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED){
                Toast.makeText(this, "Cannot run application because camera service permission have not been granted", Toast.LENGTH_SHORT).show();
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        break;
    }
}
于 2016-02-11T16:33:06.063 に答える
1

私が言えるいくつかのポイント:

  1. カメラ サービスへの接続が失敗した場合 (たとえば、カメラが他のアプリケーションによって使用されている場合、デバイス マネージャーがカメラを無効にしている場合、またはいずれかのアプリケーションがカメラを解放できなかった場合など)。
  1. コード内でカメラを安全に閉じたり離したりしてください。

  2. カメラが他の何かによって使用されているかどうか、またはポリシー マネージャーにカメラがオフになっている設定があるかどうかを確認してみましたか?

于 2015-10-04T03:56:38.920 に答える
0

このコードを manifest.xml ファイルに入れてみてください。

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

<uses-permission
    android:name="android.permission.INTERNET"
    android:maxSdkVersion="19" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_DEBUG_APP"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
于 2015-10-04T03:48:33.467 に答える
0

アクセス許可に関する最初のオプションは、その特定のアプリケーションの Android 設定に移動し、そこから必要なすべてのアクセス許可を付与することです。2 番目のオプションは、実行時にそのアクセス許可が必要かどうかを確認し、アプリに必要なアクセス許可を要求するか、そのアクセス許可を必要とする機能の実行時にアクセス許可を要求することです。

アクティビティが開いたときにアプリのアクセス許可を要求するコードをいくつか見てみましょう。ただし、そのコードを変更して、カメラの撮影などの機能が必要な場合に許可を求めることができます。

もう 1 つのことは、インターネットなどの一部のアクセス許可はシステムによって自動的に付与されるため、要求する必要がないことです。ただし、明示的に要求する必要があります。

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CAMERA"/>  

上記は、AndroidManifest の開始アプリケーション タグのすぐ上で宣言されたアクセス許可を示しています。

    public boolean hasFineLocationPermission(){
        if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED){
            return true;
        }
        else{
            return false;
        }
    }

    public boolean hasExternalStoragePermission(){
        if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        else{
            return false;
        }
    }

public boolean hasLocationForegroundService(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.FOREGROUND_SERVICE) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}



public boolean hasReadPhoneStatePermission(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}

public boolean hasCameraPermission(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}

public boolean hasAccessNetworkState(){
    if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.ACCESS_NETWORK_STATE) == PackageManager.PERMISSION_GRANTED){
        return true;
    }
    else{
        return false;
    }
}

上記のいくつかの機能があります。すべての関数が基本的に行うことは、特定の権限が付与されているかどうかを確認することです。true が返される場合、その許可を求める必要はありません。false を返さない場合は、すぐにその許可を求めてください。例えば:

if (ContextCompat.checkSelfPermission(getApplicationContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED){ return true; }

これは、カメラにアクセスできるかどうかをチェックし、アクセスできる場合は True を返し、そうでない場合は関数 hasCameraPermission() に示すように false を返します。

以下の関数を使用して、すべての権限が付与されていることを確認できます。

public void getApplicationPermissions(){

    List<String> listPermissionsNeeded = new ArrayList<>();
    if (!hasFineLocationPermission()){

        listPermissionsNeeded.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);

    }

    if (!hasExternalStoragePermission()){

        listPermissionsNeeded.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);

    }

    if (!hasLocationForegroundService()){
        listPermissionsNeeded.add(Manifest.permission.ACCESS_FINE_LOCATION);

    }
    if (!hasReadPhoneStatePermission()){
        listPermissionsNeeded.add(Manifest.permission.READ_PHONE_STATE);
    }

    if (!hasCameraPermission()){
        listPermissionsNeeded.add(Manifest.permission.CAMERA);
    }

    if (!hasAccessNetworkState()){
        listPermissionsNeeded.add(Manifest.permission.ACCESS_NETWORK_STATE);
    }

    Log.i("listPermissionsNeeded", String.valueOf(listPermissionsNeeded));

    if (!listPermissionsNeeded.isEmpty()){
        ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
    }
    else{
        Toast.makeText(this,"All Permissions Required Are Granted",Toast.LENGTH_SHORT).show();
    }

}

上記のコードは、最初List<String> listPermissionsNeeded = new ArrayList<>();に String 型のリストを宣言します。

次に、次のことを確認します。

if (!hasCameraPermission()){
            listPermissionsNeeded.add(Manifest.permission.CAMERA);
        } 

意味: カメラへのアクセス許可が付与されていない場合は、そのアクセス許可を追加してリストに追加しますManifest.permission.CAMERA

permissionsList が空の場合、ユーザーにトーストを表示するだけです。ただし、いくつかの権限がある場合はActivityCompat.requestPermissions()、コンテキスト、リスト、および識別子 (この場合は 0) を渡して関数を呼び出します。

if (!listPermissionsNeeded.isEmpty()){
    ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);
}
else{
    Toast.makeText(this,"All Permissions Required Are Granted",Toast.LENGTH_SHORT).show();
} 

その requestPermissions が呼び出されると、別の Android メソッドが呼び出されますonRequestPermissionsResult()。以下のように実装しています。

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if(requestCode=0){
         for (int i=0;i<grantResults.length;i++){
            if (grantResults[i] == -1){
                getApplicationPermissions();

            }
        }

        }

    }

リクエスト コードは 0 になり、grantResults は関数で渡したリストになります。ActivityCompat.requestPermissions(this,listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),0);

ユーザーがパーミッションを拒否すると、grantResult に -1 の値が返されます。その関数で行うことは、grantResults をループして、拒否されたアクセス許可を検索することだけです。拒否されたアクセス許可がある場合は、何度も何度も要求します。それはユーザーエクスペリエンスにとってひどいものです。しかし、あなたは要点を理解します。

最後に、使用している Android のバージョンが Android Marshmallow Api Level 23 であるかどうかを確認する必要があります。私が得たものから、android 6.0 以下 マニフェストで宣言する限り、すべての権限を取得できますが、6.0 以降から必要になります許可を求める。このようにして、それより下の API レベルでのクラッシュを回避できます。これについては 100% ではありません。矛盾する情報を見つけましたが、それで解決しました。誰かが私を訂正してください。

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            getApplicationPermissions();

        }

混乱していないことを願っています。ご不明な点がございましたら、お問い合わせいただくか、必要に応じてすべてのコードがどのように結合されているかを確認できるように、github リンク ハウジングをお送りします。

すべてのコードは Java で書かれています。

于 2021-07-21T10:08:18.430 に答える