UNI APP---Android端原生插件开发实战(一)

uni-app之android原生插件开发 阅读详情

1.前言

最近一个项目要求我们的产品必须走网络隧道,并且提供了对应的SDK,很明显只能通过原生开发的方式才能实现这个流程,笔者没有做过原生开发,也没有学过java,所以也踩了不少坑啊,花了两天时间总算完成任务,今天系统的总结下步骤,由于是根据笔者的业务进行的开发,所以步骤上与细节之处与其他原生插件开发相关的文章可能会有些出入。还请带着辩证的心态阅读本文。

2.工具材料清单

工具/材料版本/版本名
HBuilder X3.1.4
Android Studio4.1.3
UNI-SDKAndroid-SDK@3.1.4.80686_20210305
Android Gradle Plugin Version4.1.1
Gradle Version6.5

3.SDK集成文档

这里是原生SDK指的是第三方厂家提供的SDK

3.1 介绍

  1. 安全隧道的aar使用的jar包guava-18.0.jar
  2. 安全隧道的aar引用的第三方工程
    implementation 'org.bouncycastle:bcprov-jdk15on:1.55'
    implementation 'org.apache.commons:commons-lang3:3.4'
    implementation 'org.slf4j:slf4j-api:1.7.21'
复制代码
  1. 安全隧道服务器的地址的配置,在资源文件中配置好安全隧道服务器的地址
<string name="client_vpn_server_host">xxx.xxx.xxx.xx:xxxx</string>
复制代码
  1. 安全隧道在初始化后会自动获取白名单(异步动作),获取成功后会持久化到本地,因此,首次初始化安全隧道的时候可能相对会比较慢(因为有网络请求)

3.2 集成

安全隧道提供的是aar,必要的文档都已经打包在了aar中,仅需要在主工程中的build.gradle文件的

dependencies{
    ...

    implementation(name: 'MXSocksCore-x.x.x.xxxxxxxx', ext: 'aar')
    ...
}
复制代码

x.x.x.xxxxxxxx 为版本号,也可以自己修改,但要跟文件名对应 如果aar不在主工程的build.gradle文件中引用,需要在主文件的build.gradle文件的

repositories {
    flatDir {
        dirs 'libs','子工程的libs的相对路径'
    }
}
复制代码

中修改对应的相对路径

最后将aar放到工程的libs文件夹中

3.3 API

1、初始化隧道

MXAppTunnel.getInstance().initAppTunnel(context, new AppTunnelInitComplete() {
    @Override
    public void appTunnelInitComplete() {
        //初始化完成
    }

    @Override
    public void appTunnelInitError(String msg) {
        //初始化失败
    }
});
复制代码

隧道初始化必须在使用之前初始化完成,此动作是一个异步的动作,首次启动会获取白名单,获取成功后会存储到本地,之后的启动不会因为获取白名单而阻塞完成

2、安全隧道日志输出

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、安全隧道信息输出

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 安全隧道使用

安全隧道使用需要手动设置http和https请求的代理,仅提供两种网络请求的设置代理的方式,代理地址为xxx.x.x.x 端口在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);
复制代码

注:实例代码中的端口xxxx都是假的,应该使用API第三条中输出的对应的端口

3.5 Demo

文件还提供了一个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.开发

3.1原生项目运行

为了开发原生插件,那么建立原生的项目工程这是必不可少的条件,为了方便开发这里直接使用了UNI-SDK文件夹中的UniPlugin-Hello-AS这个工程,直接拖入到Android Studio(以下简称AS)点击文件-新建-Import Project,

选中UniPlugin-Hello-AS后点击确定,整个目录结构就出来了

现在点击运行按钮让示例项目跑起来。

3.2 插件开发

首先跟着Android原生插件开发教程,一步一步往下进行。 JDK安装和AS的安装就不写了,这些没啥大的问题,随便百度一个相关文章都能跑得起来

根据官方的注意,总体来说,我们在本地开发的时候注意配置gradle和tools.build:gradle 点击 文件-项目结构 查看我们的版本

安装官方的步骤,新建一个Module,在此之前我们先把项目结构转换Project类型的结构,然后点击 文件-新建-New Module

选择library

配置包名以及Module名称,点击完成(Finish)

