1 RePlugin 介绍

RePlugin是一套完整的、稳定的、适合全面使用的,占坑类插件化方案,由360手机卫士的RePlugin Team研发,也是业内首个提出”全面插件化“(全面特性、全面兼容、全面使用)的方案。

其主要优势有:

极其灵活:主程序无需升级(无需在Manifest中预埋组件),即可支持新增的四大组件,甚至全新的插件
**非常稳定:**Hook点仅有一处(ClassLoader),无任何Binder Hook!如此可做到其崩溃率仅为“万分之一”,并完美兼容市面上近乎所有的Android ROM
特性丰富:支持近乎所有在“单品”开发时的特性。包括静态Receiver、Task-Affinity坑位、自定义Theme、进程坑位、AppCompat、DataBinding等
易于集成:无论插件还是主程序,只需“数行”就能完成接入
管理成熟:拥有成熟稳定的“插件管理方案”,支持插件安装、升级、卸载、版本管理,甚至包括进程通讯、协议版本、安全校验等
数亿支撑:有360手机卫士庞大的数亿用户做支撑,三年多的残酷验证,确保App用到的方案是最稳定、最适合使用的

以上是官方的介绍,github地址如下:
https://github.com/Qihoo360/RePlugin
总之,根据网上的反馈以及14日成都参加thoughtworks 的replugin分享活动来看,Replugin将会是一个比较有潜力和完善的差价化框架。今天就来根据官方的wiki自己来接入到程序中。

Replugin的接入个人觉得分为三部分:
宿主的接入:这个主要是决定你要将那么module作为你的宿主,用于加载插件
插架的接入:这里可以新建一个module作为插件,也可以将现有的module(APK)改造为插件
插架化的使用:主要讲宿主和插件之间组件的通信,以及宿主和插件的调用等。

本篇博文不讲插件化的原理,只讲最基础的如何接入Replugin。想要了解原理的可以阅读官方的源代码及参考以下的几篇博文:
Replugin 全面解析 (1) http://www.jianshu.com/p/5994c2db1557
Replugin 全面解析 (2) http://www.jianshu.com/p/74a70dd6adc9
Replugin 全面解析 (3) http://www.jianshu.com/p/8465585b3507
Replugin 全面解析 (4) http://www.jianshu.com/p/f456f608aa92
Replugin 全面解析 (5) http://www.jianshu.com/p/fb9d40f4173c

2 宿主接入步骤

根据官方的文档,如下宿主的接入分为三个步骤:
https://github.com/Qihoo360/RePlugin/wiki/%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8B

第 1 步:添加 RePlugin Host Gradle 依赖
这一步没什么好说的。
在项目根目录的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-host-gradle 依赖:

buildscript {dependencies {classpath 'com.qihoo360.replugin:replugin-host-gradle:2.2.1'...}
}

第 2 步:添加 RePlugin Host Library依赖
在 app/build.gradle 中应用 replugin-host-gradle 插件,并添加 replugin-host-lib 依赖:

// ATTENTION!!! Must be PLACED AFTER "android{}" to read the applicationId
apply plugin: 'replugin-host-gradle'/*** 配置项均为可选配置,默认无需添加* 更多可选配置项参见replugin-host-gradle的RepluginConfig类* 可更改配置项参见 自动生成RePluginHostConfig.java*/
repluginHostConfig {/*** 是否使用 AppCompat 库* 不需要个性化配置时,无需添加*/useAppCompat = true/*** 背景不透明的坑的数量* 不需要个性化配置时,无需添加*/countNotTranslucentStandard = 6countNotTranslucentSingleTop = 2countNotTranslucentSingleTask = 3countNotTranslucentSingleInstance = 2
}dependencies {compile 'com.qihoo360.replugin:replugin-host-lib:2.2.1'...
}

需要注意的是apply plugin: ‘replugin-host-gradle’ 这一行需要放在 android{}之后。
另外一个需要注意版本的问题,目前最新的版本是2.2.1 如下,要不然下载不下来。

然后点击gradle同步,同步完成之后就可以进行下一步了

第 3 步:配置 Application 类
根据官方文档的描述可以继承RePluginApplication,也可以采用非继承式来初始化,由于我一般不太愿意采用继承式,所以就采用非继承式吧。这里我的配置如下:

