Android Studioでreleaseビルドを実行したとき、Lintが「ZoomableImageView must extend android.app.Activity [Instantiatable]」を報告して停止することがあります。典型的な原因は、カスタムViewをAndroidManifest.xmlの<activity>として誤登録していることです。この記事では、エラーの読み方から修正、確認までを整理します。
代表的なエラーログ
Lint found fatal errors while assembling a release target.
AndroidManifest.xml:54: Error: This class should provide a default constructor
(a public constructor with no arguments)
(jp.co.joker.kazuki.dangerous.substance.lite.ZoomableImageView)
[Instantiatable]
android:name=".ZoomableImageView"
~~~~~~~~~~~~~~~~~~
Error: ZoomableImageView must extend android.app.Activity [Instantiatable]
原因
<activity android:name=".ZoomableImageView">と書くと、AndroidはZoomableImageViewをActivityコンポーネントとして扱います。しかし実体がViewやAppCompatImageViewのサブクラスなら、android.app.Activityを継承していません。LintのInstantiatable検査は、Manifestに登録されたコンポーネントをシステムが生成できるか確認するため、Activity継承エラーとコンストラクタのエラーを同時に報告することがあります。
これは特定バージョンのAndroid StudioやAndroid Gradle Pluginの既知不具合ではなく、Manifestとクラス種別の不一致です。そのため「修正版への更新」ではなく、登録内容を直すのが解決策です。
発生条件
- カスタムViewが
View、ImageView、AppCompatImageViewなどを継承している。 - そのクラスを
AndroidManifest.xmlの<activity>に指定している。 lintReleaseやassembleReleaseでfatalなLintエラーが有効になっている。- product flavor、別のsource set、依存ライブラリから同じ誤登録がManifestマージされる場合もある。
推奨する解決手順
1. 誤ったActivity登録を削除する
次のような記述をManifestから削除します。
<activity
android:name=".ZoomableImageView"
android:exported="false" />
2. カスタムViewはレイアウトで使用する
完全修飾クラス名をレイアウトXMLのタグとして指定します。
<jp.co.joker.kazuki.dangerous.substance.lite.ZoomableImageView
android:id="@+id/zoomableImageView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3. XMLから生成できるコンストラクタを用意する
カスタムViewをXMLで使う場合は、ContextとAttributeSetを受け取れるコンストラクタを用意します。
class ZoomableImageView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr)
Manifestへの誤登録を残したまま、引数なしコンストラクタだけを追加する方法は推奨されません。クラスの役割の不一致が解消されないためです。
4. 画面として起動したい場合はActivityを別に作る
class ZoomableImageActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_zoomable_image)
}
}
ManifestにはViewではなく、このActivityを登録します。
<activity
android:name=".ZoomableImageActivity"
android:exported="false" />
代替策
カスタムViewはZoomableImageView(context)のようにコードから生成することもできます。別source setやライブラリ由来のManifest項目が原因なら、Android Studioの「Merged Manifest」で追加元を特定し、可能なら追加元のManifestを修正してください。マージ規則による削除は最後の手段で、必要な正規Activityまで削除しないよう確認が必要です。
修正できたか確認する方法
- Android StudioのMerged Manifestで
ZoomableImageViewが<activity>配下に残っていないことを確認します。 - Viewを含むレイアウトを表示し、XML inflationが成功することを確認します。
- 次を実行してLintとreleaseビルドを再確認します。
./gradlew lintRelease assembleRelease
注意点
- エラーを消すためにカスタムViewをActivity継承へ変更しないでください。ViewとActivityは役割もライフサイクルも異なります。
@SuppressLint("Instantiatable")やLint baselineで隠すと、実行時に起動不能となる構成ミスを見逃します。- debugで通ってreleaseだけ失敗する場合も、まずvariantごとのMerged Manifestを確認してください。
- クラス名を変更した後の古いManifest記述が残っていないかも確認してください。
まとめ
ZoomableImageView must extend android.app.Activity [Instantiatable]は、カスタムViewをActivityとしてManifestへ登録したことが主因です。<activity>からViewを削除し、レイアウトまたはコードで使用します。画面遷移先が必要ならActivityを別クラスとして作成・登録し、最後にMerged ManifestとlintRelease assembleReleaseで確認してください。
