pretty

Sunday, 23 June 2024

Kotlin Multiplatform Wizard: problems building the project in the Xcode 15.4

The sample iOS project from Kotlin Multiplatform Wizard may fail to be built in XCode 15.4. The following steps should help to resolve these.

1. Add task("testClasses") to KotlinProject/shared/build.gradle.kts, inside the kotlin block:

kotlin {
    task("testClasses")
...
}


2. If XCode cannot build the project due to pointing to Java 11, showing an error: Android Gradle plugin requires Java 17 to run. You are currently using Java 11 - put the path to Java 17 to KotlinProject/gradle.properties:

org.gradle.java.home=/Applications/Android Studio.app/Contents/jbr/Contents/Home

This path depends on Android Studio ans OS version. The above is the path used in Android Studio Jellyfish on MacOS.

Sunday, 2 July 2023

Running Docker MySQL container on MacOS

1. Launch docker desktop server on MacOS via Launchpad icon.

Docker desktop application can be downloaded from the docker web site.
After launch the docker icon should appear on the right top icon tray of the menu. Docker can be used from command line now, lets check the docker is running and the version.

docker --version
Docker version 23.0.5, build bc4487a

1.1 Get docker mysql image.
docker run mysql:latest

2. Launch MySQL docker container.
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pwd -d mysql
Arguments:
-p publish port from container and map it to host OS port, this is to connect to MySQL from the outside environment later.
-e env variables, used only MYSQL_ROOT_PASSWORD in this case and set a new 'pwd' for the root user. Optionally, we can create more users with corresponding passwords on this step, providing more env parameters like this:
docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=pwd MYSQL_USER=newuser MYSQL_PASSWORD newpwd -d mysql
-d daemon mode
-'mysql' would be a new container name

2.1 Check that container is running and get its id from table CONTAINER ID.
docker ps
CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS          PORTS                               NAMES
7fc7ee05859d   mysql     "docker-entrypoint.s…"   26 minutes ago   Up 26 minutes   0.0.0.0:3306->3306/tcp, 33060/tcp   angry_fermi

3. Connect to mysql container by CONTAINER ID (get it from previous step) using docker shell.
docker exec -it 7fc7ee05859d bash
Arguments:
-it interactive mode (shell)
-'7fc7ee05859d' is a container id
Now, inside the container, we can launch mysql console providing the root user name and actual password that was set on step 2.
mysql -uroot -p       
Enter password: 
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 28
Server version: 8.0.33 MySQL Community Server - GPL

Copyright (c) 2000, 2023, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
+--------------------+
4 rows in set (0.00 sec)

3.1 To check that MySQL is available outside of the container, as we mapped internal port of the container to OS port 3306 on step 2.

One of the options to do this is to install MySQL workbench application from Oracle on the host computer. When launched it allows to create a new MySQL Connection. Supplying 127.0.0.1:3306 for this new connection (that is localhost:port, that we mapped to on step 2), we should be able to connect to the DB inside the docker container.

Saturday, 15 April 2023

Inspect sqlite database file from iOS emulator

1. View sqlite file location.

Launch XCode and follow these steps:
- XCode Menu Bar > Product > Scheme > Edit Scheme
- Run option
- Add or modify the Arguments passed on launch to contain -com.apple.CoreData.SQLDebug 1
- Launch the application and wait for Output to have a string with db location, like:

CoreData: annotation: Connecting to sqlite database file at "/Users/user/Library/Developer/CoreSimulator/Devices/DA54BPCB-39F1-4D19-888A-FA146477606DD/data/Containers/Data/Application/567443-7273-4B75-BFDA-86756/Library/Application Support/Data.sqlite"

2. Knowing the location, open Terminal and cd into SQlite db location. It may look like this:

cd "/Users/user/Library/Developer/CoreSimulator/Devices/DA54BPCB-39F1-4D19-888A-FA146477606DD/data/Containers/Data/Application/567443-7273-4B75-BFDA-86756/Library/Application Support/"

Copy 3 database files from this directory to desktop, or some other convenient directory:
cp Data.sqlite /Users/user/Desktop
cp Data.sqlite-shm /Users/user/Desktop
cp Data.sqlite-wal /Users/user/Desktop

3. Open the copied Data.sqlite from /Desktop with apps from AppStore, like Ridill SQLite or SQLiteFlow.

Monday, 6 February 2023

Kotlin return statement in anonymous function vs inlined lambda functions

Having a return statement in an anonymous function in Kotlin acts like a break statement. The return statement without a @label always returns from the nearest function declared with a 'fun' keyword (Anonymous function doc).

On the other hand, return statement inside a lambda function, that was passed to an inline function, exits the enclosing outer function. The lambda is inlined, so the return inside it is actually treated as a return from outer function scope.

Saturday, 4 February 2023

