web-dev-qa-db-ja.com

Android 6.0マシュマロ。 SDカードに書き込めません

写真を保存するために外部ストレージを使用するアプリがあります。必要に応じて、そのマニフェストで、次のアクセス許可が要求されます

<uses-permission Android:name="Android.permission.CAMERA" />
<uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" />

そして、以下を使用して必要なディレクトリを取得します

File sdDir = Environment
            .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd", Locale.US);
String date = dateFormat.format(new Date());
storageDir = new File(sdDir, getResources().getString(
            R.string.storagedir)
            + "-" + date);

// Create directory, error handling
if (!storageDir.exists() && !storageDir.mkdirs()) {
 ... fails here

アプリはAndroid 5.1〜2.3で正常に動作します。 Google Playで1年以上使用されています。

テスト用の携帯電話の1つ(Android One)を6にアップグレードした後、必要なディレクトリ「/ sdcard/Pictures/myapp-yy-mm」を作成しようとするとエラーが返されます。

Sdカードは「ポータブルストレージ」として構成されています。 sdカードをフォーマットしました。交換しました。再起動しました。すべて役に立たない。

また、組み込みのAndroidスクリーンショット機能(Power + Lowerボリューム経由)は、「ストレージ容量が限られているか、アプリまたは組織によって許可されていない」ために失敗しています。

何か案は?

51
RudyF

私は同じ問題に直面しました。 Androidには2種類のアクセス許可があります。

  • 危険(連絡先へのアクセス、外部ストレージへの書き込み...)
  • 普通

通常の権限はAndroidによって自動的に承認されますが、危険な権限はAndroidユーザーによって承認される必要があります。

Android 6.0で危険な権限を取得する戦略は次のとおりです

  1. 許可が付与されているかどうかを確認します
  2. アプリに既に許可が与えられている場合は、先に進み、正常に実行してください。
  3. アプリにまだ許可がない場合は、ユーザーに承認を求めます
  4. OnRequestPermissionsResultでユーザーの承認を聞く

私の場合は次のとおりです。外部ストレージに書き込む必要があります。

最初に、許可があるかどうかを確認します。

...
private static final int REQUEST_WRITE_STORAGE = 112;
...
boolean hasPermission = (ContextCompat.checkSelfPermission(activity,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
if (!hasPermission) {
    ActivityCompat.requestPermissions(parentActivity,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_WRITE_STORAGE);
}

次に、ユーザーの承認を確認します。

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode)
    {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                //reload my activity with permission granted or use the features what required the permission
            } else
            {
                Toast.makeText(parentActivity, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
            }
        }
    }

}

新しい許可モデルの詳細については、こちらをご覧ください: https://developer.Android.com/training/permissions/requesting.html

49
codingpuss

最初にAndroid Mの危険な許可リスト以降のバージョンを提供します

enter image description hereenter image description here

次に、Android Mおよびlaterバージョンで許可をリクエストする方法の例を示します。

ユーザーにWRITE_EXTERNAL_STORAGE許可を求めます。

最初にAndroidメニフェストファイルに許可を追加

ステップ1 requestcodeを宣言する

 private static String TAG = "PermissionDemo";
 private static final int REQUEST_WRITE_STORAGE = 112; 

ステップ2ユーザーに許可を求める場合にこのコードを追加します

 //ask for the permission in Android M
    int permission = ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE);

    if (permission != PackageManager.PERMISSION_GRANTED) {
        Log.i(TAG, "Permission to record denied");

        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("Permission to access the SD-CARD is required for this app to Download PDF.")
                    .setTitle("Permission required");

            builder.setPositiveButton("OK", new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int id) {
                    Log.i(TAG, "Clicked");
                    makeRequest();
                }
            });

            AlertDialog dialog = builder.create();
            dialog.show();

        } else {
            makeRequest();
        }
    }

    protected void makeRequest() {
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_WRITE_STORAGE);
    }

ステップリクエストのオーバーライドメソッドを追加する

 @Override
public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
    switch (requestCode) {
        case REQUEST_WRITE_STORAGE: {

            if (grantResults.length == 0
                    || grantResults[0] !=
                    PackageManager.PERMISSION_GRANTED) {

                Log.i(TAG, "Permission has been denied by user");

            } else {

                Log.i(TAG, "Permission has been granted by user");

            }
            return;
        }
    }
}

