开源框架不能随便使用,Replugin框架年久失修,参考实现学习实现原理可以,但是如果直接使用到项目中后期维护适配成本太大,直接使用请慎重。
官方链接:https://github.com/Qihoo360/RePlugin
Replugin官网提供了详细的使用教程和原理以及demo,demo里面有很多事例,可以参考学习。
自己跑replugin官方提供的demo,搞了半天也没跑起来,https://gitee.com/ 最后在gitee上搜索别人replugin demo找别人的demo最后跑起来了
一. 透明主题修改:
官方支持的透明主题数量有限,一部分自定义的透明主题无法设置为透明,因此导致项目中一部分原本透明的界面变得不透明:
/**
* 手动判断主题是否是透明主题
*/
public static boolean isTranslucentTheme(int theme) {
return theme == android.R.style.Theme_Translucent
|| theme == android.R.style.Theme_Dialog
|| theme == android.R.style.Theme_Translucent_NoTitleBar
|| theme == android.R.style.Theme_Translucent_NoTitleBar_Fullscreen;
}
replugin支持的透明主题如上,那么我自己的透明主题咋加上去呢,这里自己想了个解决办法,修改一下Replugin透明主题的判断规则:
public static boolean isTranslucentTheme(int theme) {
Resource resource = Replugin.fetchResources("换成你自己的插件名称");
if(resource!=null){
String themeName="";
try{
themeName=resource.getResourceEntryName(theme);
}catch(Exception e){
}
if(themeName!=null&&themeName.equals("你自己的主题名称")){
return true;
}
}
... // 原来的判断逻辑
}
这样的话我们就让自己的透明界面也变得透明。
二. notification的icon图标找不到
自己的工程线上跑的时候总是发现有一些notification用到的icon资源找不到,最后仔细查看官方demo里面的例子,官方的plugindemo中对于notification的资源id有着一句特别的注释,
因为发送通知的时候是直接去系统的context去寻找资源,所以这里如果直接传入插件的资源id的话会找不到资源。
notification.icon = 0x7f030002; // 注意此处是Host宿主的通知栏图标ID,需要宿主keep该资源ID
plugin官方demo使用的是下面的方式固定资源id:
public.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 通知栏图标 -->
<public id="0x7f0e006d" name="string2" type="string" />
</resources>
但是自己测试这个在gradle高版本已经不生效,网上说是gradle插件1.3.+之后不生效,
因此自己换了个固定资源id的方法如下:
Android Studio 3.0
开始,google
默认开启了 aapt2
作为资源编译的编译器,
选项 | 说明 |
---|---|
–emit-ids path | 在给定的路径下生成一个文件,该文件包含资源类型的名称及其 ID 映射的列表。它适合与 --stable-ids 搭配使用。 |
–stable-ids outputfilename.ext | 使用通过 --emit-ids 生成的文件,该文件包含资源类型的名称以及为其分配的 ID 的列表。此选项可以让已分配的 ID 保持稳定,即使您在链接时删除了资源或添加了新资源也是如此。 |
android {
aaptOptions {
File publicTxtFile = project.rootProject.file('public.txt')
//public文件存在,则应用,不存在则生成
if (publicTxtFile.exists()) {
project.logger.error "${publicTxtFile} exists, apply it."
//aapt2添加--stable-ids参数应用
aaptOptions.additionalParameters("--stable-ids", "${publicTxtFile}")
} else {
project.logger.error "${publicTxtFile} not exists, generate it."
//aapt2添加--emit-ids参数生成
aaptOptions.additionalParameters("--emit-ids", "${publicTxtFile}")
}
}
}
public.txt
里面对于的id
改为自己想要固定的id
;--stable-ids
和根目录下的public.txt
进行资源id
的固定;参考链接:https://zhuanlan.zhihu.com/p/391082157
三.概率性的一部分资源res找不到
使用replugin框架的时候,会有很多奇怪的资源找不到问题,解决办法和上面固定资源id的方式一样,只不过会将找不到的资源copy到host中,同时将上面固定资源id的代码同时拷贝到host和plugin中,确保host和plugin中的这部分资源id一致,这样plugin中找不到的话我们可以从host中找到。
当然还有其他一些莫名其妙的java崩溃崩溃,native崩溃。。
public class PluginDexClassLoader extends DexClassLoader {
Replugin的插件类加载器就继承自DexClassLoader。
资源获取:
// PluginActivity中会对context进行替换,
private PluginResource pluginResource;
@Override
protected void attachBaseContext(Context newBase) {
newBase = RePluginInternal.createActivityContext(this, newBase);
pluginResource = new PluginResource(newBase);
super.attachBaseContext(newBase);
}
所以在plugin的activity和一些基础的组件中获取资源的时候调用的是plugin自己管理的资源类去获取资源。
host中获取插件资源:
/**
* 获取插件的Resources对象(触发插件的加载)
*/
Resources resources = RePlugin.fetchResources("plugin2");
/**
* 通过resource的getIdentifier()接口获取对应资源的id(参数参考上面的定义)
*/
final int id = resources.getIdentifier("test_plugin2_img", "drawable",
"com.test.android.plugin2");
if (id != 0) {
/**
* 通过id去读取真正的资源文件
*/
final Drawable drawable = resources.getDrawable(id);
if (drawable != null) {
mPluginImageView.setImageDrawable(drawable);
}
}
host中获取插件资源要通过getIdentifier方法。
插件中做的事情:
replugin-plugin-gradle
plugin插件中定义了很多注入器,会对插件中的代码进行统一修改,(这代码写的挺厉害的)
public enum Injectors {
//
LOADER_ACTIVITY_CHECK_INJECTOR('LoaderActivityInjector', new LoaderActivityInjector(), '替换 Activity 为 LoaderActivity'),
LOCAL_BROADCAST_INJECTOR('LocalBroadcastInjector', new LocalBroadcastInjector(), '替换 LocalBroadcast 调用'),
PROVIDER_INJECTOR('ProviderInjector', new ProviderInjector(), '替换 Provider 调用'),
PROVIDER_INJECTOR2('ProviderInjector2', new ProviderInjector2(), '替换 ContentProviderClient 调用'),
GET_IDENTIFIER_INJECTOR('GetIdentifierInjector', new GetIdentifierInjector(), '替换 Resource.getIdentifier 调用'),
COMMON_INJECTOR('CommonInjector', new CommonInjector(), '常规注入操作'),
APPLICATION_INJECTOR('ApplicationInjector', new ApplicationInjector(), 'Application注入操作')
...
}
replugin-host-gradle
hostgradle会生成带坑位的manifest,同时生成RepluginHostConfig类,方便读取配置。
Replugin涉及到的知识点很多,可以参考学习:
参考:https://github.com/Qihoo360/RePlugin/wiki/%E9%AB%98%E7%BA%A7%E8%AF%9D%E9%A2%98