转载请注明出处:https://blog.youkuaiyun.com/xiangxinzijiwonen/article/details/84103023(来自ZhongGuanYao的csdn博客)
从事海外SDK开发经常用到Firebase做推送、或数据上报分析,Firebase目前已经不支持Eclipse环境了,但项目需要用Eclipse环境接入Firebase怎样办?个人在工作中也遇到过这样的情况,现在总结一下。
操作步骤:
一、在AS环境下将Firebase相关的aar目录整理出来
Firebase文档:https://firebase.google.com/docs/android/setup
1.根据Firebase文档的步骤在AS下配置好环境,配置相关的依赖库,让编译通过。
2.工程用Project结构显示,这时我们可以在External Libraries看到gradle配置所加载的相关依赖库,如下图所示:
3.在External Libraries找到Firebase相关类库,逐个展开左三角符,右键打开类库目录,并将目录拷贝出来,如下图片:
4.整理出来的aar目录,如下图:
二、处理Firebase相关的aar目录
对上面整理出来的aar目录逐个处理,每个aar目录都包含jars目录和AndroidManifest.xml,其中有些目录还包含res资源目录,目录结构如下图:
jars目录处理:为了方便管理,将里面的classes.jar重命名,最好跟aar目录名一致,如上面截图命令为firebase-messaging-17.3.3.jar,然后将jar包拷贝到主工程里面使用。
AndroidManifest.xml文件处理:打开该文件,将里面的权限配置、activity、service、provider、reciver、meta-data等四大组件或元数据配置统统拷贝到主工程的AndroidManifest.xml里,有两点需要注意,配置里的${applicationId}要换成主工程的包名,另外,主工程里AndroidManifest.xml文件的android:minSdkVersion不能小于aar目录下该文件里的android:minSdkVersion值。
res目录处理:
- 在Eclipse下创建新类库,并将res目录的资源拷贝到该新类库;
- 将aar目录下的jars/classes.jar包重命名后,拷贝到新类库而不拷贝到主工程里,这样可以避免新类库的res资源引用到该jar包时报错;
- 新类库编译时,res资源可能会报错,可根据提示在引入相关支持库;
- 新类库下AndroidManifest.xml文件里面的包名要和aar目录下的AndroidManifest.xml文件里的包名一致;
- aar目录下的AndroidManifest.xml文件里还是按上面的操作将配置拷贝到主工程的AndroidManifest.xml里;
- 在主工程下引入新类库。
之所以在Eclipse下创建类库来放置aar目录下的res资源,是因为Firebase并不是使用反射方式获取资源的,该aar目录的jar文件代码获取资源时依赖该aar生成的R类,这里创建类库目的是构建aar下该包名的R类。
迁移到Eclipse后如下图:
其中,FirebaseDemo是主工程,其他几个为新建的类库,并且这几个类库的资源没有引用到支持库或其他依赖的jar包,处理起来很简单。
三、解决Firebase在Eclipse环境读取google-services.json配置问题
启动AS下项目时,Firebase程序会自动读取google-services.json文件里的配置进行初始化,但在Eclipse下,Firebase程序无法自动读取到该文件配置,怎么办?
我们可以查阅官方文档,找出突破点。
Firebase API文档:https://firebase.google.com/docs/reference/android/com/google/firebase/FirebaseApp
先看下官方文档关于FirebaseApp描述
The entry point of Firebase SDKs. It holds common configuration and state for Firebase APIs. Most applications don't need to directly interact with FirebaseApp.
For a vast majority of apps, FirebaseInitProvider will handle the initialization of Firebase for the default project that it's configured to work with, via the data contained in the app's google-services.json file. This ContentProvider is merged into the app's manifest by default when building with Gradle, and it runs automatically at app launch. No additional lines of code are needed in this case.
In the event that an app requires access to another Firebase project in addition to the default project, initializeApp(Context, FirebaseOptions, String) must be used to create that relationship programmatically. The name parameter must be unique. To connect to the resources exposed by that project, use the FirebaseApp object returned by getInstance(String), passing it the same name used with initializeApp. This object must be passed to the static accessor of the feature that provides the resource. For example, getInstance(FirebaseApp) is used to access the storage bucket provided by the additional project, whereas getInstance() is used to access the default project.
Any FirebaseApp initialization must occur only in the main process of the app. Use of Firebase in processes other than the main process is not supported and will likely cause problems related to resource contention.
谷歌翻译下:
Firebase SDK的切入点。它包含Firebase API的通用配置和状态。大多数应用程序不需要直接与FirebaseApp交互。
对于绝大多数应用,FirebaseInitProvider将通过应用的google-services.json文件中包含的数据,为其配置为使用的默认项目处理Firebase的初始化。默认情况下,使用Gradle构建时,此ContentProvider会合并到应用程序的清单中,并在应用程序启动时自动运行。在这种情况下,不需要额外的代码行。
如果应用程序除了默认项目之外还需要访问另一个Firebase项目,则必须使用initializeApp(Context,FirebaseOptions,String)以编程方式创建该关系。 name参数必须是唯一的。要连接到该项目公开的资源,请使用getInstance(String)返回的FirebaseApp对象,并将其与initializeApp使用的名称相同。必须将此对象传递给提供资源的功能的静态访问器。例如,getInstance(FirebaseApp)用于访问附加项目提供的存储桶,而getInstance()用于访问默认项目。
任何FirebaseApp初始化只能在应用程序的主进程中进行。不支持在主进程以外的进程中使用Firebase,这可能会导致与资源争用相关的问题。
从上面文档得出两个重要信息:
- Firebase默认通过FirebaseInitProvider实现App在启动时进行自动初始化。
- Firebase也可以通过FirebaseApp.initializeApp方法,以编程方式实现初始化,此时需要传入FirebaseOptions配置参数。
那我们可以用第2种方式解决初始化问题,看下FirebaseOptions类的文档信息如下:
由此,只要从google-services.json文件里找到上面的6个配置就能实现Firebase初始化了。
以下代码是读取Eclipse项目中assets/google-services.json文件的6个配置参数,并调用FirebaseApp.initializeApp方法初始化:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_test);
// 读取assets/google-services.json文件配置
Map<String, String> map = readJsonFile(this);
if (map == null)
return;
Log.v(TAG, "ApiKey " + map.get("ApiKey"));
Log.v(TAG, "ProjectId " + map.get("ProjectId"));
Log.v(TAG, "ApplicationId " + map.get("ApplicationId"));
Log.v(TAG, "DatabaseUrl " + map.get("DatabaseUrl"));
Log.v(TAG, "GcmSenderId " + map.get("GcmSenderId"));
Log.v(TAG, "ProjectId " + map.get("ProjectId"));
Log.v(TAG, "StorageBucket " + map.get("StorageBucket"));
// FirebaseApp初始化
if (FirebaseApp.getApps(this).isEmpty()) {
FirebaseOptions.Builder builder = new FirebaseOptions.Builder();
builder.setApiKey(map.get("ApiKey"));
builder.setApplicationId(map.get("ApplicationId"));
builder.setDatabaseUrl(map.get("DatabaseUrl"));
builder.setGcmSenderId(map.get("GcmSenderId"));
builder.setProjectId(map.get("ProjectId"));
builder.setStorageBucket(map.get("StorageBucket"));
FirebaseOptions options = builder.build();
FirebaseApp app = FirebaseApp.initializeApp(this, options);
Log.v(TAG, "FirebaseApp初始化 成功");
}
// 执行FirebaseAnalytics.getInstance(context)创建单例
if (FirebaseAnalytics.getInstance(this) != null) {
Log.v(TAG, "FirebaseAnalytics初始化 成功");
} else {
Log.v(TAG, "FirebaseAnalytics初始化 失败");
}
}
// 读取assets/google-services.json文件配置
public static Map<String, String> readJsonFile(Context context) {
String json = null;
try {
InputStream is = context.getResources().getAssets()
.open("google-services.json");
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte buffer[] = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1) {
outStream.write(buffer, 0, len);
}
outStream.flush();
json = new String(outStream.toByteArray(), "ISO-8859-1");
outStream.close();
is.close();
buffer = null;
} catch (Exception e) {
e.printStackTrace();
}
if(json == null)
return null;
return parse(json, context.getPackageName());
}
// 解析json
public static Map<String, String> parse(String jsonString, String packageName) {
if (TextUtils.isEmpty(jsonString) || TextUtils.isEmpty(packageName))
return null;
Map<String, String> map = new HashMap<String, String>();
JSONObject allObj;
try {
allObj = new JSONObject(jsonString);
JSONObject infoOjb = allObj.getJSONObject("project_info");
map.put("ProjectId", infoOjb.getString("project_id"));
map.put("DatabaseUrl", infoOjb.getString("firebase_url"));
map.put("GcmSenderId", infoOjb.getString("project_number"));
map.put("StorageBucket", infoOjb.getString("storage_bucket"));
JSONArray clientArray = allObj.getJSONArray("client");
if (clientArray == null || clientArray.length() <= 0)
return null;
JSONObject clientObj = null;
JSONObject clientInfoOjb = null;
for (int i = 0; i < clientArray.length(); i++) {
clientObj = clientArray.getJSONObject(i);
clientInfoOjb = clientObj.getJSONObject("client_info");
String pkg = clientInfoOjb.getJSONObject("android_client_info")
.optString("package_name");
if (packageName.equals(pkg)) {
map.put("ApplicationId",
clientInfoOjb.getString("mobilesdk_app_id"));
break;
}
}
if (clientObj == null)
return null;
JSONObject apiKeyObj = clientObj.getJSONArray("api_key")
.getJSONObject(0);
map.put("ApiKey", apiKeyObj.getString("current_key"));
JSONArray oauthClientArray = clientObj.getJSONArray("oauth_client");
if (oauthClientArray == null || oauthClientArray.length() <= 0)
return null;
for (int i = 0; i < oauthClientArray.length(); i++) {
JSONObject obj = oauthClientArray.getJSONObject(i);
if (obj.getInt("client_type") == 3) {
map.put("client_id", obj.getString("client_id"));// G+登录需要客户端ID
break;
}
}
} catch (JSONException e) {
e.printStackTrace();
}
return map;
}
注意:如果你在Firebase后台的同一个项目下配置了多个包名应用,下载的google-services.json文件也可能存在多个包名的配置,所以读取配置时要找到App包名下的配置。
其实还有另一种方法解决Firebase初始化问题:
在对比AS和Eclipse环境下生成的APK文件内部结构有什么不一样时,发现AS生成的APK在反编译后,google-services.json不见了,最后在res/strings.xml文件里找到了Firebase初始化的相关参数,截图如下:
原来AS环境下生成APK时,google services gradle插件对google-services.json文件做了处理,插件具体如何处理,可以参考下面链接,这里不赘述了。
The Google Services Gradle Plugin:https://developers.google.com/android/guides/google-services-plugin
所以,Eclipse环境下的工程可以在strings.xml文件配置以上几个参数也能实现Firebase自动初始化。
总结
1.首先在AS环境下找到Firebase相关的aar目录。
2.然后处理aar目录,将其下的jar包、res资源、AndroidManifest.xml配置等拷贝到Eclipse工程下,特别注意,res资源要迁移到Eclipse工程下新建的带aar包名的类库。
3.最后解决Firebase在Eclipse环境下读取google-services.json文件配置以实现初始化的问题。