Android 4.4では、Retrofitの最新バージョンを使用 に道を教える


この記事では、Android 5.0以降をサポートするアプリケーションでRetrofitの最新バージョンを使用する方法について説明します.
RetrofitはAndroidアプリケーションの開発に広く使われているネットワークライブラリです.現在のRetrofitの最新バージョンは2.90で、Android 5.0を下回る場合に実行すると、実行時エラーが発生します.原因を理解し、2つの解決方法を紹介させていただきます.
エラーログ
java.lang.ExceptionInInitializerError
    at okhttp3.OkHttpClient.newSslSocketFactory(OkHttpClient.java:263)
    at okhttp3.OkHttpClient.<init>(OkHttpClient.java:229)
    at okhttp3.OkHttpClient$Builder.build(OkHttpClient.java:1015)
の原因となる
RetrofitはJava 8、Android 5.0(MinSdk 21)を要求し、2.7版の最低要求である.理由は、Retrofitが2.7.0リリースからOkHttp 3.14.4を使用しているからです. OkHttp 3.14.4はTLS 1.2で動作するためである.Android 5.0バージョンから、GoogleはTLS 1.2をサポートしています.
Version 2.7.0 (2019–12–09)
This release changes the minimum requirements to Java 8+ or Android 5+.
https://github.com/square/retrofit/blob/master/CHANGELOG.md#version-270-2019-12-09
Google added support for TLSv1.2 in Android 5.0. Oracle added it in Java 8. In OkHttp 3.13 we require that the host platform has built-in support for TLSv1.2.
https://code.cash.app/okhttp-3-13-requires-android-5
もし私が開発したアプリケーションのminsdkが19だったら、Retrofitの最新バージョンを書きたいのですが、解決方法は2種類あります.
方法. : 3.13未満のOkHttpバージョン ドロップ
build.GradleのみがOkHttpバージョンを3.12.13に降格した.
implementation('com.squareup.retrofit2:retrofit:2.9.0')
implementation('com.squareup.okhttp3:okhttp') { version { strictly '3.12.13' } }
この方法の欠点は、将来のRetrofitバージョンがアップグレードされても、私のアプリケーションのOkHttpバージョンは3.13以下に維持されることです.また、TLS 1.1は、ユーザがTLS 1.1のセキュリティホールを引き続き使用することを可能にする.
方法2:TLS 1.2に更新されたコードを追加する
セキュリティプロバイダを更新するコードを追加すると、実行時にGoogle Play ServiceでTLSプロバイダをダウンロードできます.ダウンロードから更新まで通常30~50ミリ秒かかります.古い機械は350ミリ秒ほどかかるそうです.
Googleが提供する関数には2つの方法があり、アプリケーションの特性に応じて選択すれば使用できます.

  • どうきモード
    installIfNeeded(Context: context)

  • ひどうきコールバック
    installIfNeededAsync(Context context, ProviderInstaller.ProviderInstallListener listener)
  • 実装例
    Googleの例に基づいて非同期処理コードを記述した.
    まず構築します.Google Playサービスを使用する依存性をgradeファイルに追加します.
    implementation('com.google.android.gms:play-services-basement:18.0.0')
    適切なアクティビティを選択してSSLプロバイダを更新し、installIfNeeddAsync()関数を呼び出し、コールバックを実行して応答を受信します.この例では、MainActivityはProviderInstallerです.ProviderInstallListenerが実装されているため、リスナーとして指定します.
    import android.content.Intent
    import android.os.Build
    import android.os.Bundle
    import android.util.Log
    import androidx.appcompat.app.AppCompatActivity
    import com.google.android.gms.common.GoogleApiAvailabilityLight
    import com.google.android.gms.security.ProviderInstaller
    import com.wally.gallery.R
    
    private const val ERROR_DIALOG_REQUEST_CODE = 1
    
    class MainActivity : AppCompatActivity(), ProviderInstaller.ProviderInstallListener {
        private var retryProviderInstall: Boolean = false
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    
            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                ProviderInstaller.installIfNeededAsync(this, this)
            }
        }
    
        override fun onProviderInstallFailed(errorCode: Int, recoveryIntent: Intent?) {
            Log.w(TAG, "onProviderInstallFailed!")
            GoogleApiAvailabilityLight.getInstance().apply {
                if (isUserResolvableError(errorCode)) {
                    Log.w(TAG, "onProviderInstallFailed!")
                    showErrorDialogFragment(
                    	this@MainActivity, 
                        errorCode, 
                        ERROR_DIALOG_REQUEST_CODE) {
                        onProviderInstallerNotAvailable()
                    }
                } else {
                    onProviderInstallerNotAvailable()
                }
            }
        }
    
        override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
            super.onActivityResult(requestCode, resultCode, data)
            if (requestCode == ERROR_DIALOG_REQUEST_CODE) {
                retryProviderInstall = true
            }
        }
    
        override fun onProviderInstalled() {
            Log.d(TAG, "onProviderInstalled!")
        }
    
        override fun onPostResume() {
            super.onPostResume()
            if (retryProviderInstall) {
                ProviderInstaller.installIfNeededAsync(this, this)
            }
            retryProviderInstall = false
        }
    
        private fun onProviderInstallerNotAvailable() {
    
        }
    
        companion object {
            private const val TAG = "MainActivity"
        }
    }
    この方法にも欠点がある.これは、Google Play Serviceがインストールされていないデバイスが機能しないことを意味します.また、当社のサービスはどの国でも実行されない可能性があります.そのため、当社のグローバルサービスアプリケーションは実際の操作を確認する必要があります.
    参考資料
  • https://developer.android.com/training/articles/security-gms-provider.html
  • https://ankushg.com/posts/tls-1.2-on-android/