marunomaruno-memo

marunomaruno-memo

位置情報 (4) / ファイル・システム (2) 外部メディアに位置情報を書き込む

2012年01月11日 | Android
位置情報 (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) -->


ジャンル:
ウェブログ
キーワード
ログファイル タイムスタンプ ファイル名 アクティビティ ディレクト ファイルシステム ストビュー
コメント (0) |  トラックバック (0) |  この記事についてブログを書く
Messenger この記事をはてなブックマークに追加 mixiチェック シェア
« [Android] 位置情... | トップ | [Android] センサ... »

コメント

コメントはありません。

コメントを投稿


コメント利用規約に同意の上コメント投稿を行ってください。
※文字化け等の原因になりますので、顔文字の利用はお控えください。
下記数字4桁を入力し、投稿ボタンを押してください。この数字を読み取っていただくことで自動化されたプログラムによる投稿でないことを確認させていただいております。
数字4桁

トラックバック

この記事のトラックバック  Ping-URL

あわせて読む