MyTracks 読み (15)

いきなりシリーズ再開。てーか、調べものがあった止むを得ず、という所があったりなかったり。ちょっと版数古いかもしれませんが、そのあたりは勘弁して下さい。

始点

MyTracks の onOptionsItemSelected メソドより。

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case MyTracksConstants.MENU_START_RECORDING: {
        startRecording();
        return true;
      }

MyTracks#startRecording メソドの定義は以下か。

  /**
   * Starts the track recording service (if not already running) and binds to
   * it. Starts recording a new track.
   */
  private void startRecording() {
    if (trackRecordingService == null) {
      startNewTrackRequested = true;
      Intent startIntent = new Intent(this, TrackRecordingService.class);
      startService(startIntent);
      tryBindTrackRecordingService();
    } else {
      try {
        recordingTrackId = trackRecordingService.startNewTrack();
        Toast.makeText(this, getString(R.string.status_now_recording),
            Toast.LENGTH_SHORT).show();
        setSelectedAndRecordingTrack(recordingTrackId, recordingTrackId);
      } catch (RemoteException e) {
        Toast.makeText(this,
            getString(R.string.error_unable_to_start_recording),
            Toast.LENGTH_SHORT).show();
        Log.e(MyTracksConstants.TAG,
            "Failed to start track recording service", e);
      }
    }
  }

trackRecordingService が動いてるかどうかで動作が異なってます。動いてなければ開始させるし、動いてれば新しい id で云々なのかな。
まず Service が開始されてないケイスですが、Intent 作って startService してます。その後に呼ばれてる tryBindTrackRecordingService メソドですが定義が以下。

  /**
   * Binds to track recording service if it is running.
   */
  private void tryBindTrackRecordingService() {
    Log.d(MyTracksConstants.TAG,
        "MyTracks: Trying to bind to track recording service...");
    bindService(new Intent(this, TrackRecordingService.class),
        serviceConnection, 0);
  }

これ、何故に同じ Intent をもっかい作ってるのかが謎。あと serviceConnection ですが定義が以下かな。引用するには長いな。とりあえず

  • trackRecordingService に参照ぶち込む
    • TrackRecordingService の内部クラス ITrackRecordingService.Stub の参照
    • ちょっとこの IDL 云々が弱い
  • 新しい track 作って云々

という事で setSelectedAndRecordingTrack メソドですが SharedPreferences なナニに recordingTrackId を put しております。何故に SELECTED_TRACK と RECORDING_TRACK という形で二つ持ってるのかは謎ですが。

ちょっと脱線

AIDL 云々について。

  • MyTracks のケイスであれば ITrackRecordingService.aidl で interface を定義
  • プロジェクトの build 時に ITrackRecordingService.java が自動生成
  • Service の中で private な ITrackRecordingService.Stub な無名クラスを作って new したソレを定義
  • onBind では上記で定義されたオブジェクトを戻せばよい

KitchenTimerService 方式よりはこっちのが楽なのかどうなのか。

閑話休題

基本的にトラック記録な処理って ITrackRecordingService#startNewTrack になるのか。ポイントを以下に控え。

  • Track ってクラスについて
  • MyTracksProviderUtilsImpl#insertTrack メソドについて
    • trackId 取得のためだけにあるのかな
  • MyTracksProviderUtilsImpl#updateTrack メソドについて
  • PeriodicTaskExecuter クラスについて
    • 以前のエントリで確認入ってたりするんだろなorz

末端らへんのメソドについても確認必要。以下なあたり。

          stats = new TripStatistics(track.getStartTime());
          if (announcementFrequency != -1 && executer != null) {
            executer.scheduleTask(announcementFrequency * 60000);
          }
          length = 0;
          showNotification();
          registerLocationListener();
          splitManager.restore();
          signalManager.restore();
          return trackId;

でわ、以下で順に確認をば。

Track ってクラスについて

む。Track.java の中身によれば、Track はレコード保存単位で一つ存在するのか。測位情報は属性として ArrayList なソレで保持している模様。

MyTracksProviderUtilsImpl#insertTrack メソドについて

ええと、定義が以下です。

  @Override
  public Uri insertTrack(Track track) {
    Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.insertTrack");
    return context.getContentResolver().insert(TracksColumns.CONTENT_URI,
        createContentValues(track));
  }

ContentResolver だ。ちなみに createContentValues っていくつか overload されてますね。ただ、Track な引数を受け取る createContentValues メソドは locations な属性は相手にしていないように見えます。

MyTracksProviderUtilsImpl#updateTrack メソドについて

ええと、、定義が以下。

  @Override
  public void updateTrack(Track track) {
    Log.d(MyTracksProvider.TAG, "MyTracksProviderUtilsImpl.updateTrack");
    context.getContentResolver().update(TracksColumns.CONTENT_URI,
        createContentValues(track), "_id=" + track.getId(), null);
  }

どうやって id 特定してるのか、って思ったら

  • MyTracksProviderUtilsImpl#insertTrack でケツに id が付いた URI が戻る
  • Uri#getLastPathSegment() でケツの id 取得して trackId に保存
  • Track#setId で id セットして update

てーコトは、以下なシーケンスはそれなりに使い回しが可能なんかな。

          Uri trackUri = providerUtils.insertTrack(track);
          long trackId = Long.parseLong(trackUri.getLastPathSegment());
          track.setId(trackId);
          track.setName(String.format(getString(R.string.new_track), trackId));
          providerUtils.updateTrack(track);

うーん、素晴しい。

PeriodicTaskExecuter クラスについて

以前のエントリによると一定時間おきに何かをするソレ、という記述があります。根拠が不明なあたりが超微妙。
ってこれ、自分で定義してるんですね。で、PeriodicTaskExecuter とか PeriodicTask とかの中身を見てみても具体的な記述が全く無い状態だったので、TrackRecordingService の onCreate の中身を見たら以下な記述を発見。

    if (mTTSAvailable && (announcementFrequency != -1)) {
      if (executer == null) {
        SafeStatusAnnouncerTask announcer = new SafeStatusAnnouncerTask(this);
        executer = new PeriodicTaskExecuter(announcer, this);
      }
    }

む、SafeStatusAnnouncerTask は PeriodicTask を実装しとりますな。ここに具体的なソレが書いてあるに違いない。と思ったらこいつも wrapper だったス。これって何かのパターンなのかなぁ。
ここで一旦手を止めます。多分帰宅後に見るのは C2DM のはず。beagleboard のソレも色々確認してみたいですが。