Read and update value inside transaction in Room DB

    In the post Accessing CoreData from a single background context there is a description of the process to safely update the value in CoreData on iOS. Single background context was used to guarantee that there would be no merge conflicts during update.

    To achieve such result when using a Room DB on Android, the similar approach can also be used. The database writes may be done on a single thread. However, as Room DB is a object relational mapping library, it does not abstract away the underlying SQLite database concepts. Instead of using single thread for writing into DB we may use SQL transaction mechanism. Let's check how this works, employing a simple unit test.

    For testing purpose, there would be two database tables, Tag and PageNumber, with a one-to-one relationship. Each Tag in a database may contain a corresponding unique PageNumer entity, with the 'page' value, indicating the currently active page number for that tag.

@Entity
data class Tag(
    @PrimaryKey(autoGenerate = false)
    val name: String
)

@Entity(
    foreignKeys = [ForeignKey(
        entity = Tag::class,
        parentColumns = arrayOf("name"),
        childColumns = arrayOf("tagName"),
        onUpdate = ForeignKey.CASCADE,
        onDelete = ForeignKey.CASCADE
    )]
)
data class PageNumber(
    @PrimaryKey
    val tagName: String,
    val page: Int
) 


    In the following Data Access Object function that does read-and-update process is called getAndIncrementPageNumberForTag() and has a @Transaction annotation, placing it's body inside a SQLite transaction.

@Dao
interface TagsDao {
	
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertTag(tag: Tag)

    // Inserts new PageNumber or Updates it if it already exists (available since Room 2.5.0)
    @Upsert
    suspend fun upsertPageNumber(pageNumber: PageNumber)

    // Loads the PageNumber for Tag
    @Query("SELECT * FROM tag JOIN pageNumber ON tag.name = pageNumber.tagName")
    suspend fun loadPageNumberForTag(): Map>

    // Gets the current page number for the tag, incrementing this number and saving it afterwards.
    @Transaction
    suspend fun getAndIncrementPageNumberForTag(tagName: String, defaultPageCount: Int): Int {
        val tag = Tag(tagName)
        
        // Inserts new tag if it does not yet exist
        insertTag(tag) 
        
        // Load PageNumber for this tag
        val pageNumber = loadPageNumberForTag()[tag]?.firstOrNull() ?: PageNumber(tagName, defaultPageCount)
        val result = pageNumber.page
        
        // Increment the PageNumber.page value
        upsertPageNumber(PageNumber(tagName, result + 1))
        
        // Return an old PageNumber.page value
        return result
    }
}


  Following is an instrumented unit test to check that all increments took place without interfering with each other.

@RunWith(AndroidJUnit4::class)
class DbTest {
    private lateinit var tagsDao: TagsDao
    private lateinit var db: GuessDatabase

    @Before
    fun createDb() {
        val context = ApplicationProvider.getApplicationContext()
        db = Room.inMemoryDatabaseBuilder(
            context, GuessDatabase::class.java
        ).build()
        tagsDao = db.tagsDao
    }

    @After
    @Throws(IOException::class)
    fun closeDb() {
        db.close()
    }

    @Test
    fun test() = runBlocking {
        var pageNumber = 0
        val cnt = 10
        
        withContext(Dispatchers.Default) {
            repeat(cnt) {
                launch {
                    pageNumber = tagsDao.getAndIncrementPageNumberForTag("tagName", 1)
                }
            }
        }

        Assert.assertEquals(pageNumber, cnt)
    }
}


    If, however, the @Transaction annotation would be removed, the test will fail. Without single transaction, the function getAndIncrementPageNumberForTag() accessed simultaneously from multiple threads from Dispatchers.Default pool would put incorrect results into database due to the race conditon.

    Would putting the limit of 1 active thread on a Dispatchers.Default pool - like Dispatchers.Default.limitedParallelism(1), also protect from the race condition, just like a transaction did? In this case - no. The function getAndIncrementPageNumberForTag() would be called on a same worker actually, but the functions that it invokes - insertTag(), upsertPageNumber(), ... are all a suspend functions with implementations provided by Room library, and they would anyway run on different workers. This is why the approach with limitedParallelism(1) call on dispatcher would also require removing the suspend modifier from mentioned functions.

    Summing up, in Room DB the SQL transaction mechanism may be used to prevent the merge conflicts during read and update data operations.

Monday, 9 January 2023

Parsing XML response from service on Android without using extra libraries

The XML response from REST services is not commonplace, and today it is problematic to parse it using Retrofit or Ktor on Android. While Retrofit has the SimpleXmlConverterFactory, this library is deprecated, and no viable alternatives exist. Ktor on the other hand, has XML Converter only server side.

The solution presented in this code snippet uses built-in Android DocumentBuilder to process the XML. This class is available in Android since API Level 1. In the following sample, the application needs to fetch a list of photos from Flickr service. This service provides the XML formatted response. We are only interested in populating our FlickrPhotoModel data classes with the attributes from <photo> nodes.


