类似 Didi doraemonkit
和 LeakCanary 1.X
版本的三方库, 提供了 release-no-operation 的依赖, 里面大部分是空实现, 来实现debug/release分离的场景.
debugImplementation 'com.didichuxing.doraemonkit:doraemonkit:3.1.5'
releaseImplementation 'com.didichuxing.doraemonkit:doraemonkit-no-op:3.1.5'
对于bugly这样的三方库是没有提供 release-no-operation 的, 例如我们只需要在debug下接入bugly, 第一步在主项目中 debugImplementation bugly
, 然后在代码中初始化. 在Release下会出现编译报错.
- Debug
- Release
解决办法: 可以利用反射来避免显示的调用API, 例如
if(BuildConfig.DEDUG){
try {
Class<?> clazz = Class.forName("com.didichuxing.doraemonkit.DoraemonKit");
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Object instance = cons.newInstance();
Method method = clazz.getMethod("install", Application.class);
method.invoke(instance, context);
} catch (Exception e) {
e.printStackTrace();
System.out.println("init didi doraemon failed");
}
}
这下不会编译报错了, 有没有更好的办法?
参考LeakCanary2的实现, 我们可以把这部分逻辑逻辑下沉到我们自定义的debugLib中的ContentProvider里, 然后主项目使用 debugImplementation debugLib
依赖这个lib来实现debug/release分离.
步骤
-
定义ContentProvider
public class DebugProvider extends ContentProvider{ @Override public boolean onCreate() { if(getContext()!=null && getContext().getApplicationContext()!=null){ Application app = (Application) getContext().getApplicationContext(); initDidi(app); } return true; } /** * 初始化doraemonkit */ private void initDidi(Application context) { DoraemonKit.install(context); } }
-
lib的xml配置
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.intsig.lib4debug" > <application> <provider android:name=".DebugProvider" android:exported="false" android:authorities="${applicationId}.DebugProvider" /> </application> </manifest>
lib.gradle配置maven地址,版本, 点击或者运行gradleTask: uploadArchives
-
在工程项目中引入maven上的aar, 这里使用 debugImplementation仅在debug下依赖
debugImplementation 'com.intsig.camscanner:debugonlylib:1.0.6'
过程
Manifest merge
在打包构建的时候, gradle会把项目中的多个manifest合并成唯一的manifest, 包括我们的 debuglib.manifest
- 主工程的manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.intsig.debugonly">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name=".App"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:usesCleartextTraffic="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
- debugLib的manifest
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.intsig.lib4debug" >
<application>
<provider
android:name=".DebugOnlyProvider"
android:exported="false"
android:authorities="${applicationId}.DebugOnlyProvider" />
</application>
</manifest>
- 打包后Analyze APK查看合并后的结果.
加载时机
ActivityThread # handleBindApplication() 方法
构造Application(类加载器+反射)
-
ContentProvider.onCreate()
初始化因为这一步在
App.onCreate()
之前, 是没法拿到App.onCreate()
中才初始化的变量的. Application.onCreate()
最后在我们的工程下通过 debugImplementation 'com.intsig.camscanner:debugonlylib:1.1.0'
的方式引用, 因为SDK init的逻辑在 ContentProvider.onCreate()
中, 而在release环境下不会引入这个ContentProvider, 所以在切换到release时也不会报错.