1 Introduction
A recent project requires our products to go through a network tunnel and provide a corresponding SDK. Obviously, this process can only be realized through native development. The author has never done native development or learned java, so I also stepped on it. There are a lot of pitfalls. It took two days to finally complete the task. Today, the system summarizes the next steps. Since the development is based on the author's business, the steps and details may be somewhat different from other articles related to native plug-in development. . Please also read this article with a dialectical mentality.
2. List of tools and materials
Tools/Materials | version/version name |
---|---|
HBuilder X |
3.1.4 |
Android Studio |
4.1.3 |
UNI-SDK |
[email protected]_20210305 |
Android Gradle Plugin Version |
4.1.1 |
Gradle Version |
6.5 |
3. SDK integration document
The native SDK here refers to the SDK provided by third-party manufacturers
3.1 Introduction
- The jar package used by the aar of the secure tunnel
guava-18.0.jar
- The third-party project referenced by the aar of the secure tunnel
implementation 'org.bouncycastle:bcprov-jdk15on:1.55'
implementation 'org.apache.commons:commons-lang3:3.4'
implementation 'org.slf4j:slf4j-api:1.7.21'
复制代码
- Configure the address of the secure tunnel server, configure the address of the secure tunnel server in the resource file
<string name="client_vpn_server_host">xxx.xxx.xxx.xx:xxxx</string>
复制代码
- After the security tunnel is initialized, it will automatically obtain the whitelist (asynchronous action), and it will be persisted locally after being successfully obtained. Therefore, it may be relatively slow when initializing the security tunnel for the first time (because of network requests)
3.2 Integration
The secure tunnel provides aar, and the necessary documents have been packaged in aar, only the build.gradle file in the main project is required
dependencies{
...
implementation(name: 'MXSocksCore-x.x.x.xxxxxxxx', ext: 'aar')
...
}
复制代码
xxxxxxxxxxxx is the version number, you can also modify it yourself, but it must correspond to the file name. If aar is not referenced in the build.gradle file of the main project, it needs to be referenced in the build.gradle file of the main file
repositories {
flatDir {
dirs 'libs','子工程的libs的相对路径'
}
}
复制代码
Modify the corresponding relative path in
Finally, put the aar in the libs folder of the project
3.3 API
1. Initialize the tunnel
MXAppTunnel.getInstance().initAppTunnel(context, new AppTunnelInitComplete() {
@Override
public void appTunnelInitComplete() {
//初始化完成
}
@Override
public void appTunnelInitError(String msg) {
//初始化失败
}
});
复制代码
Tunnel initialization must be completed before use. This action is an asynchronous action. The whitelist will be obtained for the first startup, and will be stored locally after successful acquisition. Subsequent startups will not be blocked due to obtaining the whitelist.
2. Secure tunnel log output
MXAppTunnel.getInstance().setLogPrintListener(new ILogPrint() {
@Override
public void log(String tag, String format, Object... objects) {
}
@Override
public void log(String tag, String msg) {
}
@Override
public void diagnosisLog(String msg) {
}
});
复制代码
3. Safe tunnel information output
MXAppTunnel.getInstance().setProxyInfoCallBack(new IProxyInfoCallBack() {
@Override
public void sendProxyPort(int httpPort, int socksPort) {
//安全隧道两个服务器端口
//1、httpPort http本地代理服务器的端口
//2、socksPort socks本地代理服务器的端口
}
@Override
public void sendProxyWhiteList(List<String> list) {
//list 安全隧道白名单
}
});
复制代码
3.4 Use of Secure Tunnel
The secure tunnel uses a proxy that needs to be manually set up for http and https requests. It only provides two ways to set up a proxy for network requests. The proxy address is xxx.xxx, and the port is output in the third API
1、HttpClient
HttpHost httpHost = new HttpHost("xxx.x.x.x", xxxx);
httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY, httpHost);
复制代码
2、HttpURLConnection
SocketAddress sa = new InetSocketAddress("xxx.x.x.x", xxxx);
//定义代理,此处的Proxy是源自java.net
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP,sa);
(HttpURLConnection) url.openConnection(proxy);
复制代码
3、HttpsURLConnection
SocketAddress sa = new InetSocketAddress("xxx.x.x.x", xxxx);
//定义代理,此处的Proxy是源自java.net
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP,sa);
(HttpsURLConnection) url.openConnection(proxy);
复制代码
Note: The ports xxxx in the sample code are all fake, and the corresponding ports output in the third item of the API should be used
3.5 Demo
The file also provides a MainActivity.java
Demo
package com.example.administrator.networkdemo.ui;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.administrator.networkdemo.R;
import com.minxing.vpn.MXAppTunnel;
import com.minxing.vpn.callback.AppTunnelInitComplete;
import com.minxing.vpn.callback.ILogPrint;
import com.minxing.vpn.callback.IProxyInfoCallBack;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URL;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "Test";
private boolean isInit = false;
private int httpPort1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void initialize(View view) {
MXAppTunnel.getInstance().setProxyInfoCallBack(new IProxyInfoCallBack() {
@Override
public void sendProxyPort(int httpPort, int socksPort) {
//安全隧道两个服务器端⼝
//1、httpPort http本地代理服务器的端⼝
//2、socksPort socks本地代理服务器的端⼝
httpPort1 = httpPort;
Log.i(TAG, "httpPort: " + httpPort + " socksPort: " + socksPort);
Toast.makeText(MainActivity.this, "httpPort: " + httpPort + " socksPort: " + socksPort, Toast.LENGTH_LONG).show();
}
@Override
public void sendProxyWhiteList(List<String> list) {
//list 安全隧道⽩名单
StringBuilder stringBuilder = new StringBuilder();
for (String s : list) {
stringBuilder.append(s).append("\n");
}
Log.i(TAG, stringBuilder.toString());
Toast.makeText(MainActivity.this, stringBuilder.toString(), Toast.LENGTH_LONG).show();
}
});
MXAppTunnel.getInstance().initAppTunnel(MainActivity.this, new AppTunnelInitComplete() {
@Override
public void appTunnelInitComplete() {
//初始化完成
Log.i(TAG, "安全隧道初始化完成");
Toast.makeText(MainActivity.this, "安全隧道初始化完成", Toast.LENGTH_SHORT).show();
isInit = true;
}
@Override
public void appTunnelInitError(String msg) {
//初始化失败
Log.i(TAG, "安全隧道初始化失败: " + msg);
Toast.makeText(MainActivity.this, "安全隧道初始化失败: " + msg, Toast.LENGTH_SHORT).show();
}
});
MXAppTunnel.getInstance().setLogPrintListener(new ILogPrint() {
@Override
public void log(String tag, String format, Object... objects) {
Log.i(TAG, tag + " -1- format: " + format + " objects: " + objects);
}
@Override
public void log(String tag, String msg) {
Log.i(TAG, tag + " -2- msg: " + msg);
}
@Override
public void diagnosisLog(String msg) {
Log.i(TAG, " -3- msg: " + msg);
}
});
}
public void outputMXLog(View view) {
if (!isInit) {
Toast.makeText(MainActivity.this, "安全隧道未初始化", Toast.LENGTH_SHORT).show();
return;
}
}
public void outputMXInfo(View view) {
if (!isInit) {
Toast.makeText(MainActivity.this, "安全隧道未初始化", Toast.LENGTH_SHORT).show();
return;
}
request(httpPort1);
}
private void request(final int port) {
// OkHttpClient.Builder builder = new OkHttpClient.Builder();
// builder.connectTimeout(1, TimeUnit.MINUTES);
// Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("127.0.0.1", port));
// builder.proxy(proxy);
new Thread(new Runnable() {
@Override
public void run() {
String url = "http://xx.xx.xx.xxxx:xxxx/xxxxx/xxxxxx?params1=value1¶ms2=value2¶ms3=value3";
URL url1 = null;
try {
url1 = new URL(url);
SocketAddress sa = new InetSocketAddress("127.0.0.1", port);
//定义代理,此处的Proxy是源⾃java.net
Proxy proxy = new Proxy(java.net.Proxy.Type.HTTP,sa);
HttpURLConnection httpURLConnection =(HttpURLConnection) url1.openConnection(proxy);
httpURLConnection.setRequestMethod("POST");
//得到响应码
int responseCode = httpURLConnection.getResponseCode();
if(responseCode == HttpURLConnection.HTTP_OK){
//得到响应流
InputStream inputStream = httpURLConnection.getInputStream();
//将响应流转换成字符串
//String result = is2String(inputStream);//将流转换为字符串。
//Log.d("kwwl","result============="+result);
}
InputStream inputStream = httpURLConnection.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader reader = new BufferedReader(inputStreamReader);
String tempLine;
StringBuilder resultBuffer = new StringBuilder();
while ((tempLine = reader.readLine()) != null) {
resultBuffer.append(tempLine);
}
Log.i(TAG, " -5- " + resultBuffer.toString());
} catch (Exception e) {
e.printStackTrace();
Log.i(TAG, " -4- " + e.getMessage());
}
}
}).start();
// Request request = new Request.Builder().url(url).get().build();
// Call call = builder.build().newCall(request);
// call.enqueue(new Callback() {
// @Override
// public void onFailure(Call call, IOException e) {
// final String s = e.getMessage();
// Log.e(TAG, s);
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
// }
// });
// }
//
// @Override
// public void onResponse(Call call, Response response) throws IOException {
// final String s = response.body().string();
// Log.e(TAG, s);
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// Toast.makeText(MainActivity.this, s, Toast.LENGTH_LONG).show();
// }
// });
// }
// });
}
}
复制代码
3. Development
3.1 Native project operation
In order to develop native plug-ins, it is an indispensable condition to establish a native project project. For the convenience of development, the project UNI-SDK
in the folder is directly used here UniPlugin-Hello-AS
, and it is directly dragged into Android Studio
(hereinafter referred to as AS
) and clicked on File-New- Import Project
,
After selecting UniPlugin-Hello-AS
, click OK, and the entire directory structure will come out.
Now hit the run button to start the example project.
3.2 Plug-in development
First follow the Android native plug-in development tutorial , step by step. I won’t write about JDK installation and AS installation. These are no big problems. Any related article on Baidu can run.
gradle
According to the official note, in general, we pay attention to the configuration and tools.build:gradle
click on the file-project structure to view our version when developing locally
Install the official steps and create a new one. Module
Before that, we first convert the project structure to Project
the structure of the type, and then click File-New-New Module
select library
Configure the package name and Module
name, click Finish ( Finish
)
According to the official layout, after the new creation is completed, you need to configure the newly created Module
information build.gradle
, pay attention to yes Module
and app
not
The following error message may appear after the new creation is completed
Version 28 (intended for Android Pie and below) is the last version of the legacy support library, so we recommend that you migrate to AndroidX libraries when using Android Q and moving forward. The IDE can help with this: Refactor > Migrate to AndroidX... less... (Ctrl+F1)
Inspection info:There are some combinations of libraries, or tools and libraries, that are incompatible, or can lead to bugs. One such incompatibility is compiling with a version of the Android support libraries that is not the latest version (or in particular, a version lower than your targetSdkVersion). Issue id: GradleCompatible
复制代码
The specific solution can go to Baidu, but I found that this seems to be just a warning, anyway, it did not affect my compilation, operation and use in the end.
First, according to the configuration instructions of the third-party SDK, configure the address of the secure tunnel server in the resource file (note that it is under the main
folder) and create uniplugin_component
a new res file by referring to the configuration format of other modules such as Ru.
Since our network tunnel is implemented in the Module plug-in module, we will MXSDK
put it Module
in for libs
reference.
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
//导入aar需要的配置
repositories {
flatDir {
dirs 'libs' //指定arr的导入路径,默认是当前Module的libs目录
}
}
dependencies {
/**引入uniSDK必要的依赖开始**/
//以com.等开头的是第三方的远程依赖库
compileOnly 'com.android.support:recyclerview-v7:28.0.0'
compileOnly 'com.android.support:support-v4:28.0.0'
compileOnly 'com.android.support:appcompat-v7:28.0.0'
compileOnly 'com.alibaba:fastjson:1.1.46.android'
compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs') //这种引入方式 ../app/libs 指定了app目录下的模块的rarr文件
/**引入uniSDK必要的依赖结束**/
/**安全隧道的aar引用的第三方工程开始**/
implementation 'org.bouncycastle:bcprov-jdk15on:1.55'
implementation 'org.apache.commons:commons-lang3:3.4'
implementation 'org.slf4j:slf4j-api:1.7.21'
//引入MX本地arr文件(根据dirs 'libs'这个路径直接引用当前Module-libs目录)
implementation(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')
/**安全隧道的aar引用的第三方工程结束**/
}
复制代码
After the access is completed, run
after a while, it is found that there is no mistake, and the customized development will start below. Create a new class
According to the official steps, this class needs to be inherited UniModule
. According to the writing method in DEMO, the details are as follows
package com.example.kysin;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.minxing.vpn.MXAppTunnel;
import com.minxing.vpn.callback.AppTunnelInitComplete;
import com.minxing.vpn.callback.ILogPrint;
import com.minxing.vpn.callback.IProxyInfoCallBack;
import java.util.List;
import io.dcloud.feature.uniapp.common.UniModule;
public class tunnel extends UniModule {
private static final String TAG = "Test";
private boolean isInit = false;
private int httpPort1;
public void initialize(View view) {
MXAppTunnel.getInstance().setProxyInfoCallBack(new IProxyInfoCallBack() {
@Override
public void sendProxyPort(int httpPort, int socksPort) {
//安全隧道两个服务器端⼝
//1、httpPort http本地代理服务器的端⼝
//2、socksPort socks本地代理服务器的端⼝
httpPort1 = httpPort;
Log.i(TAG, "httpPort: " + httpPort + " socksPort: " + socksPort);
Toast.makeText(MainActivity.this, "httpPort: " + httpPort + " socksPort: " + socksPort, Toast.LENGTH_LONG).show();
}
@Override
public void sendProxyWhiteList(List<String> list) {
//list 安全隧道⽩名单
StringBuilder stringBuilder = new StringBuilder();
for (String s : list) {
stringBuilder.append(s).append("\n");
}
Log.i(TAG, stringBuilder.toString());
Toast.makeText(MainActivity.this, stringBuilder.toString(), Toast.LENGTH_LONG).show();
}
});
MXAppTunnel.getInstance().initAppTunnel(MainActivity.this, new AppTunnelInitComplete() {
@Override
public void appTunnelInitComplete() {
//初始化完成
Log.i(TAG, "安全隧道初始化完成");
Toast.makeText(MainActivity.this, "安全隧道初始化完成", Toast.LENGTH_SHORT).show();
isInit = true;
}
@Override
public void appTunnelInitError(String msg) {
//初始化失败
Log.i(TAG, "安全隧道初始化失败: " + msg);
Toast.makeText(MainActivity.this, "安全隧道初始化失败: " + msg, Toast.LENGTH_SHORT).show();
}
});
MXAppTunnel.getInstance().setLogPrintListener(new ILogPrint() {
@Override
public void log(String tag, String format, Object... objects) {
Log.i(TAG, tag + " -1- format: " + format + " objects: " + objects);
}
@Override
public void log(String tag, String msg) {
Log.i(TAG, tag + " -2- msg: " + msg);
}
@Override
public void diagnosisLog(String msg) {
Log.i(TAG, " -3- msg: " + msg);
}
});
}
}
复制代码
Here the IDE will prompt "cannot resolve symbolMainActivity
This involves the "current context" . According to the traditional Activity
method, we can directly integrate Activity
and then write Activity.this
or pass getApplicationContext
to get the execution context. But the official documentation says:
Activity
of the acquisition method. BymUniSDKInstance.getContext()
forcingActivity
. It is recommendedinstanceof Activity
to make a judgment first and then force it, so here we modify the encapsulation method and use themUniSDKInstance.getContext()
alternativeActivity.this
writing method
package com.example.kysin;
import android.app.Activity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.minxing.vpn.MXAppTunnel;
import com.minxing.vpn.callback.AppTunnelInitComplete;
import com.minxing.vpn.callback.ILogPrint;
import com.minxing.vpn.callback.IProxyInfoCallBack;
import java.util.List;
import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.common.UniModule;
public class Tunnel extends UniModule {
private static final String TAG = "Test";
private boolean isInit = false;
private int httpPort1;
@UniJSMethod(uiThread = false)
public void initialize() {
Log.i(TAG, "11111");
MXAppTunnel.getInstance().setProxyInfoCallBack(new IProxyInfoCallBack() {
@Override
public void sendProxyPort(int httpPort, int socksPort) {
//安全隧道两个服务器端⼝
//1、httpPort http本地代理服务器的端⼝
//2、socksPort socks本地代理服务器的端⼝
httpPort1 = httpPort;
Log.i(TAG, "httpPort: " + httpPort + " socksPort: " + socksPort);
Toast.makeText((Activity)mUniSDKInstance.getContext(), "httpPort: " + httpPort + " socksPort: " + socksPort, Toast.LENGTH_LONG).show();
}
@Override
public void sendProxyWhiteList(List<String> list) {
//list 安全隧道⽩名单
StringBuilder stringBuilder = new StringBuilder();
for (String s : list) {
stringBuilder.append(s).append("\n");
}
Log.i(TAG, stringBuilder.toString());
Toast.makeText((Activity)mUniSDKInstance.getContext(), stringBuilder.toString(), Toast.LENGTH_LONG).show();
}
});
MXAppTunnel.getInstance().initAppTunnel((Activity)mUniSDKInstance.getContext(), new AppTunnelInitComplete() {
@Override
public void appTunnelInitComplete() {
//初始化完成
Log.i(TAG, "安全隧道初始化完成");
Toast.makeText((Activity)mUniSDKInstance.getContext(), "安全隧道初始化完成", Toast.LENGTH_SHORT).show();
isInit = true;
}
@Override
public void appTunnelInitError(String msg) {
//初始化失败
Log.i(TAG, "安全隧道初始化失败: " + msg);
Toast.makeText((Activity)mUniSDKInstance.getContext(), "安全隧道初始化失败: " + msg, Toast.LENGTH_SHORT).show();
}
});
MXAppTunnel.getInstance().setLogPrintListener(new ILogPrint() {
@Override
public void log(String tag, String format, Object... objects) {
Log.i(TAG, tag + " -1- format: " + format + " objects: " + objects);
}
@Override
public void log(String tag, String msg) {
Log.i(TAG, tag + " -2- msg: " + msg);
}
@Override
public void diagnosisLog(String msg) {
Log.i(TAG, " -3- msg: " + msg);
}
});
}
}
复制代码
3.3 Plug-in testing in native APP
After writing, the tunnel initialization test needs to be performed. To implement this invocation test in the native project Module
, the following steps are required:
dcloud_uniplugins.json
Declare andModule
introduce the native plug-in through- Create a new custom
UNI
project and write the corresponding calling method
So our first step is to declare the plug-in in the native project, as described in the official document: file UniPlugin-Hello-AS
under the project . Add what you want to register under the app-src-main-assets/dcloud_uniplugins.json
node or moudles
Module
Component
{
"nativePlugins": [
{
"plugins": [
{
"type": "module",
"name": "TestModule",
"class": "io.dcloud.uniplugin.TestModule"
}
]
},
{
"plugins": [
{
"type": "component",
"name": "myText",
"class": "io.dcloud.uniplugin.TestText"
}
]
},
{
"hooksClass": "",
"plugins": [
{
"type": "module",
"name": "DCloud-RichAlert",
"class": "uni.dcloud.io.uniplugin_richalert.RichAlertModule"
}
]
},
{
"plugins": [
{
"type": "module",
"name": "test-Module", //这个名字可以随便取,只要和UNI项目中requireNativePlugin的相同就行
"class": "com.example.kysin.Tunnel"
}
]
}
]
}
复制代码
Then go to app
the module build.gradle
to add the new Moudle plug-in
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion '28.0.3'
defaultConfig {
applicationId "com.HBuilder.UniPlugin"
minSdkVersion 21
targetSdkVersion 26 //建议此属性值设为21 io.dcloud.PandoraEntry 作为apk入口时 必须设置 targetSDKVersion>=21 沉浸式才生效
versionCode 1
versionName "1.0"
multiDexEnabled true
ndk {
abiFilters 'x86','armeabi-v7a'
}
}
buildTypes {
release {
zipAlignEnabled true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
zipAlignEnabled true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
//使用uniapp时,需复制下面代码
/*代码开始*/
aaptOptions {
additionalParameters '--auto-add-overlay'
//noCompress 'foo', 'bar'
ignoreAssetsPattern "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
}
/*代码结束*/
}
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation fileTree(dir: 'libs', include: ['*.aar'])
implementation "com.android.support:support-v4:28.0.0"
implementation "com.android.support:appcompat-v7:28.0.0"
/*uniapp所需库-----------------------开始*/
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.facebook.fresco:fresco:1.13.0'
implementation "com.facebook.fresco:animated-gif:1.13.0"
/*uniapp所需库-----------------------结束*/
// 基座需要,必须添加
implementation 'com.github.bumptech.glide:glide:4.9.0'
implementation 'com.alibaba:fastjson:1.1.46.android'
// 添加uni-app插件
implementation project(':uniplugin_component')
implementation project(':uniplugin_module')
implementation project(':uniplugin_richalert')
// 添加自定义插件
implementation project(':testModule') //和你新建Module的文件夹名字保持一致
}
复制代码
testModule
Gradle.builde
The local file import in the module arr
, I have summarized the following situations
//app工程libs如没有这个arr文件会报Coulad not resolve:MXSocksCore-release_6.8.0_stable_socks_jar_160:
//app工程libs如没有这个arr文件会报Duplicate class com.google.common.annotations.Beta found in modules MXSocksCore-release_6.8.0_stable_socks_jar_160-runtime(:MXSocksCore-release_6.8.0_stable_socks_jar_160:) adn MXSocksCore-release_6.8.0_stable_socks_jar_160-runtime(MXSocksCore-release_6.8.0_stable_socks_jar_160.arr)
//app无法编译运行
implementation(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')
//情况同上
api(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')
//app工程libs如没有这个arr文件会报警告,但是程序会正常启动,但是自定义的Module事件无法触发
//Missing class:com.mingxing.vqn.callback.ApptunnellnitCompelet
//Missing class: com.minxing.vpn.callback.IProxyInfoCallBack
//Missing class: com.minxing.vpn.callback.ILogPrint
//app工程libs有这个arr文件才能不报/Missing class,能正常运行
compileOnly(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')
//app工程libs如没有这个arr文件也能正常运行
//但是打包arr时报错:Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken because the classes and Android resources from any local .aar file dependencies would not be packaged in the resulting AAR. Previous versions of the Android Gradle Plugin produce broken AARs in this case too (despite not throwing this error). The following direct local .aar file dependencies of the :testModule project caused this error: C:\Users\jnp\Desktop\jianshu\[email protected]_20210305\UniPlugin-Hello-AS\testModule\libs\MXSocksCore-release_6.8.0_stable_socks_jar_160.aar
api fileTree(include: ['MXSocksCore-release_6.8.0_stable_socks_jar_160.aar'], dir: './libs')
//app工程libs如没有这个arr文件也能正常运行
//但是打包arr时报错:Direct local .aar file dependencies are not supported when building an AAR. The resulting AAR would be broken because the classes and Android resources from any local .aar file dependencies would not be packaged in the resulting AAR. Previous versions of the Android Gradle Plugin produce broken AARs in this case too (despite not throwing this error). The following direct local .aar file dependencies of the :testModule project caused this error: C:\Users\jnp\Desktop\jianshu\[email protected]_20210305\UniPlugin-Hello-AS\testModule\libs\MXSocksCore-release_6.8.0_stable_socks_jar_160.aar
implementation fileTree(dir: 'libs', include: ['*.aar'])
复制代码
Based on this, this article can only choose compileOnly(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')
a way to arr
reference third-party plug-ins. Here, you need to put a copy of the third-party arr package in the app
module libs
folder
plugins {
id 'com.android.library'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
//导入aar需要的配置
repositories {
flatDir {
dirs 'libs' //指定arr的导入路径,默认是当前Module的libs目录
}
}
dependencies {
/**引入uniSDK必要的依赖开始**/
//以com.等开头的是第三方的远程依赖库
compileOnly 'com.android.support:recyclerview-v7:28.0.0'
compileOnly 'com.android.support:support-v4:28.0.0'
compileOnly 'com.android.support:appcompat-v7:28.0.0'
compileOnly 'com.alibaba:fastjson:1.1.46.android'
compileOnly fileTree(include: ['uniapp-v8-release.aar'], dir: '../app/libs') //这种引入方式 ../app/libs 指定了app目录下的模块的arr文件
/**引入uniSDK必要的依赖结束**/
/**安全隧道的aar引用的第三方工程开始**/
implementation 'org.bouncycastle:bcprov-jdk15on:1.55'
implementation 'org.apache.commons:commons-lang3:3.4'
implementation 'org.slf4j:slf4j-api:1.7.21'
//引入MX本地arr文件(根据dirs 'libs'这个路径直接引用当前Module-libs目录)
compileOnly(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')
/**安全隧道的aar引用的第三方工程结束**/
}
复制代码
Then go to create a new UNI
project and write the code to call the native plug-in
After the writing is complete, click Publish-Native APP Local Packaging-Generate Local Packaging APP Resources
Delete the entire file app-src-main-assets-apps
in the directory of the original project , and then paste the file you have packaged and renamed to the directory that has just been deleted. Here is an example.__UNI__BCEC007
APPID
apps
__UNI__911FD69
Then go to app-src-main-assets-data-dcloud_control.xml
modify it appid
to the one you just copiedappid
Click run
, and then click the icon on the home page of the app to call the native method to see logcat
the input log
As can be seen above, the call can be made normally. Plugin test successful
3.4 Plug-in packaging
The first step of plug-in packaging is still very simple, click Gradle
the icon on the right side of the IDE, find uniPlugin-Hello-AS-testModule-Tasks-other-assembleRelease
, double-clickassembleRelease
testModule-build-outputs-arr
Find our plugin in the folder testModule-release.arr
according to the official document generationuni-app
Before packaging, you must remember to manifest.json
select the local native plug-in, and you will find that the plug-in name is the field package.json
in the previous name
.
When packaging, choose to run - run to the mobile phone or emulator - make a custom debugging base, wait for the packaging to complete and click to run this
This article is quoted from the unsupported tadpole of Rare Earth Nuggets.