While this approach may not be ideal if we need to work extensively with XML API, it is a proper fallback to use for just a bunch of requests.

Thursday, 10 November 2022

How to copy MySQL database from server

Steps for the local console session.

1. Dump database from remote server (ClearDB DBaaS is used in this post as an example).

mysqldump heroku_2fXXXXXX --host=us-xxxx-xxxx-xx.cleardb.com --user=xxxxxxxxx --password --no-tablespaces --column-statistics=0 > dbcopy.sql

heroku_2fXXXXXX is a database name (can be seen in heroku -> select your app in a list -> Configure add-ons -> click onClearDB MySQL item

--host is an endpoint url (can be seen in App -> Configure add-ons -> click on ClearDB MySQL item -> My Account button -> Enpoint URL)

--username is a db user (can be seen in App -> Configure add-ons -> click on ClearDB MySQL item -> My Account button -> Username)

The --no-tablespaces --column-statistics=0 are needed since MySQL upgrade, more info available here: https://dba.stackexchange.com/a/274460 and https://anothercoffee.net/how-to-fix-the-mysqldump-access-denied-process-privilege-error/

The mysqldump will prompt for a password, and download the database.

The dump file dbcopy.sql shold now be created in a local path. It is not an actual database, but a file with a set of SQL statements to recreate the database. Check it's size and contents.

2. Create new local db to fill it with the dumped data with the next step.

mysqladmin -uroot -p create newdb

3. Transfer the data from the dump file into a newdb file.

Substitute 'root' to the actual local mysql user name, if needed.

mysql -uroot -p newdb < dbcopy.sql

4. Check the newdb local database structure.

Launching local mysql console:
mysql -uroot -p

Now type the following in the console, to check the DB tables are there:
USE newdb; SHOW TABLES;


Note: If the tables in DB are empty check again the present databases with SHOW DATABASES. Sometimes, the backup sql would create a database with a specific name. This name can be seen opening the backup sql file in any text viewer, and checking for the command at the start of the file. The command will look like CREATE DATABASE IF NOT EXISTS `some_database_name`.

Checking DB size:
SELECT table_schema "DB Name", ROUND(SUM(data_length + index_length) / 1024, 1) "Database Size Kb"
FROM information_schema.tables
GROUP BY table_schema;


5. Save newdb database to external file.

Type: exit;

in mysql console to close it.
Now run mysqldump again.
mysqldump --databases --user=root --password newdb > newdb.sql

Observe the newdb.sql file created in the directory where the mysqldump command was run.




>>> Optional. Below are the steps to perform the dump inside a Docker MySQL container. This is useful when you do not have mysql installed in local environment, and instead prefer to use MySQL in a Docker container.

A. Launching docker with mysql image.

docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password -d mysql

-d option to launch the container in detached mode, so it will continue running in background. Check its status with docker -ps command.
-e option to set env variable, MYSQL_ROOT_PASSWORD in this case.
-p option to expose continer on 3306 port if it is needed to connect external tools to our DB, like MySQL Workbench application. The port 3306 should then be specified as the DB connection port in MySQL Workbench application.

Copy dump file to the inside of the container root diretory, using docker cp command:
docker cp ./dbcopy.sql 6d2b07ed8c00:/.

- 6d2b07ed8c00 should be replaced with your Container ID (find it by running docker ps command).

B. Launch bash session inside the launched container.


docker exec -it 6d2b07ed8c00 bash

- 6d2b07ed8c00 container id, exact id could be seen using docker ps command.
- it option to run interactivey.


Proceed with the steps 1 - 5 from above description for the local console, but this time inside the docker mysql container.

C. Copy file from docker container to host PC

In the docker container type pwd to check the path to directory containing db. Now type exit to exit docker bash. Let's use docker cp command with docker container id and path to db inside container. Container id could be found runnig the
docker container ls

Finally, the last parameter to docker cp command is the destination directory, or just . to copy file to current directory on host machine.
Example:

docker cp 6d2b07ed8c00:/newdb.sql .


As a result we should be able to see the new MySQL DB file on the host machine.

Saturday, 5 November 2022

Accessing CoreData from a single background context

The usual scheme when working with Core Data is using one or more of the background contexts for modifying data, and one viewContext for reading and displaying it.

Using multiple background contexts may seem like a good idea because it allows data to be modified simultaneously from different threads. However, doing so increases the complexity of the program. Properly configuring Core Data’s merge policies to handle all possible cases can be tricky, which may lead to concurrency-related issues.

The following unit test recreates the basic concurrency issue on purpose, when the same data entity is being modified from those two background contexts on lines 59 and 75:

The test will pass if line 78 is uncommented, because it sets a merge policy that resolves conflicts by keeping the persisted version instead of the newer changes. However, a more practical approach is to use a single background context for all modifications, executing them sequentially. This simplifies concurrency handling and avoids merge conflicts, making the code easier to manage and reason about.

To employ sequential CoreData writing scheme it's convenient to have a wrapper class that would hold the required single background context. The wrapper class in this sample is named CoreDataInventory and should be used for every CoreData interaction in the application. CoreDataInventory wrapper class in this design is a singleton:

Having this class let's change the unit test, and call the new method CoreDataInventory.instance.perform(...) instead of performBackgroundTask(...). The CoreData merge policy in the test is also the default one (used by CoreData when no specific policy is set), and should throw the error if there is a merge conflict.

This test would pass without merge conflicts.

In conclusion, many background contexts may be used only when it is proven that concrete application performs a high amount of time-consuming data writes, and practically benefits from having multiple background contexts. Otherwise, more practical alternative is to use a single background context for writing to CoreData.

Thursday, 14 July 2022

Http status codes: 401 vs 403

401 is about failed Authentication - I say who I am, do you believe me? If not - respond with 401 Unauthenticated

Example: login failed due to invalid credentials.


403 is about failed Authorization - my authentication is accepted, can I access this? If not - respond with 403 Unauthorized

Example: access to specific resource is not permitted with my role.

Wednesday, 24 February 2021

Kotlin contravariance samples

Contravariance is used to allow adding into collection values of type T and T subtypes. The collection itself, however, is allowed to contain items of type T and any super types of T (because of this it is not safe to get from this collection, with the exception of getting the root of type hierarchy Any?).

1. Use site variance

Sample: list with contravariance acts as a consumer.
It is safe to add into list the object of declared type B, or it's subtypes. It is not safe to retrieve object of type B from the list, because list of objects with super types of B may be provided. However, it is safe to retrieve the Kotlin most basic type Any? from the list.
 
open class A
open class B : A()
class C : B()

// Can receive list of B or its super types (A, Any, Any? types in this sample).
// Allows to put B or B subtypes into list (C in this sample).
// Cannot get fom List - except, can only get most generic type Any? from list.

fun listAsConsumer(list: MutableList<in B>) {
     // Type mismatch: inferred type is Any? but B was expected
     // val b: B = list[0] 
     
     // Type mismatch: inferred type is A but B was expected 
     // list.add(A())  
     
     // Cannot get fom List - except, can only get most generic type Any? from list.
     val any: Any? = list[0]
     
     // Allowed to add B or its subtypes into list
     list.add(B())
     list.add(C())
}

fun main() { 
    listAsConsumer(mutableListOf<A>(A())) 
} 

Sample: contravariance in Comparator.
Compare list of Ints using Comparator<Number>.
fun <T> Iterable<T>.sortedWithWrapper(comparator: Comparator<in T>): List<T> {
    return this.sortedWith(comparator)
}

fun main() { 
    val comparator: Comparator<Number> = Comparator { o1: Number, o2: Number ->
        o1.toDouble().compareTo(o2.toDouble())
    }
    val intList: List<Int> = listOf(4, 7)
    val result: List<Int> = intList.sortedWithWrapper(comparator)
}

The same sample without contravariance for Comparator is below. Without contravariance sortedWithWrapper() function could not return List<Int>, but only List<Number>. This happens because List<Int> is upcasted to List<Number>, to match the invariant T of the Comparator.
fun <T> Iterable<T>.sortedWithWrapper(comparator: Comparator<T>): List<T> {
    return this.sortedWith(comparator)
}

fun main() { 
    val comparator: Comparator<Number> = Comparator { o1: Number, o2: Number ->
        o1.toDouble().compareTo(o2.toDouble())
    }
    val intList: List<Int> = listOf(4, 7)
    
    // Type mismatch: inferred type is List<Number> but List<Int> was expected
    // val result: List<Int> = intList.sortedWithWrapper(comparator)
    val result: List<Number> = intList.sortedWithWrapper(comparator)
}


2. Declaration site variance

Declaration site variance is available in Kotlin, unlike Java where only use-site variance is possible.

Sample: Class with contravariance. Consumer cannot have contravariant type T in invariant or out positions.
open class A
open class B : A() 
class C : B() 
 
class Consumer<in T> {
    
    // Type parameter T is declared as 'in' but occurs in 'invariant' 
    // position in type T?
    // var x: T? = null

    fun consume(x: T) {}  
     
    // Ok, List is defined as List<out T>,
    // so it is a producer for this Consumer class
    fun consume(x: List<T>) {} 
     
    // Type parameter T is declared as 'in' but occurs in 'invariant' position 
    // in type Array<T>
    // fun consume(x: Array<T>) {} 
     
    // Type parameter T is declared as 'in' but occurs in 'out' position
    // in type T?
    // fun produce() : T? { return x }   
}

fun main() {   
    // Consumer of B (can be assigned Consumer of supertypes (A, Any, Any?)),
    // but now minimum B or its subtypes are accepted into consume()
    val consumerB: Consumer<B> = Consumer<A>() 
    consumerB.consume(C())
    
    // Type mismatch: inferred type is A but B was expected
    // consumerC.consume(A()) 
    
    // Type mismatch: inferred type is Consumer<B> but Consumer<A> was expected
    // val consumerA: Consumer<A> = Consumer<B>()  
}

Consumer class with contravariant T cannot accept another consumer with contravariant T.
class AnotherConsumer<in T> {
     fun consume() {}
}

class Consumer<in T> {
   
    // Type parameter T is declared as 'in' but occurs in 'out' position
    // in type AnotherConsumer
    // fun consume(x: AnotherConsumer<T>) {}  
}

This is due to AnotherConsumer can be extended by the class that cancels the contravariant 'in' modifier:
open class AnotherConsumer<in T> {
     open fun consume(x: T) {}
}

class AnotherConsumerChild: AnotherConsumer<T>() {
    var x: T? = null
    
    override fun consume(x: T) {
        this.x = x
    }  
    
    fun getT(): T? {
        return x
    }
}

Allowing to accept another contravariant consumer would lead to ClassCastException.
As an example of the above, in this snippet AnotherConsumerC would be called with the wrong type A (omitting compiler variance checking with @UnsafeVariance annotation).
open class A
open class B : A() 
class C : B() {
    fun c() {}
} 

open class AnotherConsumer<in T> {
     open fun consume(x: T) {}
}

// Overrides contravariance in parent type
class AnotherConsumerC: AnotherConsumer<C>() {
    override fun consume(x: C) {
        x.c()
    }  
}

open class Consumer<in T> {  
    open fun consume(anotherConsumer: AnotherConsumer<@UnsafeVariance T>) {}  
}  

open class ConsumerA: Consumer<A>() {  
    override fun consume(anotherConsumer: AnotherConsumer<A>) {
        anotherConsumer.consume(A())
    }  
}  

fun main() {   
    val consumerA = ConsumerA()
    
    // Allowed because parent Consumer<in T> is contravariant
    val consumerC: Consumer<C> = consumerA
    
    // Produces Exception in thread "main" 
    // java.lang.ClassCastException: A cannot be cast to C
    consumerC.consume(AnotherConsumerC())
}

Wednesday, 24 July 2019

Android ScrollView child width is larger than the screen (Api 21 bug)


On Android Lollipop this layout would produce a screen where children views of the ScrollView would have incorrect width.

This is how the view looks on api level 21 emulator


And this the correct result, from api 27


To fix this ScrollView issue on api 21 devices, the child view "android:layout_margin" should be replaced with "android:padding". If it is not acceptable for the layout to have padding instead of margin (for example due to background color or click area placement) - padding can be set on parent container views. rows="20"

Thursday, 31 January 2019

GoScores: leaderboard on Google Cloud


Simple Leaderboard server, written in Go language and hosted on Google Cloud. Source (Github).

Sample app that uses the leaderboard: Buildris on Google Play


Update: Migrating to Go 1.16 on Google Cloud.

Google Cloud no longer supports Go 1.11 runtime, the migration guide from Google is available.

Appengine google.golang.org/appengine/* packages were removed and google cloud cloud.google.com/go/* packages have to be used instead (or Go standard library packages were applicable).

For example:
google.golang.org/appengine/datastore -> cloud.google.com/go/datastore (from Google cloud library) google.golang.org/appengine/log -> log (from Go standard library package)

Sample diff of migration for GoScores app from 1.11 to 1.16, that uses Google Cloud datastore.

To highlight other migration changes:
  • Port should now be set explicitly
  • Data store API was modified, for example, query.GetAll(() is now dsClient.GetAll().
  • Some functions calls changed, for example RunInTransaction() no longer accepts nil for TransactionOptions. Passing nil would result in 503 Server Error and in logs: “panic serving … nil TransactionOption goroutine [running]:”

To test locally:
  • Credentials should be locally available, run export with a path to the file with key:
    export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/service-account-file.json"
    Key is available for download in Cloud Console, IAM and Admin -> Service accounts -> Keys tab. https://cloud.google.com/docs/authentication/production
  • go run scores.go
  • check GET response on localhost:8080


To deploy:
Inside app dir call (given google-cloud-sdk directory is one level up) ../google-cloud-sdk/bin/gcloud app deploy

-----
Full logs of app execution and errors are available in Logs Explorer: https://console.cloud.google.com/logs

Sunday, 3 June 2018

Managing long running task in React Native


In React Native network request is normally done with non-blocking method like fetch(). On receiving result redux-thunk middleware can be used to dispatch the new state.

Now there is a question: how to make our own non-blocking method for performing lengthy computation. This can be done using 3rd-party native extensions that introduce threading or WebWorkers to react native, or writing our own extension. There is also a workaround with creating a WebView and moving the long running function there.

There is a lighter solution, that would block the JS thread, but it allows for showing the progress indicator. Assume I want to perform an action that takes a few seconds, but first shows a progress indicator.

This action would block JS thread:
export const getData = () => {
   for (i = 0; i < 5000; i++) {
       console.log(i)
   }
   return { type: DATA_READY } 
}


To show the progress let us switch from 'return' to redux-thunk 'dispatch', and add additional state change before the computation. The flag 'loading' would be used to show progress bar in view.
export const getData = () => {
  return (dispatch) => {
    dispatch({type: DATA_CALCULATION loading: true})

    for (i = 0; i < 5000; i++) {
       console.log(i)
    }

    dispatch( { type: DATA_READY loading: false })
  }
}


The progress would not be shown despite the new state would have 'loading:true' set before computation. This happens because getData() action blocks for quite some time after the first dispatch(). This prevent the view from receiving its render() call. To make the view render before the blocking code we should move the lengthy cycle into the requestAnimationFrame() callback:
export const getData = () => {
  return (dispatch) => {
    dispatch({type: DATA_CALCULATION loading: true})

    requestAnimationFrame(() => {

       for (i = 0; i < 5000; i++) {
          console.log(i)
       }

       dispatch( { type: DATA_READY loading: false })
   })
  }
}


The requestAnimationFrame() callback is invoked before next repaint, and view now gets its render() call before the 'for' loop being launched. Progress indicator is shown and then it gets updated on main (UI) thread, while JS thread would be blocked.

Saturday, 5 May 2018

Handling exceptions thrown from ActivityThread in Android


Sometimes it is desired to prevent the default exception handler from terminating application. This may be done via setting custom default uncaught exception handler.

What happens if the caught exception is thrown from ActivityThread? See Android/Sdk/sources/android-26/android/app/ActivityThread.java for the source. When exception is thrown from ActivityThread the Looper.loop() cycle invoked from ActivityThread.main() method terminates. As the exception is caught by custom default exception handler, it does not instruct application to finish. However, application becomes frozen. Main looper no longer processes new messages.

To make application run again, the main looper needs to be revived. This can be done inside the custom exception handler:
    private void setOnErrorFailedExceptionHandler() {
        android.os.Handler handler = new android.os.Handler(Looper.getMainLooper());
        Thread.UncaughtExceptionHandler uncaughtExceptionHandler =
                                        Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler((t, e) -> {

            if (e instanceof AndroidRuntimeException 
                        /*&& e is a specific exception that needs treatment*/) {
                android.util.Log.e("error", "DefaultUncaughtExceptionHandler", e);
                Looper.loop() // Revive main looper 
            } else if (uncaughtExceptionHandler != null) {
                // All other exceptions should be treated by default
                uncaughtExceptionHandler.uncaughtException(t, e); 
            }
        });
    }


