滴滴插件化工具VirtualAPK的使用

简介

Android 插件化技术是比较热门领域,VirtualAPK 是滴滴2017年6月3号开源,框架功能完备,支持 Android 四大组件,良好的兼容性,且入侵性较低,作为加载耦合插件方案是较好选择。

环境准备

Gradle版本号为2.14.1,可以在gradle/wrapper/gradle-wrapper.properties中更改版本号:
distributionUrl=https://services.gradle.org/distributions/gradle-2.14.1-all.zip
com.android.tools.build的版本号为2.1.3
这里写图片描述
这里写图片描述

宿主工程接入

  1. 在宿主工程根目录的build.gradle添加依赖
dependencies {
    classpath 'com.didi.virtualapk:gradle:0.9.0'
}
  1. 在App的工程模块的build.gradle添加使用gradle插件
apply plugin: 'com.didi.virtualapk.host'

3.添加VirtualAPK SDK compile依赖

dependencies {
    compile 'com.didi.virtualapk:core:0.9.0'
}

4.在App的工程模块proguard-rules.pro文件添加混淆规则

-keep class com.didi.virtualapk.internal.VAInstrumentation { *; }
-keep class com.didi.virtualapk.internal.PluginContentResolver { *; }
-dontwarn com.didi.virtualapk.**
-dontwarn android.content.pm.**
-keep class android.** { *; }

5.HostApplication类是继承了Application,覆写attachBaseContext函数,进行插件SDK初始化工作

@Override
protected void attachBaseContext(Context base)  {
      super.attachBaseContext(base);

      PluginManager.getInstance(base).init();
}

6.在使用插件之前加载插件,可以根据具体业务场景选择合适时机加载,我是在HostActivity的onCreate时机加载

protected void onCreate(Bundle savedInstanceState) {
        // 加载plugin.apk插件包
        PluginManager pluginManager = PluginManager.getInstance(this);
        File apk = new File(getExternalStorageDirectory(), "Plugin.apk");
        if (apk.exists()) {
            try {
                pluginManager.loadPlugin(apk);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } 
    }

从SD卡上直接加载Plugin.apk,在AndroidMainifest.xml记得添加下面的权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

具体代码如下:
HostActivity.java

package com.jackie.virtualapkhost;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import com.didi.virtualapk.PluginManager;

import java.io.File;

import static android.os.Environment.getExternalStorageDirectory;

public class HostActivity extends AppCompatActivity {
    private Button mLaunchPluginBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_host);

        initView();
        initEvent();
    }

    private void initView() {
        mLaunchPluginBtn = (Button) findViewById(R.id.btn_launch_plugin);

        // 加载Plugin.apk插件
        PluginManager pluginManager = PluginManager.getInstance(this);

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

    private void initEvent() {
        mLaunchPluginBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setClassName("com.jackie.virtualapkplugin", "com.jackie.virtualapkplugin.PluginActivity");
                startActivity(intent);
            }
        });
    }
}

注意,我这里的取名是HostActivity.java和activity_host.xml,是为了和插件APK区分,使用过程中宿主APK和插件APK的资源名称不能相同。

插件工程接入

1.. 在插件工程根目录的build.gradle添加依赖

dependencies {
    classpath 'com.didi.virtualapk:gradle:0.9.0'
}

2.在插件工程模块的build.gradle添加使用gradle插件和插件配置信息,信息需要放在文件最下面。

// 插件配置信息,放在文件最下面
virtualApk {
    // 插件资源表中的packageId,需要确保不同插件有不同的packageId.
    packageId = 0x6f

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

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

3.生成插件

gradle clean assemblePlugin
或者
gradlew clean assemblePlugin  

编译出来的插件如下:
这里写图片描述
效果如下:
这里写图片描述

遇到的问题:

1.Error:A problem occurred configuring project ‘:app_plugin’. > The directory of host application doesn’t exist
解决办法:修改宿主路径,建议targetHost写成绝对路径。
2.Error:A problem occurred configuring project ‘:app_plugin’. > Failed to notify project evaluation listener. > com/android/builder/dependency/ManifestDependency
解决办法:修改项目gradle 版本为2.1.3
3.Error:A problem occurred configuring project ‘:app_plugin’. > Can’t find E:\Workspace\VirtualAPKHost\app\build\VAHost\Host_R.txt, please check up your host application need apply com.didi.virtualapk.host in build.gradle of host application
解决办法:在工程根目录的build.gradle添加使用gradle插件;
同时clean project
4. Can’t find E:\Workspace\VirtualAPKHost\app\build\VAHost\versions.txt
解决办法:在3的基础上 rebuild project

/**
* 结论:
* 1、loadPlugin之后可以像以往一样启动Service,但是必须加一句代码否则会崩溃:it.setPackage(“com.plugin.plugin.plugindemo”);
* 2、实验证明插件工程和宿主工程是在同一个进程中,在插件中用sp保存的数据在宿主工程中可以正常获取,反之也可以
* 3、插件和宿主可以正常发广播和接收广播, 都能正常收到
* 4、生成插件要用gradlew assemblePluginRelease命令,否则Activity打开一片空白
* 5、无论是插件还是宿主activity主题都必须使用这个类型的主题:Theme.AppCompat, 否则不能打包
* 6、插件1和插件2 能像宿主跟插件一样来交互, 如果要加载n个插件就loadPlugin n次
* 7、build.gradle最下面配置packageId = 0x6f,这是packageId,每个插件设置成不一样
* 8、广播和Service、Activity在插件与插件之间交互都一样,没有什么区别
*
* 暂不支持:
* 不支持 Activity 的部分属性,比如 process、configChanges 等;
* 暂不支持 overridePendingTransition(int enterAnim, int exitAnim) 这种形式的转场动画;
* 插件中弹通知,不能使用插件中的资源,比如图片
* 从Android 6.0开始,系统采用了新的权限机制,但是暂时不支持在插件中动态申请权限
*
* 插件管理:
* 一般做法是从服务端下发插件,宿主端管理插件的下载,加载,更新,回滚。宿主端升级,旧版本的插件全部作废重新等待下发。
* 每个插件保存在应用的私有目录下,在私有目录下初始化一个当前最新的版本,并标明当前的版本号, 如果需要就从服务端下载更新的插件,
* 每次升级插件都要作废,是因为每次升级的时候私有目录下已经放了最新的插件。
*/

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值