端末回転時の処理とエラー

1,789 views
Skip to first unread message

hiros

unread,
Feb 21, 2012, 3:39:27 AM2/21/12
to android-g...@googlegroups.com
お世話になっております。いつも質問や回答を拝見して勉強させて頂いております。

早速ですが、端末回転時の処理について質問させてください。

現象:
 端末の回転が有効になっている状態の時に、高速で端末を縦/横繰り返すとクラッシュエラーが発生する
 クラッシュ内容は NullPointerException でした。

処理内容:
@Override
public void onCreate(Bundle savedInstanceState) {
  :
  : 略
  :
  // MapViewのオーバーレイ作成
  locationOverlay=new LocationOverlay(
    getResources().getDrawable(R.drawable.location_icon),map);
  map.getOverlays().add(locationOverlay);
  :
  // センサーの初期化
  mSensorManager=(SensorManager)getSystemService(SENSOR_SERVICE);
  :
}

@Override
protected void onSaveInstanceState(Bundle outState) {
  // 現在のマーカー位置の保存等(特に現象に関する処理は無い)
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
  // 現在のマーカー位置の復帰等(特に現象に関する処理は無い)
}

@Override
protected void onResume() {
  super.onResume();
  map.requestLayout();
  if(mSensorManager!=null){
    List<Sensor> sensors=mSensorManager.getSensorList(Sensor.TYPE_ORIENTATION);
    if(sensors.size()>0){
      Sensor sensor = sensors.get(0);
      mRegisteredSensor=mSensorManager.registerListener(
        this,sensor,SensorManager.SENSOR_DELAY_NORMAL);
    }
  }
  :略
}

@Override
protected void onPause() {
  super.onPause();
  if(mRegisteredSensor){
    mSensorManager.unregisterListener(this);
    mRegisteredSensor = false;
  }
  :略
}

@Override
protected void onDestroy(){
  :略
  map.getOverlays().remove(locationOverlay);
  locationOverlay.clear();
  locationOverlay=null;

  mSensorManager=null;
}

public void onSensorChanged(SensorEvent event) {
  if(event.sensor.getType() == Sensor.TYPE_ORIENTATION){
    locationOverlay.SetAngle(event.values[0]);
    map.invalidate();
  }
}

public void onLocationChanged(Location location) {
  locationOverlay.SetLocation(location);
  map.invalidate();
}

通常の端末回転時のシーケンスは
  開始
    onSaveInstanceState
    onPause
    onDestroy
  復帰
    onCreate
    onRestoreInstanceState
    onResume
だと認識していますし、ブレークポイントを置いて確認してもこの通りでした。

ブレークポイントを置いた状態で端末を回転させても現象は発生しませんでした。
ブレークポイントを置かずに表示が回転した事を確認してから縦/横を続けても発生しませんが
表示を無視して縦/横回転をササっと続けると1分もせずに強制終了し
locationOverlay が null の状態で onSensorChanged や onLocationChanged が発生します。
念のためlocationOverlayをnullにしている所を検索してみましたが
onDestroy以外には無い為、処理が追いつかず onSensorChanged 等の
イベントが溜り onDestroy と onCreate の間に届いているのではと考えます。

対策として locationOverlay をアクセスする所に
if(locationOverlay!=null) を入れるとエラーが発生しなくなった様に見えていますし
onDestroyの locationOverlay=null; の行を削除しても現象は発生しないようです。

あまりにも長くなるので略しましたが
実は、LocationListner は Services で動いていて Messenger を使って
Activity と通信しています。
この回転を続けた時の現象はlocationOverlayだけではなく
Messenger のハンドラーでも発生しています。
Messenger も同様にonPauseで通信を停止し onDestroy でnullにしています。

既に公開済のアプリでエラー報告は届いていませんが
気付いてしまったからには、何か対策したいと考えています
綺麗な対策方法がみつかりませんので
良い対策方法等ありましたら御教授頂きたいと思い質問させて頂きました。

余談になりますが、他のアプリはどうなのかなっと
Marketからダウンロードして使わせて頂いているアプリで同じ事をしてみると
クラッシュするアプリも有りました。

そんな事をする人が悪い! そんな事をする人は居ない!
っと自分に言い聞かせてみましたが気になって離れられません。

何かお気付きの事が有りましたら、よろしくお願いします。

kacodama

unread,
Feb 21, 2012, 5:38:01 AM2/21/12
to 日本Androidの会
このグループの投稿をcleanupViewで検索してみてください。
縦横高速切替で落ちているのはほとんどがこれで解消するのが実感です。
以上ご参考になれば幸いです。

hiros

unread,
Feb 21, 2012, 6:05:59 AM2/21/12
to android-g...@googlegroups.com
kacodamaさん、解答をありがとうございました。

実は以前既ににメモリリークの問題と向き合っていまして。
cleanupView は組み込んであります。
今回の問題はメモリリークとは違って NullPointer になる所です。

一応メモリリークについても再度調査してみます、
ありがとうございました。


gorou yamamoto

unread,
Feb 21, 2012, 8:12:25 PM2/21/12
to android-g...@googlegroups.com
android初心者のgorouです。
同じような現象をonConfigurationChangedで回避しました。
参考URL
画面の向きを切り替えた時の問題




2012年2月21日20:05 hiros <fp.c...@gmail.com>:


--
このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msg/android-group-japan/-/c4YoP-BXMSMJ にアクセスしてください。

このグループに投稿するには、android-g...@googlegroups.com にメールを送信してください。
このグループから退会するには、android-group-j...@googlegroups.com にメールを送信してください。
詳細については、http://groups.google.com/group/android-group-japan?hl=ja からこのグループにアクセスしてください。