Some edge cases may be solved with this trick. For example, take the introduction of new exception in Android O "android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()". The stacktrace for this exception shows that is is thrown from ActivityThread:
"android.app.ActivityThread$H.handleMessage(ActivityThread.java:1775)"
"android.os.Handler.dispatchMessage(Handler.java:105)"
"android.os.Looper.loop(Looper.java:164)"
"android.app.ActivityThread.main(ActivityThread.java:6541)"
"java.lang.reflect.Method.invoke(Native Method)"
"com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)"
"com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)"


The android.app.RemoteServiceException is a private exception, that subclasses AndroidRuntimeException.

This exception is meant to terminate application, so that the developers should follow the new foreground service rules. If time span between the startForegroundService() and setForegorund() call in Service.onCreate() is longer than ANR period, then this exception is thrown. It was not designed to be caught. There are a few catches though, when terminating application with a crash is undesired, given that "startForegroundService() -> startForeground()" rule is already implemented.

1. During debug sessions this exception fires quite often, because of slower emulator - or just the breakpoints being hit.
2. When released, the actual user device may lag in the very unfortunate moment between those calls, and app will be hit with the crash.

Especially in the second case it would be much better to retry the service start, or opt it to be started later, instead of crashing app altogether. Catching such exception and reviving main loop afterwards is possible with this approach.

