ヽ|∵|ゝ(Fantom) の 開発blog? ホーム »SDカード
このページの記事一覧

【Android】【Java】エラーをSDカードに書き出す  


 エミュレータではエラーが出ない(もしくは操作などの関係でテストしにくいなど)のに、実機ではエラーが出るときってあるよね。だけど「エラー:〇〇が予期せず停止しました。」だけだとデバッグするのが難しいので、例外のスタックトレースをそのままSDカードに保存する方法をやってみよう。あくまでも開発者用で特別な機能は何も付いてないので、その辺は自分で自由に付け足して使って欲しい。

●例外(エラー)のスタックトレースをSDカードに書き出すクラス
import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import android.os.Environment;

/**<h1>エラーを SDカードに書き出す</h1>
* <p>マニフェストファイルに WRITE_EXTERNAL_STORAGE の属性と、SDカードをあらかじめマウントしておくことが必要。</p>
*/
public class ErrorReporter implements UncaughtExceptionHandler {

//マニフェストファイルに
//<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
//が必要

//※SDカードのルートに保存される。ルートは実機によって「/sdcard」や「/mnt/sdcard」など異なる。
static final File errorReportFile = new File(Environment.getExternalStorageDirectory().getPath()
+ File.separator + "error_report.txt"); //※ファイル名は任意

//コンストラクタ
public ErrorReporter() {
}

//catch されなかった例外を受け取るハンドラ
@Override
public void uncaughtException(Thread thread, Throwable ex) {
PrintWriter pw = null;
try {
//※ここでエラーにならないように注意(要 SD カード[マウント])
pw = new PrintWriter(new FileOutputStream(errorReportFile));
ex.printStackTrace(pw);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
pw.close();
} catch (Exception e2) {
}
pw = null;
}
}
}

 使い方は以下に示す使用例のように Activity のコンストラクタ(または onCreate()) などでハンドラとしてインスタンス化すれば良いだけだ。ボタンなどはテスト用なので任意で良い。終了処理などは何も付けてないので、実際にエラーが発生したときはフリーズしたようになる。なので、アプリは手動で終了して欲しい。

 try~catch~finally ブロックは少し冗長に感じるが、try-with-resources 文は API 19(Android4.4)以降しか使えないようなので、この方が無難だろう。使用中はSDカードが実機に入っていることを前提としている。普通にSDカードが読み書きできている分にはここで例外が発生することは無いハズだ。もちろん、マニフェストファイルに「SDカードへ書き込み許可」を書いておくことも必要となる。例外捕捉中に例外を発生させないよう気をつけよう(笑)。

●SD カードの書き込みを許可するタグ(<manifest>~</manifest>の間が良い)
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


●使用例(ボタンを押すとエラーが発生するテスト用)
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

int[] data = new int[3]; //エラーテスト用

public MainActivity() {
//catch されなかった例外を受け取るハンドラを設定
Thread.setDefaultUncaughtExceptionHandler(new ErrorReporter()); //onCreate() でも良い
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main); //ボタン1個のレイアウト(任意)

//わざと例外を発生させるボタン
Button button1 = (Button)this.findViewById(R.id.button1);
button1.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("LOG", "button1 onClick");
data[5] = 1; //ArrayIndexOutOfBoundsException
}
});
}
}

●出力された「error_report.txt」の内容
java.lang.ArrayIndexOutOfBoundsException
at com.example.testandroid.MainActivity$1.onClick(MainActivity.java:28)
at android.view.View.performClick(View.java:2408)
at android.view.View$PerformClick.run(View.java:8816)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4627)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
at dalvik.system.NativeStart.main(Native Method)

 実際にメインに必要なコードは「Thread.setDefaultUncaughtExceptionHandler(new ErrorReporter());」の1行だけになる。上の例ではコンストラクタで定義しているが、onCreate() でも構わない。もちろん、エラーが発生する前である必要はある。

 出力されたテキストファイルはSDカードのルートに置かれ(実機によって「/sdcard」や「/mnt/sdcard」など異なる)、スマホに元から入っている「マイファイル」(アプリ名は端末よる)などでそのまま覗ける。エミュレータ上でも「DDMS」で右上にある「pull」ボタンを押せば、ローカルに落とせる。テキストエディタなどで確認すれば良い。なお、ファイル名は任意で良い。


 ちなみに元ネタは「アプリがエラーで強制終了したときに、バグレポートを送信する機能」で、それを超簡単にして、SDカードにエラーを吐き出させる機能のみに限定したものだったりする。なのでユーザー向けでなく、自分用(開発者用)という感じなので、ダイアログすら付けてない。ちゃんとしたものが欲しければ以下のURLを参照して欲しい。メモリ使用状況やビルドのバージョン情報などを付加するのもデバッグの役に立つかも知れない。