按照官方的布置,新建完成了要去配置刚创建的Module的build.gradle信息,注意是Module的而不是app的

新建完成可能会出现如下的错误信息

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
复制代码

具体的解决办法可以去百度,但是我发现这貌似仅仅是个警告,反正最后没有影响我的编译、运行和使用。

首先按照第三方SDK的配置说明,在资源文件中配置好安全隧道服务器的地址(注意是在main文件夹下) 参考如uniplugin_component等其他模块的配置格式新建res文件。

由于我们的网络隧道是做到Module插件模块中的,所以我们讲MXSDK放在Module的libs中进行引用。

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引用的第三方工程结束**/
}
复制代码

接入完毕,run一下,发现没抱错,下面开始定制化的开发。 新建一个类

按照官方的步骤这个类需要继承UniModule,按照DEMO里面的写法,具体如下

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);
            }
        });
    }
}    
复制代码

这里IDE会提示“不能解决符号MainActivity

这里就涉及到了 "当前的上下文环境",按照传统的Activity方法,我们可以直接集成Activity然后写Activity.this或者通过getApplicationContext来得到执行的上下文。但是官方文档写到:

  • Activity的获取方式。通过mUniSDKInstance.getContext()强转Activity。建议先instanceof Activity判断一下再强转 所以这里我们改造下封装的方法,用mUniSDKInstance.getContext()代替Activity.this写法
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 在原生APP里进行插件测试

写完之后需要进行隧道初始化的测试 要在原生工程中实现这个Module的调用测试,需要进行下步骤:

  • 将原生插件在通过dcloud_uniplugins.json进行声明和Module引入
  • 新建一个自定义的UNI项目,并编写对应的调用方法

所以我们第一步是先去原生工程中进行插件的声明,按照官方文档描述: 在UniPlugin-Hello-AS工程下app-src-main-assets/dcloud_uniplugins.json文件。 在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"
        }
      ]
    }
  ]
}
复制代码

然后还要去app模块的build.gradle去添加新增的Moudle插件

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中本地的arr文件引入,我总结了以下几种情况

//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\Android-SDK@3.1.4.80686_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\Android-SDK@3.1.4.80686_20210305\UniPlugin-Hello-AS\testModule\libs\MXSocksCore-release_6.8.0_stable_socks_jar_160.aar
implementation fileTree(dir: 'libs', include: ['*.aar'])
复制代码

基于此本文只能选择compileOnly(name: 'MXSocksCore-release_6.8.0_stable_socks_jar_160', ext: 'aar')方式进行第三方arr插件引用,这里需要把第三方的arr包放1份到app模块的libs文件夹下

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引用的第三方工程结束**/
}
复制代码

然后去新建一个UNI项目,编写调用原生插件的代码

编写完成后,点击 发行-原生APP本地打包-生成本地打包APP资源

把原生工程中app-src-main-assets-apps目录下的__UNI__BCEC007这整个文件删除,然后把你打包完成以新的APPID命名的文件粘贴到刚刚删除干净的apps目录下这里以__UNI__911FD69为例子。

然后去app-src-main-assets-data-dcloud_control.xml中修改appid为你刚刚复制过来的那个appid

点击run,然后点击app首页的图标调用原生的方法 看看logcat的输入日志

以上可以看到能够正常的进行调用。插件测试成功

3.4 插件打包

插件打包第一步还是很简单的,点击IDE右侧的Gradle图标,找到uniPlugin-Hello-AS-testModule-Tasks-other-assembleRelease,双击assembleRelease

在testModule-build-outputs-arr文件夹找到我们的testModule-release.arr 按照官方文档生成uni-app插件

打包之前一定要记得去manifest.json选择本地的原生插件,你会发现插件名就是之前package.json中的name字段。

打包的时候选择 运行-运行到手机或模拟器-制作自定义调试基座,等待打包完成点击运行本

 本文引自 稀土掘金网的扶不起的蝌蚪。

手摸手教你uniapp原生插件开发 公共证书, 自由证书,云端证书都行 随意 我用的公共证书打包好后就可以运行自定义基座了end大家结合看参考资料其他参考资料。 阅读详情

相关推荐

【Uniapp】Uniapp Android原生插件开发指北

