[Android] Bitmapを外部ストレージに保存してギャラリーに追加する

投稿日:2017年1月31日 更新日:

こんばんは、okuzawatsです。
仕事ではiOSアプリの開発ばかりで、Androidを忘れてしまいそうです。
プライベートでコツコツとAndroidアプリを開発して、Androidアプリも忘れないようにしていきたいと思います。

さて、Androidアプリを開発していると、Bitmapを画像ファイルとして書き出して、ギャラリーアプリから画像を見られるようにしたいことがあると思います。
わたくしはありました。

画像ファイルの保存とギャラリーとの共有がそれほど難しいことだとは思っていませんでした。
ところが、実際にやってみると微妙にハマるところがあって、思っていたよりも時間がかかってしまいました。

備忘録を兼ねて、この記事に要点や注意点を書き残しておこうと思います。
Bitmapの保存、ギャラリーへの画像の追加などで困っている人の参考になれば幸いです。
よろしくお願いします。

ちなみに言語はJavaです。

内部ストレージと外部ストレージの違い

まずはAndroidで画像を保存する場所について。
Androidでは、ファイルの保存先として「内部ストレージ」と「外部ストレージ」が選択できます。

内部ストレージは端末内部、外部ストレージはSDカード、と言いたいところですが、リファレンスには
一部の端末では、永続的なストレージ領域を「内部」と「外部」のパーティションに分割しており、リムーバブル記憶媒体が備わっていない場合でも常に2つのストレージスペースがあり、外部ストレージが取り外し可能であるか否かにかかわらず、APIの動作は同じです
などと書いてあり、混沌としています。

調べたところ、外部ストレージは「Androidとパソコンをつないだ状態でパソコンからアクセスできる領域」(出典:Androidプログラミングの教科書)だそうです。

内部ストレージと外部ストレージの特徴は以下の通りです。

内部ストレージ:

  • 常に使用可能。
  • ここに保存されたファイルは、自分のアプリからのみアクセスできます。
  • ユーザーがアプリをアンインストールすると、システムは内部ストレージから当該アプリのファイルをすべて削除します。

ユーザーからも他のアプリからも、自分のファイルにアクセスできないようにしたい場合、内部ストレージが最適です。

出典:ファイルを保存する | Android Developers、最終アクセス:2017年1月30日

外部ストレージ:

  • ユーザーは、USB ストレージなどの外部記憶装置をマウントできますが、端末から取り外す場合もあるため、常に使用可能というわけではありません。
  • 誰でも読み取り可能なため、ここに保存されたファイルは自分のコントロールの及ばない所で読み取られる可能性があります。
  • ユーザーがアプリをアンインストールすると、当該アプリのファイルは、getExternalFilesDir() からディレクトリに保存された場合に限り、ここからすべて削除されます。

アクセス制限を必要としないファイルや、他のアプリと共有したり、ユーザーがコンピュータ経由でアクセスできるようにしたりするファイルの場合、外部ストレージが最適です。

出典:ファイルを保存する | Android Developers、最終アクセス:2017年1月30日

内部ストレージは、常に利用可能な点が便利ですが、内部ストレージに保存するとユーザーからも他のアプリからもアクセスできないとのこと。
ギャラリーアプリからもアクセスできない、ということでしょうか。

一方、外部ストレージは取り外されることがあるので嫌ですが、外部ストレージに保存したファイルは他のアプリと共有することが可能です。
ということは、ギャラリーアプリと画像を共有したい時は内部ストレージよりも外部ストレージを使った方が良さそうですね。

ということで、外部ストレージにBitmapを画像として保存したのち、ギャラリーにも追加してみます。

AndroidManifest.xmlにパーミッションを追加

Androidアプリから外部ストレージにアクセスするためには、マニフェストファイルにパーミッションを追加しておかなければなりません。

外部ストレージからの読み込みは READ_EXTERNAL_STORAGE 、外部ストレージへの書き込みは WRITE_EXTERNAL_STORAGE のパーミッションが、それぞれ必要になります。
AndroidManifest.xmlに読み込みと書き込みのパーミッションをそれぞれ書いておきます。
こんな感じです。

これで、Androidアプリ内部から外部ストレージの読み書きができるようになりました。

外部ストレージの読み書き可否を確認する

外部ストレージからファイルを読んだり、ファイルを書き込んだりする前に、外部ストレージが利用可能かどうかを確認しておきます。
Environment#getExternalStorageState で外部ストレージの状態を取得できるので、これを MEDIA_MOUNTEDMEDIA_MOUNTED_READ_ONLY の値と比べてあげれば良いですね。

こんな感じでメソッドを作っておきます。

画像保存用のディレクトリを作成する

アプリから画像を保存するためのディレクトリを作成します。
外部ストレージ内にファイルを保存できるディレクトリのパスは Environment.getExternalStorageDirectory().getAbsolutePath() で取得できるので、この中に適当な名前でディレクトリを作ります。
ディレクトリの作成は File#mkdir です。

Bitmapを画像として保存する

Bitmapを画像として保存します。
jpeg形式とpng形式で保存できるのは確認しました。
Bitmapの保存には、 Bitmap#compress を使います。
フォーマット(jpeg、png、webp)、クオリティ、FileOutputStreamのインスタンスなどを渡します。
FileOutputStreamのインスタンスに対して、 flush() したり close() したりします。

画像をギャラリーに追加する

保存した画像は、そのままではギャラリーに追加されないので、 ContentResolver#insert を使ってギャラリーに追加します。
ContentValuesに画像の形式や画像ファイルのパスを渡した後、ContentResolver経由でinsertします。

まとめ

ここまでやってきた、Bitmapを画像ファイルとして外部ストレージに保存して、画像ファイルをギャラリーに追加する、という処理をまとめるとこんな感じになるでしょうか。
saveImageExternal() 内でBitmapをjpeg形式で保存し、 addImageToGallery() 内でギャラリーに追加しています。

何か間違ったこと書いていたら教えていただけると嬉しいです。以上、よろしくお願いします。

茨城県つくば市在住のAndroidアプリエンジニアです。Androidアプリ開発に関して何かあれば、メールフォームからお問い合わせください。できる範囲でお答えします。

メールフォーム

-Android

Copyright© Androidアプリ開発@つくば , 2017 AllRights Reserved Powered by AFFINGER4.