Android 的 Firebase Admin SDK,未找到方法
Firebase Admin SDK for Android, methods not found
我正在构建一个由 Firebase 应用程序支持的 Android 应用程序,我希望能够创建一个可以编辑或删除其他用户帐户的管理员帐户。如果我理解正确,Firebase Admin SDK 应该允许我这样做。所以我按照说明 here.
在我的应用程序中设置 Admin SDK。我将以下内容添加到 build.app:
compile 'com.google.firebase:firebase-admin:4.1.1'
并且在我的申请 class 中,我添加了这个:
FileInputStream serviceAccount = null;
try {
serviceAccount = new FileInputStream("app/<MY-DATABASE>.json");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (serviceAccount != null) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredential(FirebaseCredentials.fromCertificate(serviceAccount))
.setDatabaseUrl("https://<MY-APP>.firebaseio.com/")
.build();
FirebaseApp.initializeApp(options);
}
但是,它告诉我:
- FirebaseOptions.Builder 中没有名为 setCredential() 的方法,
- FirebaseApp.initializeApp() 采用 Context 对象,而不是 FirebaseOptions。
根据文档,FirebaseOptions.Builder.setCredential()
是一种新方法,它取代了已弃用的 FirebaseOptions.Builder.setServiceAccount()
。但是setServiceAccount()
也不存在。
这是怎么回事?
您不能在 Android 应用程序中将 Firebase Admin SDK 与 Firebase Android 客户端库一起使用。这两个 SDK 都提供 classes 具有完全相同的包和 class 名称,因此它不可能同时使用它们(编译器如何知道您打算使用哪个构建到您的应用程序中?)。
例如,查看 Android 客户端库中 FirebaseOptions Builder 的 java 文档:
com.google.firebase.FirebaseOptions.Builder
现在从 java Admin SDK 查看相同的 class(注意 URL 不同):
com.google.firebase.FirebaseOptions.Builder
您可以亲眼看到它们是不同的东西,即使它们具有相同的名称。因此,您的编译器正在查看 Android SDK 定义,而不是管理 SDK 定义。
正如 Frank 所说,您可能不想在 Android 应用程序中使用管理库。如果您想使用管理 SDK,请从您控制的服务器上使用它,并在需要时让您的 Android 应用与其通信。
正如已接受的答案所述,将 Firebase Admin 放入使用 Firebase 的 Android 应用中并不是一个好主意,因为有 类 个具有相同名称的应用。
我想创建一个自定义令牌 (https://firebase.google.com/docs/auth/android/custom-auth),所以我最终做了:
1) 创建一个单独的 UI-less 服务器应用程序。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
finish() // Just end the app here.
}
}
<resources>
<style name="AppTheme" parent="@android:style/Theme.NoDisplay">
</style>
</resources>
2) 将 IntentService 添加到服务器应用程序并生成自定义令牌。
class CustomTokenService : IntentService(CustomTokenService::class.java.simpleName) {
// Runs in its own thread.
override fun onHandleIntent(intent: Intent?) {
// Initialize FirebaseApp only when not already initialized.
try {
FirebaseApp.getInstance()
} catch (ex: IllegalStateException) {
try {
val inputStream = assets.open("serviceAccountKey.json")
val options = FirebaseOptions.Builder().
setCredential(FirebaseCredentials.fromCertificate(inputStream)).
setDatabaseUrl("https://YOUR_APP.firebaseio.com/").
build()
FirebaseApp.initializeApp(options)
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
// In real life, you should verify ID/PW before creating custom token.
val id = intent!!.getStringExtra("ID")
val pw = intent.getStringExtra("PW")
val additionalClaims = HashMap<String, Any>()
additionalClaims.put("premiumAccount", true)
FirebaseAuth.getInstance().createCustomToken(id, additionalClaims).
addOnSuccessListener { customToken ->
// Send custom token back to client.
val resultReceiver = intent.getParcelableExtra<ResultReceiver>(RESULT_RECEIVER)
val bundle = Bundle()
bundle.putString(CUSTOM_TOKEN, customToken)
resultReceiver.send(Activity.RESULT_OK, bundle)
}
}
}
请注意,我通过 "ResultReceiver" 将自定义令牌发送回客户端,但您可以自由使用其他方式,例如 "Messenger" 或 "BroadcastReceiver"。
3) 从客户端,我启动驻留在服务器应用程序中的服务。
String MYSERVER = "SERVER_ID"; // e.g. "com.domain.myserver"
String CUSTOM_TOKEN_SERVICE = MYSERVER + ".CustomTokenService";
Intent intent = new Intent();
intent.putExtra("ID", ID);
intent.putExtra("PW", PW);
intent.putExtra(RESULT_RECEIVER, mResultReceiver);
intent.setComponent(new ComponentName(MYSERVER, CUSTOM_TOKEN_SERVICE));
getContext().startService(intent);
4) 当我从服务器应用程序收到自定义令牌时,我登录到 Firebase。
ResultReceiver resultReceiver = new ResultReceiver(new Handler()) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
String customToken = resultData.getString(CUSTOM_TOKEN);
mFirebaseAuth.signInWithCustomToken(customToken);
}
};
Parcel parcel = Parcel.obtain();
resultReceiver.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
mResultReceiver = ResultReceiver.CREATOR.createFromParcel(parcel);
parcel.recycle();
5) 和 Gradle 配置文件。
buildscript {
ext.kotlin_version = '1.2.21'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
---------------------------------------------------------------------------
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
applicationId "org.solamour.myserver"
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath false
}
}
resConfigs "auto"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Conflict with dependency 'com.google.code.findbugs:jsr305' in project ':app'.
// Resolved versions for app (1.3.9) and test app (2.0.1) differ.
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:2.0.1' // Or "1.3.9".
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.google.firebase:firebase-admin:4.1.6'
implementation 'com.android.support:multidex:1.0.2'
}
我能够使用的最高 Firebase Admin 版本是“4.1.6”;之后的任何事情都涉及对 gradle 文件的大量修改(结果也不是很好)。
现在 FirebaseOptions class 从其他依赖项中获取,您可以使用排除标记从其他依赖项中删除 firebase 组件,如下所示。
compile 'com.google.firebase:firebase-admin:5.8.0'
compile ('com.google.firebase:firebase-messaging:9.6.1'){
exclude module: 'firebase-common'
}
compile ('com.google.firebase:firebase-auth:9.6.1'){
exclude module: 'firebase-common'
}
compile ('com.google.firebase:firebase-database:9.6.1'){
exclude module: 'firebase-common'
}
compile ('com.firebase:firebase-client-android:2.5.0'){
exclude module: 'firebase-common'
}
我正在构建一个由 Firebase 应用程序支持的 Android 应用程序,我希望能够创建一个可以编辑或删除其他用户帐户的管理员帐户。如果我理解正确,Firebase Admin SDK 应该允许我这样做。所以我按照说明 here.
在我的应用程序中设置 Admin SDK。我将以下内容添加到 build.app:
compile 'com.google.firebase:firebase-admin:4.1.1'
并且在我的申请 class 中,我添加了这个:
FileInputStream serviceAccount = null;
try {
serviceAccount = new FileInputStream("app/<MY-DATABASE>.json");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
if (serviceAccount != null) {
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredential(FirebaseCredentials.fromCertificate(serviceAccount))
.setDatabaseUrl("https://<MY-APP>.firebaseio.com/")
.build();
FirebaseApp.initializeApp(options);
}
但是,它告诉我:
- FirebaseOptions.Builder 中没有名为 setCredential() 的方法,
- FirebaseApp.initializeApp() 采用 Context 对象,而不是 FirebaseOptions。
根据文档,FirebaseOptions.Builder.setCredential()
是一种新方法,它取代了已弃用的 FirebaseOptions.Builder.setServiceAccount()
。但是setServiceAccount()
也不存在。
这是怎么回事?
您不能在 Android 应用程序中将 Firebase Admin SDK 与 Firebase Android 客户端库一起使用。这两个 SDK 都提供 classes 具有完全相同的包和 class 名称,因此它不可能同时使用它们(编译器如何知道您打算使用哪个构建到您的应用程序中?)。
例如,查看 Android 客户端库中 FirebaseOptions Builder 的 java 文档:
com.google.firebase.FirebaseOptions.Builder
现在从 java Admin SDK 查看相同的 class(注意 URL 不同):
com.google.firebase.FirebaseOptions.Builder
您可以亲眼看到它们是不同的东西,即使它们具有相同的名称。因此,您的编译器正在查看 Android SDK 定义,而不是管理 SDK 定义。
正如 Frank 所说,您可能不想在 Android 应用程序中使用管理库。如果您想使用管理 SDK,请从您控制的服务器上使用它,并在需要时让您的 Android 应用与其通信。
正如已接受的答案所述,将 Firebase Admin 放入使用 Firebase 的 Android 应用中并不是一个好主意,因为有 类 个具有相同名称的应用。
我想创建一个自定义令牌 (https://firebase.google.com/docs/auth/android/custom-auth),所以我最终做了:
1) 创建一个单独的 UI-less 服务器应用程序。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
finish() // Just end the app here.
}
}
<resources>
<style name="AppTheme" parent="@android:style/Theme.NoDisplay">
</style>
</resources>
2) 将 IntentService 添加到服务器应用程序并生成自定义令牌。
class CustomTokenService : IntentService(CustomTokenService::class.java.simpleName) {
// Runs in its own thread.
override fun onHandleIntent(intent: Intent?) {
// Initialize FirebaseApp only when not already initialized.
try {
FirebaseApp.getInstance()
} catch (ex: IllegalStateException) {
try {
val inputStream = assets.open("serviceAccountKey.json")
val options = FirebaseOptions.Builder().
setCredential(FirebaseCredentials.fromCertificate(inputStream)).
setDatabaseUrl("https://YOUR_APP.firebaseio.com/").
build()
FirebaseApp.initializeApp(options)
inputStream.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
// In real life, you should verify ID/PW before creating custom token.
val id = intent!!.getStringExtra("ID")
val pw = intent.getStringExtra("PW")
val additionalClaims = HashMap<String, Any>()
additionalClaims.put("premiumAccount", true)
FirebaseAuth.getInstance().createCustomToken(id, additionalClaims).
addOnSuccessListener { customToken ->
// Send custom token back to client.
val resultReceiver = intent.getParcelableExtra<ResultReceiver>(RESULT_RECEIVER)
val bundle = Bundle()
bundle.putString(CUSTOM_TOKEN, customToken)
resultReceiver.send(Activity.RESULT_OK, bundle)
}
}
}
请注意,我通过 "ResultReceiver" 将自定义令牌发送回客户端,但您可以自由使用其他方式,例如 "Messenger" 或 "BroadcastReceiver"。
3) 从客户端,我启动驻留在服务器应用程序中的服务。
String MYSERVER = "SERVER_ID"; // e.g. "com.domain.myserver"
String CUSTOM_TOKEN_SERVICE = MYSERVER + ".CustomTokenService";
Intent intent = new Intent();
intent.putExtra("ID", ID);
intent.putExtra("PW", PW);
intent.putExtra(RESULT_RECEIVER, mResultReceiver);
intent.setComponent(new ComponentName(MYSERVER, CUSTOM_TOKEN_SERVICE));
getContext().startService(intent);
4) 当我从服务器应用程序收到自定义令牌时,我登录到 Firebase。
ResultReceiver resultReceiver = new ResultReceiver(new Handler()) {
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
String customToken = resultData.getString(CUSTOM_TOKEN);
mFirebaseAuth.signInWithCustomToken(customToken);
}
};
Parcel parcel = Parcel.obtain();
resultReceiver.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
mResultReceiver = ResultReceiver.CREATOR.createFromParcel(parcel);
parcel.recycle();
5) 和 Gradle 配置文件。
buildscript {
ext.kotlin_version = '1.2.21'
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
---------------------------------------------------------------------------
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 26
defaultConfig {
applicationId "org.solamour.myserver"
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
javaCompileOptions {
annotationProcessorOptions {
includeCompileClasspath false
}
}
resConfigs "auto"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Conflict with dependency 'com.google.code.findbugs:jsr305' in project ':app'.
// Resolved versions for app (1.3.9) and test app (2.0.1) differ.
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:2.0.1' // Or "1.3.9".
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
implementation 'com.google.firebase:firebase-admin:4.1.6'
implementation 'com.android.support:multidex:1.0.2'
}
我能够使用的最高 Firebase Admin 版本是“4.1.6”;之后的任何事情都涉及对 gradle 文件的大量修改(结果也不是很好)。
现在 FirebaseOptions class 从其他依赖项中获取,您可以使用排除标记从其他依赖项中删除 firebase 组件,如下所示。
compile 'com.google.firebase:firebase-admin:5.8.0'
compile ('com.google.firebase:firebase-messaging:9.6.1'){
exclude module: 'firebase-common'
}
compile ('com.google.firebase:firebase-auth:9.6.1'){
exclude module: 'firebase-common'
}
compile ('com.google.firebase:firebase-database:9.6.1'){
exclude module: 'firebase-common'
}
compile ('com.firebase:firebase-client-android:2.5.0'){
exclude module: 'firebase-common'
}