react-native-fbsdk が Android でコンパイルできない

EDIT:

https://github.com/facebook/react-native-fbsdk/pull/373#issuecomment-352859817
PR がマージされたようなので、近い将来、対応が不要になります。

---

## 問題

2017年12月6日、運用している RN アプリの Android ビルドが突然落ちだした。
エラーメッセージは次の通り。

```
> Building 25% > :react-native-fbsdk:processReleaseResources:react-native-fbsdk:processReleaseResources
/home/ubuntu/wamazing-react-native/node_modules/react-native-fbsdk/android/build/intermediates/res/merged/release/values-v26/values-v26.xml:15:21-54: AAPT: No resource found that matches the given name: attr 'android:keyboardNavigationCluster'.
    
> Building 25% > :react-native-fbsdk:processReleaseResources/home/ubuntu/wamazing-react-native/node_modules/react-native-fbsdk/android/build/intermediates/res/merged/release/values-v26/values-v26.xml:15: error: Error: No resource found that matches the given name: attr 'android:keyboardNavigationCluster'.
```

[react-native-fbsdk](https://github.com/facebook/react-native-fbsdk) の gradle タスクの中でエラーが出ている。
とりあえず、`No resource found that matches the given name: attr 'android:keyboardNavigationCluster'.` でググったらば、compileSdkVersion を 26 にしてみな、みたいな回答が見つかる。

例えば、https://stackoverflow.com/a/45310170

しかし、RN アプリではそんなに気安く compileSdkVersion をあげられないという前提がある。
少なくとも公式には、23 を設定するようにドキュメントで書いている。
https://facebook.github.io/react-native/docs/getting-started.html

なので、上記回答はそれほどカジュアルに対応できる内容ではないし、かつ、プロジェクトの compileSdkVersion をあげるだけでは解決しなかった。

（実際には、23 より上のバージョンを compileSdkVersion としても、それほど大きな問題は起きないと思われる。てか実際運用しているアプリでは 25 を指定しているし、[react-native-navigation](https://github.com/wix/react-native-navigation/blob/master/android/app/build.gradle#L5) という、それなりに市民権を得てかつたくさん使われているライブラリなんかも、25 を指定している。てかそもそも [react-native-fbsdk が compileSdkVersion 25 だし](https://github.com/facebook/react-native-fbsdk/blob/master/android/build.gradle#L4)）

## 予想される原因

昨日まで通っていたビルドが通らないってことだから、原因としては大きく、

- package.json なり build.gradle なりをいじったことによって落ちる（内部要因）
- 我々のアプリがコントロールしている部分以外の影響によって落ちる（外部要因）

という二つが考えられる。

今回の場合は、package.json ないし build.gradle についての diff はなかったので、外部要因であることがわかる。
そこを頼りに react-native-fbsdk ないしその依存先である [facebook-android-sdk](https://github.com/facebook/facebook-android-sdk) を見てみると、ちょうど障害発生の数時間前に、新しいバージョン（[4.29.0](https://github.com/facebook/facebook-android-sdk/releases/tag/sdk-version-4.29.0)）がリリースされていた。
どうやらこのバージョンの SDK 以降、compileSdkVersion が 26 以上であることを要求しているようだった。

## 根本原因は何か

弊社アプリの build.gradle の、関係あるそうな部分を抜粋してみると、だいたい以下のようになる。
ちなみに RN のバージョンは 0.49.5 である。

```
dependencies {
    // ... 他ライブラリへの依存の宣言
    compile(project(':react-native-fbsdk')) {
        exclude group: 'com.facebook.android', module: 'facebook-android-sdk'
    }
    compile "com.facebook.android:facebook-android-sdk:4.22.1"
}
```

これを見てみると、一次依存先としての facebook-android-sdk のバージョンは `4.22.1` を指定しており、最新バージョンである `4.29.0` の影響は受けないように思われる。

原因はここにはないようなので、さらに react-native-fbsdk の build.gradle を見てみる。

```
apply plugin: 'com.android.library'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.3"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 25
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:25.0.0'
    compile 'com.facebook.react:react-native:+' // support react-native-v0.22-rc+
    compile('com.facebook.android:facebook-android-sdk:4.+')
}
```

一番最後にある依存宣言として、facebook-android-sdk の 4 系の最新を要求しており、これが原因で現行の最新バージョンである `4.29.0` が依存解決されてしまうことが予想される。

実際、Android プロジェクトのビルドの際の中間生成物として、fbsdk には `4.29.0` が DL されていることが確認できた。

<img width="287" alt="33648732-9448c82a-da9e-11e7-876a-bffa5778cd77.png" src="https://qiita-image-store.s3.amazonaws.com/0/75088/834c191c-cff2-450f-23c0-5e773d462f40.png">

ここまでで、fbsdk が解決するバージョンを、本体の一次依存先と統一すれば問題が解決するのではないかという筋道が見えてくる。


## 解決方法

一次依存先のライブラリが依存するライブラリ（二次依存先）のバージョンを上書きする方法を調べたら、まんまのブログがあったので、それを真似して以下のように書く。
（元ネタは[こちら](http://tapestryjava.blogspot.jp/2014/02/gradle-overruling-third-party.html)）

```groovy:{project_root}/android/build.gradle
def versionOverrides = [
    "com.facebook.android:facebook-android-sdk": "4.22.1",
]

allprojects {
    repositories { /** 省略 **/ }
    configurations.all {
        resolutionStrategy.eachDependency { DependencyResolveDetails details ->

            def overrideVersion = versionOverrides[details.requested.group + ":" + details.requested.name]

            if (overrideVersion != null && details.requested.version != overrideVersion) {
                println("********************************************************")
                println("Overriding dependency ${details.requested.group}:${details.requested.name} version ${details.requested.version} --> $overrideVersion")
                details.useVersion overrideVersion
                println("********************************************************")
            }
        }
    }
}
```

以上を追加してビルドを行うと、fbsdk の依存先が `4.22.1` で固定されることになり、無事問題が解決する。

っていうのを、fbsdk の PR にコメントしたんだけど、日本語として情報があってもいいと思ったので Qiita にも書いてみました。
PR 自体は fbsdk の compileSdkVersion をあげるという、個人的にあまりイケているとは思えない対応だったので、方法を頑張って模索して見た次第です。

https://github.com/facebook/react-native-fbsdk/pull/373#issuecomment-349562744