上一篇文章讲解了如何在云端配置项目的依赖:
【Android】自定义Gradle Plugin实现云端配置项目依赖
这篇文章讲解,当在云端修改了项目的依赖(dependencies{…})代码,比如删除或者新增了SDK组件,如何保证项目正常编译运行不受影响,即实现组件可插拔。
例如,应用依赖了library1、library2、library3、…这些library都是可选的,即可以全都要,可以全不要或者只要其中一部分。现在在云端选择了应用需要依赖哪些library,达到不需要重新修改应用代码,即可感知到功能的变化。
新建Demo Project名为MavenRepoDemo,新增三个Library,分别为:
aar_upload.gradle
是用来上传library到maven仓库的脚本。
app module中build.gradle没有配置任何依赖:
apply plugin: 'com.android.application'
apply plugin: 'MyPlugin'
android {
compileSdkVersion 29
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "com.devnn.mavenrepodemo"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
//note: here is nothing
}
依赖是通过云端来完成的,具体内容参阅文章开头的链接。
app module中的MainActivity代码如下:
package com.devnn.mavenrepodemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.devnn.commonlibrary.ILibrary;
import com.devnn.commonlibrary.LibraryLoader;
public class MainActivity extends AppCompatActivity {
private TextView tvMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvMessage = findViewById(R.id.main_text);
ILibrary library1 = LibraryLoader.getLibrary1();
ILibrary library2 = LibraryLoader.getLibrary2();
if (library1 != null) {
tvMessage.append("\n");
tvMessage.append(library1.hello());
}
if (library2 != null) {
tvMessage.append("\n");
tvMessage.append(library2.hello());
}
}
}
当两个library加载成功后,运行显示:
其中Hello,World!
是tvMessage控件默认的文本内容。
可以看到加载library是通过LibraryLoader来完成的。没有找到library时getLibraryX()方法返回null。
接下来讲解具体实现。
在commonlibrary中建立LibraryLoader.java类,用来加载其它的library。代码如下:
package com.devnn.commonlibrary;
import android.content.Context;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* create by nannan on 2020/6/23
*/
public class LibraryLoader {
public static ILibrary getLibrary1() {
ILibrary library = loadLibrary("com.devnn.library1.Library1");
return library;
}
public static ILibrary getLibrary2() {
ILibrary library = loadLibrary("com.devnn.library2.Library2");
return library;
}
private static ILibrary loadLibrary(String path) {
ILibrary library = null;
try {
Class mClass = Class.forName(path);
Constructor constructor = mClass.getConstructor();
library = (ILibrary) constructor.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return library;
}
}
可以看到要加载的library存在与否并不影响应用编译和运行。
其实这里核心技术点就是动态加载class文件。
另外,新建ILbrary.java
类,作为其它library共同的接口:
package com.devnn.commonlibrary;
/**
* create by nannan on 2020/6/22
*/
public interface ILibrary {
String hello();
}
只是简单输出一段文本。
在library1 module中新建Library1.java
类,实现ILibrary接口:
package com.devnn.commonlibrary;
package com.devnn.library1;
import com.devnn.commonlibrary.ILibrary;
/**
* create by nannan on 2020/6/10
*/
public class Library1 implements ILibrary {
public String hello() {
return "hello,I am Library1";
}
}
在library2 module中新建Library2.java
类,实现ILibrary接口:
package com.devnn.library2;
import com.devnn.commonlibrary.ILibrary;
/**
* create by nannan on 2020/6/10
*/
public class Library2 implements ILibrary {
public String hello(){
return "hello,I am Library2";
}
}
整个Demo代码就这么多。
当在云端修改了要依赖的library,重新编译运行即可感知到功能的变化。
Demo Project源码:
https://github.com/devnns/MavenRepoDemo/tree/feathure/load_library_dynamic