在uniapp开发中当HBuilderX中提供的能力无法满足App功能需求,需要通过使用Andorid/iOS原生开发实现时,或者是第三方公司提供的是Android的库,这时候可使用App离线SDK开发原生插件来扩展原生能力。插件类型有两种,Module模式和Component模式Module模式:能力扩展,无嵌入窗体的UI控件。大部分插件都是属于此类,比如调用计步器API。代码写法为通过js进行require,然后调用该插件对象的方法。如涉及一些弹出框、全屏ui,也仍然属于Module模式。

奋斗的小菜鸡的博客 4669

uniapp Android 原生插件开发(Module 扩展为例·2022)

uniapp原生android插件开发流程,uniapp原生android插件调试流程,uniapp原生android插件自定义基座打包流程一览。

lauwen 2万+

Android uni-app 封装原生插件

前言 据广大用户的需求,需要把我们anyRTC的SDK,封装到uni-app来使用,并且实现音视频通话。这边文章图文讲解一下怎么封装原生插件,并且在下一章uni-app实现音视频通话。 anyRTC开发者中心 1.uni-app是什么? 一个使用Vue.js开发所有前端应用框架,开发者编写一套代码,可发布到ios、Android、H5、以及各种小程序(微信/支付宝/百度/头条/QQ/钉钉)等多个平台。 如果你还没有了解uni-app是什么:点击这里:这篇文章举例说明。 2.uni-app的特点 1、跨

anyRTC的博客 6638

uni-app跨平台开发全攻略

