当前位置: 首页 > 工具软件 > RePlugin > 使用案例 >

Replugin实践记录

范云
2023-12-01

Replugin实践记录分享:

开源框架不能随便使用,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}")
        }
    }
}
  1. 首次编译,会生成public.txt,里面会有所有资源的名称和id的对应关系
  2. 再将public.txt里面对于的id改为自己想要固定的id;
  3. clean下项目,然后再次编译,通过--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崩溃。。

自己简单记录一些插件化知识
  1. ​ Android中的ClassLoader:PathClassLoader用于加载已经安装的apk文件,DexClassLoader用来加载未安装的dex/jar文件,而后者是Replugin框架的基础,
public class PluginDexClassLoader extends DexClassLoader {

Replugin的插件类加载器就继承自DexClassLoader。

  1. 资源获取:

    		// 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方法。

  2. 插件中做的事情:

    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

 类似资料: