I took a deeper look at the issue. The same performance problem plagues the "Recent" tab as well. I believe this is the relevant discussion in the Jackson itself:
TLDR: deserialization that uses reflection will be slow on Android on the first run because reflection itself is slow. Only when the deserializer is initialized on the first run and cached can Jackson speed up. And it does speed up, in my test, after warming-up the subsequent deserializations are over an order of magnitude faster then the first one.
This PR is just a quick bandaid that removes one of the performance bottlenecks: reuses the ObjectMapper.
Unfortunately that does not really help with the issue itself. You can see below that the processing in getAllItemsInProgress is now fast enough, but unfortunately this just highlights the same issue with the "Recent" tab, it just takes too long. And there are no such low-hanging fruits there. That would require migrating from Jackson to something not based on reflection like Moshi or kotlinx.serialization.
How have you tested this?
With Desktop Head Unit and a real device (Pixel 6a, android 15).
Using real data and a few log instructions, the following is the performance improvement in the loop in getAllItemsInProgress (numbers are ms on each iteration and for the whole loop).
2025-05-17 20:47:40.988 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1520
2025-05-17 20:47:42.337 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1349
2025-05-17 20:47:43.682 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1345
2025-05-17 20:47:44.969 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1287
2025-05-17 20:47:46.327 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1358
2025-05-17 20:47:47.595 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1268
2025-05-17 20:47:48.700 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1105
2025-05-17 20:47:49.667 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 967
2025-05-17 20:47:50.728 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1061
2025-05-17 20:47:51.166 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 438
2025-05-17 20:47:51.166 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 11698
2025-05-17 20:44:54.971 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 70
2025-05-17 20:44:54.974 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 3
2025-05-17 20:44:54.975 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 1
2025-05-17 20:44:54.977 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 1
2025-05-17 20:44:54.979 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 2
2025-05-17 20:44:54.980 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 1
2025-05-17 20:44:54.982 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 2
2025-05-17 20:44:54.986 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 4
2025-05-17 20:44:54.988 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 2
2025-05-17 20:44:54.992 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 4
2025-05-17 20:44:54.992 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 91
🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.
## 📋 Pull Request Information
**Original PR:** https://github.com/advplyr/audiobookshelf-app/pull/1577
**Author:** [@golinski](https://github.com/golinski)
**Created:** 5/17/2025
**Status:** ✅ Merged
**Merged:** 5/19/2025
**Merged by:** [@advplyr](https://github.com/advplyr)
**Base:** `master` ← **Head:** `jackson-reuse`
---
### 📝 Commits (2)
- [`ea59ad2`](https://github.com/advplyr/audiobookshelf-app/commit/ea59ad2953ff358c43b01139ae98beede0f4dfc6) Reuse the existing ObjectMapper
- [`c4fe068`](https://github.com/advplyr/audiobookshelf-app/commit/c4fe0680f3ef1b58969d1d98b0aa89be1036cd83) Remove unused imports
### 📊 Changes
**2 files changed** (+3 additions, -6 deletions)
<details>
<summary>View changed files</summary>
📝 `android/app/src/main/java/com/audiobookshelf/app/data/ItemInProgress.kt` (+2 -4)
📝 `android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt` (+1 -2)
</details>
### 📄 Description
<!--
For Work In Progress Pull Requests, please use the Draft PR feature,
see https://github.blog/2019-02-14-introducing-draft-pull-requests/ for further details.
If you do not follow this template, the PR may be closed without review.
Please ensure all checks pass.
If you are a new contributor, the workflows will need to be manually approved before they run.
-->
## Brief summary
Reuse Jackson ObjectMapper when rendering the "Continue" Android Auto tab.
## Which issue is fixed?
Fixes partially #1570
## Pull Request Type
Android backend-only change
## In-depth Description
I took a deeper look at the issue. The same performance problem plagues the "Recent" tab as well. I believe this is the relevant discussion in the Jackson itself:
https://github.com/FasterXML/jackson-module-kotlin/issues/69
TLDR: deserialization that uses reflection will be slow on Android on the first run because reflection itself is slow. Only when the deserializer is initialized on the first run and cached can Jackson speed up. And it does speed up, in my test, after warming-up the subsequent deserializations are over an order of magnitude faster then the first one.
This PR is just a quick bandaid that removes one of the performance bottlenecks: reuses the ObjectMapper.
Unfortunately that does not really help with the issue itself. You can see below that the processing in `getAllItemsInProgress` is now fast enough, but unfortunately this just highlights the same issue with the "Recent" tab, it just takes too long. And there are no such low-hanging fruits there. That would require migrating from Jackson to something not based on reflection like Moshi or kotlinx.serialization.
## How have you tested this?
With Desktop Head Unit and a real device (Pixel 6a, android 15).
Using real data and a few log instructions, the following is the performance improvement in the loop in `getAllItemsInProgress` (numbers are ms on each iteration and for the whole loop).
```
2025-05-17 20:47:40.988 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1520
2025-05-17 20:47:42.337 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1349
2025-05-17 20:47:43.682 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1345
2025-05-17 20:47:44.969 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1287
2025-05-17 20:47:46.327 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1358
2025-05-17 20:47:47.595 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1268
2025-05-17 20:47:48.700 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1105
2025-05-17 20:47:49.667 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 967
2025-05-17 20:47:50.728 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 1061
2025-05-17 20:47:51.166 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 438
2025-05-17 20:47:51.166 11100-11814 getAllItemsInProgress com.audiobookshelf.app.debug E 11698
```
```
2025-05-17 20:44:54.971 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 70
2025-05-17 20:44:54.974 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 3
2025-05-17 20:44:54.975 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 1
2025-05-17 20:44:54.977 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 1
2025-05-17 20:44:54.979 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 2
2025-05-17 20:44:54.980 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 1
2025-05-17 20:44:54.982 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 2
2025-05-17 20:44:54.986 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 4
2025-05-17 20:44:54.988 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 2
2025-05-17 20:44:54.992 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 4
2025-05-17 20:44:54.992 7411-9008 getAllItemsInProgress com.audiobookshelf.app.debug E 91
```
---
<sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
📋 Pull Request Information
Original PR: https://github.com/advplyr/audiobookshelf-app/pull/1577
Author: @golinski
Created: 5/17/2025
Status: ✅ Merged
Merged: 5/19/2025
Merged by: @advplyr
Base:
master← Head:jackson-reuse📝 Commits (2)
ea59ad2Reuse the existing ObjectMapperc4fe068Remove unused imports📊 Changes
2 files changed (+3 additions, -6 deletions)
View changed files
📝
android/app/src/main/java/com/audiobookshelf/app/data/ItemInProgress.kt(+2 -4)📝
android/app/src/main/java/com/audiobookshelf/app/server/ApiHandler.kt(+1 -2)📄 Description
Brief summary
Reuse Jackson ObjectMapper when rendering the "Continue" Android Auto tab.
Which issue is fixed?
Fixes partially #1570
Pull Request Type
Android backend-only change
In-depth Description
I took a deeper look at the issue. The same performance problem plagues the "Recent" tab as well. I believe this is the relevant discussion in the Jackson itself:
https://github.com/FasterXML/jackson-module-kotlin/issues/69
TLDR: deserialization that uses reflection will be slow on Android on the first run because reflection itself is slow. Only when the deserializer is initialized on the first run and cached can Jackson speed up. And it does speed up, in my test, after warming-up the subsequent deserializations are over an order of magnitude faster then the first one.
This PR is just a quick bandaid that removes one of the performance bottlenecks: reuses the ObjectMapper.
Unfortunately that does not really help with the issue itself. You can see below that the processing in
getAllItemsInProgressis now fast enough, but unfortunately this just highlights the same issue with the "Recent" tab, it just takes too long. And there are no such low-hanging fruits there. That would require migrating from Jackson to something not based on reflection like Moshi or kotlinx.serialization.How have you tested this?
With Desktop Head Unit and a real device (Pixel 6a, android 15).
Using real data and a few log instructions, the following is the performance improvement in the loop in
getAllItemsInProgress(numbers are ms on each iteration and for the whole loop).🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.