firebase / firebase-android-sdk

Firebase Android SDK
https://firebase.google.com
Apache License 2.0
2.28k stars 578 forks source link

Initialize FirebaseApp without google-services.json #66

Open paularius opened 6 years ago

paularius commented 6 years ago

[REQUIRED] Step 3: Describe the problem

I am trying to initialize the FirebaseApp, only by using the FirebaseOptions builder, like this, first thing in my application (I use only applicationId and ApiKey, since I only want the analytics for start) :

FirebaseOptions options = new FirebaseOptions.Builder()
                    .setApplicationId(applicationID) // Required for Analytics.
                    .setApiKey(apiKey) // Required for Auth.
                    .build();
FirebaseApp.initializeApp(context, options);

I do not have a google-services.json file in my project, I have removed the FirebaseInitProvider, like this, in my AndroidManifest.xml:

<provider
    android:name="com.google.firebase.provider.FirebaseInitProvider"
    android:authorities="${applicationId}.firebaseinitprovider"
    tools:node="remove"/>.

When starting the application, while trying to View events in the Android Studio debug log, like this: adb shell setprop log.tag.FA VERBOSE adb shell setprop log.tag.FA-SVC VERBOSE adb logcat -v time -s FA FA-SVC

I get Missing google_app_id. Firebase Analytics disabled. See https://goo.gl/NAOOOI .

samtstern commented 6 years ago

@paularius could you try adding a string entry to your project (in strings.xml)

<!-- Use the same value you used for applicationID above -->
<string name="google_app_id">YOUR_APPLICATION_ID</string>

For a more comprehensive guide on not using the json file, see this blog post: https://medium.com/@samstern_58566/how-to-use-firebase-on-android-without-the-google-services-plugin-93ecc7dc6c4

paularius commented 6 years ago

@samtstern Thanks for your reply. I have already tried this, and seems to work. The thing is that I would like to initialize the FirebaseApp more dynamically, since I would like to have different projects within the same apk (so using different flavors for the xml, would not be useful). I am trying a part of your approach and try to change the resource dynamically, using reflection, before initializeApp, but that doesn't seem to work either. Moreover, what is the use of the FirebaseOptions, if I have to use a file in my project?

samtstern commented 6 years ago

@paularius you're right that you shouldn't need the XML file at all. Where are you calling InitializeApp() and is it possible you're trying to use FirebaseAnalytics before that happens?

paularius commented 6 years ago

@samtstern I call it first thing in onCreate, in my application class. Also, before I try to use any firebaseAnalytics call, I check that the FirebaseApp (the "[DEFAULT]" one) exists. But even if I tried to initialize it in some other place (for example when I get a response from my server), even by losing some events, should it be a problem?

adityashri7 commented 6 years ago

I have had the same issue since months now. I have a running thread with the support team, and it doesn't look like this issue is going anywhere. In my scenario, I have the manual Firebase initialization in a library, and I have multiple apps that use that library. So based on the application thats using it, I initialize the Firebase app. Everything initializes well, but Firebase analytics, which complains about "Missing google_app_id".

kjsingh815 commented 6 years ago

We're having the same issue, really hope Firebase team moves faster on this as it's impacting many developers and we're considering moving off of Firebase due to it.

samtstern commented 6 years ago

Thanks everyone for chiming in! I filed an internal bug (b/117609738) with the analytics team since they're likely not watching this issue as that SDK is still closed source. Hopefully they can help me get to the bottom of this.

If you'll let me take one more blind stab: what happens if you create a bogus google_app_id string resource just to satisfy the SDK, but then initialize the default FirebaseApp yourself at runtime. Does Analytics send events to the right place?

adityashri7 commented 6 years ago

@samtstern if I just add a google app id, Firebase initializes the app on its own, but ofc doesn't add the other parameters like database url etc. So the manual initialization also fails, because it sees that the app has already been initialized. I even tried to add all the items as generated by firebase to my resources, as mentioned in this post: https://medium.com/@samstern_58566/how-to-use-firebase-on-android-without-the-google-services-plugin-93ecc7dc6c4. It works, but then like @paularius mentioned, I wouldn't be able to switch the app environments dynamically during run time.

dekan1 commented 5 years ago

Please, I have same problem with initialization app, is this problem solved yet, or exist any workaround?

renatobenks-zz commented 5 years ago

There's no PR yet for that? How is going on the work to solve that?

I'm with the same problem, I want to load dynamically apps on runtime.

tonyyyrocks39 commented 5 years ago