(参考)
Androidアプリのバグ報告システムを考える
Android キャッチされなかった例外を処理する
Android でアプリケーションが強制終了したとき、エラーレポートを送るようにする

(SDカードの書き込み権限など)
【スマホのコツ】Android5.0以上でSDカードへ書き込みができるファイラーアプリとその方法


(関連記事)
【Android】エミュレータでSDカードをマウントして使う方法 その2
【Android】SDカードにテキストファイルを保存する
【Android】SDカードからテキストファイルを読み込む
【Android】内部ストレージにテキストファイルを保存する
【Android】Preference 機能を使ってデータを読み込み・保存する
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


スポンサーサイト

category: Android

thread: プログラミング

janre: コンピュータ

tag: テキストファイル保存  SDカード 
tb: 0   cm: --

【Android】【Java】SDカードのファイルを削除する  


基本的にはローカルファイルの操作と同じなのだが、ファイル削除も書いておこう。

isMountSDCard(), toSDCardAbsolutePath() は以前に作ったものを、そのまま使う。
(※) Android4.4(API 19)あたりから内部ストレージの仮想パスに変わった模様

また、マニフェストファイル(AndroidManifest.xml) に以下のパーミッションが必要になる。

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

//SDCard のファイルを削除する(Android 用)
//<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> が必要。
public static final boolean deleteFileSDCard(String fileName) {
if (!isMountSDCard()) {
return false;
}
File file = new File(toSDCardAbsolutePath(fileName));
return file.delete();
}

//メインでは... (※例外処理は省略)
String fileName = "sample.jpg"; // "mnt/sdcard/sample.jpg" or "sdcard/sample.jpg" 等になる(端末による)
deleteFileSDCard(fileName);
//ファイルが削除されていれば成功(エミュレータならDDMSで確認できる)

例外処理は手抜きしてるので、必要あれば適当に。
実機での確認は、「マイファイル」のようなアプリで簡単に覗くことができる(アプリは端末による)。

戻値は File.delete() の成功をそのまま返しているが、必要なければ無視しても良い。

SDカードのマウント状態だけしかチェックしてないので、必要なら MOUNTED_READ_ONLY (読み取り専用) などもチェックした方が良いかも知れない。その辺は外部ストレージの状態を参考にして、色々付け加えると良いだろう。例外処理にはしてないので、必要ならば、例外を throw するように修正しても良いと思う。


(関連記事)
【Android】SDカードに画像ファイルを保存する(jpg)
【Android】SDカードに画像ファイルを保存する(png)
【Android】SDカードから画像ファイルを読み込む
【Android】SDカードからテキストファイルを読み込む
【Android】SDカードにテキストファイルを保存する
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


category: Android

thread: プログラミング

janre: コンピュータ

tag: SDカード 
tb: 0   cm: --

【Android】【Java】SDカードに画像ファイルを保存する(jpg)  


前回の「SDカードに画像ファイルを保存する(png)」と同じもので、画像フォーマットを変えただけ。

isMountSDCard(), toSDCardAbsolutePath() は以前に作ったものを、そのまま使えば良い。
(※) Android4.4(API 19)あたりから内部ストレージの仮想パスに変わった模様

また書き込みの場合は、マニフェストファイル(AndroidManifest.xml) に以下のパーミッションが必要になる。

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

