質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Q&A

解決済

1回答

649閲覧

uriデータをプレファレンスデータに保存して使用する事が出来ない(android/java)

rstaff

総合スコア2

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

0グッド

0クリップ

投稿2023/02/13 06:36

質問

JavaでAndroidアプリの開発をしています。
ギャラリーを開き写真を選択し、選択した写真をimageviewに表示させます。
また次起動した時もその写真を表示させたいので、取得したuriをプレファレンスデータに保存して、再表示させようとしています。

現在は写真を選択してimageviewに表示させる所までは出来たのですが、プレファレンスデータに保存したuriを取得してimageviewに表示させようとすると落ちてしまいます。

以下ソースになります。
★の所で落ちているみたいです。
保存したuriから表示出来ない事で原因が分かる方がいらっしゃいましたらご教授お願い致します。

①写真を選択してimageviewに表示
(流れ:取得したuriからbitmapに変換してimageviewに表示。またuriをuri.toString()で文字列にしてプレファレンスデータに保存)

private final ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { Intent resultData = result.getData(); if (resultData != null) { try{ Uri uri = resultData.getData(); ParcelFileDescriptor pfDescriptor = getContentResolver().openFileDescriptor(uri, "r"); if(pfDescriptor != null){ FileDescriptor fileDescriptor = pfDescriptor.getFileDescriptor(); Bitmap bmp = BitmapFactory.decodeFileDescriptor(fileDescriptor); pfDescriptor.close(); ImageView imageView = (ImageView) findViewById(R.id.page61Image); imageView.setImageBitmap(bmp); //プレファレンスデータに文字列データを登録 SharedPreferences.Editor editor = PreferencesData.edit(); editor.putString("Data61Photo", uri.toString()); editor.apply(); TextView textView62 = findViewById(R.id.page62Edit); textView62.setText(uri.toString()); //P6-2のデータをテキストに表示 } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } });

②プレファレンスデータからuriを取得してimageviewに表示
(流れ:プレファレンスデータから文字列のuriを取得して、Uri.parseでUriに戻し同様にbitmapにして表示させる)

private void func() throws IOException { Data61Photo = PreferencesData.getString("Data61Photo", null); //データを取得 if (Data61Photo != null) { Uri uri = Uri.parse(Data61Photo); ParcelFileDescriptor pfDescriptor = getContentResolver().openFileDescriptor(uri, "r"); ★ここで落ちる FileDescriptor fileDescriptor = pfDescriptor.getFileDescriptor(); Bitmap bmp = BitmapFactory.decodeFileDescriptor(fileDescriptor); pfDescriptor.close(); ImageView imageView = (ImageView) findViewById(R.id.page61Image); imageView.setImageBitmap(bmp); } }

補足

・保存前も保存後もuriをデバックすると以下表示となっています。
content://com.android.providers.media.documents/document/image%3A1000000044

・以下でも質問しましたがuriから絶対パスを取得出来ないことは分かりました。
https://teratail.com/questions/u21iy6lazjyuu0?_from_e=e_qctq_title

・パーミッションの指摘を受けましたが以下追加しました。
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
AndroidManifest.xmlに以下追加しました。
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

しかしgetContentResolver().takePersistableUriPermissionで落ちてしまいます。
あまり理解出来ていないかもしれませんがパーミッション設定の方法が原因であればアドバイス願いたいです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ドキュメントプロバイダから OpenDocument 等で得た uri に対して永続的な権限を設定する必要があります。
以下は、 FAB で選択した画像 uri をプリファレンスに保存し、起動時に再表示するものです。

MainActivity.java

java

1import android.content.*; 2import android.net.Uri; 3import android.os.*; 4import android.util.Log; 5import android.widget.ImageView; 6 7import androidx.activity.result.ActivityResultLauncher; 8import androidx.activity.result.contract.ActivityResultContracts; 9import androidx.appcompat.app.AppCompatActivity; 10 11import com.google.android.material.floatingactionbutton.FloatingActionButton; 12 13public class MainActivity extends AppCompatActivity { 14 private static final String LOG_TAG = "MainActivity **"; 15 16 @Override 17 protected void onCreate(Bundle savedInstanceState) { 18 super.onCreate(savedInstanceState); 19 setContentView(R.layout.activity_main); 20 21 ImageView imageView = findViewById(R.id.imageView); 22 23 SharedPreferences pref = getPreferences(MODE_PRIVATE); 24 25 String uriStr = pref.getString("uri", null); 26 if(uriStr != null) { 27 Uri uri = Uri.parse(uriStr); 28 Log.d(LOG_TAG, "pref uri=" + uri); 29 imageView.setImageURI(uri); 30 } 31 32 ActivityResultLauncher<String[]> launcher = registerForActivityResult(new ActivityResultContracts.OpenDocument(), uri -> { 33 Log.d(LOG_TAG, "image uri=" + uri); 34 imageView.setImageURI(uri); 35 if(Build.VERSION.SDK_INT >= 19) { 36 Log.d(LOG_TAG, "takePersistableUriPermission"); 37 getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 38 } 39 Log.d(LOG_TAG, "pref save"); 40 pref.edit().putString("uri", uri.toString()).commit(); 41 }); 42 43 FloatingActionButton fab = findViewById(R.id.fab); 44 fab.setOnClickListener(v -> { 45 Log.d(LOG_TAG, "launch selector"); 46 launcher.launch(new String[]{"image/*"}); 47 }); 48 } 49}

res/layout/activity_main.xml

xml

1<?xml version="1.0" encoding="utf-8"?> 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 tools:context=".MainActivity"> 8 9 <ImageView 10 android:id="@+id/imageView" 11 android:layout_width="0dp" 12 android:layout_height="0dp" 13 app:layout_constraintBottom_toBottomOf="parent" 14 app:layout_constraintEnd_toEndOf="parent" 15 app:layout_constraintStart_toStartOf="parent" 16 app:layout_constraintTop_toTopOf="parent" /> 17 18 <com.google.android.material.floatingactionbutton.FloatingActionButton 19 android:id="@+id/fab" 20 android:layout_width="wrap_content" 21 android:layout_height="wrap_content" 22 android:src="@android:drawable/ic_menu_report_image" 23 android:contentDescription="add" 24 android:layout_margin="16dp" 25 app:layout_constraintBottom_toBottomOf="parent" 26 app:layout_constraintEnd_toEndOf="parent" /> 27 28</androidx.constraintlayout.widget.ConstraintLayout>

実行時ログ(実行→FAB押下して画像選択→終了→再実行)

D/MainActivity **: launch selector D/MainActivity **: image uri=content://com.android.providers.media.documents/document/image%3A14167 D/MainActivity **: takePersistableUriPermission D/MainActivity **: pref save D/MainActivity **: pref uri=content://com.android.providers.media.documents/document/image%3A14167

投稿2023/02/13 08:37

編集2023/02/15 06:00
jimbe

総合スコア12696

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

rstaff

2023/02/15 04:19

jimbe様 ご回答ありがとうございます。 永続的な権限を得るとは以下の処理の事でしょうか。 getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); 補足にもありますが、getContentResolver().takePersistableUriPermissionを実施しても落ちてしまいました。 具体的な実施場所は以下となります。 """---------------------------------------------------------------------------------------------------------- private final ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult( new ActivityResultContracts.StartActivityForResult(), result -> { if (result.getResultCode() == Activity.RESULT_OK) { Intent resultData = result.getData(); if (resultData != null) { try{ Uri uri = resultData.getData(); getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION); ★追加。でも落ちる """---------------------------------------------------------------------------------------------------------- サンプルコードも同じ処理をしているように見えたのですが違いはありますでしょうか。
jimbe

2023/02/15 06:26 編集

違いは uri の取り方です、 takePersistableUriPermission はドキュメントプロバイダ(≠コンテンツプロバイダ?)に対して ACTION_OPEN_DOCUMENT/AQCTION_CREATE_DOCUMENT/ACTION_OPENN_DOCUMENT_TREE を発行することで得た uri に対してのみ行えるようです。 これは uri の内容が違うのでは無く、取得の仕方によってクライアントに一時的に付与されるパーミッションが(永続化出来る/出来ないが)違うのではないかと思います。 この辺り明確なドキュメントが見つかりませんでしたが、[共有ストレージのドキュメントやファイルにアクセスする]( https://developer.android.com/training/data-storage/shared/documents-files?hl=ja )ではユースケースとしてこの三つが書かれた上で、「権限を保持する」方法として takePersistableUriPermission が使われています。
rstaff

2023/02/15 08:23

jimbe様 ご回答ありがとうございます。 ご指摘通り以下[StartActivityForResult]で取っていたのを[OpenDocument]に変更した所実装出来ました。 [変更前] ActivityResultLauncher<Intent> activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> { [変更後] ActivityResultLauncher<String[]> launcher = registerForActivityResult(new ActivityResultContracts.OpenDocument(), uri -> { かなり苦戦していたので出来た時は感動しました。 本当に助かりました。ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問