插件框架Virtualapk使用要点记录

一、先记录一下使用过程中的要点:

1.宿主APP需要先运行一遍(编译APK或直接运行到手机),才能编译插件

2.宿主和插件的类名,资源名命名不要一样

3.插件apk如果放在SD卡,记得申请权限

4.插件打包必须签名

5.插件清单文件中有icon属性,插件中有application,不影响使用

6.插件的清单文件中可以带权限,宿主申请权限时同时要申请所有插件的权限

二、配置如下:

1.宿主

宿主工程根目录的build.gradle

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath 'com.didi.virtualapk:gradle:0.9.8.6'  // 这个是需要加的

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

宿主app的build.gradle如下:

apply plugin: 'com.android.application'
apply plugin: 'com.didi.virtualapk.host' // 这个是主项目中添加的

android {
    compileSdkVersion 27

    defaultConfig {
        applicationId "com.example.lhy.mainproject"
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    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'

    implementation 'com.didi.virtualapk:core:0.9.8'
}

宿主继承Application的代码

public class BaseApplication extends Application {
    @Override
    protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
        PluginManager.getInstance(base).init();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        PluginManager pluginManager = PluginManager.getInstance(this);
        //此处是当查看插件apk是否存在,如果存在就去加载(比如修改线上的bug,把插件apk下载到sdcard的根目录下取名为Demo.apk)
        File apk = new File(Environment.getExternalStorageDirectory(), "plugin1.apk");
        if (apk.exists()) {
            try {
                pluginManager.loadPlugin(apk);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

宿主主页(只是为了测试使用,要根据实际需要修改)

public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    private Button toPlugin1Btn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toPlugin1Btn = findViewById(R.id.main_btn1);
        toPlugin1Btn.setOnClickListener(this);
        // 权限检查
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
            return;
        }else {
            File apk = new File(Environment.getExternalStorageDirectory(), "plugin1.apk");
            PluginManager pluginManager = PluginManager.getInstance(this);
            if (apk.exists()) {
                try {
                    pluginManager.loadPlugin(apk);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.main_btn1:
                if (PluginManager.getInstance(MainActivity.this).getLoadedPlugin("com.example.lhy.pluginproject1") == null) {
                    Toast.makeText(MainActivity.this, "插件未加载", Toast.LENGTH_SHORT).show();
                } else {
                    Intent intent = new Intent();
                    intent.setClassName("com.example.lhy.pluginproject1", "com.example.lhy.pluginproject1.Plugin1MainActivity");
                    startActivity(intent);
                }
                break;
            default:
                break;
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        switch (requestCode){
            case 1:
                if(grantResults.length >0 && grantResults[0]  == PackageManager.PERMISSION_GRANTED) {
                    File apk = new File(Environment.getExternalStorageDirectory(), "plugin1.apk");
                    PluginManager pluginManager = PluginManager.getInstance(this);
                    if (apk.exists()) {
                        try {
                            pluginManager.loadPlugin(apk);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    } else {
                        Toast.makeText(MainActivity.this, "插件不存在", Toast.LENGTH_SHORT).show();
                    }
                }
                break;
            default:
                break;
        }
    }
}

2.插件工程:

根目录build.gradle

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.0.1'
        classpath 'com.didi.virtualapk:gradle:0.9.8.6'

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

app的build.gradle

apply plugin: 'com.android.application'
apply plugin: 'com.didi.virtualapk.plugin'//注意这个是plugin结尾,宿主是以host结尾的

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "com.example.lhy.pluginproject1"
        minSdkVersion 15
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    signingConfigs{
        release{
            storeFile file('../PluginProject1.jks')
            storePassword "android"
            keyAlias "pluginProdect1"
            keyPassword "android"
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            signingConfig signingConfigs.release
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    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'
}

virtualApk {
    // 插件资源表中的packageId,需要确保不同插件有不同的packageId.
    packageId = 0x06

    // 宿主工程application模块的路径,插件的构建需要依赖这个路径,我这个宿主工程和插件工程在同一级目录下,所以下面这样写
    targetHost = '../MainProject/app'

    //默认为true,如果插件有引用宿主的类,那么这个选项可以使得插件和宿主保持混淆一致
    applyHostMapping = true
}

宿主编译一遍之后再编译插件

也可以使用命令编译,gradlew clean assemblePlugin

发布了12 篇原创文章 · 获赞 4 · 访问量 9599

猜你喜欢

转载自blog.csdn.net/lhy24680/article/details/102458192