/*** Email: 1273482124@qq.com* Created by qiyei2015 on 2017/5/8.* Version: 1.0* Description:*/
public class BaseApplication extends Application {@Overrideprotected void attachBaseContext(Context base) {super.attachBaseContext(base);RePlugin.App.attachBaseContext(this);}@Overridepublic void onCreate() {super.onCreate();RePlugin.App.onCreate();try {SDKManager.initSDK(this);} catch (Exception e) {e.printStackTrace();}//初始化皮肤管理器SkinManager.getInstance().init(this);}@Overridepublic void onLowMemory() {super.onLowMemory();/* Not need to be called if your application's minSdkVersion > = 14 */RePlugin.App.onLowMemory();}@Overridepublic void onTrimMemory(int level) {super.onTrimMemory(level);/* Not need to be called if your application's minSdkVersion > = 14 */RePlugin.App.onTrimMemory(level);}@Overridepublic void onConfigurationChanged(Configuration config) {super.onConfigurationChanged(config);/* Not need to be called if your application's minSdkVersion > = 14 */RePlugin.App.onConfigurationChanged(config);}}

多余的代码暂时不用管,这里注意事项就参照官方的来吧。

另外,如果你的宿主配置过程中有什么错误,建议先下载官方的demo进行运行,然后参考官方的demo进行配置。

3 插件接入步骤

插件部分的接入也可以按照官方的教程来。插件的接入其实很简单,不管是新开发的还是将现有的项目改造成插件化。都一般有以下几步

第 1 步:添加 RePlugin Plugin Gradle 依赖
在项目根目录的 build.gradle(注意:不是 app/build.gradle) 中添加 replugin-plugin-gradle 依赖:

buildscript {dependencies {classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.1'...}
}

这里我想说以下的是,如果是单独的一个工程肯定是没问题的,但是大家看我的工程目录:

EssayJokeApp 与appdemo都是Application类型的module,因此我刚刚在主工程已经添加了RePlugin Host Gradle的依赖,这里我就直接在该工程下添加RePlugin Plugin Gradle 即可。

buildscript {repositories {jcenter()}dependencies {classpath 'com.android.tools.build:gradle:2.3.2'classpath 'com.qihoo360.replugin:replugin-host-gradle:2.2.1'classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.2.1'// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}
}

第 2 步:添加 RePlugin Plugin Library 依赖
在 app/build.gradle 中应用 replugin-plugin-gradle 插件,并添加 replugin-plugin-lib 依赖:

apply plugin: 'replugin-plugin-gradle'dependencies {compile 'com.qihoo360.replugin:replugin-plugin-lib:2.2.1'...
}

由于这里我想将appdemo module作为一个插架,因此配置如下:

apply plugin: 'com.android.application'android {compileSdkVersion 25buildToolsVersion "26.0.0"defaultConfig {applicationId "com.qiyei.appdemo"minSdkVersion 16targetSdkVersion 25versionCode 1versionName "1.0"testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}sourceSets {main {jni.srcDirs = ['src/main/jni', 'src/main/cpp/']jniLibs.srcDirs = ['libs']  //配置生成jniLibs}}aaptOptions {cruncherEnabled = falseuseNewCruncher = false}repositories {// 本地的libs目录flatDir {dirs 'libs' //aar目录}}//解决多语言未翻译问题lintOptions {checkReleaseBuilds falseabortOnError false}
}// 这个plugin需要放在android配置之后,因为需要读取android中的配置项
apply plugin: 'replugin-plugin-gradle'
/**** plugin配置*/
repluginPluginConfig {pluginName = "appdemo"hostApplicationId = "com.qiyei.essayjoke"hostAppLauncherActivity = "com.qiyei.essayjoke.activity.WelcomeActivity"
}dependencies {compile fileTree(include: ['*.jar'], dir: 'libs')compile(name: 'hotfix_core-release-3.1.0', ext: 'aar')androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {exclude group: 'com.android.support', module: 'support-annotations'})compile 'com.android.support:appcompat-v7:25.3.1'compile 'com.qihoo360.replugin:replugin-plugin-lib:2.2.1'testCompile 'junit:junit:4.12'compile project(':Framework')compile files('libs/alicloud-android-utils-1.0.3.jar')compile files('libs/utdid4all-1.1.5.3_proguard.jar')
}

其中repluginPluginConfig是我参考官方demo加上去的。

官方到这里就完了,但是我强烈建议你把下面一步也做了。

第3步 配置插件的别名
在插件的AndroidManifest.xml文件中配置插件的别名,方便后面的使用

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.qiyei.appdemo">......<application
        android:name=".BaseApplication"android:allowBackup="true"android:icon="@mipmap/icon"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><!--声明其插件别名--><meta-data
            android:name="com.qihoo360.plugin.name"android:value="appdemo" />......</application></manifest>

android:value字段的值就是插件的别名。

这样,插件部分的接入基本完成了。下面有一步我觉得可以做一下。

4 测试插件的正确性
将接入后的插架编译成单独的apk,测试其功能是否正常,如果不正常则需要排查原因,做这一步是表明插件在安装前是正常的,防止因为插件本身的问题导致的错误。

4 使用插架

宿主接入了,插件也接入了,并且单独运行也没问题了,接下来我就看如何使用了。关于插架的使用可以参考官方文档。不过我这里要介绍一下我的一些简单使用。

1 插件的安装
Replugin的插件分为内部插件,外部插件。简单的说在编译前放入assets\plugins 下的xxx.jar就是内部插件,其他在运行期从网上下载的或者放入到其他位置,例如SD卡上的xxx.apk都是外部插件。内部插件与外部插件有以下几大不同
1 内部插件是xxx.jar(编译出apk以后,直接将文件改成xxx.jar)目录是assets\plugins,外置插件就是xxx.apk形式(内置插件进行升级之后也变为外置插件了)
2 内部插件不需要安装,直接调用。外部插件需要安装
3 内部插件默认的别名是 文件名 ,外部插件如果没有在manifest.xml中声明就是默认的包名
所以这也是为什么我刚刚让manifest.xml进行声明的原因了。
因此,对于内部插件,直接在编译前放入assets\plugins即可。后续可直接使用
对于外部插件,使用前需要调用RePlugin.install(fileName);进行显式的安装,安装代码如下:

            String pluginApk = "appdemo.apk";String fileName = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + pluginApk;File pluginFile = new File(fileName);//文件不存在就返回if (!pluginFile.exists()){return;}PluginInfo info = null;if (pluginFile.exists()) {info = RePlugin.install(fileName);}

我这里是直接将apk放在了sd卡根目录的,也可以从网上下载下来进行安装的。

2 宿主调用插件的组件
这里以启动一个插件的Activity为例进行介绍。
先来看我的插件中声明的Activity

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.qiyei.appdemo"><uses-permission android:name="android.permission.INTERNET"/><uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/><uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/><uses-permission android:name="android.permission.VIBRATE"/><application
        android:name=".BaseApplication"android:allowBackup="true"android:icon="@mipmap/icon"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><!--声明其插件别名--><meta-data
            android:name="com.qihoo360.plugin.name"android:value="appdemo" /><meta-data
            android:name="com.taobao.android.hotfix.IDSECRET"android:value="Yours" /><meta-data
            android:name="com.taobao.android.hotfix.APPSECRET"android:value="Yours" /><meta-data
            android:name="com.taobao.android.hotfix.RSASECRET"android:value="Yours" /><activity android:name=".activity.MainActivity"><intent-filter><action android:name="android.intent.action.MAIN"/><category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity><activity android:name=".activity.ColorTrackTextViewActivity"></activity><activity android:name=".activity.ViewPagerTestActivity"></activity><activity android:name=".activity.RecyclerViewTestActivity"></activity><activity android:name=".activity.EasyJokeMainActivity"></activity><activity android:name=".activity.SkinTestActivity"></activity><activity android:name=".activity.BannerTestActivity"></activity><activity android:name=".activity.ImageSelectedTestActivity"></activity><activity android:name=".activity.TestActivity"></activity><service
            android:name=".service.LocalService"android:enabled="true"android:exported="false"></service><service
            android:name=".service.RemoteService"android:enabled="true"android:exported="false"android:process=":remote"></service><service
            android:name=".service.JobWakeUpService"android:enabled="true"android:exported="false"android:permission="android.permission.BIND_JOB_SERVICE"></service><service
            android:name=".service.TestService"android:enabled="true"android:exported="false"></service><activity android:name=".activity.DataCenterTestActivity"></activity><activity android:name=".activity.BinderTestActivity"></activity></application></manifest>

这里我在宿主中去调用插件中的com.qiyei.appdemo.activity.MainActivity。代码如下:

/*** Email: 1273482124@qq.com* Created by qiyei2015 on 2017/6/24.* Version: 1.0* Description: app的主界面*/
public class HomeActivity extends BaseSkinActivity {@ViewById(R.id.main_tab_layout)private FrameLayout mMainTabLayout;@ViewById(R.id.tab_list)private RadioGroup mTabList;@ViewById(R.id.home_rb)private RadioButton mHomeButton;@ViewById(R.id.find_rb)private RadioButton mFindButton;@ViewById(R.id.new_rb)private RadioButton mNewButton;@ViewById(R.id.message_rb)private RadioButton mMessageButton;private HomeFragment mHomeFragment;private FindFragment mFindFragment;private NewFragment mNewFragment;private MessageFragment mMessageFragment;private FragmentHelper mFragmentHelper;private CommonTitleBar navigationBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initData();initView();}@Overrideprotected void initContentView() {setContentView(R.layout.activity_home);}@Overrideprotected void initData() {mContext = this;mFragmentHelper = new FragmentHelper(getSupportFragmentManager(),R.id.main_tab_layout);if (!RePlugin.isPluginInstalled("appdemo")){String pluginApk = "appdemo.apk";String fileName = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + pluginApk;File pluginFile = new File(fileName);//文件不存在就返回if (!pluginFile.exists()){return;}PluginInfo info = null;if (pluginFile.exists()) {info = RePlugin.install(fileName);}if (info != null){//预先加载RePlugin.preload(info);ToastUtil.showLongToast("安装 appdemo 成功 " + info.getName());}}}@Overrideprotected void initView() {navigationBar = new CommonTitleBar.Builder(this).setTitle(getString(R.string.home_page)).setRightText(getString(R.string.test)).setRightClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {RePlugin.startActivity(mContext, RePlugin.createIntent("appdemo","com.qiyei.appdemo.activity.MainActivity"));}}).build();SystemStatusBarUtil.statusBarTintColor(this,getResources().getColor(R.color.title_bar_bg_day));mHomeButton.setOnClickListener(this);mFindButton.setOnClickListener(this);mNewButton.setOnClickListener(this);mMessageButton.setOnClickListener(this);mHomeFragment = new HomeFragment();mFragmentHelper.add(mHomeFragment);mHomeButton.setChecked(true);}@Overridepublic void onClick(View v) {switch (v.getId()){case R.id.home_rb:if (mHomeFragment == null){mHomeFragment = new HomeFragment();}navigationBar.setTitle(getString(R.string.home_page));mFragmentHelper.switchFragment(mHomeFragment);break;case R.id.find_rb:if (mFindFragment == null){mFindFragment = new FindFragment();}navigationBar.setTitle(getString(R.string.find));mFragmentHelper.switchFragment(mFindFragment);break;case R.id.new_rb:if (mNewFragment == null){mNewFragment = new NewFragment();}navigationBar.setTitle(getString(R.string.fresh));mFragmentHelper.switchFragment(mNewFragment);break;case R.id.message_rb:if (mMessageFragment == null){mMessageFragment = new MessageFragment();}navigationBar.setTitle(getString(R.string.message));mFragmentHelper.switchFragment(mMessageFragment);break;default:break;}}/*** 从assets目录中复制某文件内容*  @param  fileName assets目录下的Apk源文件路径*  @param  newFileName 复制到/data/data/package_name/files/目录下文件名*/private void copyAssetsFileToAppFiles(String fileName, String newFileName) {InputStream is = null;FileOutputStream fos = null;int buffsize = 1024;try {is = new FileInputStream(fileName);fos = this.openFileOutput(newFileName, Context.MODE_PRIVATE);int byteCount = 0;byte[] buffer = new byte[buffsize];while((byteCount = is.read(buffer)) != -1) {fos.write(buffer, 0, byteCount);}fos.flush();} catch (Exception e) {e.printStackTrace();} finally {try {is.close();fos.close();} catch (Exception e) {e.printStackTrace();}}}
}

这里我做了插件的预加载,提供了首次的运行速度。另外其他代码忽略,只看下面即可。

RePlugin.startActivity(mContext, RePlugin.createIntent("appdemo","com.qiyei.appdemo.activity.MainActivity")); 

这里的appdemo就是我指定的别名,要是是外置插件,不指定别名,就只能去按照包名“com.qiyei.appdemo”来调用了。

实测效果如下:

录制的效果不太好,有白屏的现象出现,真实的情况是没有的。另外我的机器是nexus 6p 系统是Android8.0 目测支持的还不错,没有什么大问题。

由于我的插件中集成了阿里的Sophix热修复框架,可能有一些bug,不过暂时不用管。

综合来看,Replugin还是挺有意思的,继续深入理解原理吧。
参考代码:github https://github.com/qiyei2015/EssayJoke dev分支

Android 360开源全面插件化框架RePlugin 实战相关推荐

  1. Android组件化与插件化开发项目实战整理分享(含支付宝、360、美团、滴滴等大厂项目实战)

    小公司不说,但是在大公司的项目发展到一定程度,就必须进行模块的拆分.模块化是一种指导理念,其核心思想就是分而治之.降低耦合.而在 Android 开发的实践,目前有两种途径来实现,一个是组件化,一个是 ...

  2. android开发模式之插件化开发

    一.简介 插件化开发是将整个app拆分成很多模块,这些模块包括一个宿主和多个插件,每个模块都是一个apk(组件化的每个模块是个lib),最终打包的时候将宿主apk和插件apk分开或者联合打包.由宿主A ...

  3. 360手机卫士插件化RePlugin今日开源,官方全面解读

    作者:张炅轩,360手机卫士·客户端技术专家 写在前面 "RePlugin将在6月底开源,这将是我们献给安卓世界最好的礼物."当我们宣布这一消息时,心中的激动,无以言表.是的,三年 ...

  4. 滴滴开源Android插件化框架VirtualAPK原理分析

    概述 滴滴出行公司的首个对外开源项目 - VirtualAPK.地址:github.com/didi/Virtua- 滴滴自行研发了这款插件化框架,功能全面.兼容性好,还能够适用于有耦合的业务插件,这 ...

  5. [Android]用架构师角度看插件化(2)-Replugin 唯一hook点

    Replugin,为何我选择要研究这个的插件呢?很大的原因是因为它的介绍中说明,他只会有一个hook点. 一.Hook hook点是什么? 我们入门Android的时候,一定会看到过这个图,但是你确定 ...

  6. android中的插件开发框架,设计并开发一个 Android 的插件化框架

    结合动态加载系列文章的分析,现在开始设计并开发一个 Android 的插件化框架,命名为 Frontia.Frontia 有 "前端" 的意思,寓意着 Android 插件能像前端 ...

  7. 58同城Android端-最小插件化框架实战和原理分析

    目录 背景 插件化需要了解的知识 2.1 类加载过程和类加载器 2.2 ClassLoader 的 findClass.findLibrary.findResource 2.3 DexClassLoa ...

  8. Shadow插件化框架设计——replugin原理(架构师进阶之旅)

    DroidPlugin原理解析 从系统设计的角度,组件和窗口的逻辑实体都存在于系统服务,比如Activity创建后,其逻辑控制主体在AMS,对于窗口,其逻辑控制主体在WMS android将逻辑主体放 ...

  9. Android 插件化框架DroidPlugin

    上一次项目迭代中,接触到了插件化框架. 使用场景:我们的app需要集成某一直播app.即在不安装第三方直播app到手机的情况下,点击我们app内部的某一连接能跳转到直播app中,运行里面原有的所有功能 ...

  10. 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★

    Android 插件化系列文章目录 [Android 插件化]插件化简介 ( 组件化与插件化 ) [Android 插件化]插件化原理 ( JVM 内存数据 | 类加载流程 ) [Android 插件 ...

最新文章

  1. 删除条目时的确认对话框
  2. 索尼爱立信M608C使用心得!
  3. OpenGL绘制Triangle三角形
  4. php 调取子栏目,Dedecms 如何调取某个栏目所在的顶级栏目及顶级下的子栏目
  5. [编程入门]猴子吃桃的问题
  6. 华为鸿蒙麒麟玉兔_华为P50除了麒麟9000,还预装鸿蒙系统,比iPhone12值得买
  7. Windows Internet Explorer 8 简体中文正式版发布!
  8. 精述字符编码(读这篇就够了)
  9. linux嵌入式工作室,【课程培训】嵌入式linux驱动开发系列教程 | linux2web工作室...
  10. c ++ 继承_了解C ++中的继承概念
  11. C语言中的宏之#define
  12. pythonos文件目录方法_python12-OS模块(文件/目录方法)
  13. win10下使用mklink命令给C盘软件搬家
  14. 职业规划范文500字计算机专业,技校计算机专业职业生涯规划500字左右
  15. 《工业设计史》 绪论
  16. PCBA可靠性测试有哪些?
  17. 计算机玩游戏特别卡,Win7电脑游戏卡顿怎么办 win7玩游戏卡如何解决
  18. 论剑大数据技术,效率为王!天善智能掘金数据技术沙龙【上海站 12.09】
  19. 数据分析项目实战——Airbnb数据分析
  20. 如何使html中的图片居中

热门文章

  1. 终极版Python打包exe文件,并修改图标,这将是你见过最详细的教程~
  2. Bladex Workflow工作流引擎开发进阶-版本v1.2.2
  3. 平板做笔记本电脑的副屏教程
  4. GitHub 用户专属福利,实际到账 3K+,Namebase Airdrop
  5. arn-linux-gcc编译失败,arm-linux-gcc 编译ARM裸机汇编程序失败,解决方法
  6. ICASPP2022论文阅读记录2 - Transformer-S2A
  7. Approaching ANXIETY DISORDER
  8. Nginx搭建虚拟主机环境
  9. centos7+ 安装RabbitMQ
  10. 再更新:2022 京东/淘宝双11活动一键自动完成任务脚本app来了,顺便说个事情...