注:メニフェストファイルに権限を追加することを忘れないでください

複数の権限とすべてのシナリオをカバーする以下の最良の例

簡単に理解できるようにコメントを追加しました。

import Android.Manifest;
import Android.content.DialogInterface;
import Android.content.Intent;
import Android.content.pm.PackageManager;
import Android.net.Uri;
import Android.provider.Settings;
import Android.support.annotation.NonNull;
import Android.support.v4.app.ActivityCompat;
import Android.support.v4.content.ContextCompat;
import Android.support.v7.app.AlertDialog;
import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.view.View;
import Android.widget.Button;
import Android.widget.Toast;

import com.production.hometech.busycoder.R;

import Java.util.ArrayList;

public class PermissionInActivity extends AppCompatActivity implements View.OnClickListener {

    private static final int REQUEST_PERMISSION_SETTING = 99;
    private Button bt_camera;
    private static final String[] PARAMS_TAKE_PHOTO = {
            Manifest.permission.CAMERA,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
    private static final int RESULT_PARAMS_TAKE_PHOTO = 11;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_permission_in);

        bt_camera = (Button) findViewById(R.id.bt_camera);

        bt_camera.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {

        switch (view.getId()) {

            case R.id.bt_camera:

                takePhoto();

                break;

        }
    }


