はじめに
こんにちは。Daddy's Officeの市川です。
先日、使わなくなったAndroidを監視カメラにするソフト「LiveCapture3 Android」を公開したのですが、そこではまったのが、古いAndroidへの対応です。
使わなくなったスマホを利用するというコンセプトなので、なるべく古いAndroidでも動くようにする必要があり、最終的には、Android2.3(APIレベル9)以降すべてで動作する方向で開発しました。
あまり凝ったUIは不要だったので、基本的にはAndroid Support Libraryを外してしまえばOKかと思ったのですが、Android6.0(APIレベル23)以降で導入された、実行時パーミッションリクエストを実装するために、Support Library V4のActivityCompatとContextCompatだけは使う必要がありました。
当然、これらを使用せずとも同様の処理は記述可能なのだと思いますが(すいません、調べていませんが、、、)、権限処理に時間を割くのは本質的ではないので、これら権限処理に必要なSupportLibraryV4だけをリンクできる方法を調査しました。
SupportLibrary V4の最小APIレベル
単純にSupportLibraryV4だけを追加すればよいと思っていたのですが、現在、SupportLibraryV4の最小APIレベルはAndroid4.0(APIレベル14)に上がっているとのこと。
バージョン サポートとパッケージ名
Support Library バージョン 26.0.0(2017 年 7 月リリース)以降、すべてのサポート ライブラリ パッケージを対象に、サポートされる最小 API レベルが Android 4.0(API レベル 14)に変更されました。このため、サポート ライブラリの最近のリリースを使用する場合は、v# パッケージ表記が最小の API サポートレベルを示しているとは限りません。最近のリリースで行われたこの変更は、v4 と v7 のライブラリ パッケージでサポートされる API の最小レベルが本質的に同等であることも意味します。たとえば、support-v4 と support-v7 のどちらのパッケージも、26.0.0 リリース以降の Support Library に対して最小 API レベル 14 をサポートします。
これではminSdkVersionを14まで上げないと、Gradle Syncがエラーになってしまいます。。。
ということで、まずは、minSdkVersion 9のままでSupportLibraryV4を追加してもGradle Syncがエラーにならないようにします。
プロジェクト作成
まず、AndroidStudioで、「No Activity」で、空のプロジェクトを作成し、Gradleの内容を書き換えます。
- minSdkVerisonを9に変更
- support-v4ライブラリ以外の依存ライブラリをすべて削除
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.daddysoffice.sample.test"
minSdkVersion 9
targetSdkVersion 28
versionCode 1
versionName "1.0"
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:support-v4:28.0.0'
}
この状態でGradle Syncを実行すると、マニフェストのマージエラーになります。
ERROR: Manifest merger failed : uses-sdk:minSdkVersion 9 cannot be smaller than version 14 declared in library [com.android.support:support-v4:28.0.0] C:\Users\xxx\.gradle\caches\transforms-2\files-2.1\xxxxxxxxxxxx7f111f5\AndroidManifest.xml as the library might be using APIs not available in 9
Suggestion: use a compatible library with a minSdk of at most 9,
or increase this project's minSdk version to at least 14,
or use tools:overrideLibrary="android.support.v4" to force usage (may lead to runtime failures)
エラーの内容は、前述のとおりで、com.android.support:support-v4:28.0.0で定義されているversion 14よりもminSdkVersion 9 は小さい、とのこと。
この解決策として提示されている最後の方法を使います。
use tools:overrideLibrary="android.support.v4" to force usage (may lead to runtime failures)
tools:overrideLibrary
マニフェストファイルに、uses-sdk tools:overrideLibrary="xxxx" を記載すると、xxxxライブラリ内に記載されたminSdk/targetSdkVersionを無視することになるとのこと。
今回は、Android6.0(APIレベル23)以降のときにだけ、実行時パーミッション処理を走らせます。
APIレベル22以下の場合にはライブラリを使わないので、この最後の方法で問題ありません。
早速、AndroidManifest.xmlに、この記述を追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.daddysoffice.sample.broadcasttest"
xmlns:tools="http://schemas.android.com/tools"> <<これと
<uses-permission android:name="android.permission.CAMERA" />
<uses-sdk tools:overrideLibrary="android.support.v4/> << これを追加
これで、再度Gradle Syncを走らせると、今度は下記のエラーが発生。
Manifest merger failed : uses-sdk:minSdkVersion 9 cannot be smaller than version 14 declared in library [com.android.support:support-fragment:28.0.0] C:\Users\xxxxx\.gradle\caches\transforms-2\files-2.1\xxxxxxxxxx710eb0a2\AndroidManifest.xml as the library might be using APIs not available in 9
Suggestion: use a compatible library with a minSdk of at most 9,
or increase this project's minSdk version to at least 14,
or use tools:overrideLibrary="android.support.fragment" to force usage (may lead to runtime failures)
どうやら、supportV4をリンクすることで、いろいろとくっついてくる模様。。。
とりあえず、マニフェストのtools:overrideLibraryに、android.support.fragmentを追加して、再度Gradle Syncを実行。
すると、また別のライブラリがエラーで出てくる。。
これをエラーが出てこなくなるまで繰り返した結果、tools:overrideLibraryは、以下のようになりました。
<uses-sdk tools:overrideLibrary="android.support.v4,android.support.mediacompat,
android.support.fragment,android.support.coreui,android.support.coreutils,
android.support.v7.appcompat,android.support.graphics.drawable,
android.support.loader, android.support.v7.viewpager,
android.support.coordinatorlayout,android.support.drawerlayout,
android.support.slidingpanelayout,android.support.customview,
android.support.swiperefreshlayout, android.support.asynclayoutinflater,
android.support.compat,androidx.versionedparcelable,
android.arch.lifecycle,android.support.documentfile,
android.support.localbroadcastmanager,
android.support.print,android.support.interpolator,
android.support.cursoradapter,android.arch.lifecycle.viewmodel,
android.arch.lifecycle.livedata,
android.arch.lifecycle.livedata.core,android.arch.core" />
これ、方向性としてあっているのか?と少し疑問に思いながらも、とりあえずビルドができるようになりました。
ビルドを通す
とりあえず、Gradle Syncが通るようになったので、あとはビルドエラーを潰します。
Theme.AppCompatなどは使わないので、styles.xmlを削除し、AndroidManifest.xmlのapplicationに記載されたandroid:themeも削除
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" /> << これを削除
これでビルドが通りました!
実装
実装はOSバージョンごとに処理を分岐しながらコードを記述する必要があります。
コード中で処理を分岐する場合は、Build.VERSION.SDK_INT を使用して処理を分岐します。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
//
// Android6.0(APIレベル23)以降の場合は、実行時にパーミッションのチェックを行う
if (checkPermission()){
startApplication();
}
}
else {
//
// 上記以外はそのままアプリケーション開始
startApplication();
}
指定したOSバージョン以下では使わないことが確実なメソッドの場合は、@TargetApiアノテーションを使うことで、Lint(静的解析ツール)のエラーを制御できます。
ただし、この方法は単にLintのエラーを出さなくしているだけなので、実行時に対象OSレベル以下の場合に、このメソッドがコールされないように自分で注意して実装する必要があります。
@TargetApi(Build.VERSION_CODES.M)
private boolean checkPermission(){
int permissionCheck
= ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(
this,
new String[] { android.Manifest.permission.CAMERA },
PERMISSIONS_REQUEST_CAMERA);
return false;
}
return true;
}
おわりに
この方法で何とか、Android2.3以降の対応アプリを開発できましたが、本来、こんなことをしないで良いようにSDKのバージョンアップをしてほしいところです。。
いまいちAndroidが好きになれなかったのですが、今回の件で、ますます嫌いになってしまった気がします。。。