Name | Divyansh Kushwaha |
---|---|
Organization | AnkiDroid |
Proposal | Divyansh's GSoC Proposal |
Objective | AsyncTask to Coroutines Migration Issue #7108 |
Hi I am Divyansh, and I've finally completed my Google Summer of Code 2022 project under the mentorship of organisation AnkiDroid
AnkiDroid is an Android companion app for Anki, a flashcards application that helps people learn and memorize a diverse variety of topics.
AnkiDroid: Anki flashcards on Android. Your secret trick to achieve superhuman information retention.
My first interaction with a maintainer happened accidentally. I was working for a startup which used a fork of AnkiDroid as a base of their project. I had to remove the sync feature from existing fork and I mistakenly made a pull request to the main AnkiDroid repo 😆(kind of felt funny, but yeah this was it). So my first PR was an accidental one and was closed unmerged. David Allison was the first to comment on that and asked me to share a link to the project, so I joined discord server. And that was the begining of me starting contributing to AnkiDroid. There I saw a separate channel for GSoC, and since I was looking to apply for it I started asking questions on the server and found the community very welcoming and helpful. From there my journey to open source contributions began.
After joining the discord server, I came to know that there is something called good first issues
which is for newcomers to get familiar and start their contributions. During that time, AnkiDroid was going through Java to Kotlin migrations. There were some changes that would reduce and simplify code using Koltin features, such changes were marked as KotlinCleanup
. Before starting GSoC I started with these cleanup tasks. And eventually started working on migration of startActivityForResult
to newer APIs. Done a few KotlinMigrations myself as well.
AnkiDroid was initially built on Java and used AsyncTask to handle asynchronous operations. Since AsyncTask is deprecated and AnkiDroid was already migrating to Kotlin, the asynchronous operations such as network calls, database access etc can be moved to Kotlin Coroutines. I already wanted to learn more about concurrency and parallelism concepts.
So, the Migration from AsyncTask to Coroutines was the objective of my GSoC project.
After choosing the GSoC topic and getting familiar with the codebase, it was time to prepare my proposal. For that I really needed to put together my strategies which I could use to do the migration. After going through these all, I did a sample migration just to show how the actual project might look like. I wrote the Application from the standard template that was provided by the organisation only. I asked for a review on my Application by maintainers and received a lot of them. It went through 3-4 rounds of review till I finally submitted my proposal.
It is the initial time which Google gives to involve in the community activities that happened within an organisation. Since I already had been contributing and knew the community it went very well, even I started my coding in this period. I had a video call meet together with the other community members and my fellow GSoC students as well. I was a fun session and we shared our journey and experiences with the community so far.
First thing to do before starting the migration to Coroutines is to migrate required classes from Java to Kotlin because coroutines are a Kotlin feature. During the first week, I worked on migrating existing Java files which contain AsyncTask codes to Kotlin. I utilised my this time well by doing many Kotlin migrations. Thanks to the automated script that we have in AnkiDroid which made this process a lot easier. For migration to Kotlin we followed a specific set of rules to make sure nothing breaks and transition is smooth (like avoiding functional changes). A more detailed documentation is written by community members which you can find here.
For the coroutines, my initial planning was to create a base wrapper class similar to AsyncTask but instead of using underlying raw threads I would use suspend functions for the callbacks. I thought that migration should be done without changing the already existing architecture, like a patch on existing code but discussing with the mentors and other community members I came to a conclusion to follow a different approach. Most of the architecture was laid down to handle cancellations and memory leaks due to AsyncTask which we do not need to worry about when using Coroutines. Using right CoroutineScopes and bounding the coroutines to a LifecycleOwner handle these two very well, so we do not have to worry about checking if the UI is still alive or not.
Following a strategy, the doInBackground
callback of AsyncTask would be replaced by a suspend
function performing the same operation. And onPreExecute
and onPostExecute
callbacks would be called as it is before and after calling the suspend function. Using this concept, I started migrating firstly with the independent classes and later when I became much familiar with the codebase I started migrating the more core part of the codebase.
List of merged PRs in which I've done the migrations:
- Migrate PerformDowngradeTask to Coroutines #11651
- migrated AnkiStatsTaskHandler.createReviewSummaryStatistics() to suspend function #11667
- migrated LoadPronunciationActivity to Coroutines #11681
- migrated CollectionLoader to Coroutines #11816
- Migrated CollectionTask.AddNote to Coroutines #11952
- Migrated CollectionTask.UpdateNote to Coroutines #12199
- Migrated CollectionTask.UpdateMultipleNotes to Coroutines #12272
- Migrated LoadDeck to Coroutines #12328
- Coroutines migration PreloadNextCard #12362
- Coroutines migration CollectionTask.DeleteModel #12365
- Migrated LoadCollectionComplete to Coroutines #12361
- Coroutines migration CollectionTask.SaveCollection #12330
- migrated AnkiStatsTaskHandler.createStatisticsOverview() to suspend function #11628
- migrated AnkiStatsTaskHandler.createChart() to suspend function #11556
- Coroutines migration DeleteMedia #12373
- Coroutines migration CheckMedia #12375
- Coroutines migration CollectionTask.DeleteDeck #12391
- Coroutines migration ChangeSortField #12421
- Coroutines migration add field #12434
- Migrated CollectionTask. ConfReset to Kotlin Coroutines #12454
- Migrated CollectionTask.Flag to Coroutines #12462
- Migrated RepairCollection to Coroutines #12498
- Coroutines migration UpdateValuesFromDeck #12522
- Coroutines migration CollectionTask.MarkNoteMulti #12525
- Coroutines migration conf change #12534
- Coroutines migration save model #12535
- Migrated CountModels to Coroutines #12541
- Migrated ConfRemove to Coroutines #12546
- Migrated EmptyCram to Coroutines #12551
- Migrated CheckCardSelection to Coroutines #12556
- Migrated CollectionTask.Reset to Coroutines #12557
- Coroutines migration RenderBrowserQA #12571
- Coroutines migration DeleteNoteMulti #12577
- Coroutines migration RebuildCram #12584
- Coroutines migration SuspendCardMulti #12585
- Migrated CollectionTask.Reorder to Coroutines #12591
- Coroutines migration ConfSetSubdecks #12592
After the migration, I realised that I have completely deviated from my initial plans but for good. Migrating with the newer approach made the codes much cleaner and easy to follow.
Since my changes do not include UI changes, I cannot show it but by the end of GSoC I have migrated most parts of the AnkiDroid to Coroutines. Although I couldn't complete everything within the given timeline, but yeah almost 80-85 percent of the migration has been done. I be also keep contributing to the AnkiDroid after GSoC and will put my efforts to improve the codebase. I would like to thank Shridhar, Arthur, Mike, Luksbit, David and other folks around the community. You guys are awesome and I've had a very good time together with you spending my summer discussing and programming.
- AnkiDroid: https://github.com/ankidroid
- GSoC: https://summerofcode.withgoogle.com/
Before working on GSoC I did not have much experience with Git-GitHub workflow, PRs, Code reviews, coding discussion, planning, having conversations with community members, etc. These are the most valuable things I would say I learned from my GSoC period. Also working on the project I learned Kotlin Coroutines in great depth along with the concepts of AsyncTask, how Android used to be before Kotlin. I would say GSoC had a very large impact on my skills.