Sunday, 3 July 2016

Completion thread pool for C++


This is an implementation of a thread pool for c++ I wrote a month ago. It has two classes, the general ThreadPool that accepts new tasks and returns std::future to receive the result from them and the CompletionThreadPool that works more along the lines of ExecutionCompletionService from java. The CompletionThreadPool Based on regular thread pool allowa to retrieve tasks results in order of the completion. It can be used as follows. Create a task to be submitted to the pool:
int f(int sleep_time) {
    sleep(sleep_time);
    return sleep_time;
}
Create CompletionThreadPool instance with designated task result type:
CompletionThreadPool completionThreadPool;
Submit any number of tasks for execution (they will be enqueued for execution immediately):
completionThreadPool.Submit(f, 5);
completionThreadPool.Submit(f, 1);
Block waiting for the results in order of their completion:
std::future firstCompleted = completionThreadPool.Take();
std::future secondCompleted = completionThreadPool.Take();
The source is located at github. There is also a qt sample application that uses CompletionThreadPool to download images in separate threads and show them in a window in order of readiness.

Friday, 13 November 2015

How to link pre-built library with CMake


1. Provide path to library (for example, default path may be set to usr/local/lib on Linux, and the lib won't be found even when located in the same directory as CMakeLists.txt and even with full path provided inside target_link_libraries). Suppose library is in the subfolder /lib of the folder where CMakeLists.txt resides, then:
set(PATH_TO_LIB ${CMAKE_CURRENT_LIST_DIR}/lib/)
find_library(MyLibVariable NAMES "MyLibrary" PATHS ${PATH_TO_LIB} NO_DEFAULT_PATH)
NAMES should provide the name of library, without the lib prefix. So if there is libMyLibrary.so it shoud read "MyLibrary". NO_DEFAULT_PATH removes all default paths from search. CMAKE_CURRENT_LIST_DIR is the directory where CMakeLists.txt is located.

2. Use target_link_libraries on the found library, presented by MyLibVariable
target_link_libraries(${APP_NAME} 
    ${MyLibVariable}
)
APP_NAME is the name of application to link the library to, added with add_executable(${APP_NAME} ${SRC} ${HEADERS}) in this CMakeLists.txt
In case the library still cannot be located you can debug the path is really the one you suppose it should be, including in CMakeLists.txt the following:
MESSAGE('--- MyLibVariable ---')
MESSAGE(${MyLibVariable})
In case path is still wrong make sure to delete the CMakeCache.txt, as it might have cached previous value.

Monday, 28 September 2015

Morphing animation for Toolbar back button and Navigation drawer icon (Android)


It is common to use one of the third-party libs to create a morphing animation for icon, I have used the popular material-menu library. With this libray it is easy to use to morph animation for the navigation drawer icon, or any other icon in ui. However, the problem came up when I wanted to animate the transition from the Navigation drawer burger icon to Toolbar back button. The scenario is as follows. There is a Navigation drawer icon on the left side of Toolbar, also there is a collapsed SearchView in Toolbar. When the user clicks on search icon on the left side of Actionbar, SearchView expands and the burger icons turns into the back icon. Pressing this back button hides SearchView. The desired effect is to morph the back button into the hamburger icon and vice versa.

This is easier said then done because the burger button and back button are actually two different buttons. But, looking into Android source for android.support.v7.widget.Toolbar it is obvious that the two buttons are actually mNavButtonView and mCollapseButtonView, and they luckily have the same layout parameters and positioning (see ensureNavButtonView() and ensureCollapseButtonView() methods).

Now, the basic idea is to setup the same morphing drawable on both buttons, and play animation on them simultaneously - and hence animation will always be seen irrespective of the current visible button. For the Navigation drawer button drawable can be set simply with the call to setHomeAsUpIndicator(). To set the back button drawable we must use reflection to get to the mCollapseButtonView field and ensureCollapseButtonView() method. We need to call the ensureCollapseButtonView() method first to make sure that the mCollapseButtonView is created.


import com.balysv.materialmenu.MaterialMenuDrawable;

...


private MaterialMenuDrawable mToolbarMorphDrawable;
private MaterialMenuDrawable mSearchViewMorphDrawable;

... 

private void setupActionBar(final Toolbar toolbar) {
  
mToolbarMorphDrawable = new MaterialMenuDrawable(this, Color.BLACK,
                                           MaterialMenuDrawable.Stroke.THIN);
    
mToolbarMorphDrawable.setIconState(MaterialMenuDrawable.IconState.BURGER);
 
mSearchViewMorphDrawable = new MaterialMenuDrawable(this, Color.BLACK, 
                                           MaterialMenuDrawable.Stroke.THIN);

mSearchViewMorphDrawable.setIconState(MaterialMenuDrawable.IconState.BURGER);

Toolbar toolbar = setupActionBar((Toolbar) findViewById(R.id.upperToolbar));
setSupportActionBar(toolbar);
final ActionBar ab = getSupportActionBar();
if (ab != null) {
    ab.setHomeAsUpIndicator(mToolbarMorphDrawable);
    ab.setDisplayHomeAsUpEnabled(true);
}

try {
    Method ensureCollapseButtonView = android.support.v7.widget.Toolbar.class
                        .getDeclaredMethod("ensureCollapseButtonView", null);

    ensureCollapseButtonView.setAccessible(true);
    ensureCollapseButtonView.invoke(toolbar, null);

    Field collapseButtonViewField = android.support.v7.widget.Toolbar.class.
                                    getDeclaredField("mCollapseButtonView");

    collapseButtonViewField.setAccessible(true);

    ImageButton imageButtonCollapse = (ImageButton) collapseButtonViewField
                                                             .get(toolbar);

    imageButtonCollapse.setImageDrawable(mSearchViewMorphDrawable);
}
catch (Exception e) {
    // Something went wrong, let the app work without morphing the buttons :)
    e.printStackTrace(); 
}
}


That is the OnActionExpandListener that starts morphing animation when the expanded action view (SearchView in our case) expands and collapses.

MenuItemCompat.setOnActionExpandListener(searchMenuItem, 
                            new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                mToolbarMorphDrawable.animateIconState(MaterialMenuDrawable
                                                         .IconState.BURGER);
                mSearchViewMorphDrawable.animateIconState(MaterialMenuDrawable
                                                         .IconState.BURGER);
                return true;
            }

            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                mToolbarMorphDrawable.animateIconState(MaterialMenuDrawable
                                                          .IconState.ARROW);
                mSearchViewMorphDrawable.animateIconState(MaterialMenuDrawable
                                                          .IconState.ARROW);
                return true;
            }
        });


