UNI APP---Android native plug-in development practice (1)

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

  1. The jar package used by the aar of the secure tunnelguava-18.0.jar
  2. 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'
复制代码
  1. 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>
复制代码
  1. 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.javaDemo

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&params2=value2&params3=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-SDKin 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.

gradleAccording 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. ModuleBefore that, we first convert the project structure to Projectthe structure of the type, and then click File-New-New Module

select library

Configure the package name and Modulename, click Finish ( Finish)

According to the official layout, after the new creation is completed, you need to configure the newly created Moduleinformation build.gradle, pay attention to yes Moduleand appnot

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 mainfolder) and create uniplugin_componenta 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 MXSDKput it Modulein for libsreference.

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, runafter 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 Activitymethod, we can directly integrate Activityand then write Activity.thisor pass getApplicationContextto get the execution context. But the official documentation says:

  • Activityof the acquisition method. By mUniSDKInstance.getContext()forcing Activity. It is recommended instanceof Activityto make a judgment first and then force it, so here we modify the encapsulation method and use the mUniSDKInstance.getContext()alternative Activity.thiswriting 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.jsonDeclare and Moduleintroduce the native plug-in through
  • Create a new custom UNIproject 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-ASunder the project . Add what you want to register under the app-src-main-assets/dcloud_uniplugins.jsonnode or moudlesModuleComponent

{
  "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 appthe module build.gradleto 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的文件夹名字保持一致
}
复制代码

testModuleGradle.buildeThe 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 arrreference third-party plug-ins. Here, you need to put a copy of the third-party arr package in the appmodule libsfolder

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 UNIproject 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-appsin 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__BCEC007APPIDapps__UNI__911FD69

Then go to app-src-main-assets-data-dcloud_control.xmlmodify it appidto 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 logcatthe 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 Gradlethe icon on the right side of the IDE, find uniPlugin-Hello-AS-testModule-Tasks-other-assembleRelease, double-clickassembleRelease

testModule-build-outputs-arrFind our plugin in the folder testModule-release.arr according to the official document generationuni-app

Before packaging, you must remember to manifest.jsonselect the local native plug-in, and you will find that the plug-in name is the field package.jsonin 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.

Guess you like

Origin blog.csdn.net/std7879/article/details/127700007