2013年5月24日金曜日

Google Maps V2 API でNoClassDefFoundErrorが出た。

java.lang.NoClassDefFoundError: com.google.android.gms.R$string
at com.google.android.gms.common.GooglePlayServicesUtil.c(Unknown Source)
at com.google.android.gms.internal.e.a(Unknown Source)
at com.google.android.gms.internal.e.onCreateView(Unknown Source)
...


Androidでこのエラーが出ていた場合、「Google Play開発者サービス」アプリがアンインストールされている可能性が考えられます。

このアプリはgoogleが勝手にインストールするアプリで、アンインストールしてもしてもしても一定時間で生き返ります。
現在発売されている端末には最初からインストールされているのですが、2012年10月頃以前の端末には説明が不十分なままアップデートで追加され、しかも容量が大きかったためにユーザーから大批判を買い、アンインストールするユーザーが続出しているようです。

もちろん、一定時間が経てばまたインストールされるのですが、Androidユーザーは「とにかく不必要なアプリは消したい」という思いが強いので、こまめにチェックしてはアンインストールしているようです。
今まで目立った不具合がなかったようなのですが、今回Google Map V2が使えないことが分かりました。

さらにこのアプリ、Google Play Storeから検索してもヒットしないんですね。
Google検索にはヒットするのでそこから入れなおすか、自動更新されるのを待つしかないので、ダウンロードの導線を付けてあげたほうが良いです。
(ちなみにfoursquareは、このアプリが入っていないとアラートを表示してダウンロードを促しています。)

今回はGoogle Maps V2 APIを利用しているところでこのエラーが発生しましたが、
今後GooglePlayServicesUtil.cを参照するプログラムで問題になってくると思います。

2013年5月22日水曜日

phpと外部js間で値を渡そうとして詰まったお話・・

phpでhtmlサイトを作っているとき、ふとphpの値をjsでも使いたいな・・・と思った。
jsは外部ファイルから読み込んでいる。

ぐぐってみた結果、php→javascriptの値渡しは簡単にできそうなことが分かった。

$a = 100;

<script type="text/javascript">
  var a = <?php echo $a; ?>;
</script>

しかし、全く動かない。
ていうか、値取れてないし!!




調べた結果、これには【 同じソース内に書かれているという大前提】があった。
つまり、外部javascriptを読み込んでいる場合はダメ。


よく考えてみたら確かに、phpでhtmlを出力してる時点でhtmlなわけなんだけど、
ぐぐって出てきたら「そんなことできるの!?」って思っちゃうじゃないですか。


こんな大前提なこと、書いてるサイトもひっかからないし・・・。
ということで、メモ。

2013年5月8日水曜日

Androidの位置情報取得 - 1 (現在地取得)

■端末の設定から現在地取得が利用可能か確認する

    LocationManager mLocationManager = (LocationManager) getActivity().getSystemService(Context.LOCATION_SERVICE);
    // 3Gまたはwifiから位置情報を取得する設定
    boolean networkFlg =  mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    // GPSから位置情報を取得する設定
    boolean gpsFlg = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

■現在地を取得する
  LocationListenerをimplementsする。
  LocationListenerの必須メソッドは以下の4つ。
  ・public void onLocationChanged(Location location)
  ・public void onProviderDisabled(String provider)
  ・public void onProviderEnabled(String provider)
  ・public void onStatusChanged(String provider, int status, Bundle extras)

  位置情報を取得したら onLocationChanged が呼ばれ、引数に取得した位置情報が入る。
  ※位置情報取得に失敗したことを検知するメソッドはない。

  requestLocationUpdates()で位置情報取得を開始する。
  removeUpdates()で取得処理を終了する。
  終了するまで、不定期で取得し続ける。
  
  ※removeUpdates()を忘れると、しばらくのあいだonLocationChangedが
   呼ばれなくなることがあるらしいので、onPause()などに書いて
   必ず呼ばれるようにしておくこと。
  参考:http://d.hatena.ne.jp/glass-_-onion/20101113/1289615195

  requestLocationUpdates()には引数の種類がいくつかあるけど、今回はこれを利用。
  mLocationManager.requestLocationUpdates(provider, minTime, minDistance, listener);
  provider:network または gps  ← providerの選び方は次の記事で
  minTime:位置情報を取得する最小時間
  minDistance:位置情報を取得する最小距離
  listener:リスナーを実装しているクラス

  ※minTime, minDistanceはそれぞれ取得する最小時間、最小距離なので、必ずこの間隔で取得されるとは限らない

  位置情報の取得を1回しか行わない場合は、onLocationChanged()でremoveUpdates()を呼べばいい。


  AsyncTaskのdoInBackgroundではrequestUpdatesが書けない。


以下、サンプルコード。
ネットワークとGPSで位置情報を取りに行って、両方取得したら処理を終了する。
onLocationChangedはnetworkとgps、それぞれ取得出来たら1回ずつ呼ばれる。
(重要なとこだけ。これだけでは動かない。)

public class LocationBaseFragment implements LocationListener {

public LocationManager mLocationManager;
/** 利用できる現在地プロバイダの数 */
private int providerNum = 0;


@Override
public void onResume() {
super.onResume();
// 利用できるプロバイダをカンマ区切りで取得
String gpsStatus = android.provider.Settings.Secure.getString(getActivity().getContentResolver(), Secure.LOCATION_PROVIDERS_ALLOWED);
if (gpsStatus.matches(".*" + LocationManager.NETWORK_PROVIDER + ".*")) {
providerNum++;
// ネットワークから取得を開始する
mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 10, this);
}
if (gpsStatus.matches(".*" + LocationManager.GPS_PROVIDER + ".*")) {
providerNum++;
// GPSから取得を開始する
mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 10, this);
}
}


@Override
public void onPause() {
super.onPause();
if (mLocationManager != null) {
mLocationManager.removeUpdates(this);
}
}

@Override
public void onLocationChanged(Location location) {

// 全てのプロバイダからの現在地を取得したら取得処理をやめる
providerNum--;
if (providerNum == 0) {
mLocationManager.removeUpdates(this);
}
/* 取得したlocationを好きに使う */
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
}