【Android】插件化開發的學習一:RePlugin簡單使用

一、瞭解

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的簡單使用已初步實現,後續將進行更深入的學習。 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章