//SDCard に、画像ファイルを保存する(jpg) (Android 用)
//<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> が必要。
public static final boolean saveJpegSDCard(String fileName, Bitmap bitmap, int quality)
throws IOException, IllegalArgumentException {
if (!isMountSDCard()) {
throw new IOException("No Mount");
}
BufferedOutputStream bos = null;
Bitmap tmp = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(toSDCardAbsolutePath(fileName)));
tmp = bitmap.copy(Config.ARGB_8888, true);
return tmp.compress(Bitmap.CompressFormat.JPEG, quality, bos);
} finally {
if (tmp != null) {
tmp.recycle();
tmp = null;
}
try {
bos.close();
} catch (Exception e) {
//IOException, NullPointerException
}
}
}

//メインでは... (※例外処理は省略)
String fileName = "sample.jpg"; // "mnt/sdcard/sample.jpg" or "sdcard/sample.jpg" 等に保存される(端末による)
saveJpegSDCard(fileName, bitmap, 60); //bitmap は保存する画像, 画質60
//ファイルが保存されていれば成功(エミュレータならDDMSで確認できる)

例外処理は手抜きしてるので、必要あれば適当に。
実機での確認は、「マイファイル」のようなアプリで簡単に覗くことができる(アプリは端末による)。

前回の PNG フォーマットと、画質の引数(quality)を合わせて1つの関数にしても良いが、高圧縮→jpg、無圧縮→png と使いわける事が多いため、あえて分離してある。エンコードは Bitmap クラスの compress() メソッドを使っているが、他のエンコーダーを用意しても良い。そういうときもフォーマットごとに分かれてる方が修正し易い気がする。戻値の boolean は compress() の成功をそのまま返しているが、必要なければ無視しても良い。

また、引数 quality は0~100以内の値(0:低画質で高圧縮、100:高画質で低圧縮)でなれけば、IllegalArgumentException が発生する。面倒なら、関数内で修正するコードを付け加えても良いだろう。

SDカードのマウント状態だけしかチェックしてないので、必要なら MOUNTED_READ_ONLY (読み取り専用) などもチェックした方が良いかも知れない。その辺は外部ストレージの状態を参考にして、色々付け加えると良いだろう。


(関連記事)
【Android】SDカードに画像ファイルを保存する(png)
【Android】SDカードから画像ファイルを読み込む
【Android】SDカードからテキストファイルを読み込む
【Android】SDカードにテキストファイルを保存する
【Android】SDカードのファイルを削除する
【Android】内部ストレージに画像ファイルを保存する(png)
【Android】内部ストレージに画像ファイルを保存する(jpg)
【Android】assets フォルダから画像ファイルを読み込む
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


category: Android

thread: プログラミング

janre: コンピュータ

tag: 画像ファイル保存  SDカード 
tb: 0   cm: --

【Android】【Java】SDカードに画像ファイルを保存する(png)  


基本的には「SDカードにテキストファイルを保存する」と同じ考え方で、ストリームを開いた後は、画像フォーマットによるエンコード処理に置き換えれば良い。

isMountSDCard(), toSDCardAbsolutePath() は以前に作ったものを、そのまま使う。
(※) Android4.4(API 19)あたりから内部ストレージの仮想パスに変わった模様

また書き込みの場合は、マニフェストファイル(AndroidManifest.xml) に以下のパーミッションが必要になる。

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

//SDCard に、画像ファイルを保存する(png) (Android 用)
//<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> が必要。
public static final boolean savePngSDCard(String fileName, Bitmap bitmap) throws IOException {
if (!isMountSDCard()) {
throw new IOException("No Mount");
}
BufferedOutputStream bos = null;
Bitmap tmp = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(toSDCardAbsolutePath(fileName)));
tmp = bitmap.copy(Config.ARGB_8888, true);
return tmp.compress(Bitmap.CompressFormat.PNG, 100, bos);
} finally {
if (tmp != null) {
tmp.recycle();
tmp = null;
}
try {
bos.close();
} catch (Exception e) {
//IOException, NullPointerException
}
}
}

//メインでは... (※例外処理は省略)
String fileName = "sample.png"; // "mnt/sdcard/sample.png" or "sdcard/sample.png" 等に保存される(端末による)
savePngSDCard(fileName, bitmap); //bitmap は保存する画像
//ファイルが保存されていれば成功(エミュレータならDDMSで確認できる)

