Nutrient Android SDK 11.0 release notes
4 February 2026
Nutrient Android SDK 11 is a major release focused on performance and memory management. We’ve rebuilt the core rendering system using Jetpack Compose, started modernizing our APIs to use Kotlin coroutines, and added significant memory optimizations for viewing complex documents.
This release begins our transition to coroutine-based APIs, starting with AnnotationProvider and DocumentJsonFormatter. We’ll continue migrating additional APIs in future releases. For projects not yet using coroutines, we provide helper classes to ease the transition.
We also plan to expand our use of Jetpack Compose throughout the SDK, building on the Compose-based page rendering introduced in this release.
This release contains several fixes and enhancements. For the full list of changes, refer to our changelog.
Compose-based page rendering
We’ve rebuilt the core PageView rendering system using Jetpack Compose, replacing the legacy Android view system. This architectural change brings several benefits:
- Improved performance — Faster page transitions and smoother scrolling.
- Better memory handling — More efficient bitmap management during rapid scrolling.
- Fixed tile artifacts — Removed tile rendering for detailed bitmaps, which eliminates visual artifacts that could appear during zooming.
Memory management improvements
This release introduces memory monitoring and automatic trimming to reduce memory pressure when viewing complex or large documents. The SDK now tracks memory usage and automatically releases resources when the system is under pressure, reducing the likelihood of out-of-memory crashes.
We’ve also improved memory management during rapid scrolling, ensuring smoother performance when navigating through documents quickly.
Enhanced stylus support
We’ve added support for Samsung S-Pen custom motion events on devices like Tab S6 Lite that use non-standard action values, as well as support for the stylus eraser tool type (TOOL_TYPE_ERASER). When using a supported stylus, flipping to the back end of the stylus automatically switches to eraser mode.
Multi-stage IME composition
This release adds proper support for multi-stage IME composition in content editing, such as Korean character input. The editing performance for these input methods has been significantly improved.
Brotli compression support
The SDK now supports Brotli decompression for PDFs, an emerging compression standard that offers better compression ratios than traditional methods. For more information, refer to the PDF Association announcement on Brotli compression(opens in a new tab).
UI improvements
- Document tabs — Tabs now fill the full width of the tab bar with even distribution, matching our iOS SDK implementation.
- Form filling — Form filling is now enabled in annotation creation mode when no annotation tool is selected.
- Instant JSON — Improved import and export by preserving form field IDs across operations.
Bug fixes
This release addresses numerous issues:
- Fixed a crash that could occur when rapidly pressing the undo button.
- Fixed an OOM exception when rendering text blocks during content editing at very high zoom levels.
- Fixed an ANR issue on heavy documents when opening the outline view.
- Fixed the eraser tool missing ink annotation segments at high zoom levels.
- Fixed ink annotation lines appearing jagged during erasing until touch-up.
- Fixed various issues with XFDF export, text markup annotations, and font handling.
Instant improvements
autosaveEnabledinInstantPdfFragmentis now configurable. It remains enabled by default but can be disabled viaPdfConfiguration.Builder.autosaveEnabled(false)if you experience sync issues during lifecycle transitions. Documents are still automatically synced every second after annotation changes.- Added safeguards against
ALREADY_SYNCINGrace conditions during sync retries. - Improved error handling to prevent infinite sync loops when critical errors are received. The SDK now stops syncing after encountering unrecoverable errors such as
INVALID_REQUEST, allowing apps to handle the error gracefully. - Added support for recovering from server-side document recreation scenarios where annotation IDs no longer match the local cache. Refer to the Instant example(opens in a new tab) in the Catalog app for a demonstration of cache reset and document reopening.
Looking ahead
We’re continuing to invest in performance and developer experience:
- Progressive rendering — We’re working on progressive page rendering for faster initial display of complex pages. Expected delivery in one of the upcoming minor V11 releases.
- Toolbar customization — Improved APIs for customizing toolbars and annotation tools.
- Stability — Continued focus on reducing crashes and improving reliability.
- Memory management — Further optimizations for memory usage on resource-constrained devices.
Migration guide {#migration-guide}
This release includes breaking changes to several APIs. Review the following sections to update your code.
AnnotationProvider API changes
The AnnotationProvider interface now uses Kotlin coroutine suspend functions instead of RxJava or synchronous methods. This change improves performance and provides better cancellation support.
Before (RxJava):
document.annotationProvider .getAnnotationsAsync(pageIndex) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe { annotations -> // Handle annotations }After (Kotlin coroutines):
lifecycleScope.launch { val annotations = document.annotationProvider.getAnnotations(pageIndex) // Handle annotations}Java interoperability
For Java code or projects not yet using coroutines, we provide two helper classes as temporary solutions:
AnnotationProviderRxJava — Bridges suspend APIs to RxJava types:
import static com.pspdfkit.annotations.AnnotationProviderRxJava.*;
getAnnotationsObservable(document.getAnnotationProvider(), pageIndex) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(annotations -> { // Handle annotations });AnnotationProviderBlocking — Provides blocking versions for synchronous Java code:
import static com.pspdfkit.annotations.AnnotationProviderBlocking.*;
// Must be called off the main threadList<Annotation> annotations = getAnnotationsBlocking( document.getAnnotationProvider(), pageIndex);These helper classes are deprecated and will be removed in a future release (expected in 2027). We recommend migrating to Kotlin coroutines.
DocumentJsonFormatter API changes
DocumentJsonFormatter now uses suspend functions. The importDocumentJsonAsync and exportDocumentJsonAsync methods are deprecated but still available.
Before:
DocumentJsonFormatter.exportDocumentJsonAsync(document, outputStream) .subscribeOn(Schedulers.io()) .subscribe()After:
lifecycleScope.launch(Dispatchers.IO) { DocumentJsonFormatter.exportDocumentJson(document, outputStream)}For synchronous calls from Java, use exportDocumentJsonBlocking or importDocumentJsonBlocking, which must be called off the main thread.
PdfDrawableProvider API changes
The PdfDrawableProvider.getDrawablesForPage method is now a suspend function. Previously this was a synchronous method.
Before:
class MyDrawableProvider : PdfDrawableProvider() { override fun getDrawablesForPage( context: Context, document: PdfDocument, pageIndex: Int ): List<PdfDrawable>? { // Return drawables }}After:
class MyDrawableProvider : PdfDrawableProvider() { override suspend fun getDrawablesForPage( context: Context, document: PdfDocument, pageIndex: Int ): List<PdfDrawable>? { // Return drawables }}Removed theme attribute
The pspdf__tabBarMaximumWidth theme attribute has been removed. Document tabs now automatically fill the available width and are distributed evenly. If you were using this attribute in your theme, remove it:
<!-- Before --><style name="MyTheme" parent="Theme.Nutrient"> <item name="pspdf__tabBarMaximumWidth">300dp</item> <!-- Remove this line --></style>
<!-- After --><style name="MyTheme" parent="Theme.Nutrient"> <!-- No longer needed --></style>If your use case requires a maximum tab width, please contact our support team(opens in a new tab) to discuss your requirements.
Null license keys and manual initialization
Passing a null license key for demo mode is now supported for [`Nutrient.setLicenseKey]setLicenseKey if you are initializing manually for testing. If you’re manually initializing the SDK, you must set the `nutrient_automatic_initialize` flag to `false` in your `AndroidManifest.xml`:
<application> <meta-data android:name="nutrient_automatic_initialize" android:value="false" /></application>For more details on initialization options, refer to the Initializing Nutrient guide.