    /**
     * shouldShowRequestPermissionRationale() = This will return true if the user had previously declined to grant you permission
     * NOTE :  that ActivityCompat also has a backwards-compatible implementation of
     * shouldShowRequestPermissionRationale(), so you can avoid your own API level
     * checks.
     * <p>
     * shouldShowRequestPermissionRationale() =  returns false if the user declined the permission and checked the checkbox to ask you to stop pestering the
     * user.
     * <p>
     * requestPermissions() = request for the permisssiion
     */
    private void takePhoto() {

        if (canTakePhoto()) {

            Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show();

        } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

            Toast.makeText(this, "You should give permission", Toast.LENGTH_SHORT).show();
            ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO);

        } else {
            ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO);
        }

    }

    //  This method return  permission denied String[] so we can request again
    private String[] netPermisssion(String[] wantedPermissions) {
        ArrayList<String> result = new ArrayList<>();

        for (String permission : wantedPermissions) {
            if (!hasPermission(permission)) {
                result.add(permission);
            }
        }

        return (result.toArray(new String[result.size()]));

    }

    private boolean canTakePhoto() {
        return (hasPermission(Manifest.permission.CAMERA) && hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE));
    }

    /**
     * checkSelfPermission() = you can check if you have been granted a runtime permission or not
     * ex = ContextCompat.checkSelfPermission(this,permissionString)== PackageManager.PERMISSION_GRANTED
     * <p>
     * ContextCompat offers a backwards-compatible implementation of checkSelfPermission(), ActivityCompat offers a backwards-compatible
     * implementation of requestPermissions() that you can use.
     *
     * @param permissionString
     * @return
     */
    private boolean hasPermission(String permissionString) {
        return (ContextCompat.checkSelfPermission(this, permissionString) == PackageManager.PERMISSION_GRANTED);
    }

    /**
     * requestPermissions() action goes to onRequestPermissionsResult() whether user can GARNT or DENIED those permisssions
     *
     * @param requestCode
     * @param permissions
     * @param grantResults
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == RESULT_PARAMS_TAKE_PHOTO) {

            if (canTakePhoto()) {

                Toast.makeText(this, "You can take picture", Toast.LENGTH_SHORT).show();

            } else if (!(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) {


                final AlertDialog.Builder settingDialog = new AlertDialog.Builder(PermissionInActivity.this);
                settingDialog.setTitle("Permissioin");
                settingDialog.setMessage("Now you need to enable permisssion from the setting because without permission this app won't run properly \n\n  goto -> setting -> appInfo");
                settingDialog.setCancelable(false);

                settingDialog.setPositiveButton("Setting", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                        dialogInterface.cancel();

                        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package", getPackageName(), null);
                        intent.setData(uri);
                        startActivityForResult(intent, REQUEST_PERMISSION_SETTING);
                        Toast.makeText(getBaseContext(), "Go to Permissions to Grant all permission ENABLE", Toast.LENGTH_LONG).show();

                    }
                });
                settingDialog.show();

                Toast.makeText(this, "You need to grant permission from setting", Toast.LENGTH_SHORT).show();

            }

        }

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_PERMISSION_SETTING) {

            if (canTakePhoto()) {

                Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show();

            }

        }

    }


}

構成変更の特殊なケース

許可ダイアログがフォアグラウンドにあるときに、ユーザーがデバイスを回転させたり、構成の変更をトリガーしたりする可能性があります。私たちのアクティビティはそのダイアログの背後にまだ表示されているため、破棄されて再作成されますが、アクセス許可ダイアログを再度再表示する必要はありません。

そのため、isInPermissionという名前のブール値を使用します。これは、アクセス許可を要求中かどうかを追跡します。 onSaveInstanceState()でその値を保持します。

@Override
protected void onSaveInstanceState(Bundle outState) {
  super.onSaveInstanceState(outState);
  outState.putBoolean(STATE_IN_PERMISSION, isInPermission);
}

onCreate()で復元します。必要なアクセス許可のすべてを保持していないが、isInPermissionがtrueの場合、既に実行中のため、アクセス許可の要求をスキップします。

21
Arpit Patel

Androidは、エラーの原因であるAndroid 6.0でのアクセス許可の動作を変更しました。ユーザーが使用する許可を許可したかどうかを実際に要求して確認する必要があります。そのため、マニフェストファイルの権限は21以下のapiでのみ機能します。api23で権限が要求される方法のスニペットについては、このリンクを確認してください http://Android-developers.blogspot.nl/2015/09/google-play- services-81-and-Android-60.html?m = 1

コード:-

If (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
                PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_RC);
            return;
        }`


` @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == STORAGE_PERMISSION_RC) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //permission granted  start reading
            } else {
                Toast.makeText(this, "No permission to read external storage.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}
7
codeFreak

プロジェクトで生成されたコードからマニフェストクラスを使用できない場合があります。そのため、Android SDKのマニフェストクラス「Android.Manifest.permission.WRITE_EXTERNAL_STORAGE」を使用できます。ただし、Marsmallowバージョンでは、ストレージカテゴリのWRITEおよびREAD EXTERNAL STORAGEを付与する必要があります。私のプログラムを参照してください。私のプログラムは、ユーザーが「はい」を選択するまで許可を要求し、許可が与えられた後に何かをします。

            if (Build.VERSION.SDK_INT >= 23) {
                if (ContextCompat.checkSelfPermission(LoginActivity.this, Android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(LoginActivity.this, Android.Manifest.permission.READ_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(LoginActivity.this,
                            new String[]{Android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Android.Manifest.permission.READ_EXTERNAL_STORAGE},
                            1);
                } else {
                    //do something
                }
            } else {
                    //do something
            }
6
icaksama

右。だから私はついに問題の底に着きました:それは失敗したインプレースOTAアップグレードでした。

Garmin Fenix 2がBluetooth経由で接続できず、「マシュマロアップグレードの問題」をグーグルで検索した後、私の疑念が強まりました。とにかく、「工場出荷時設定へのリセット」は問題を修正しました。

驚くべきことに、リセットしても電話機は元のキットカットに戻りませんでした。代わりに、ワイププロセスはOTAでダウンロードされた6.0アップグレードパッケージを選択して実行し、「よりクリーンな」アップグレードになったと思います。

もちろん、これは、インストールしたアプリがすべて電話で失われたことを意味します。しかし、私のものを含め、新しくインストールしたアプリは、変更なしで機能します(つまり、下位互換性があります)。ふう!

3
RudyF

Manifest.permission.Manifest.permission.WRITE_EXTERNAL_STORAGE状態に関するAndroidドキュメント:

APIレベル19以降、この権限は、getExternalFilesDir(String)およびgetExternalCacheDir()によって返されるアプリケーション固有のディレクトリ内のファイルの読み取り/書き込みにnotが必要です。 。


Ithinkこれは、WRITE_EXTERNAL_STORAGEアプリがアプリに固有ではないディレクトリに書き込む場合を除き、許可。

マニフェストで許可ごとに最大SDKバージョンを定義できます:

 <uses-permission Android:name="Android.permission.WRITE_EXTERNAL_STORAGE" Android:maxSdkVersion="19" />

また、マニフェストではなくbuild.graddleのターゲットSDKを必ず変更してください。Gradle設定は常にマニフェスト設定を上書きします。

Android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
    minSdkVersion 17
    targetSdkVersion 22
}
3
Stefano D