APIレベル29javaで非推奨のEnvironment.getExternalStorageDirectory()


109

Android Javaで作業しており、最近SDKをAPIレベル29に更新したところ、警告が表示され、

Environment.getExternalStorageDirectory() APIレベル29で非推奨になりました

私のコードは

private void saveImage() {

if (requestPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

    final String folderPath = Environment.getExternalStorageDirectory() + "/PhotoEditors";
    File folder = new File(folderPath);
    if (!folder.exists()) {
        File wallpaperDirectory = new File(folderPath);
        wallpaperDirectory.mkdirs();
    }


    showLoading("Saving...");
    final String filepath=folderPath
                + File.separator + ""
                + System.currentTimeMillis() + ".png";
    File file = new File(filepath);

    try {
        file.createNewFile();
        SaveSettings saveSettings = new SaveSettings.Builder()
                .setClearViewsEnabled(true)
                .setTransparencyEnabled(true)
                .build();
        if(isStoragePermissionGranted() ) {
            mPhotoEditor.saveAsFile(file.getAbsolutePath(), saveSettings, new PhotoEditor.OnSaveListener() {
            @Override
            public void onSuccess(@NonNull String imagePath) {
                hideLoading();
                showSnackbar("Image Saved Successfully");
                mPhotoEditorView.getSource().setImageURI(Uri.fromFile(new File(imagePath)));
                sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,Uri.fromFile(new File(filepath))));
                Intent intent = new Intent(EditImageActivity.this, StartActivity.class);
                startActivity(intent);
                finish();

            } 

            @Override
            public void onFailure(@NonNull Exception exception) {
                hideLoading();
                showSnackbar("Failed to save Image");
            }
       });
   }

これに代わるものは何でしょうか?

回答:


107

使用getExternalFilesDir()getExternalCacheDir()またはgetExternalMediaDirs()(メソッドのContext代わりに)Environment.getExternalStorageDirectory()

または、mPhotoEditorを使用できるように変更してから、次のようにしますUri

  • ユーザーが選択した場所に移動ACTION_CREATE_DOCUMENTするために使用するUri、または

  • 使用MediaStoreContentResolverおよびinsert()取得するためのUriメディアの特定の種類(例えば、画像)のために-を参照して、このサンプルアプリのWebサイトからMP4の動画をダウンロードするためにこれをやって証明します

また、Android 7.0以降では、を使用してwithがクラッシュUri.fromFileするACTION_MEDIA_SCANNER_SCAN_FILEはずであることに注意してくださいFileUriExposedException。Android Qでは、MediaStore/insert()オプションのみがコンテンツのインデックスをMediaStoreすばやく取得します。

マニフェストの要素をtargetSdkVersion使用android:requestLegacyExternalStorage="true"して、Android 10および11でこれらの「スコープストレージ」の変更をオプトアウトできることに注意してください<application>。Playストア(およびおそらく他の場所)を通じてアプリを配布する場合は、2021年のいつか30以上になる必要があるため、これは長期的な解決策targetSdkVersionはありません。


12
@CommonsWare様、実際にライブでお会いする場合は、ブログ投稿にビールをお借りしていることをお知らせください。私は29にtargetSdkレベルを上げたように私は今、私が知っているとすぐに、特定の外部ライブラリが働いて停止した理由を見つける全体の午後を過ごした理由は...
Nantoka

1
getExternalFilesDir()とgetExternalCacheDir()を使用すると、API 29で機能しますが、アプリがアンインストールされると、このメソッドを使用してすべてのデータが削除されます...アプリケーションタグ "android:requestLegacyExternalStorage =" true ""内でこの属性を使用すると、APIでも機能します。 29。アプリunistalledファイル名を終了するが、データは削除する場合
Ucdemir

1
@miladsalimi:申し訳ありませんが、あなたの質問がわかりません。別のStackOverflowの質問をして、懸念事項を詳細に説明することをお勧めします。
CommonsWare

3
そこにはありませんgetExternalMediaDirContext、それを自分を参照してください、:developer.android.com/reference/android/content/Context そしてgetExternalMediaDirs:廃止されて、また見てdeveloper.android.com/reference/android/content/...を 両方getExternalFilesDir()getExternalCacheDir()(アプリケーションのアンインストール時に削除されます同じURLを参照してください)。したがって、上記のいずれもを置き換えることはできませんEnvironment.getExternalStorageDirectory()
DIMS

1
それは私のユースケースではなく、上記のtopicstarterのユースケースです。
DIMS

33

destPath新しいAPI呼び出しでを取得します。

String destPath = mContext.getExternalFilesDir(null).getAbsolutePath();

3
この方法を使用できます:developer.android.com/training/data-storage/…?あなたはそれについてどう思いますか ?:)
aeroxr1

