Androidのアクティビティ
前回はAndroidアプリのコンポーネントについて説明しました。今回はその中の1つであるアクティビティについて説明します。
アクティビティとは
アクティビティとはUIを提供するコンポーネントです。ホームボタンでアプリアイコンをタップした時や、共有ボタンで選択された時に、Android OSによって起動されます。アクティビティが起動すると、Android OSは起動したアクティビティの画面を表示します。
アプリプロジェクトをEmpty Activityで作成した場合、MainActivity.ktというファイルが生成されます。このファイル中身をみていきましょう。
MainActivity
Android Studioが生成したMainActivity.ktの中身は次のようになっています。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
アクティビティはテンプレートメソッドパターンで実装されています。アプリ開発者はActivityクラスを継承し、必要なメソッドをオーバーライドすることでふるまいを記述していきます。この中で最も大事なメソッドはonCreate()です。このメソッドは、Android OSによってアクティビティオブジェクトが生成された時に呼ばれます。
super.onCreate()
先ほど、アプリ開発者はActivityクラスのメソッドをオーバーライドすることで処理を記述すると説明しました。Java/Kotlinの仕様上、オーバーライドした側でsuper.メソッド名()を呼ばなかった場合、スーパークラスで定義してある処理は実行されません。一方、Activityクラスでは、onCreate()などで初期設定や画面の復元などといった大事な処理が行われています。そのため、アプリ開発者はメソッドをオーバーライドした際、かならず必ずスーパークラスのメソッドを呼ぶ必要があります。Android SDKには呼び忘れを防止するための仕組みが実装されており、もし呼び忘れがあった場合、SuperNotCalledExceptionが投げられます。次はonStart()を「呼んでいる」部分のコードを抜粋したものです。mCalledフィールドはsuper.onStart()を呼んだ時にtrueとなるよう、Activityクラスで実装されています。
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStart()");
}
setContentView
Android Studioが生成したMainActivityでは、onCreate()の中でsetContentView()を呼んでいます。これは画面にどのようなものを表示するかを指定するメソッドです。引数には画面の部品であるViewオブジェクトか、レイアウトファイルを指定します。レイアウトファイルの指定はファイル名ではなく、「R.layout.ファイル名」形式の定数で指定します。レイアウトファイルがどのようなものかは次回説明します。
コンストラクタで初期化はしないの?
Java/Kotlinプログラミングの基礎を学んだ方なら、「なぜアクティビティオブジェクトの初期化をコンストラクタでやらないのだろう?」と思うでしょう。コンストラクタで初期化を行わない主な理由は次の2つです。
- オブジェクトの生成には引数なしコンストラクタが使用される
- オブジェクト生成後、必要なデータをセットする処理が行われている
アクティビティはコンポーネントの1つなので、オブジェクトの生成と破棄はAndroid OSが行います。オブジェクトの生成にはコンストラクタの呼び出しが必要です。Android OSはリフレクションを用いて引数なしのコンストラクタを呼びます。
// android.app.Instrumentation.java
public Activity newActivity(Class<?> clazz, Context context,
IBinder token, Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
Object lastNonConfigurationInstance) throws InstantiationException,
IllegalAccessException {
Activity activity = (Activity)clazz.newInstance();
...
}
引数なしコンストラクタしか呼べないので、当然ですがデータの設定などはこの時点では行えません。Android OSはnewInstance()でオブジェクト生成後、次のようにattach()を呼ぶことでデータの設定を行っています。onCreate()はこの処理の後に呼ばれます。
activity.attach(context, aThread, this, token, 0 /* ident */, application, intent,
info, title, parent, id,
(Activity.NonConfigurationInstances)lastNonConfigurationInstance,
new Configuration(), null /* referrer */, null /* voiceInteractor */,
null /* window */, null /* activityConfigCallback */);
MainActivityでコンストラクタを作成し、その中でsetContentView()といったメソッドを呼んだ場合、上記のattach()でセットされるオブジェクトが無いためエラーとなります。
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.Window$Callback android.view.Window.getCallback()' on a null object reference
まとめ
アクティビティクラスがどのようなものであるかを説明しました。イマドキのアプリはアクティビティに処理をほとんど記述しないため、説明も簡単なものにしました。