• 検索結果がありません。

Application に Service の実体を突っ込んでいる

第 1 章 駄目コードとその対策 6

1.10 Application に Service の実体を突っ込んでいる

動けばいいという意味ではラッパークラス作らず、そのまま書いてしまっても違いはありま せん。しかしコード後から見たときや修正するときに、キーやデータ型のように手で合わせな いといけないものがそのままになっていると、誤って記述した際に不具合の原因となってしま います。特にこれらのキーやデータ型は誤って記述してしまうとコンパイルエラーにもなら ないため非常に厄介です。これら避けるため、ラッパークラスを作成して問題点の責務をラッ パークラスに持たせ、扱う側が無駄に悩まなくていいようにしましょう。ラッパークラスは単 にラッピングしているのではなく、そういった責務も封じ込めているのです。

@Override

public void onDestroy() { super.onDestroy();

DameApplication app = (DameApplication) getApplication();

app.setDameService(null);

}

public String hoge() { return "hogehoge";

} }

この

Service

hoge

メソッドから文字列を受け取り、画面に表示するだけの乱暴な実装

Activity

がリスト

1.31

になります。

リスト1.31: 悪い例 ResidentDameActivity.java

public class ResidentDameActivity extends Activity implements View.OnClickListener {

private EditText editText1;

@Override

protected void onCreate(Bundle savedInstanceState) { /* 省略 */

}

@Override

public void onClick(View v) { if (v.getId() == R.id.button1) {

DameApplication app = (DameApplication) getApplication();

ResidentDameService service = app.getDameService();

if (service != null) {

editText1.setText(app.getDameService().hoge());

} } } }

こ の

Activity

ResidentDameService#onCreate

が 呼 び 出 さ れ た 後 な ら ば 動 く こ と は 動 き ま す 。し か し

Service

が 起 動 す る 前 や 再 起 動 し た 場 合 は

DameApplica-tion.getDameService()

null

を返すため正常に動作しません。また

Activity

側で対応し ようとしても、起動や再起動が発生したか否かのイベントが通知されないため、対応が行

えません。これを回避するには

Application

に保持させたりせず、標準の手順にあるとおり

ServiceConnection

を使います。

まず

onCreate

メソッド内で

Application

に自身を保持させたり、

onDestroy

メソッド内で 解除したりするような乱暴な実装を削除します。

リスト1.32: 良い例 ResidentRefineService.java

public class ResidentRefineService extends Service {

public class LocalBinder extends IResidentRefineService.Stub { public ResidentRefineService getService() {

return ResidentRefineService.this;

} }

private LocalBinder mBinder = new LocalBinder();

@Override

public IBinder onBind(Intent intent) { return mBinder;

}

public String hoge() { return "hogehoge";

} }

Activity

側もリスト

1.33

のように、

ServiceConnection

を作成し、標準の手続きにした がって

Service

bind

するようにします。

リスト1.33: 良い例 ResidentRefineActivity.java

public class ResidentRefineActivity extends Activity implements View.OnClickListener {

private EditText editText1;

private ResidentRefineService mService;

ServiceConnection mServiceConnection = new ServiceConnection() {

@Override

public void onServiceConnected

(ComponentName name, IBinder service) {

mService = ((LocalBinder) service).getService();

}

@Override

public void onServiceDisconnected(ComponentName name) { mService = null;

} };

@Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

findViewById(R.id.button1).setOnClickListener(this);

editText1 = (EditText) findViewById(R.id.editText1);

}

@Override

protected void onStart() { super.onStart();

Intent intent = new Intent(this, ResidentRefineService.class);

bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);

}

@Override

protected void onPause() { super.onPause();

unbindService(mServiceConnection);

}

@Override

public void onClick(View v) { if (v.getId() == R.id.button1) {

if (mService != null) {

editText1.setText(mService.hoge());

} } } }

一見面倒には見えますが、不安定な状態をハンドリングできるようにするために必要な手続 きです。これは

Activity

Service

にはライフサイクルの違いこそあれ、2つは対等な関係 であるからです。対等な2つを接続するということはそれなりの手順が必要になるというこ とです。

なお

Bind

した

Service

は忘れずに

Unbind

しましょう。これを忘れると

ServiceCon-nection

のリークに繋がります。この例では

Service

bind/unbind

するタイミングは

on-Start()/onStop()

を使用していますが、

onCreate()/onDestroy()

onPause()/onResume()

など、

Activity

のライフサイクル上で必ず通る組み合わせを使用するとリークを回避すること

ができます。