Is this issue fixed...I mean can we initialize firebase without this json file.. Also what is the purpose of firebaseoptions.builder if we can intialize firebase without the builder by simply copying the json file in project.

damianun commented 5 years ago

Yes, we have similar requirement where we would like to be able to switch on startup which Firebase project along with Analytics and Crashlytics should be used. Any feedback from Analytics teams on this?

mkarecki commented 5 years ago

Hi, we also have the same issue, it's stopping us from full migration from GA to Firebase because we need to setup Firebase in runtime with use of data received from our backend. Is there any ongoing work on this bug?

mmert1988 commented 5 years ago

Having the same issue. Will be there any fix soon?

igorbiscanin commented 5 years ago

@samtstern Any news here?

ddukesterman commented 5 years ago

@samtstern Any update on this issue?

vokrut commented 5 years ago

Google Analytics for Firebase (GA4F) doesn't support dynamic initialization. Our engineers are checking the possible solutions to support this. It's just that we still haven't found a definite timeline as to when (or if) this will be available. GA4F will not work without the google-services.json file (or Gradle on your end). Even though you can initialize the FirebaseApp dynamically through code, GA4F will not recognize this and will only result in the error message you are seeing. The scenario you are getting is only specific to Google Analytics for Firebase. However, you can still use other products like Firestore, Realtime Database, Storage even if you are not using Gradle plugin.

https://stackoverflow.com/a/55006495/5862482

pepitoria commented 4 years ago

Just checking up on this, status of this has not changed, right?

HarrisonRenny commented 4 years ago

@pepitoria AFAIK the last time we spoke with google - this issue is not going to be resolved.

RustamSitdikov commented 4 years ago

bug with Firebase using in Chromium

RustamSitdikov commented 4 years ago

in Chromium problem was due to string resources obfuscation (so google_app_id was obfuscated). adding resource_type/resource_name#no_obfuscate in aapt2.config solved problem

ashwinraghav commented 4 years ago

Copied from https://github.com/firebase/firebase-android-sdk/issues/187#issuecomment-635485459

Thanks for the question and for waiting patiently.

Many Firebase SDKs products provide an overload that allows you to pass in a custom FirebaseApp getInstance(FirebaseApp).

This API will allow you inject these Firebase instances as dependencies into your application code so they continue remaining agnostic to prod,staging,dev versions of your app.

I suspect your question pertains to eagerly initialized SDKs like Analytics, Crashlytics and Firebase Performance that require no developer interaction and start working automagically once included in the app. Note that these SDKs do not support the desired getInstance(FirebaseApp) overload.

The challenge with designing an API like getInstance(FirebaseApp) for these products is that it would have to be wired in super early in the App's lifecycle, to (say) allow Crashlytics to capture crashes that happen early on, or to (say) allow Performance to capture app start metrics early on.

In the absence of this API, you may be able to do this by disabling the default init provider and creating your own equivalent. The key here is to perform the custom initialization super early in your app's lifecycle, like in a Content Provider.

FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
    .setApplicationId("1:0123456789012:android:0123456789abcdef")
    .setApiKey("your_api_key")
    .setDatabaseUrl("https://your-app.firebaseio.com")
    .setStorageBucket("your-app.appspot.com");
FirebaseApp.initializeApp(this, builder.build());

I realize is a suboptimal experience. It does get the job done for now. Feel free to propose ideas you may have to help up build an API to accommodate the nuance I described above.

jruvel commented 4 years ago

Sadely @ashwinraghav this doesn't seem to solve the issue. We have struggled with this for almost two years. We have a multi-tenant application with dynamic switching of the firebase project and we got that working but analytics has to all be sent to one place by hardcoding "google_app_id" in values/strings.xml - this is the only thing that somewhat works. I just tried a Content Provider and the code runs fine but still see an error "Missing google_app_id." We continue to wait hopefully for one day a getInstance(FirebaseApp) for FirebaseAnalytics. Note this all works for the iOS SDKs.

ashwinraghav commented 4 years ago

Sadely @ashwinraghav this doesn't seem to solve the issue. We have struggled with this for almost two years. We have a multi-tenant application with dynamic switching of the firebase project and we got that working but analytics has to all be sent to one place by hardcoding "google_app_id" in values/strings.xml - this is the only thing that somewhat works. I just tried a Content Provider and the code runs fine but still see an error "Missing google_app_id." We continue to wait hopefully for one day a getInstance(FirebaseApp) for FirebaseAnalytics. Note this all works for the iOS SDKs.

Duly noted. This feedback is helpful for us to prioritize.