2
それでも環境変数を取得できますgetExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)がEnvironment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)を置き換えます
Rowan Berry

非推奨のメソッドを、29をターゲットとする外部ストレージのcreateNewFileからのこの修正済みの「Permissiondenied」エラーに更新しました。ありがとうございました!
coolcool19 9420

これはパブリックディレクトリではありません
Irfan UlHaq20年

25

Android Qの場合android:requestLegacyExternalStorage="true"、マニフェストの要素に追加できます。これにより、レガシーストレージモデルが選択され、既存の外部ストレージコードが機能します。

<manifest ... >
<!-- This attribute is "false" by default on apps targeting
     Android 10 or higher. -->
  <application android:requestLegacyExternalStorage="true" ... >
    ...
  </application>
</manifest>

技術的には、これはtargetSdkVersion29に更新した場合にのみ必要targetSdkVersionです。値が小さいアプリはデフォルトでレガシーストレージandroid:requestLegacyExternalStorage="false"にオプトインするため、オプトアウトする必要があります。


1
この投稿により、8時間続いた以前に開発されたコードを使用して最新バージョンのAndroidStudioの外部ストレージにデータを保存するソリューションの検索が保存されました。ありがとうございました。
SriramNadiminti20年

1
API 29まで機能します。「注意:アプリをAndroid 11(APIレベル30)をターゲットに更新した後、アプリがAndroid 11デバイスで実行されている場合、システムはrequestLegacyExternalStorage属性を無視するため、アプリはスコープをサポートする準備ができている必要がありますストレージを作成し、それらのデバイス上のユーザーのアプリデータを移行します。」 developer.android.com/training/data-storage/use-cases
avisper

まだ同じエラーを取得
olajide

6

Android 10でファイルを作成するときはgetExternalFilesDir()getExternalCacheDir()代わりにを使用してくださいEnvironment.getExternalStorageDirectory()

以下の行を参照してください。

val file = File(this.externalCacheDir!!.absolutePath, "/your_file_name")

1
こんにちは、getExternalFilesDir()画像を保存するためのカスタムパスでどのように使用できますか?これを使用して画像がギャラリーに表示されないようにしますか?デフォルトでは、保存先Andorid/data/packagename/...
miladsalimi20年

ファイルをアプリディレクトリに保存してから、Media uri
AjithMemana20年

6
間違った答え../ storage / emulated / 0 / MyFolder /を取得する方法についての実際の質問ですが、anser /data/user/0/com.example.package/files/MyFolder
TarifChakder20年

5

これは、デフォルトのカメラを使用して写真を撮り、それをDCIMフォルダー(DCIM / app_name / filename.jpg)に保存する場合に、ファイルのURIを取得する方法の小さな例です。

カメラを開く(CAMERAの許可について覚えておいてください):

private var photoURI: Uri? = null

private fun openCamera() {
    Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
        photoURI = getPhotoFileUri()
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
        takePictureIntent.resolveActivity(requireActivity().packageManager)?.also {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
        }
    }
}

そしてURIを取得します:

private fun getPhotoFileUri(): Uri {
    val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
    val fileName = "IMG_${timeStamp}.jpg"

    var uri: Uri? = null
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        val resolver = requireContext().contentResolver
        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
            put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg")
            put(MediaStore.MediaColumns.RELATIVE_PATH, "DCIM/app_name/")
        }

        uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues)
    }

    return uri ?: getUriForPreQ(fileName)
}

private fun getUriForPreQ(fileName: String): Uri {
    val dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
    val photoFile = File(dir, "/app_name/$fileName")
    if (photoFile.parentFile?.exists() == false) photoFile.parentFile?.mkdir()
    return FileProvider.getUriForFile(
        requireContext(),
        "ru.app_name.fileprovider",
        photoFile
    )
}

Q以前のWRITE_EXTERNAL_STORAGE権限を忘れず、AndroidManifest.xmlにFileProviderを追加してください。

そして結果を得る:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        REQUEST_IMAGE_CAPTURE -> {
            photoURI?.let {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    val thumbnail: Bitmap =
                        requireContext().contentResolver.loadThumbnail(
                            it, Size(640, 480), null
                        )
                } else {
                    // pre Q actions
                }
            }
        }
    }
}

また、非推奨です
LeonardoHenao20年

1
ポスト
イット

5

これは私のために働いた

この行をmanifestファイルのアプリケーションタグに追加します

android:requestLegacyExternalStorage="true"

 <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:networkSecurityConfig="@xml/network_security_config"
        android:requestLegacyExternalStorage="true"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

 </application>

ターゲットSDKは29です

  defaultConfig {
        minSdkVersion 16
        targetSdkVersion 29
        multiDexEnabled true
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

1
このコメントの複製:stackoverflow.com/a/61774820/7487335
JoshCorreia20年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.