ContentProvider returns null unless app manually launched — request for background-initializable entry point
Checked for duplicates?
- [x] This issue is not a duplicate
Does it also happen in the desktop version?
- [ ] This bug does not occur in the latest version of Anki Desktop
What are the steps to reproduce this bug?
- Install AnkiDroid but do not open it.
- From another app, use ContentResolver.query() on content://com.ichi2.anki.api/deck.
- Observe that the query returns null.
- Now open AnkiDroid once and return to the other app.
- Repeat the query — it now succeeds.
Reproduced with Pleco as well.
Expected behaviour
The contentProvider can be made available independently from the UI lifecycle, possibly with a silent Intent, so that API queries should succeed even if AnkiDroid hasn’t been manually opened, assuming permissions are met.
Debug info
AnkiDroid Version = 2.20.1 (e32a82c33646807ea19623c9c88be38965bc07e6)
Backend Version = 0.1.48-anki24.11 (24.11 c47638ca36f99dd4f3b81ae82d964aec66e392e0)
Android Version = 15 (SDK 35)
ProductFlavor = play
Manufacturer = HONOR
Model = PGT-AN10
Hardware = qcom
Webview User Agent = Mozilla/5.0 (Linux; Android 15; PGT-AN10 Build/HONORPGT-AN10; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/135.0.7049.111 Mobile Safari/537.36
ACRA UUID = 8f9e47ec-8911-4c6c-88cd-4a5b699726a6
FSRS = 1.4.3 (Enabled: false)
Crash Reports Enabled = true
(Optional) Anything else you want to share?
Maybe consider exposing a headless BroadcastReceiver or lightweight Service (e.g. com.ichi2.anki.WarmupReceiver) that can be used to start the app process without invoking the UI — allowing external apps to "warm up" the AnkiDroid process in the background before sending API requests?
Research
- [x] I have checked the manual and the FAQ and could not find a solution to my issue
- [x] (Optional) I have confirmed the issue is not resolved in the latest alpha release (instructions)
Please share a video by reproducing the error with the help of Pleco.
Absolutely, interestingly enough, it's mentioned in a YT video I just watched from 4y ago
Here's mine. Sequence of events:
- My pleco is set to save notes into Anki
- Anki and Pleco are closed
- I open pleco to show you the config, click on "flashcard" and it crashes (calling Anki API)
- I then go to a random word, click save note, nothing happens (API call exception caught)
- I open Anki
- Successfully perform card operations, and flashcard settings
- Close anki
- Changing flashcard settings crashes Pleco
Hey, @licryle. How did you know that the problem is related to the ContentProvider API? I'm a bit confused because I don't know about this Pleco app. Are you debugging some code that you can provide? I had issues using the API, but not with the getDeckName method that you mentioned in the other issue. I suppose you realized it's a API bug in these steps:
- I open pleco to show you the config, click on "flashcard" and it crashes (calling Anki API)
- I then go to a random word, click save note, nothing happens (API call exception caught)
Do you have any error logs to give us some guidance?
Hi folks,
Sorry, this will be a bit long, and I took longer than expected to come back to you as I wanted to write a real full answer. I've investigated deeper and I think I found the root cause., I will also talk about #18263 here.
TLDR:
I estimate around 20-42% of Android devices sold in 2024 don't support the API mechanism out of the box. It's something ROMs disable by default for battery optimization purposes, and not something Anki itself can fix although it could provide a better entry point/screen/activity to acknowledge maybe. At least, we might want to alert in the documentation and example of how to walk around it. Happy to chat more if that helps, or help draft something.
Background
A bit of background to see where I come from:
- I'm an independant dev (primarily Product Mgr now) in the process of releasing a new version of my Chinese learning app with a strong (and easy!) integration with Anki, so I've run into all issues. App in case of curiosity - https://github.com/licryle/Android-HSKFlashcardsWidget
- I live in China and use a top-tier device from Honor (spin off from Huawei), with a Chinese build. As far as I know, all chinese android build have that battery saver... and it's a lot of android devices!.
- And btw, Pleco is the most well known Chinese dictionary app, and almost all non-Chinese mandarin learners use it. It has an Anki integration, also not so known (too complex to set up IMO)
Issue:
Most, if not all Chinese Android ROMs (close to 1B devices!), and maybe just all Chinese manufactured android devices have an agressive battery saving feature.
That battery saving feature disables, by default, the launch of any app in the background (unless it's in a whitelist Anki would not be in). By default all is turned off in this:
As a result -- any API call to contentProvider API will simply return null. Not fail, just return null, even if app is installed and permissions granted. One has to launch Anki first for API calls to work. UNLESS "Secondary launch" is manually activated by the user. A.k.a. this will almost never happen.
That can create either a crash (which #18263 enables by assuming null won't be returned) or simply a failed API call if handled gracefully.
Unless I'm mistaken, what API users are forced to do, is to start the Anki Activity in the foreground and ask users --- with a toast --- to press return. This is what I (sadly) do bellow, it works wonders, but that's an ugly user experience:
- Wrap any Anki operation in a bunch of checks, where 3/4 are the work around
- (1) Check if app is installed
- (2) Check if permissions are OK
- (3) Check if app is running / API is ready with
[api.getDeckList() != null](https://github.com/licryle/Android-HSKFlashcardsWidget/blob/master/app/src/main/java/fr/berliat/hskwidget/data/store/AnkiStore.kt#L21) - (4) If not, start Anki and display an ugly Toast to ask user to press return
- (5) Do the API call!
My initial ask in this FR was to help launch the activity, but if the activity can't be launched in the background, it might have to be in the foreground. I can do some tests if you deem the [P1] below of interest.
Recommendations in best world scenario
- [P0] Document for API consumers
- [P0] Ensure the provided API helper code (#18263 for example) gracefully handle that case, and provides a method to check that case
- [P1] Have a dedicated Activity/screen in Anki that any API consumer could launch, and:
- Indicate an API consumer launched Anki to do some Anki operations, that if expected, no worries
- Indicate why this is happening, and link to an article on how to change configuration if wanted
- A back button to return to the API consumer app "Proceed"
- [P2] Maybe an "abort" button that kills the activity even in background
- [P2] Maybe a checkbox to always allow starting the app, in which case that activity starts and close on it's own after 1s?
getDeckName()should not return null in this case. This is exceptional and an exception should be thrown
Document for API consumers
Absolutely, at least for now while the issue is unresolved, ensuring we can make future fixes without breaking API contracts
Ensure the provided API helper code gracefully handle that case, and provides a method to check that case
Firstly, we need to know what our options are. Is there a way that we can trigger this issue on a phone without these restrictions, or is it built into the OS in a manner which we can't replicate?
Then: what options to we have to resolve the problem? Can we provide a service/activity which would keep the app in a usable state for a ContentProvider? If so, which would be the best to pick?
Ideally our API module should do the right thing, and not need to expose a state where contentResolver is invalid. We should only consider other options (activity/screen/API methods) if we can't get it right (we have control over AnkiDroid and the API, I suspect this is manageable)
Have a dedicated Activity/screen in Anki that any API consumer could launch
We could potentially do this, but I'd hope that if we can silently handle this problem, it wouldn't be necessary.
Firstly, we need to know what our options are. Is there a way that we can trigger this issue on a phone without these restrictions, or is it built into the OS in a manner which we can't replicate? By design, this is an OS feature. I'm not aware on how to replicate other than using one said OS (Vivo, Honor, Huawei, Realme, Oppo, maybe more?). We could try maybe to have a battery saving app that could disallow this? Or run a kill process infinite loop via adb commands? Not sure if the kill would be fast enough though!
Here's what it looks like on some devices, mine is a drastically different experience as per screenshot above. As far as I understand, this is not a stock Android feature but each manufacturer converging on an identical need to extend battery life.
I found this community article which attempts to document it.
Then: what options to we have to resolve the problem? Can we provide a service/activity which would keep the app in a usable state for a ContentProvider? If so, which would be the best to pick? We could potentially do this, but I'd hope that if we can silently handle this problem, it wouldn't be necessary. I'm with you, but if I understand well that OS feature, it's specifically designed to avoid any background tasks from being spun (and use battery). In such case, I don't see how you can walk around a OS feature to trigger background processes when the OS specifically disallows it.
Documentation is scarce, and this doesn't seem to be a well known issue. I couldn't find any documentation either on Android, but I did find one stackoverflow thread with the same conclusion at the bottom.
FYI - I've packaged my API wrapper as a separate Android module/repo which automatically walks around that issue by launching AnkiDroid if that issue is hit (best I can do afaik)
happy to get feedback if there's interest in the lib
Hello 👋, this issue has been opened for more than 3 months with no activity on it. If the issue is still here, please keep in mind that we need community support and help to fix it! Just comment something like still searching for solutions and if you found one, please open a pull request! You have 7 days until this gets closed automatically
Still open to discussing options with you, in particular an activity to briefly launch the process.
@licryle Simply launching the sync activity doesn't solve the problem? As far as I remember, it forced AnkiDroid to start but don't actually open it.
val intent = Intent().apply {
action = "com.ichi2.anki.DO_SYNC"
addCategory(Intent.CATEGORY_DEFAULT)
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
Ahhh, how did I miss that one - perfect, thank you!
That addresses the P1 above. May I suggest improvement to the documentation, and sample code via a pull request?
The issue remain for all aggressively battery-saving ROMs (Chinese ROMs) so it's quite a lot of devices. Ideally the API still would have a couple of methods or wrappers for that.
I solved it by having a wrapper that always check if app is installed, then running, or attempts to start it...
protected open suspend fun ensureAnkiDroidIsRunning() {
withContext(Dispatchers.Main) {
if (!isAnkiRunning()) startAnkiDroid()
}
}
protected fun isApiAvailable(): Boolean {
return AddContentApi.getAnkiDroidPackageName(context) != null
}
protected open suspend fun startAnkiDroid(): Boolean {
// Necessary, based on https://github.com/ankidroid/Anki-Android/issues/18286
val intent = Intent().apply {
action = AddContentApi.getAnkiDroidPackageName(activity) + ".DO_SYNC"
addCategory(Intent.CATEGORY_DEFAULT)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
return try {
context.startActivity(intent)
repeat(10) {
if (isAnkiRunning()) return true
else Log.e("hello", "hell")
delay(100)
}
throw Exception("Couldn't start Anki in 0.5 second")
} catch (e: ActivityNotFoundException) {
Log.e(TAG, "Anki is not installed, cannot start: $e")
false
}
}
@SuppressLint("ServiceCast")
private fun isAnkiRunning(): Boolean {
return AddContentApi(context).deckList != null
}