smilehari commented 4 years ago

Is it possible to set up Firebase Cloud messaging from the instance of the second app ?

iliaskomp commented 4 years ago

We are also affected from this issue since we are using a content provider instead of google-services.json. We are trying to migrate to firebase crashlytics with mappingFileUploadEnabled set to true. The build fails with the following error:

Crashlytics could not find the resource file generated by Google Services. You may need to execute the :process<Variant>GoogleServices Task. Please check your Firebase project configuration (https://firebase.google.com/docs/android/setup). 

The issue is two years old and it is the highest voted firebase sdk github issue, shouldn't it have a top priority?

hollmannlu commented 4 years ago

We are also affected from this issue since we are using a content provider instead of google-services.json. We are trying to migrate to firebase crashlytics with mappingFileUploadEnabled set to true. The build fails with the following error:

Crashlytics could not find the resource file generated by Google Services. You may need to execute the :process<Variant>GoogleServices Task. Please check your Firebase project configuration (https://firebase.google.com/docs/android/setup). 

The issue is two years old and it is the highest voted firebase sdk github issue, shouldn't it have a top priority?

we're facing exactly the same problem with mappingFileUploadEnabled I totally agree, this should have more priority.

jruvel commented 4 years ago

Is it possible to set up Firebase Cloud messaging from the instance of the second app ?

We do use messaging successfully from the new app that we switch to.

ashwinraghav commented 4 years ago

The issue is two years old and it is the highest voted firebase sdk github issue, shouldn't it have a top priority?

Certainly is top priority :) We'll be sure to share our plans to address this issue with folks on this issue, to get some early feedback.

jruvel commented 4 years ago

@ashwinraghav - does this mean a fix on the Android side is close? https://github.com/firebase/firebase-ios-sdk/pull/6142 - Not exactly related but going the right direction.

mtrakal commented 4 years ago

Google Play console start showing:

Please upgrade to the generally available Firebase Crashlytics SDK to continue receiving crash reports in the Firebase console after 15 November 2020.

We really would like, but it's not possible when we still have no option to log to the correct project :)

Do you have some estimation, when this issue will be resolved? We don't like it when we have just a few days to implement new features until Google kills the old one :). When you have one month releasing window (contracting), you don't have much time...

prilaga commented 4 years ago

It was a wonderful time to work with the Fabric, until google bought it. I could init crashlytics, analytics manually when I needed it, but now I can't. Why do I need this - because I want to keep securely all the firebase keys and run firebase, crashlytics, analytics dynamically. But for now I can init the firebase without json file, but crashlytics doesn't work in this case:

Logs when trying to build a release project: FAILURE: Build failed with an exception.

Please rollback to previous build project feature, like it was with Fabric.

Update:

Here is a tested workaround, how to to init crashlytics and firebase when you need:

  1. Make sure the app is ready to release and no changes will be added to the current build. Decide where you will enable default Firebase app with your keys copied from google-services.json file.
  2. Check that you have network connection.
  3. Build a release project as usually with:
    google-services.json file
    dependencies {
        classpath 'com.google.gms:google-services:4.3.3'
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
    }
    // Google Services Gradle plugin
    apply plugin: 'com.google.gms.google-services'
    // Apply the Crashlytics Gradle plugin
    apply plugin: 'com.google.firebase.crashlytics'
  4. Wait for several minutes while plugin post the mapping.txt file to firebase. Not sure how long it should be.
  5. Find and copy the key from build folder to your string.xml with next name for crashlytics:
    <string tools:ignore="UnusedResources,TypographyDashes"
        name="com.crashlytics.android.build_id"
        translatable="false">*********************</string>

    For analytics:

    <string name="google_app_id" translatable="false">************</string>
  6. Remove all from 2 point from the project:
    google-services.json file
    dependencies {
        classpath 'com.google.gms:google-services:4.3.3'
        classpath 'com.google.firebase:firebase-crashlytics-gradle:2.3.0'
    }
    // Google Services Gradle plugin
    apply plugin: 'com.google.gms.google-services'
    // Apply the Crashlytics Gradle plugin
  7. Disable FirebaseInitProvider in AndroidManifest:
    <provider
            android:name="com.google.firebase.provider.FirebaseInitProvider"
            android:authorities="${applicationId}.firebaseinitprovider"
            tools:node="remove"/>
  8. Clean the project - build folder should be cleared and created without hardcoded keys.
  9. Build a release again and publish the app.

I created a test crash with this solution and found the report in the dashboard.

Tapchicoma commented 4 years ago

Workaround when you have generated xml file, but not google-services plugin itself:

val copyGoogleIdValuesTask = tasks.register("copyGoogleIdValues", Copy::class.java) {
    from("src/main/res/values/google-services.xml")
    into("${project.buildDir}/generated/res/google-services/values.xml")
}

tasks.withType<com.google.firebase.crashlytics.buildtools.gradle.tasks.UploadMappingFileTask>() {
    dependsOn(copyGoogleIdValuesTask)
}

May not work in multivariant apps.

yash-numino commented 4 years ago

Is there any update on this problem wrt. Firebase Analytics, as of September 29th, 2020. I'm having to solve the exact same problem, and I'm out of solutions.

Does anyone else have any other way by which this problem can be solved?

1951FDG commented 3 years ago

Workaround (groovy syntax) when you have generated xml file located in src/release/res/values/values.xml

task copyGoogleIdValuesTask(type: Copy) {
    from 'src/release/res/values/values.xml'
    into "$project.buildDir/generated/res/google-services/release/values/"
}

import com.google.firebase.crashlytics.buildtools.gradle.tasks.UploadMappingFileTask

tasks.withType(UploadMappingFileTask).configureEach {
    dependsOn(copyGoogleIdValuesTask)
}

Works for release build variant.

jae-12 commented 3 years ago

Any update on this? Still facing this issue in March of 2021 😩

shadowsheep1 commented 3 years ago

The @1951FDG workaround works, but if we update to crashlytics gradle plugin 2.7.+ we have this error now: https://github.com/firebase/firebase-android-sdk/issues/2721

shafayathossain commented 2 years ago

Still facing this issue in December of 2021. They resolved 926 issues. But this 66th issue is open for over 3 years. Flutter developers have no idea that this is a native android SDK issue. Because it's working fine for iOS.

waochi commented 2 years ago

Any update on this?

gemunet commented 2 years ago

Still no solution for this? March 2022 :(

electricbolt commented 2 years ago

Hi, we've implemented a workaround for use of Firebase Analytics. We've not tested any other usage. Your milage may vary.

Overview

It appears the actual analytics code is implemented in play-services-measurement-*.jar files (which I assume are downloaded automatically via Google Play Services).

To initialise Firebase I call the following programmatically:

FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
    .setApplicationId(FirebaseConstants.APP_ID)
    .setApiKey(FirebaseConstants.API_KEY)
    .setGcmSenderId(FirebaseConstants.MESSAGING_SENDER_ID)
    .setProjectId(FirebaseConstants.PROJECT_ID);
FirebaseApp.initializeApp(getContext(), builder.build());

FirebaseAnalytics.getInstance(getContext());

The context provided by getContext() is internally converted by Firebase to an application context, which is then cast to an android.app.Application. The play-services-measurement-* code then calls the application's getResources() method, followed by a call to Resource.getIdentifier("google_app_id", "string", "<your-package>") to return the R.string.google_app_id constant. Then Resource.getString(R.string.google_app_id) is called to return the actual value you should have declared in strings.xml (which won't exist as we're trying to do this programmatically).

Implementation

Add the following to your android.app.Application subclass:

    private FirebaseResourcesWrapper firebaseResources;

    @Override
    public Resources getResources() {
        if (firebaseResources == null)
            firebaseResources = new FirebaseResourcesWrapper(super.getResources());
        return firebaseResources;
    }

The FirebaseResourcesWrapper subclasses android.content.res.Resources and implements the class wrapper pattern. Here we forward all visible public methods to the original wrapped class, except for getIdentifer() and getString() which we override as follows:

public class FirebaseResourcesWrapper extends Resources {

    private static final String GOOGLE_APP_ID = "google_app_id";
    private static final int R_STRING_GOOGLE_APP_ID = 1_999_999_999;

    private final Resources wrapped;

    public FirebaseResourcesWrapper(Resources wrapped) {
        super(wrapped.getAssets(), wrapped.getDisplayMetrics(), wrapped.getConfiguration());
        this.wrapped = wrapped;
    }

    public int getIdentifier(String name, String defType, String defPackage) {
        if (GOOGLE_APP_ID.equals(name) && "string".equals(defType))
            return R_STRING_GOOGLE_APP_ID;
        return wrapped.getIdentifier(name, defType, defPackage);
    }

    public String getString(int id) throws NotFoundException {
        if (id == R_STRING_GOOGLE_APP_ID)
            return FirebaseConstants.APP_ID;
        return wrapped.getString(id);
    }

    public String getString(int id, Object... formatArgs) throws NotFoundException {
        return wrapped.getString(id, formatArgs);
    }

Full implementation of FirebaseResourcesWrapper.java in this GitHub gist:

https://gist.github.com/electricbolt/423c03f09bc0303d0d5696b8beb392bd

FarzaneGhb commented 1 year ago

Any update on this?

DarshanMagneto commented 1 year ago

Any update on this issue?

jmcculloch commented 1 year ago

👋

leontodd commented 1 year ago

Hi @ashwinraghav, have there been any updates on a fix for this issue?

gongshoudao commented 1 year ago

same issue.

image
sergey-avagyan commented 10 months ago

Any news on this issue?

Hunter54 commented 9 months ago

Same issue here. 5-6 years later, still no plan to fix it?

Using this workaround seems to work. https://github.com/firebase/firebase-android-sdk/issues/66#issuecomment-1142669342

prilaga commented 9 months ago

@sergey-avagyan @Hunter54 Stop waiting, just use FirebaseResourcesWrapper or similar solution. Gооglе never fixes issues, until they become a problem, until it threatens their business.

as-stefit commented 7 months ago

Hi, we've implemented a workaround for use of Firebase Analytics. We've not tested any other usage. Your milage may vary.

Overview

It appears the actual analytics code is implemented in play-services-measurement-*.jar files (which I assume are downloaded automatically via Google Play Services).

To initialise Firebase I call the following programmatically:

FirebaseOptions.Builder builder = new FirebaseOptions.Builder()
    .setApplicationId(FirebaseConstants.APP_ID)
    .setApiKey(FirebaseConstants.API_KEY)
    .setGcmSenderId(FirebaseConstants.MESSAGING_SENDER_ID)
    .setProjectId(FirebaseConstants.PROJECT_ID);
FirebaseApp.initializeApp(getContext(), builder.build());

FirebaseAnalytics.getInstance(getContext());

The context provided by getContext() is internally converted by Firebase to an application context, which is then cast to an android.app.Application. The play-services-measurement-* code then calls the application's getResources() method, followed by a call to Resource.getIdentifier("google_app_id", "string", "<your-package>") to return the R.string.google_app_id constant. Then Resource.getString(R.string.google_app_id) is called to return the actual value you should have declared in strings.xml (which won't exist as we're trying to do this programmatically).

Implementation

Add the following to your android.app.Application subclass:

    private FirebaseResourcesWrapper firebaseResources;

    @Override
    public Resources getResources() {
        if (firebaseResources == null)
            firebaseResources = new FirebaseResourcesWrapper(super.getResources());
        return firebaseResources;
    }

The FirebaseResourcesWrapper subclasses android.content.res.Resources and implements the class wrapper pattern. Here we forward all visible public methods to the original wrapped class, except for getIdentifer() and getString() which we override as follows:

public class FirebaseResourcesWrapper extends Resources {

    private static final String GOOGLE_APP_ID = "google_app_id";
    private static final int R_STRING_GOOGLE_APP_ID = 1_999_999_999;

    private final Resources wrapped;

    public FirebaseResourcesWrapper(Resources wrapped) {
        super(wrapped.getAssets(), wrapped.getDisplayMetrics(), wrapped.getConfiguration());
        this.wrapped = wrapped;
    }

    public int getIdentifier(String name, String defType, String defPackage) {
        if (GOOGLE_APP_ID.equals(name) && "string".equals(defType))
            return R_STRING_GOOGLE_APP_ID;
        return wrapped.getIdentifier(name, defType, defPackage);
    }

    public String getString(int id) throws NotFoundException {
        if (id == R_STRING_GOOGLE_APP_ID)
            return FirebaseConstants.APP_ID;
        return wrapped.getString(id);
    }

    public String getString(int id, Object... formatArgs) throws NotFoundException {
        return wrapped.getString(id, formatArgs);
    }

Full implementation of FirebaseResourcesWrapper.java in this GitHub gist:

https://gist.github.com/electricbolt/423c03f09bc0303d0d5696b8beb392bd

Thanks for your solution. I've got one question, how do you define FirebaseConstants? In my app, while initializing flutter app I pull secrets from secrets manager, save it in FlutterSecureStorage and initialize FirebaseApplication with appropriate secrets pulled in previous step. Firestore and crashlytics work fine, but analytics don't. The solution proposed by you looks promising, but I'm missing one part, how to share these secrets in FirebaseResourcesWrapper class. I would be grateful if you could share your ideas!