例外処理は手抜きしてるので、必要あれば適当に。
実機での確認は、「マイファイル」のようなアプリで簡単に覗くことができる(アプリは端末による)。

とりあえず、PNG フォーマットで、画質100固定になっているので、引数で変更しても良いだろう(⇛JPEG保存版)。エンコードは Bitmap クラスの compress() メソッドを使っているが、他のエンコーダーを用意しても良い。戻値の boolean は compress() の成功をそのまま返しているが、必要なければ無視しても良い。

SDカードのマウント状態だけしかチェックしてないので、必要なら MOUNTED_READ_ONLY (読み取り専用) などもチェックした方が良いかも知れない。その辺は外部ストレージの状態を参考にして、色々付け加えると良いだろう。


(関連記事)
【Android】SDカードに画像ファイルを保存する(jpg)
【Android】SDカードから画像ファイルを読み込む
【Android】SDカードからテキストファイルを読み込む
【Android】SDカードにテキストファイルを保存する
【Android】SDカードのファイルを削除する
【Android】内部ストレージに画像ファイルを保存する(png)
【Android】内部ストレージに画像ファイルを保存する(jpg)
【Android】assets フォルダから画像ファイルを読み込む
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


category: Android

thread: プログラミング

janre: コンピュータ

tag: 画像ファイル保存  SDカード 
tb: 0   cm: --

【Android】【Java】SDカードから画像ファイルを読み込む  


 これも基本は「SDカードからテキストファイルを読み込む」と同じ。変更点はストリームを開いた後のデコーダーを、画像用に書き換えれば良いだけ。

 isMountSDCard(), toSDCardAbsolutePath() は以前に作ったものを、そのまま使えば良い。
(※) Android4.4(API 19)あたりから内部ストレージの仮想パスに変わった模様
// SDCard のマウント状態をチェックする(Android 用)
public static final boolean isMountSDCard() {
final String state = Environment.getExternalStorageState();
if (state.equals(Environment.MEDIA_MOUNTED)) {
return true; //マウントされている
} else {
return false; //マウントされていない
}
}

// SDCard のルートディレクトリを取得(Android 用)
public static final File getSDCardDir() {
return Environment.getExternalStorageDirectory();
}

// SDCard 内の絶対パスに変換(Android 用)
public static final String toSDCardAbsolutePath(String fileName) {
return getSDCardDir().getAbsolutePath() + File.separator + fileName;
}


//SDCard から、画像ファイルを読み込む(Android 用)
public static final Bitmap loadBitmapSDCard(String fileName) throws IOException {
if (!isMountSDCard()) {
throw new IOException("No Mount");
}
BufferedInputStream bis = null;
try {
bis = new BufferedInputStream(new FileInputStream(toSDCardAbsolutePath(fileName)));
return BitmapFactory.decodeStream(bis);
} finally {
try {
bis.close();
} catch (Exception e) {
//IOException, NullPointerException
}
}
}


//メインでは... (※例外処理は省略)
String fileName = "sample.jpg"; //"mnt/sdcard/sample.jpg" or "sdcard/sample.jpg" 等になる(端末による)
Bitmap bitmap = loadBitmapSDCard(fileName);

 例外処理は手抜きしてるので、必要あれば適当に。

 SDカードは基本的にマウント状態等のデバイス特有処理以外は、通常のローカルシステムのファイル処理と変わらないので、実は応用は非常に簡単。


(関連記事)
【Android】SDカードに画像ファイルを保存する(png)
【Android】SDカードに画像ファイルを保存する(jpg)
【Android】SDカードからテキストファイルを読み込む
【Android】SDカードにテキストファイルを保存する
【Android】SDカードのファイルを削除する
【Android】assets フォルダから画像ファイルを読み込む
【Android】内部ストレージから画像ファイルを読み込む
【Android】【Applet】【Java】テキストファイルの読み込み・保存 まとめ


category: Android

thread: プログラミング

janre: コンピュータ

tag: 画像ファイル読み込み  SDカード 
tb: 0   cm: --
IS<インフィニット・ストラトス> アーキタイプ・ブレイカー
キルドヤ


プロフィール

Twitter

検索フォーム

全記事一覧

カテゴリ

ユーザータグ

最新記事

リンク

PR

▲ Pagetop