位置情報 (4) / ファイル・システム (2) 外部メディアに位置情報を書き込む
================================================================================
このアプリケーションは、つぎつぎと変化する位置情報を、SD カードのファイルに書き
込む。GpsLogger01 は、メモリー内のファイルであったが、これは SD カードを使うのが
違う点である。 また、利用者の指定によって、ファイルの内容を表示したり、削除した
りするのは、GpsLogger01 と同じ。
コード
---
GpsLogger01Activity.java アクティビティ
GpsLocationListener.java ロケーションのリスナー
TextViewPrintStream.java System.out オブジェクトの置き換え(SyystemOut01 と同じ)
LogDao.java ログの入出力を行うユーティリティのクラス
---
◆ アクティビティのクラス
採ったログを表示するための「ログ表示」ボタン、ログファイルをクリアする「ログクリ
ア」ボタンが追加になっているので、これに対するハンドラー・メソッド
onClickLogPrintButton()
onClickLogClearButton()
を追加している。メソッドは、単に LogDao のメソッドを呼び出しているだけである。
ファイル名は、先のサンプル GpsLogger01 は、固定だったが、これは、ログを採る開始
日時がファイル名の一部に入る。
ログファイルの扱いは、LogDao クラスなので、このアクティビティ・クラスはほとんど
変更がない。ログファイルにタイムスタンプが入っているので、それを取得するコードが
入った程度である。タイムスタンプに対するゲッター・メソッドが追加になっている。
□ Gps02Activity.java
---
package jp.marunomaruno.android.gpslogger;
import java.io.FileNotFoundException;
import java.io.IOException;
import android.app.Activity;
import android.location.Criteria;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
/**
* @author marunomaruno
* @version 1.0, 2011-12-14
* @since 1.0
*/
public class GpsLogger02Activity extends Activity {
private boolean isRunning = false; // 現在起動中かどうか
private static final String IS_RUNNING = "isRunning";
private long timestamp; // 開始したときのタイムスタンプ // (1)
private static final String TIMESTAMP = "timestamp";
private LocationManager locationManager;
private LocationListener sensorEventListener;
// onPause()が呼ばれたときに、すべてのリスナーを解除するため
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d(getString(R.string.logTag), String.format("onCreate() "
+ getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.time), System.currentTimeMillis()));
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 標準出力を置き換え(標準出力(LogCat)+テキストビュー)
TextView sysout = (TextView) findViewById(R.id.sysout);
System.setOut(new TextViewPrintStream(System.out, sysout));
System.setErr(new TextViewPrintStream(System.err, sysout));
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);
}
@Override
protected void onResume() {
Log.d(getString(R.string.logTag), String.format("onResume() "
+ getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.time), System.currentTimeMillis()));
super.onResume();
Button startStopButton = (Button) findViewById(R.id.startStopButton);
Log.d(getString(R.string.logTag), "起動中 = " + isRunning);
if (isRunning) { // 起動中のとき、終了ボタンを表示する
startStopButton.setText(R.string.stopButton);
} else {
startStopButton.setText(R.string.startButton);
}
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
Log.d(getString(R.string.logTag), String.format("onRestoreInstanceState() "
+ getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.time), System.currentTimeMillis()));
super.onRestoreInstanceState(savedInstanceState);
isRunning = savedInstanceState.getBoolean(IS_RUNNING);
timestamp = savedInstanceState.getLong(TIMESTAMP);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
Log.d(getString(R.string.logTag), String.format("onSaveInstanceState() "
+ getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.time), System.currentTimeMillis()));
super.onSaveInstanceState(outState);
outState.putBoolean(IS_RUNNING, isRunning);
outState.putLong(TIMESTAMP, timestamp);
}
/**
* 開始/終了ボタン押下時
* @param view
*/
public void onClickStartStopButton(View view) {
Log.d(getString(R.string.logTag), String.format("onClickStartButton() "
+ getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.time), System.currentTimeMillis()));
Button startStopButton = (Button) view;
if (isRunning) { // 起動中のとき、とめる
// すべてのリスナーを解除する
removeListeners();
// 開始ボタンにする
startStopButton.setText(R.string.startButton);
System.out.println(getString(R.string.stopButton));
System.out.println(String.format(
getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.startButton), timestamp));
System.out.println(String.format(
getString(R.string.sensor_value_format_for_timestamp),
getString(R.string.stopButton), System
.currentTimeMillis()));
} else {
// 起動していないとき、
// GPSロケーションを取得して登録する
setProviderAndListener();
// 停止ボタンにする
startStopButton.setText(R.string.stopButton);
timestamp = System.currentTimeMillis();
// タイムスタンプを取得する // (2)
try {
System.out.printf(getString(R.string.startMessageFormat),
LogDao.getFile(this));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println();
}
isRunning = !isRunning; // 起動中状態を逆にする
}
private void setProviderAndListener() {
// プロバイダーを取得する条件を作成する
Criteria fineAndLowPower = new Criteria();
fineAndLowPower.setAccuracy(Criteria.ACCURACY_FINE);
fineAndLowPower.setPowerRequirement(Criteria.POWER_LOW);
// プロバイダーを取得する
String provider = locationManager.getBestProvider(fineAndLowPower, true);
Log.d(getString(R.string.logTag), "provider = "
+ provider);
TextView providerNameTextView = (TextView) findViewById(R.id.provider_name);
providerNameTextView.setText(provider);
// プロバイダーがみつからなければ、NETWORK_PROVIDER を設定しておく
if (provider == null) {
provider = LocationManager.NETWORK_PROVIDER;
providerNameTextView.setText(R.string.no_provider);
}
// 位置情報のリスナーを取得して登録する
sensorEventListener = GpsLocationListener.getInstance(this);
locationManager.requestLocationUpdates(
provider,
0,
0,
sensorEventListener);
}
/**
* ログをクリアする。
* @param view
*/
public void onClickLogClearButton(View view) {
try {
LogDao.delete(this);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* ログを表示する。
* @param view
*/
public void onClickLogPrintButton(View view) {
try {
LogDao.print(this);
} catch (FileNotFoundException e) {
System.out.println(getString(R.string.logNotFound));
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
private void removeListeners() {
if (sensorEventListener != null) {
locationManager.removeUpdates(sensorEventListener);
}
}
public long getTimestamp() { // (3)
return timestamp;
}
}
---
(1)(3) 開始したときのタイムスタンプとそのゲッター
private long timestamp; // 開始したときのタイムスタンプ // (1)
public long getTimestamp() { // (3)
(2) タイムスタンプの取得
timestamp = System.currentTimeMillis(); // タイムスタンプを取得する // (2)
◆ センサーのリスナーのクラス。
以下の GpsLogger01 プロジェクトと同じ。
位置情報 (3) / ファイル・システム (1) メモリー内のファイルに位置情報を書き込む
http://blog.goo.ne.jp/marunomarunogoo/d/20120110
◆ ログを管理するクラス
外部メディアを使うので、ローカルのときとは少し違う。
ただし、基本は一緒で、ログへの出力は PrintWriter オブジェクト、ログの取得は、
BufferedReader オブジェクトを作るのは変わらない。
外部メディア関係を扱うクラスは Environement クラス。
ここが、メモリー内のファイルとは違う部分である。
□ LogDao.java
---
package jp.marunomaruno.android.gpslogger;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import android.content.Context;
import android.location.Location;
import android.os.Environment;
/**
* 位置情報ログにアクセスするクラス。
* すべてのメソッドはstatic。
* @author marunomaruno
*/
public class LogDao {
public static final String SEPARATER_CHARACTER = ",";
private LogDao() {
}
/**
* ログファイルを取得する。
* このファイルがない場合は、必要なディレクトリーも含めてファイルを生成する。
* @param context コンテキスト
* @return ログファイル
* @throws IOException
*/
public static File getFile(Context context) throws IOException {
String status = Environment.getExternalStorageState(); // (1)
if (!status.equals(Environment.MEDIA_MOUNTED)) { // (2)
throw new IOException(context.getString(R.string.mediaDisable));
}
File file = new File(String.format(context
.getString(R.string.logFilePathFormat),
Environment.getExternalStorageDirectory(), // (3)
context.getPackageName(),
context.getString(R.string.logFilePrefix),
((GpsLogger02Activity) context).getTimestamp(),
context.getString(R.string.logFileExtension))); // (4)
File directory = file.getParentFile();
if (!directory.exists()) { // (5)
directory.mkdirs();
}
return file;
}
/**
* locationのデータをログファイルにCSV形式で書き出す。
* @param context
* @param location
* @throws UnsupportedEncodingException
* @throws FileNotFoundException
* @throws IOException
*/
public static void write(Context context, Location location)
throws UnsupportedEncodingException, FileNotFoundException,
IOException {
PrintWriter out = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(getFile(context), true), context
.getString(R.string.logFileEncoding))); // (6)
out.printf("¥"%1$tF %1$tT¥"", location.getTime());
// ファイルをわかりやすくするため
out.print(SEPARATER_CHARACTER);
out.print(location.getTime());
out.print(SEPARATER_CHARACTER);
out.print(location.getLatitude());
out.print(SEPARATER_CHARACTER);
out.print(location.getLongitude());
out.print(SEPARATER_CHARACTER);
out.print(location.getAccuracy());
out.print(SEPARATER_CHARACTER);
out.print(location.getAltitude());
out.print(SEPARATER_CHARACTER);
out.print(location.getSpeed());
out.print(SEPARATER_CHARACTER);
out.print(location.getBearing());
out.println();
out.close();
}
/**
* ログファイルの内容を標準出力にプリントする。
* @param context
* @throws UnsupportedEncodingException
* @throws FileNotFoundException
* @throws IOException
*/
public static void print(Context context)
throws UnsupportedEncodingException, FileNotFoundException,
IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(getFile(context)), context
.getString(R.string.logFileEncoding))); // (7)
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
}
/**
* ログファイルを削除する。
* @param context
* @throws IOException
*/
public static void delete(Context context) throws IOException {
getFile(context).delete(); // (8)
}
}
---
(1)(2) 外部メディアが使用可能かどうかを判断する
外部メディア(SD カード)の状態を取得する。
String status = Environment.getExternalStorageState(); // (1)
取得した状態が、実機にマウントされているかどうかを判断する。されていなければ、
IOException をスローする。
if (!status.equals(Environment.MEDIA_MOUNTED)) { // (2)
throw new IOException(context.getString(R.string.mediaDisable));
}
・Environment クラス
SD カードのような、外部メディア関係を扱うユーティリティのクラスとして、
Environment クラスがある。
java.lang.Object
+ android.os.Environment
・メソッド
---
static File getDataDirectory()
static File getDownloadCacheDirectory()
static File getExternalStorageDirectory()
static File getExternalStoragePublicDirectory(String type)
static String getExternalStorageState()
static File getRootDirectory()
static boolean isExternalStorageEmulated()
static boolean isExternalStorageRemovable()
---
・getExternalStorageState() メソッドの返り値になる定数(型はすべて String)
---
MEDIA_BAD_REMOVAL 外部メディアをアンマウントする前に取り外した
MEDIA_CHECKING 外部メディアのチェックを始めた
MEDIA_MOUNTED 外部メディアが装着されている(読み取り・書き込み可)
MEDIA_MOUNTED_READ_ONLY 外部メディアが装着されている(読み取り専用)
MEDIA_NOFS 外部メディアは装着されているが、ブランクか
サポートしていないファイルシステムを使っている
MEDIA_REMOVED 外部メディアが装着されていない
MEDIA_SHARED 外部メディアは USB メモリーとして共有されている(使えない状態)
MEDIA_UNMOUNTABLE 外部メディアは装着されているが、マウントできない
MEDIA_UNMOUNTED 外部メディアは装着されているが、マウントされていない
---
(3)(4) ファイル・オブジェクトの取得
File file = new File(String.format(context
.getString(R.string.logFilePathFormat),
Environment.getExternalStorageDirectory(), // (3)
context.getPackageName(),
context.getString(R.string.logFilePrefix),
((GpsLogger02Activity) context).getTimestamp(),
context.getString(R.string.logFileExtension))); // (4)
ルートからのパスを作って、ファイル・オブジェクトを取得する。
ファイル名は、リソースで定義しているフォーマットにしたがって、文字列化する。
実際の値は、次のメソッドで、
context.getString(R.string.logFilePathFormat)
取得でき、つぎの文字列を持っている。
%1$s/data/%2$s/%3$s%4$tY%4$tm%4$td_%4$tH%4$tM%4$tS.%5$s
それぞれの値はつぎのメソッドで取得する。(右は、このサンプルの例))
---
%1$s Environment.getExternalStorageDirectory() /mnt/sdcard/
%2$s context.getPackageName()
jp.marunomaruno.android.gpslogger
%3$s context.getString(R.string.logFilePrefix) location
%4$tX ((GpsLogger02Activity) context).getTimestamp() タイムスタンプ
%5$s context.getString(R.string.logFileExtension) txt
---
(5) ディレクトリーがないときは作成する
if (!directory.exists()) { // (5)
directory.mkdirs();
}
(6) locationのデータをログファイルにCSV形式で書き出す。
File オブジェクトが (3)(4) によって作れれば、これをもとにして、JavaSE と同じよう
に、PrintWriter オブジェクトを作る。
PrintWriter out = new PrintWriter(new OutputStreamWriter(
new FileOutputStream(getFile(context), true), context
.getString(R.string.logFileEncoding))); // (6)
(7) ログファイルの内容を標準出力にプリントする。
ログファイルの読み込みも、File オブジェクトがあれば、(6) と同様に、
BufferedReader オブジェクトを作る。
BufferedReader in = new BufferedReader(new InputStreamReader(
new FileInputStream(getFile(context)), context
.getString(R.string.logFileEncoding))); // (7)
(8) ログファイルを削除する。
File オブジェクトなので、その delete() メソッドを使えばよい。
getFile(context).delete(); // (8)
◆ レイアウト
以下の GpsLogger01 プロジェクトと同じ。
位置情報 (3) / ファイル・システム (1) メモリー内のファイルに位置情報を書き込む
http://blog.goo.ne.jp/marunomarunogoo/d/20120110
◆ 文字列の定数
□ res/values/string.xml
---
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">GpsLogger02</string>
<string name="logTag">TEST</string>
<string name="no_provider">プロバイダーは見つかりませんでした。</string>
<string name="sensor_value_format_for_real">%1$s = %2.2f</string>
<string name="sensor_value_format_for_timestamp">%1$s = %2$tF %2$tT</string>
<string name="latitude">緯度</string>
<string name="longitude">経度</string>
<string name="accuracy">精度</string>
<string name="altitude">標高</string>
<string name="time">時間</string>
<string name="speed">速度</string>
<string name="bearing">ベアリング</string>
<string name="logFilePrefix">location</string>
<string name="logFileExtension">txt</string>
<string name="logFilePathFormat">
%1$s/data/%2$s/%3$s%4$tY%4$tm%4$td_%4$tH%4$tM%4$tS.%5$s</string>
<!-- /mnt/sdcard / data / app_name / location yyyymmdd_hhmmss.txt -->
<!-- (1) -->
<string name="logFileEncoding">UTF-8</string>
<string name="startButton">開始</string>
<string name="stopButton">終了</string>
<string name="logPrintButton">ログ表示</string>
<string name="logClearButton">ログクリア</string>
<string name="logNotFound">ログはありません。</string>
<string name="startMessageFormat">開始します。ファイル名 = %s</string>
<string name="providerDisable">無線ネットワークを使用できるようにしてください。</string>
<string name="providerEnabled">無線ネットワークを使用します。</string>
<string name="mediaDisable">SDカードが使用できません。</string>
</resources>
---
(1) ログファイルのパスも含めたファイル名
<string name="logFilePathFormat">
%1$s/data/%2$s/%3$s%4$tY%4$tm%4$td_%4$tH%4$tM%4$tS.%5$s</string>
<!-- /mnt/sdcard / data / app_name / location yyyymmdd_hhmmss.txt -->
<!-- (1) -->
たとえば、2012年1月2日6時57分14秒に開始すれば、
/mnt/sdcard/data/jp.marunomaruno.android.gpslogger/location20120102_065714.txt
になる。
◆マニフェスト
□AndroidManifest.xml
---
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.marunomaruno.android.gpslogger"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- (1) -->
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name" >
<activity
android:label="@string/app_name"
android:name=".GpsLogger02Activity" >
<intent-filter >
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
---
(1) SD カードへの書き込み許可
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- (1) -->