You might ask, why there are two separate drawables for these two buttons. Yes, we might have designated one drawable instance but then the animation wouldn't work as expected because one of the buttons "locks" the drawable and this makes the animation to freeze when another button becomes invisible.

Note that the reflection is used on the android.support.v7.widget.Toolbar.class, but if you use android.widget.Toolbar then change this line. Other than that android.widget.Toolbar seems to have the same fields and methods names so that you can try the same technique for it.

Wednesday, 24 June 2015

Using javah to generate jni header for Android class


1. Open the directory containing the root of android package. For example, if android class is located in SampleActivity.java, and it's package is com.sample.android, then open the folder containing the /com folder (in my case it is located in android_project/app/src/main/java/).

2. From this folder isssue the command javah com.sample.android.SampleActivity.

Corresponding .h file should be generated in-place in this folder. You can move it to another folder.

Wednesday, 17 June 2015

C++ 11 when do detached threads termiante?


What is the fate of the thread that is detached via std::thread::detach()? In the common situation the detached thread would be abandoned when the std::exit() would be called, and std::exit() is called on return from main() whether there are additional threads in the process or not.
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
#include "string"
#include "pthread.h"

class Global {
public:
    ~Global()
    {
        std::cout << "Global object dtor\n";
    }
};

class A {
  public:
  ~A()
  {
     std::cout << "A dtor\n";
  }
};

