0

I have two different falvours in my Androdi app that should use different SQL implementations. One use:

android.database.sqlite.SQLiteDatabase

and second:

net.sqlcipher.database.SQLiteDatabase

I have methods like that:

getAll(SQLiteDatabase conn)

How should I solve this situation to avoid copy&paste? What is the best pratcice? I have few ideas: first one (the worst with a lot of copypaste) is to provide different methods :

 getAll(android.database.sqlite.SQLiteDatabase conn)
 getAll(net.sqlcipher.database.SQLiteDatabase conn)

second is to wrap this class with some other in every flavour importing proper database(aggregation, composition as SQLiteDatabase is final):

import android.database.sqlite.SQLiteDatabase;
public class SQLliteDatabaseFlavoured   {
     SQLiteDatabase sqLiteDatabase;
}

With usage:

getAll(SQLliteDatabaseFlavoured.SQLiteDatabase conn)
Artur Latoszewski
  • 735
  • 1
  • 10
  • 21

2 Answers2

1

You can use the gradle buildVariant if you are using Android Studio

flavor1 -> packagename: com.example.flavor1 
flavor2 -> packagename: com.example.flavor2

using this in your gradle script:

filter{
    String line -> line.replaceAll("<complete line of regular expression>",
                                   "<complete line of modified expression>")
}

How to replace a string for a buildvariant with gradle in android studio?

If not and you are worried about your app footprint or you want to ensure that the cipher library is only included when needed, then you should go with your Option #2 so that the compiler will remove the unused libraries. Option 1 will always include both libraries, which is unnecessary. Also, Option #2 gives you more flexibility if you ever change implementations (another SQLite product comes along).

From a "best practices" standpoint, you should consider maintenance and debug. Your first option guarantees that you will know exactly which library caused a problem. Otherwise, it must be very clear which flavor is being used or you are always provided a full stack trace when you receive a bug report. If you assume that both libraries are implemented in exactly the same way, then you have a testing nightmare doing either of the above if all you get is an error with a line number or less information. Even with a full stack trace, it may be difficult to debug issues and certainly not obvious to new people on the project. Also, from a best practices standpoint, ideally, you would not change out this type of dependency at all. But I understand that may not be possible.

PlusInfosys
  • 3,416
  • 1
  • 19
  • 33
Jim
  • 10,172
  • 1
  • 27
  • 36
  • I'm using Android Studio and Gradle system. With this replace solution, if I good understand it, I have to use gradle copy method on every file that use SQLiteDatabase? – Artur Latoszewski Nov 18 '15 at 14:57
  • gradle should search and replace every instance of the package string in your project (not aar's or jar's). You should not have to specify it anywhere except in the flavor variant of your gradle script – Jim Nov 18 '15 at 16:29
  • Is this should look like this? `flavour1{} flavour2{ copy{ from('/src/flavour1/java/com/sql') into '/src/flavour2/java/com/sql' filter{ String line -> line.replaceAll("net.sqlcipher.database.", "android.database.sqlite.") } } }` – Artur Latoszewski Nov 18 '15 at 17:47
  • In this solution I have to copy files from somewhere and I need to remember to change this files only in base location. This could bring a lot of troubles in future or I don't understand something here :( – Artur Latoszewski Nov 19 '15 at 13:17
  • You are not copying files. You are just changing the import statements. To gradle this is a pre-compile string change only. – Jim Nov 19 '15 at 19:32
  • but filter option is only in copy method of gradle https://docs.gradle.org/current/dsl/org.gradle.api.tasks.Copy.html I can't find any other usage of this. – Artur Latoszewski Nov 19 '15 at 20:00
0

ok I finally found solution based on @Jim's answer. Big Thanks!

It's not the cleanest but it wokrs. I create two tasks to copy files

task copyNoEncryption << { 
//copy to temp folder
copy {
    from("src/com/sql")
    into("src/temp/sql")
}
//copy back to correct folder and replace string's
copy {
    from("src/temp/sql")
    into("src/com/sql")

    filter {
 //you have to remember that first argument is REGEX and second is normal String
        String line ->
            line.replaceAll("before REGEX",
                    "after STRING")
    }
}
//delete temp folder
delete("src/temp")
}

and second task working analogously

task copyEncryption << {
//same body but reverse string swap 
//REMEMBER in replaceAll 1st arg is REGEX and second is String
}

now I'm adding execution this task before respectively flavor

android.buildTypes.all{ theBuildType ->
tasks.whenTaskAdded{ theTask ->
    if(theTask.name == "generateFlavorWithoutEncryptionr${theBuildType.name.capitalize()}Sources"){
        theTask.dependsOn "copyNoEncryption"
    }
    else if(theTask.name == "generateFlavourWithEncryption${theBuildType.name.capitalize()}Sources"){
        theTask.dependsOn "copyEncryption"
    }
}
}

Now every time when I'm building flavor I have correct libraries. Hope that someday this will help someone.

Artur Latoszewski
  • 735
  • 1
  • 10
  • 21