Since we keep switching our backend depedencies (Previously parse, then backendless now firebase), I am trying to create a backend library for all my projects with adapters for specific backends and common interface to be used in the projects (Adapter Design pattern).
For example,
The backend library interface will have method,
save(User user)
and the adapters will have implementation to save the User in firebase or backendless, and I can easily switch
But the way the firebase sdk works, we need to have 'google-services.json' in our path with predefined 'package name', otherwise we would get the following error in gradle build,
Execution failed for task ':backend:processReleaseGoogleServices'.
No matching client found for package name 'com.lib.backend'
The error is obvious, as the google-services.json would have the packagename of the specific project.
So now is there a way or a procedure I could follow, where my projects will not use the 'Firebase' classes directly and it works only through the 'backend' library.
A simple combination of Bridge pattern and Dependency Injection should be enough. Create an interface in your app/library, create another library module which implement that interface using Firebase (with the plugin), and use DI in order to load your implementation. Whenever you want to change implementation, just replace this library module to some other implementation.
Edit: an example
We'll create a project with 3 modules:
Application module
Interface module (for reference from two other modules)
Firebase implementation of interface
You project should look like this:
Firebase module package name same as the app package name.
Next, we'll create the Firebase impl gradle file:
apply plugin: 'com.android.library'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.google.gms:google-services:3.0.0'
}
}
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
minSdkVersion 25
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-database:10.0.1'
provided project(':proxylib')
}
apply plugin: 'com.google.gms.google-services'
In our app module gradle build file, we'll need to include both modules:
...
dependencies {
...
compile project(':firebaseproxy')
compile project(':proxylib')
}
And we'll put our google-services.json file in the firebase module dir:
Code is simple. The interface looks something like this:
public interface BackendProxy {
void save(String user);
}
And our Firebase implementation looks like:
public class BackendProxyImpl implements BackendProxy {
#Override
public void save(String message) {
FirebaseDatabase database = FirebaseDatabase.getInstance();
...
//save data to db
}
}
Note: I haven't tried actually implementing the database part, but it seems like it is running the google play gradle task. While it seems ok to me, I would have expect the google-play task to use the applicationId property, which is not available in the library module. If something is build wrong because of this, you'll have to run the google-play gradle task from the app module.
Related
My Crashlytics data is not appearing in the Firebase Crashlytics view. It does show "Crash-free statistics" as being 75%, so it appears to be recording some data. I also see the crashes appearing instantly both in Fabric and in the old Firebase Crash Reporting interface, as well as in the DebugView as general events but nothing in the Issues section of the Crashlytics page. I thought that perhaps my Fabric project had not been linked to my Firebase project, but when I tried doing it manually using this link https://www.fabric.io/firebase_migration/apps it tells me the projects are already linked, specifically it says Project already contains a linked app with that bundle ID and platform. Every time I open my app in Android studio I immediately run the command adb shell setprop debug.firebase.analytics.app ie.moses.keepitlocal so that my events will appear in the DebugView, perhaps this could be affecting it, but I doubt it.
Here is my project build.gradle file:
buildscript {
repositories {
google()
jcenter()
maven {
url 'https://maven.fabric.io/public'
}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.1.4'
classpath 'com.google.gms:google-services:3.3.0'
classpath 'io.fabric.tools:gradle:1.25.4'
}
}
allprojects {
repositories {
google()
jcenter()
maven {
url 'https://maven.google.com/'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
and this is the build.gradle for my specific app module
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
def supportLibraryVersion = '27.1.1'
android {
compileSdkVersion 27
defaultConfig {
applicationId "ie.moses.keepitlocal"
minSdkVersion 16
targetSdkVersion 27
versionCode 5
versionName "0.3.1-alpha"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
buildConfigField 'boolean', 'CRASHLYTICS', 'true'
}
release {
buildConfigField 'boolean', 'CRASHLYTICS', 'true'
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (details.requested.group == 'com.android.support') {
// Used to prevent conflicting versions of Android support library
// contained in other dependencies (e.g. all the firebase dependencies)
details.useVersion supportLibraryVersion
}
}
}
dependencies {
implementation files('libs/YouTubeAndroidPlayerApi.jar')
implementation "com.android.support:appcompat-v7:$supportLibraryVersion"
implementation "com.android.support:recyclerview-v7:$supportLibraryVersion"
implementation "com.android.support:cardview-v7:$supportLibraryVersion"
implementation 'com.google.firebase:firebase-core:16.0.3'
implementation 'com.google.firebase:firebase-database:16.0.1'
implementation 'com.google.firebase:firebase-auth:16.0.3'
implementation 'com.google.firebase:firebase-crash:16.2.0'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.5'
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
implementation 'com.google.guava:guava:26.0-android'
}
apply plugin: 'com.google.gms.google-services'
I am using a Button to force a crash with the following code (as I mentioned, these crashes appear almost immediately in Fabric and in the old Crash Reporting interface which has been deprecated and will be removed in 2 days!). Here is the code for the crash button:
Button crashButton = new Button(this);
crashButton.setText("Crash!");
crashButton.setOnClickListener(view -> {
throw new IllegalStateException("you hit the crash button!");
});
addContentView(crashButton, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
EDIT
I have just noticed that on the firebase transition status page it says "0/1 APP LINKED" and "NO PROJECTS YET".
I also need to reinstall the Crashlytics library from the Fabric plugin every time I open Android studio. The dependencies already exist in my gradle file but the plugin does not seem to recognise them.
EDIT
I am now trying to unlink fabric and firebase so I can try linking them again from fresh. I came across this question Unlink an existing firebase app?, but fabric is not even listed as an integration in my project.
Forget Fabric, it's not required. Just follow instructions in your Firebase console and Firebase documentation: https://firebase.google.com/docs/crashlytics/get-started#android
Don't mess with Fabric console, you should completly remove any calls to Fabric console and configure your project from scratch as Firebase doc says. As soon as you run app correctly configured, you will see Crashlytics panel in your Firebase console
The only solution I could find was as follows (I am including all steps exactly as I carried them out even if they may not be relevant):
I exported my realtime database to JSON and made a copy of my database rules.
I deleted my app from the Firebase project.
I deleted the Firebase project.
I deleted my project from Fabric.
I removed all references to Fabric, Crashlytics and the old Firebase Crash Reporting library from my project, this consisted of removing the dependencies from both my build.gradle files and deleting the fabric.properties file.
I ran gradlew.bat clean (I'm on Windows) on my project from the command line.
I did a Ctrl+Shift+F (search all project files) for the words fabric and crash just to make sure there was absolutely no remaining references to Fabric, Crashlytics or the deprecated Crash Reporting library (which was still a dependency in my build.gradle without me realising it because Firebase assistant tool added that as the crash reporting dependency when I first tried to get this working and then immediately told me it was deprecated :/ ).
I searched in File Explorer on windows for any files referencing "Fabric" or "Crash" (there weren't any).
I created a new project on Firebase.
I imported my realtime database from the previously exported JSON file and copy/pasted back in my rules.
I recreated my one test user account (thankfully the app is not yet in production :p).
I reenabled analytics (although this was simply a matter of going to the Analytics tab and seeing that it was already enabled as I still had the dependency in my build.gradle for my app module).
I went to the Crashlytics tab and saw the original screen explaining how to enable Crashlytics. This time however I did not create the project on Fabric or install the Fabric plugin, I only copied in the Fabric dependencies as I think #jake was suggesting, these can be found here https://firebase.google.com/docs/crashlytics/get-started?authuser=0.
I then tried to produce a crash using the method described here https://firebase.google.com/docs/crashlytics/force-a-crash?authuser=0.
And then voilà! The crash appeared immediately in the Crashlytics tab of Firebase. No Fabric project, no Fabric plugin, just the dependencies.
This method had the added benefit of allowing me to be rid of extra databases and user properties I did not want (which Firebase does not currently allow you to remove once you create them >:( ), but this method obviously may not be feasible for anyone who already has a lot invested in their Firebase project and cannot simply start over. Luckily for me this was not too much of a pain.
add apply plugin: 'com.google.firebase.crashlytics' in app module
and dependencies {
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
classpath 'com.google.gms:google-services:4.3.3'
} in project module
I had the same issue as you. I was able to link Fabric to Firebase after unlinking Crashlytics from Firebase following the instructions from this answer. Sadly this could only be done with building a custom url myself.
I have the following code in my library:
#Documented
#IntDef({OpacityAnimationType.NONE,
OpacityAnimationType.BLINKING,
OpacityAnimationType.SHINY,
OpacityAnimationType.AURA,
})
public #interface OpacityAnimationType {
int NONE = 0;
int BLINKING = 1;
int SHINY = 2;
int AURA = 3;
}
In gradle for library I have
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
minSdkVersion 16
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["library" : "true"]
}
}
}
}
and
configurations {
javadocDeps
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "com.android.support:appcompat-v7:$supportLibraryVersion"
compile "com.android.support:support-annotations:$supportLibraryVersion"
javadocDeps "com.android.support:support-annotations:$supportLibraryVersion"
}
Which I deploy to JFrog BinTray, and then try to use it in my app. I have to exclude appcompat-v7 and support-annotations from library dependency, but I build still fails with:
Error:Failed to resolve: annotationProcessor
Now I'm stuck, tried many things but nothing helps. I can't build main project with this library.
Do I need to implement any custom AnnotationProcessor to be able to use #IntDef's?
So finally I've been able to overcome this issue!
It looks like in case of custom annotations custom annotation processor is also required. For now I've decided to skip creating custom annotation processor and not use custom annotations for enumerations with #IntDef.
But anyways, if your library uses existing annotations and you publish it on mavenCentral or jCenter or other repository and use it in other projects, that you'll need to add some magic to javadoc task.
It starts here:
https://github.com/vulko/AnimatedArcProgressView/blob/master/library/build.gradle with
configurations {
javadocDeps
}
dependencies {
// ...
compile("com.android.support:support-annotations:$supportLibraryVersion") {
transitive false;
}
javadocDeps "com.android.support:support-annotations:$supportLibraryVersion"
}
and then continues in publishing gradle script here: https://github.com/vulko/AnimatedArcProgressView/blob/master/gradle/publish-library.gradle with:
task androidJavadocs(type: Javadoc) {
source = android.sourceSets.main.java.source
// this is the magic
classpath += configurations.javadocDeps
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
Anyways, all the code can be found here:
https://github.com/vulko/AnimatedArcProgressView/
Been googling this issue and looks like if there are any annotations in library, library needs an annotations project which will contain those. Not sure about the processor though, hopefully it wouldn't be needed, otherwise I'll have to switch from custom annotations to #IntDef with some static final int's instead.
There's also some gradle magic that is required to do this, and so far no good tutorials, but a bunch of code on github with processors and annotations. I'd like to avoid using custom processor, cause I simply don't need it.
Any ways, I'll try to keep this thread updated when I finally manage to solve this issue.
Now the story continues.
I had to create a annotations submodule in library project for annotations, and I had to move #IntDef's out of it, otherwise it wouldn't be able to import any of android annotations to submodule. So I've moved #IntDef's to library, and they use custom annotations from submodule now.
Even though I'm able to build and deploy it, the library artifact can't be imported, cause:
Error:Could not find
com.kvolkov.animatedprogressviews:annotations:unspecified. Searched in
the following locations: Required by:
project : > com.kvolkov.animatedprogressviews:library:1.0-RC5
I assume this happens because I need to deploy annotations submodule as a separate artifact now, which I can't do, since I wasn't able to match code from some tutorial for .gradle of annotations module with the plugins for bintray to deploy it there from studio...
Well, this is still not the end.
If any1 is interested or willing to help, you might take a look at current code of library here:
https://github.com/vulko/AnimatedArcProgressView
I'd be glad to recieve some help or advice, cause all info regarding this uses apt instead of annotation processor.
What is the apt dependency scope in android gradle files i see sometimes ?
An example looks like this?
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 20
buildToolsVersion '20.0.0'
defaultConfig {
applicationId "org.ligboy.test.card.module1"
minSdkVersion 14
targetSdkVersion 20
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
final DAGGER_VERSION = '2.0.2'
dependencies {
compile "com.google.dagger:dagger:${DAGGER_VERSION}"
apt "com.google.dagger:dagger-compiler:${DAGGER_VERSION}"//what is this scope
provided 'org.glassfish:javax.annotation:10.0-b28'
}
and in the top level build.gradle file it has this global dependency:
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
}
Notice in the dependencies section there is a apt scope ? i only know of compile, package and provided scope. compile
includes the dependency at compile time and in your package, provided says only include the library at compile time and discard it at
package time so its not included in final build. and Package is the reverse, it includes the dependency in the package and not at compile time.
But what is apt dependency scope which we obviously need the com.neenbedankt.android-apt for it to work so i know its android based.
update:
why cant i use provided dependency scope instead of apt scope ? How do they differ ?
i created a tutorial on dagger dependency scopes for those who need more info.
From the android-apt project page:
The android-apt plugin assists in working with annotation processors in combination with Android Studio. It has two purposes:
Allow to configure a compile time only annotation processor as a dependency, not including the artifact in the final APK or library
Set up the source paths so that code that is generated from the annotation processor is correctly picked up by Android Studio.
You are using Dagger, which uses annotation processing to generate code. The annotation processing code shouldn't be included in the final APK, and you want the generated code to be visible to Android Studio. android-apt enables this behavior.
This sounds very similar to the provided scope, but apt differs from provided in a few key ways. The first difference is that code generated by an apt dependency is available to the IDE, whereas code generated by a provided dependency is not.
Another important difference is that the code in a library using the provided scope is on the IDE classpath (i.e. you can import the classes and attempt to use them), whereas code in an apt dependency is not. With provided, your code will crash at runtime if you don't actually provide the referenced dependencies with a compile scoped counterpart.
You can find a discussion about apt vs provided on this android-apt issue.
In the case of Dagger, there should be no reason to include the annotation processor and code generator in any of your code (which the provided scope would allow). Thus the apt scope is more appropriate.
Update for October 2016:
You probably don't need apt and the android-apt plugin anymore. Version 2.2 of the Android Gradle plugin has an annotationProcessor configuration that you should be using instead.
See more at What's next for android-apt?
Just to add how to change this in Studio 2.2 +
dependencies {
compile 'com.google.dagger:dagger:2.4'
annotationProcessor "com.google.dagger:dagger-compiler:2.4"
}
Add this in apps gradle module. No need to change any other thing.
Happy coding :)
Here is a test project: click
I have a test Gradle Android project with three modules: app, library_a, library_b. app depends on library_a, then library_a depends on library_b:
build.gradle (app)
dependencies {
...
compile (project(":library_a")){
transitive = false;
}
}
build.gradle (library_a)
dependencies {
...
compile (project(":library_b")){
transitive = false;
}
}
Note that I set transitive = false because I don't want classes from library_b to be accessed from app
Every module has just one class, code is pretty simple:
app:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//...
ClassA classA = new ClassA();
classA.doSomething();
}
}
library_a:
public class ClassA
{
public void doSomething(){
Log.i("Test", "Done A!");
ClassB classB = new ClassB();
classB.doSomething();
}
}
library_b:
public class ClassB
{
public void doSomething(){
Log.i("Test", "Done B!");
}
}
Well, here is the problem: I'm building my project with gradlew. Apk is compiling successfully, but when I run it I get NoClassDefFoundError.
I/Test﹕ Done A!
E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.NoClassDefFoundError: ru.pvolan.library_b.ClassB
at ru.pvolan.somelibrary.ClassA.doSomething(ClassA.java:12)
...
If I set transitive = true in both .gradle files, it runs ok, but, as I noted above, I don't want dependency to be transitive, as far as I don't want ClassB can be accessed from MainActivity - only ClassA.
What am I doing wrong?
This is a problem that Gradle has simplified in Gradle v3.4.
If you convert library A to use v3.4 there is a simple fix.
Gradle 3.4 changes the "compile" configuration to a set of configurations "api" and "implementation".
First you should upgrade gradle to 3.4 and use the java-library plugin in lieu of the java plugin.
You should use the "api" configuration on any jar that is explicitly used in the API method calls (return type, input parameters, etc).
For all other jars that you want to "hide" (like Library B) you should use the "implementation" configuration. As Library B is only used within the body of implementation methods there is no need to expose it to any other jars at compile time; however it still needs to be available at runtime so Library A can use it.
To implement this your Library A script should replace
apply plugin: 'java'
dependencies {
...
compile (project(":library_b")){
transitive = false;
}
}
with
apply plugin: 'java-library'
dependencies {
implementation project(":library_b")
}
This change will tell Gradle to include Library B as a runtime dependency of app, so that app cannot compile against it, but Library B still will be available at runtime for Library A to use. If for some reason app ends up needing Library B in the future, it would be forced to explicitly include Library B in it's dependency list to ensure it gets the desired version.
See this description from Gradle itself for more details and examples:
https://blog.gradle.org/incremental-compiler-avoidance
The problem is that library_b is a required dependency. You can't simply exclude it, since you need it to be on the classpath at runtime. You are effectively misrepresenting your actual dependencies in order to enforce a code convention and therefore losing any advantage of leveraging a dependency management system like Gradle. If you want to enforce class or package blacklist I'd suggest using a source analysis tool like PMD. Here's an an example of a rule to blacklist specific classes.
If that is not possible for some reason you can get your above example to "work" by simply adding library_b to the runtime classpath of app.
dependencies {
runtime project(':library_b')
}
Do you use multidex?
When I had a problem like this I used multidex and called class from different module. I could fix it only by turning off multidex and running proguard.
UPD
android {
compileSdkVersion 21
buildToolsVersion "21.1.0"
defaultConfig {
...
minSdkVersion 14
targetSdkVersion 21
...
// Enabling multidex support.
multiDexEnabled true
}
...
}
dependencies {
compile 'com.android.support:multidex:1.0.0'
}
more about multi dex https://developer.android.com/tools/building/multidex.html
and about proguard http://developer.android.com/tools/help/proguard.html
I have been using zxing module as an android library for some time without problems. I have imported my Eclipse project into Android Studio (which I use for everything else!) and the zxing library is added as a module.
In one of my classes in the main module I reference the com.google.zxing.android.client.Intents class which is in the zxing module. I can write code in Android studio and these references have the correct imports selected.
When I run a gradlew clean build I am getting messages saying 'cannot find symbol class Intents'.
In settings.gradle I have includes for both my main module and the zxing module.
In the build.gradle of my main module I have 'compile project(':zxing'). These were both added during the import from Eclipse. I can also see that the zxing module is being built as part of the gradlew clean build.
I have tried deleting the zxing module and references then trying to add it manually. I have compared the way that this module is implemented to another project with a similar module that works. All looks fine.
Is anyone able to make a suggestion?
Edit:
settings.gradle file
include ':app'
include ':captureActivity'
build.gradle of main module
apply plugin: 'com.android.application'
android {
compileSdkVersion 19
buildToolsVersion "20.0.0"
defaultConfig {
applicationId "com.wigglyamps.littlegreenbutton"
minSdkVersion 10
targetSdkVersion 19
versionCode 1
versionName "1.0"
}
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile project(':captureActivity')
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:19.+'
}
Java class file in main module
public class test {
public void hello () {
String i = Intents.Scan.ACTION;
}
}
I don't really know the zxing framework, but have you tried to use the maven artifact instead of referencing a module?
If you want to use the artifact add this to the dependencies of the project/module that uses zxing: compile 'com.google.zxing:core:3.1.0'
Edit: You said you imported the project from eclipse; I'm in the middle of transitioning to gradle and the import function hardly produces anything useful if the eclipse project contained references to library projects (not jar's). I rebuild my projects from scratch by creating a new app(with modules) in Android Studio and copy/paste code and resources into the new app's directories