一、了解
1、博客:有关Android 插件化的思考
学习链接:https://www.cnblogs.com/cr330326/p/7222489.html
主流框架(详细介绍见上方链接):
(1)DL 动态加载框架 ( 2014 年底)
(2)DroidPlugin ( 2015 年 8 月)
(3)Small ( 2015 年底)
(4)VirtualAPK (2017年 6 月)
(5)RePlugin (2017 年 7 月)
2、博客:Android插件化-RePlugin项目集成与使用
学习链接:https://www.cnblogs.com/codingblock/p/7766365.html
3、博客:【Android插件化】RePlugin集成配置
学习链接:https://www.jianshu.com/p/42d35abff2d4
二、尝试
1、使用:RePlugin
(1)Android 工程根目录中的build.gradle文件中:添加RePlugin Host Gradle依赖
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files // 1、添加RePlugin Host Gradle依赖 classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.1' } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
(2)Android 工程中app项目(主APP)中,build.gradle文件中:添加RePlugin Host Library依赖
apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "yc.bluetooth.testrepluginpro" minSdkVersion 18 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } // 集成 RePlugin 添加的配置 pply plugin: 'replugin-host-gradle' // 集成 RePlugin 添加的配置 repluginHostConfig { useAppCompat = true // 如果项目需要支持 AppComat,则需要将此配置置为 true // 如果应用需要个性化配置坑位数量,则需要添加以下代码进行配置 // countNotTranslucentStandard = 6 // countNotTranslucentSingleTop = 2 // countNotTranslucentSingleTask = 3 // countNotTranslucentSingleInstance = 2 } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' // 集成 RePlugin 添加的配置 implementation 'com.qihoo360.replugin:replugin-host-lib:2.3.2' }
(3)Android 工程中app项目(主APP)中,新建 MyApplication 直接继承自 RePluginApplication:
package yc.bluetooth.testrepluginpro; import com.qihoo360.replugin.RePlugin; import com.qihoo360.replugin.RePluginApplication; import com.qihoo360.replugin.RePluginConfig; /** * 注意:MyApplication要在AndroidManifest.xml中添加名字 */ public class MyApplication extends RePluginApplication { @Override public void onCreate() { super.onCreate(); //不会自动将“主程序签名”加入进来。如有需要,建议您自行加入。 // RePlugin.addCertSignature("379C790B7B726B51AC58E8FCBCFE4567"); } @Override protected RePluginConfig createConfig() { RePluginConfig c = new RePluginConfig(); //若为Debug环境下则无需校验签名,只有Release才会校验 //打开签名校验 c.setVerifySign(!BuildConfig.DEBUG); return c; } }
(4)Android 工程中app项目(主APP)中,在AndroidManifest.xml文件中 给Application标签添加name属性 :
(5)Android 工程中app项目(主APP)中,在AndroidManifest.xml文件中 声明访问存储空间的权限:
注意:Android 6.0以上需要动态申请权限
(6)Android 工程根目录中的build.gradle文件中:添加RePlugin Plugin Gradle依赖(插件工程用)
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files // 1、添加RePlugin Host Gradle依赖 classpath 'com.qihoo360.replugin:replugin-host-gradle:2.3.1' // 2、添加RePlugin Plugin Gradle依赖(插件工程用) classpath 'com.qihoo360.replugin:replugin-plugin-gradle:2.3.2' } } allprojects { repositories { google() jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
(7)Android 工程中插件项目(插件 APP)中,build.gradle文件中:添加RePlugin Plugin Library依赖
apply plugin: 'com.android.application' android { compileSdkVersion 28 defaultConfig { applicationId "yc.bluetooth.pluginapp1" minSdkVersion 18 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } } // 集成 RePlugin 添加的配置 apply plugin: 'replugin-plugin-gradle' dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' // 集成 RePlugin 添加的配置 implementation 'com.qihoo360.replugin:replugin-plugin-lib:2.3.2' }
(8)编译Sync now报错:
No signature of method: com.android.build.gradle.internal.scope.VariantScopeImpl.getMergeAssetsTask() is applicable for argument types: () values: []
解决:降低gradle plugin版本至3.1.4(由3.3.2降至3.1.4)
学习博客:
1、360插件化踩坑记录(一),RePlugin无法Sync成功,No signature of method: com.android.build.gradle.internal.scope
https://blog.csdn.net/MengJun1/article/details/88529208
2、RePlugin 初体验
https://www.jianshu.com/p/bc79ba98662f
(10)插件 APP界面和逻辑:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="这是pluginApp 的界面!!" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
MainActivity.java
package yc.bluetooth.pluginapp1; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }
(11)打包插件 APP生成apk文件: pluginapp1-release.apk
(12)主APP界面和逻辑:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/bt_start_plugin1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="打开插件1!" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> </android.support.constraint.ConstraintLayout>
MainActivity.java
package yc.bluetooth.testrepluginpro; import android.os.Environment; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.qihoo360.replugin.RePlugin; import com.qihoo360.replugin.model.PluginInfo; import com.qihoo360.replugin.utils.FileUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; public class MainActivity extends AppCompatActivity { private Button btStartPlugin1; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btStartPlugin1 = findViewById(R.id.bt_start_plugin1); btStartPlugin1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { simulateInstallExternalPlugin(); } }); } /** * 模拟安装或升级(覆盖安装)外置插件 * 注意:本demo将外置插件放置到/storage/emulated/0/2019yayplugin/xxx.apk目录下 * * todo 测试:重新安装项目后apk路径需要重新设定,重新将apk放到目新目录中 */ private void simulateInstallExternalPlugin() { //sd卡路径 //获取外部存储的路径返回绝对路径的,就是你的设备SD卡的文件路径 String pluginName = "pluginapp1-release.apk"; String externalPluginPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/2019yayplugin/" + pluginName; String internalPluginPath = getFilesDir() + "/" + pluginName; File externalPluginFile = new File(externalPluginPath); File internalPluginFile = new File(internalPluginPath); if (externalPluginFile.exists()) { if (internalPluginFile.exists()) { FileUtils.deleteQuietly(internalPluginFile); } //复制文件 更新操作 copyFile(externalPluginPath, internalPluginPath); PluginInfo info = RePlugin.install(externalPluginPath); if (info != null) { RePlugin.startActivity(this, RePlugin.createIntent(info.getName(), "yc.bluetooth.pluginapp1.MainActivity")); } else { Toast.makeText(this, "加载插件失败", Toast.LENGTH_SHORT).show(); } } else { if (internalPluginFile.exists()) { PluginInfo info = null; if (internalPluginFile.exists()) { info = RePlugin.install(internalPluginPath); } if (info != null) { RePlugin.startActivity(this, RePlugin.createIntent(info.getName(), "yc.bluetooth.pluginapp1.MainActivity")); } else { Toast.makeText(this, "加载插件失败", Toast.LENGTH_SHORT).show(); } } else { Toast.makeText(this, "没有插件", Toast.LENGTH_SHORT).show(); Log.d("Plugin1", "没有插件2"); } } } /** * 复制单个文件 * * @param oldPath String 原文件路径 * @param newPath String 复制后路径 * @return boolean */ public void copyFile(String oldPath, String newPath) { try { int bytesum = 0; int byteread = 0; File oldfile = new File(oldPath); if (oldfile.exists()) { //文件存在时 InputStream inStream = new FileInputStream(oldPath); //读入原文件 FileOutputStream fs = new FileOutputStream(newPath); byte[] buffer = new byte[1444]; while ((byteread = inStream.read(buffer)) != -1) { bytesum += byteread; //字节数 文件大小 System.out.println(bytesum); fs.write(buffer, 0, byteread); } inStream.close(); } } catch (Exception e) { System.out.println("复制单个文件操作出错"); e.printStackTrace(); } } }
(13)运行主APP,点击“打开插件1”,跳转插件APP的MainActivity界面。
至此,RePlugin的简单使用已初步实现,后续将进行更深入的学习。