2011年7月5日火曜日

Honeycomb(Android 3.0)まとめ(その2)

前回に引き続き、7月4日に行われたAndroid Develoer Lab Private Sessionの内容について、備忘録を兼ねて要所をまとめてみる。

今回はプログラミングTipsについて。

互換性

parallel activities pattern

Honeycomb(Android OS 3.0)以上であるか、タブレット端末であるか、従来のスマートフォン(Android OS 2.x)であるかによってActivityを切り替えるTips

// OSバージョンを判定する方法
boolean isHoneycomb = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;

// さらに画面サイズを判定する方法
int layoutSize
  = context.getResources().getConfiguration().screenLayout
  & Configuration.SCREENLAYOUT_SIZE_MASK;

boolean isTablet = layoutSize == Configuration.SCREENLAYOUT_SIZE_XLARGE;


Interfaceでセンサーを隠蔽

例として画面の傾きを取得するための手段を提供するinterfaceを定義し、その実装としてデバイスの搭載センサーに応じてジャイロスコープセンサーと加速度センサーを切り替えるといった方法

// 傾きを取得するための手段を提供するinterface
interface IOrientationSensorListener
{
  public String getValue();
}

ジャイロスコープセンサーを利用する実装

// ジャイロスコープセンサーを利用する実装
class Gyro implements IOrientationSensorListener, SensorEventListener
{
  private SensorManager mSensor;
  private String mValue;
  
  // C'tor
  public Gyro(SensorManager sm)
  {
    mSensor = sm;
    mSensor.registerListener(
      this,
      sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE),
      SensorManager.SENSOR_DELAY_UI);
  }

  // センサーの値が変化した際に呼び出される
  @Override
  public void onSensorChanged(SensorEvent event)
  {
    if (event.sensor.getType() != Sensor.TYPE_GYROSCOPE)
    {
      return;
    }

    // 傾き状態を取得
    mType = MessageFormat.format(
      "x:{0},y:{1},z:{2}",
      event.values[0],
      event.values[1],
      event.values[2]);
  }

  @Override
  public String getValue()
  {
    return mValue;
  }
}

加速度センサーを利用する実装

// 加速度センサーを利用する実装
class Accelerometer implements IOrientationSensorListener, SensorEventListener
{
  private SensorManager mSensor;
  private String mValue;
  
  // C'tor
  public Accelerometer(SensorManager sm)
  {
    mSensor = sm;
    mSensor.registerListener(
      this,
      sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), // ★
      SensorManager.SENSOR_DELAY_UI);
  }

  // センサーの値が変化した際に呼び出される
  @Override
  public void onSensorChanged(SensorEvent event)
  {
    if (event.sensor.getType() != Sensor.TYPE_ACCELEROMETER) // ★
    {
      return;
    }

    // 傾き状態を取得
    mType = MessageFormat.format(
      "x:{0},y:{1},z:{2}",
      event.values[0],
      event.values[1],
      event.values[2]);
  }

  @Override
  public String getValue()
  {
    return mValue;
  }
}
※飽くまでコンセプト。上記くらいの差異なら実装は一つにして、初期化パラメータでジャイロと加速度センサーを振り分けたほうが見通しが良さそう。

サポートしているフィーチャーの確認方法

一応。
PackageManager pm = getPackageManager();

// 加速度センサーのサポート状況
boolean supported
  = pm.hasSystemFeature(
      PackageManager.FEATURE_SENSOR_ACCELEROMETER);


画面の向きはPortraitが標準ではない

Tipsとは少し違うけど、タブレットで顕著になった画面の向きという要素に気をつける。Android主要端末の画面サイズ(small, normal, large, xlarge)でも触れたように、layoutのxmlを格納するディレクトリをport用およびland用に作成して対応すべき。



トラッキング

ユニークなインストール数の取得や、特定ユーザーの挙動をトラッキングする目的で使用できる識別子は2種類ある。また、前提として…

  • 工場出荷初期化を行っても変化しない値は使用すべきではない
  • それなりに高い精度でのトラッキングが目的であり、厳密に個人を識別する値と考えてはいけない


その1

工場出荷初期化を行うとクリアされる値であり、Root権限を取得している端末では書き換え可能な値なので個人を識別するものとして利用してはいけない。似ているけどSettings.System.ANDROID_IDではないので注意。

Settings.Secure.ANDROID_ID;


その2

数学的に重複する可能性が低いとみなすことのできるユニークな値。

UUID.randomUUID().toString();


Location Based Service

位置情報サービスを利用する際には常に電力消費の大きさが問題になる。また、位置情報を取得するまでの時間的なイニシャルコストも同様である。これらの問題を解決するためのTips

  • 前回取得した位置情報を活用する
  • 位置情報取得を1度だけ行う
  • 適切な頻度で位置情報をトレースする
  • 位置情報プロバイダの生死をハンドリングする
  • バックグラウンドで位置情報を取得しておく


前回取得した位置情報を活用する

利用可能な位置情報プロバイダが保有している位置情報を検査し、一定の条件を満たす有効なデータがあればこれを利用する。

List<String> providers = locationManager.getAllProviders();
for (String provider: providers)
{
  Location location
    = locationManager.getLastKnownLocation(provider);
  
  // locationの時刻を一定の閾値でテストして有効なものを利用する
  ...
}


位置情報取得を1度だけ行う

位置情報を追いかけ続ける必要性がなければ、可能なかぎり素早く現在の位置情報を取得するといった方法を採る。※但し、criteria(現在のデバイス状況など)に従って決定されるため、位置情報プロバイダは必ずしも素早いとは限らない。

locationManager.requestSingleUpdate(criteria, singleUpatePI);

また、上記はGingerbreadより前のAndroid OSでは利用できないため代替手段などを使う必要がある。



適切な頻度で位置情報をトレースする

例えば、頻度は毎15分、75m以上の移動があった場合など。

locationManager.requestLocationUpdates(
  AlarmManager.INTERVAL_FIFTEEN_MINUTES,
  75,
  new Criteria(),
  pendingIntent);


位置情報プロバイダの生死をハンドリングする

使用中のプロバイダが使えなくなった場合や、より良いプロバイダが利用可能になった場合についてブロードキャストレシーバを登録してハンドリングする。

// 位置情報プロバイダが利用できなくなったよーブロードキャストを
IntentFilter intentFilter
  = new IntentFilter(
      PlacesConstants.ACTIVE_LOCATION_UPDATE_PROVIDER_DISABLED);

// ハンドリングするレシーバを登録
registerReceiver(
  locProviderDisabledReceiver,
  intentFilter);


バックグラウンドで位置情報を取得しておく

UIの起動と同時に位置情報に依存した挙動を行う必要性がある場合には、サービスにおいて位置情報の取得を行っておく。但し、電力消費を考慮しFroyo(Android 2.2)以降では、Passive Providerを利用する。

locationManager.requestLocationUpdates(
  LocationManager.PASSIVE_PROVIDER,
  minTime,
  minDistabce,
  pendingIntent);

0 件のコメント:

コメントを投稿