Help us understand the problem. What is going on with this article?

Android2.3(APIレベル9)~の対応プログラムの開発について

はじめに

こんにちは。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に、この記述を追加します。

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は、以下のようになりました。

AndroidManifest.xml
    <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も削除

AndroidManifest.xml
   <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が好きになれなかったのですが、今回の件で、ますます嫌いになってしまった気がします。。。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away