void thread_main()
{
  int i = 0;
  A a;
  while (true) {
   std::cout << "thread_main" << i++ << std::endl;
   sleep(1);

   if (i == 5) break;
  }
}

void atexit_handler()
{
    std::cout << "atexit handler\n";
}

Global global_variable; 

int main(int argc, char **argv)
{
  const int result = std::atexit(atexit_handler);
  std::thread t(thread_main);
  t.detach();
  return 0;
}
This snippet would produce the following output, note that the destructor for the object allocated on detached thread is not getting called.
atexit handler
thread_main0
Global object dtor
*** Exited normally ***
So the thread is abandoned non-gracefully, in a manner that reminds of the daemon thread in Java. Really, while JVM would not finish application if there is at least one active non-daemon thread, but if the thread is daemon thread it's existence wouldn't prevent app from exiting, and such thread would be abandoned - no stack unwinding, no finally blocks getting called for it. Such threads are used for tasks like garbage collection.

What if std::exit() would not be called from main()? In the following snippet I'm finishing the main thread without calling return and hence exit() is not getting called, because there's still a running thread around.
int main(int argc, char **argv)
{
  const int result = std::atexit(atexit_handler);
  std::thread t(thread_main);
  t.detach();
  pthread_exit(NULL);
  //return 0;
}
This will let the detached thread complete and then the process exits releasing all the resources.
thread_main0
thread_main1
thread_main2
thread_main3
thread_main4
A dtor
atexit handler
Global object dtor
*** Exited normally ***

Thursday, 11 June 2015

C++11 async launch::async vs launch::deferred


With launch::async flag the foo() function would be executed in a separate spawned thread immediately.
On the other hand with launch::deferred foo() will be executed on the same thread, and at the moment when future.wait() or future.get() would be called.
void  foo () {
    std::cout << "foo threadid = " << std::this_thread::get_id() << '\n';
}

int main()
{
    std::cout << "my threadid = " << std::this_thread::get_id() << '\n';
    std::future fut = std::async(std::launch::deferred, foo);
    fut.get();
    return 0;
}
my threadid =  140737353906048
foo threadid = 140737353906048