hiros

unread,
Feb 22, 2012, 12:24:05 AM2/22/12
to android-g...@googlegroups.com
gorou57さん

完璧な解答をありがとうございました。
正に、教えて頂いた物を探しておりました。

if 文でnullを検知したり フラグを立てるような方法も考えていましたが
根本的な解決になっていないと考えていました。
結果としては、
android:configChanges="orientation|keyboardHidden" をManifestに追加しただけで
現象は再現されず解決できました。
public void onConfigurationChanged(Configuration newConfig) には
並べたボタンを横サイズに合わせていた物を再計算して
描画し直すだけで本当の解決に至りました。

余談ですが、ProgressDialogを表示してAsyncTaskで実行するような長い処理に入る時

private void controlOrientationFix(boolean fixOrient) {
  if (fixOrient) {
    int ori = getResources().getConfiguration().orientation;
    if (ori == Configuration.ORIENTATION_LANDSCAPE) {
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    }  else {
      setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }
  } else {
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED );
  }
}
の様な処理を挟んで回転を止めていましたが、これも不要になった気がします。

configChangesについて調べてみました。
他にもonDestroyの中でgetChangingConfigurations()によりonDestroyが発生した理由
を取得する方法もあるようですが、今回はそのものズバリだったので gorou57さんに
教えて頂いた物を、そのまま実装させて頂きました。


今回の現象について以下にまとめます。
=======================
SensorEventListenerのようなイベントが発生するActivityで
処理が追いつかない程に端末を回転させた時
イベントは蓄積され予期しない時にイベントを受信する事がある

回転開始
  onSaveInstanceState
  onPause
  onDestroy
   ↓
センサーイベント
  onSensorChanged
   ↓
回転復帰
  onCreate
  onRestoreInstanceState
  onResume

この様な場合
Manifestファイルの対象のActivityに
android:configChanges="orientation|keyboardHidden"
を追加する事で onDestroy と onCreate を実行しない事が可能である

上記をManifestに追加した場合には
public void onConfigurationChanged(Configuration newConfig) 
を Override する事で端末の状態が変化した事を検知できる
ここで検知出来るのは端末の回転の他にも
ソフトキーボードの表示/非表示 フォントの変更 言語の変更・・・等が有る
回転でレイアウトが変わる様な場合には、ここで処理する
========================

本当にありがとうございました。

gorou yamamoto

unread,
Feb 22, 2012, 1:04:59 AM2/22/12
to android-g...@googlegroups.com
hiros様

お役に立てて、うれしいです。


2012年2月22日14:24 hiros <fp.c...@gmail.com>:

--
このメールは Google グループのグループ「日本Androidの会」の登録者に送られています。
このディスカッションをウェブ上で閲覧するには、https://groups.google.com/d/msg/android-group-japan/-/Tj8VNtklqlAJ にアクセスしてください。

松村

unread,
Feb 22, 2012, 6:57:05 AM2/22/12
to 日本Androidの会
hiros様

松村と申します。

既に解決済みですが、いくつか気になる点がありましたので投稿させて頂きます。

> android:configChanges="orientation|keyboardHidden" をManifestに追加しただけで現象は再現されず解決できました。
Android3.0以降は、screenSizeのconfigChangeも受け取る必要があるのでご注意ください。
参考:http://developer.android.com/guide/topics/resources/runtime-
changes.html#HandlingTheChange

上記ページにもありますが、configChangeのハンドリングはシステムが自動で最適なリソースを管理している部分の処理を
全てアプリ責で行う必要がでてきますので、よく考えた上で使用することをおすすめします。
(といっても多くのアプリがconfigChange(特にorientation)を利用しています...)
/ / サイトより一部引
用 / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
configChangeのハンドリングは最後の案と考えるべきで、ほとんどのアプリで推奨されない。
/ / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / / /
# 誠に個人的な意見ですが、本不具合の発生でデータ破壊などユーザへの不利益がないのであれば、
# このためだけにconfigChangeをハンドルするのではなく、現状(あるいはフラグ制御)で良い気がします

> SensorEventListenerのようなイベントが発生するActivityで処理が追いつかない程に端末を回転させた時
> イベントは蓄積され予期しない時にイベントを受信する事がある
手元で現象再現させたところ、例外発生時のonSensorChangedイベントが直前に破棄されたActivityのインスタンスに対して
コールされていました。
Sensorのunregistはsensors_enable_sensor のJNIメソッドをコールしているので即unregistとはいかない
のかもしれませんね。
後はご推察の通り、解除したはずのコールバックが蓄積したイベントから届いているのかもしれません。

今回の件は非常に勉強になりました。ありがとうございます。
以上。失礼します。

hiros

unread,
Feb 22, 2012, 8:59:54 AM2/22/12
to android-g...@googlegroups.com
松村さん

またまた、参考になるご意見をありがとうございます。

今回のバグを発見したのは本当に偶然で
自分が使いたくて作ったアプリだったので使っていた時
本体を落としそうになり、何度かはじくようになり無事にキャッチしました
その時に強制終了となり初めて現象を確認しました。

家に戻ってから現象を再現させようと、端末に付いているボタンを
触ったりして、グルグルっと回った時に発生する事を確認しました。

そんな訳で普通に使っている限り問題は発生しませんし
まだまだ少ないですが、使って頂いている500人余りのユーザーからは
今の所、エラー報告は届いていません

松村さんのご意見も参考に、もう一度考えなおしてみます。

貴重なご意見をありがとうございました。



Reply all
Reply to author
Forward
0 new messages