Moshi ArrayOutOfBoundsException при добавлении фабрики - PullRequest
2 голосов
/ 19 марта 2020

Я нахожу это исключение, связанное с Moshi, иногда при открытии приложения:

Caused by java.lang.ArrayIndexOutOfBoundsException: length=33; index=33
       at java.util.ArrayList.add(ArrayList.java:468)
       at com.squareup.moshi.Moshi$Builder.add(Moshi.java:231)

Мы инициализируем репозиторий в BaseApplication, что иногда приводит к упомянутому cra sh при инициализации Moshi. Я нахожу эту ошибку в отчетах приложения, но не могу ее воспроизвести. Давайте перейдем к тому, что у нас есть, и посмотрим, есть ли у вас подсказка.

Эта фабрика используется для создания экземпляров Moshi, получая cra * sh при добавлении KotlinJsonAdapterFactory:

object MyMoshiConverterFactory {

    fun create(setup: (Moshi.Builder.() -> Unit)? = null): Converter.Factory {
        val moshi = MoshiUtil.createMoshi()
        setup?.let { moshi.it() }
        moshi.add(KotlinJsonAdapterFactory()) // Here is the crash!
        return MoshiConverterFactory.create(moshi.build())
    }
}

Здесь у нас есть класс, в котором есть все используемые нами преобразователи. У него действительно гораздо больше преобразователей, но я удалил некоторые из них для простоты:

object MoshiUtil {

    private val lazyMoshi by lazy {
        Moshi.Builder().apply {
            add(DateAdapter())
            add(DefaultOnDataMismatchAdapter.newFactory(FeedItem::class.java, null))
            add(SkipListNullValuesAdapter.createFactory(Element::class.java))
            add(SkipListNullValuesAdapter.createFactory(Post::class.java))
            add(SkipListNullValuesAdapter.createFactory(MetadataItem::class.java))
            add(GeoGeometry::class.java, GeometryAdapter())
        }
    }

    fun createMoshi() = lazyMoshi
}

И, наконец, в нашем BaseApplication у нас есть что-то вроде этого:

class BaseApplication {

   @Override
    public void onCreate() {
        super.onCreate();

        val myService = getMyService(applicationContext)

    }

    private fun getMyService(appContext: Context): MyService {
       val converterFactory = MyMoshiConverterFactory.create()

       return Retrofit.Builder().baseUrl(baseUrl).apply {
                addConverterFactory(converterFactory)
                client(okHttpClientBuilder.build())
            }.build().create(MyService::class.java)
        }
    }
 }

Итак, вы видите что-нибудь, что может быть причиной этого? Как вы думаете, это может быть проблема параллелизма, возникающая при запуске, когда несколько мест в приложении создают объект MoshiUtils одновременно? Ждем от вас, ребята, спасибо!

1 Ответ

1 голос
/ 10 апреля 2020

Moshi.Builder является изменяемым и не ориентированным на многопотоковое исполнение, поэтому эта ошибка, которую вы иногда получаете, является результатом гонки. Вы должны вызвать .build() в этом базовом MoshiUtil экземпляре, чтобы получить неизменяемый Moshi экземпляр, а затем сделать возвращаемое значение MoshiUtil.createMoshi равным moshi.newBuilder() (создает Moshi.Builder, уже настроенный как существующий Moshi экземпляр ), вот так:

object MoshiUtil {
    private val baseMoshi: Moshi = Moshi.Builder().apply {
        // ...
    }.build()

    fun createMoshi(): Moshi.Builder = baseMoshi.newBuilder()
}

Поскольку каждый, кто вызывает createMoshi, теперь получает свой собственный экземпляр Moshi.Builder, проблем с параллелизмом больше не должно быть.

...