uni-app作为基于Vue.js的跨平台开发框架,通过“一次开发,多端发布”解决iOS、Android、小程序等多平台适配难题。其核心优势包括:兼容90%的Vue语法、支持15+平台发布、接近原生的性能(借助weex引擎),以及完善的HBuilderX开发工具链。开发中需注意统一代码结构(单文件组件)、条件编译(如#ifdef H5处理平台差异)和双渲染引擎选择(Webview/nvue)。项目优化可通过分包加载、图片压缩及组件懒加载实现。

2402_88947053的博客 244

uniapp原生安卓插件开发之路

声明: 我的功能需求:全局悬浮框,也就是在其他应用上依旧能悬浮。查遍uni文档,无法实现,甚至native.js也无法实现,无奈只能走上原生插件开发这条不归路了。(吐槽:天天写前端js,突然写了下android简直要炸,public static String啥的,var不香嘛,不过别人严谨肯定还是有别人道理的,加油吧,争取能入个门!) 本人不懂安卓开发,完全安卓小白,所以安卓方面可能表达的不是很好,安卓代码可能写的不优美,但我功能最终还是实现了。但是为了需求,能力总能被“逼”出来。有描述错误的还望指正

Jone's Area 1万+

uni-app x,一个纯原生的Android App开发工具

下一代uni-app,一个神奇的产品。用vue语法、uni的组件、api,以及uts语言,编译出了kotlin的app。不再使用js引擎和webview。纯纯的kotlin原生app。uni-app x,让“跨平台开发性能不如原生”的这条曾广为流传的规则,扔进了历史的垃圾桶!

djemx2025的博客 6090

uni-app混合原生安卓开发

uni-app和原生App混合开发问题 首先务必确认uni-app和原生代码,谁是主谁是从的问题。 1、如果你的应用是uni-app开发的,需要扩展一些原生能力,那么首先去插件市场看看有没有现成的插件,如果没有,就自己开发,开发文档请参考原生插件开发教程https://nativesupport.dcloud.net.cn/NativePlugin/course/android。 2、如果你的App是原生开发的,部分功能栏目想通过uni-app实现,有2种方式: 在原生App里集成uni小程序sdk,参考

乌龟赛跑 1万+

uni-app原生打印插件开发

草稿

qq_36561118的博客 3778

uni-app与原生插件混合开发调试3-安卓原生插件开发调试和打包

上面已经介绍了怎么安装开发和调试环境,接下来就是安卓原生插件的具体开发和调试步骤:将uniapp前端项目的index.vue文件新增代码。重新打包,重新放到Android studio项目app/src/main/assps/目录下。重新启动run,项目启动成功。

rightkk的博客 1819

uni-app总结5-UTS插件开发

uni-app开发系统教程之UTS插件开发

Heyuan_Xie的博客 3270

uni-app与原生插件混合开发调试1-环境准备

简称uni,是dcloud旗下的产品,基于H5和vue,可用于开发各平台小程序,并且可以将开发的小程序跨平台打包,支持Android和iOS。,H是HTML的首字母,Builder是构造者,X是HBuilder的下一代版本。我们也简称HX。HX是轻如编辑器、强如IDE的合体版本。是dcloud提供的用于在本地打包的SDK,其本质就是一个Android工程,下载之后导入到Android studio,再将uni-app打包的本地资源包复制到离线SDK工程,依托离线SDK运行。

rightkk的博客 1720

Uni-APP 安卓原生插件开发及使用

HBuilderX学习笔记3 安卓插件开发@[TOC](HBuilderX学习笔记3 安卓插件开发)一、开发环境二、开发步骤1. 下载sdk2. 导入 UniPlugin-Hello-AS工程3. 编译其中的uniplugin_module,生成aar4. 在 HBuilder项目里添加插件package.json内容:5. 配置manifest.json自定义调试基座 一、开发环境 jdk 1.8 android studio 2.9.8+ 的 android平台SDK 二、开发步骤 1. 下载sdk

猿来这样-谢厂节的博客 6129

记一次uni-app Android原生插件开发

项目使用到了RFID射频手持设备(PDA),用于读取羊只耳标功能。原来有写过一个插件,新的设备与原来的不一样,所以需要重新写一遍。之前没有记录,这次重新记录下过程。使用的硬件设备Demo 为: uhfg_v2.2.10;...

郊眠寺 2280

uni-app使用原生安卓aar文件包,让uni-app可以调用安卓原生aar文件内的方法

桥接第三方aar文件,uni-app内使用第三方aar文件内的方法,uni-app引入原生安卓aar,uni-app引入安卓aarSDK文件,SDK引入,桥接开发

weixin_69477112的博客 4480

UniAPP-Android原生插件开发与打包

UniAPP-Android原生插件开发与打包为什么要写这篇文章开发流程1. 下载我封装好的项目1.1. 修改pictureeditor_uni项目1.1.1. 修改**uniplugin_test**项目1.1.2. 修改**app**项目1.2. 修改uni_app_test项目2. 插件开发2.1 导入现有项目3.打包插件创建基座3.1 创建插件目录3.2 获取aar文件3.3 提交打包 为...

similing的博客 1万+

uni-app安卓原生插件的编写与调试

一、为什么会有这篇文章 这里是官方给出的开发文档链接。但是还是有很多诸如以下的情况出现: 官方可能也是疲于回复这些问题,这些情况把打算入门插件开发的小伙们挡在了门外,这里结合当初笔者学习的一些经验,从一个Android开发者的角度给大家做一些分享。 二、自定义基座 uni-app开发的原生插件,必须运行在自定义基座中,这个很好理解,你要调试原生的东西,肯定要有运行环境,自定义基座就是这...

qwe749082787的博客 1万+

【uniapp】 uni-app 之 Android 原生插件开发

在"android"文件夹下创建一个新的 Java 类,命名为"YourPluginNamePlugin",并继承自"UniPlugin"类。在上述配置中,我们添加了一个名为"your-plugin-name"的插件,平台为"android",描述为"你的插件描述",版本为"1.0.0",提供者为"your-plugin-provider"。填写项目名称和保存路径,点击"创建"按钮。在 HBuilderX 中,点击右上角的"运行"按钮,选择"运行到手机或模拟器",然后选择你的设备进行编译和运行。

菜狗小测试 1419

【项目实战-MATLAB】:基于机器学习的虹膜识别系统设计

虹膜识别主要包括虹膜定位、虹膜裁剪、极坐标变换、归一化、HOG特征提取、虹膜识别六个主要步骤。本系统采用MATLAB作为开发工具实现 主要算法:霍夫变换预处理 HOG特征提取 SVM进行识别

qq_45047246的博客 6537
上一篇: Java快速启动
下一篇: UTS 在安卓系统上的开发应用
std7879
博客等级 码龄5年 273粉丝 283原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

std7879

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值