Uniapp: 原生Android插件开发调试打包详细历程 --- 不再为付费插件买单(例:安卓原生 TCPSocket 连接通信插件实现)

一、前言

最近uniapp 开发移动app项目遇到一个棘手问题,我想实现 TCPScoket 通信。我们知道 uniapp 开发基本用的是一些前端框架(如:vue, mpvue等)和一些 uni 自身 api。要想 uniapp 使用 Android 原生代码有以下三种常用方法:
一: 使用 plus.android.importClass() 调用安卓的类进行原生代码编写,但是经测试效果很不理想,速度很慢!
二:开发原生 Android 插件,运行速度快,但需要一定的安卓开发经验,难度较高。
三:购买其他开发者开发的原生插件,但是一般价格不低。
在这里插入图片描述
综上所述,我们必须学会自己开发原生插件!


二、前期准备

1、Android Studio 开发工具。 下载地址:https://developer.android.google.cn/studio/ 安装教程:https://blog.csdn.net/weixin_43883917/article/details/108942788
2、离线SDK下载(下载最新版)。 下载地址: https://nativesupport.dcloud.net.cn/AppDocs/download/android
在这里插入图片描述
3、保证 HbuilderX 也是最新版
4、签名证书。 生成方法:https://blog.csdn.net/weixin_58605808/article/details/117387435


三、创建一个 Uniapp 案例 (TCP-Plugin)

1、 开发者中心创建一个应用:https://dev.dcloud.net.cn/app/index?type=0
在这里插入图片描述
2、 点击应用名称进入应用,选择 离线打包Key管理 ,输入如下信息:
在这里插入图片描述

签名证书获取:
在这里插入图片描述

3、 点击保存会生成 appkey,后面要用到:在这里插入图片描述4、 启动 HbuilderX,左下角登录自己的账户,新建一个和刚刚同名的项目的默认模板项目,查看 manifest.json AppID 是否云端 APPID 对应:

在这里插入图片描述
以上 uniapp demo 算是创建完成!


四、Android 原生插件开发

一、导入已下载好的离线SDK中的原生插件开发框架项目UniPlugin-Hello-AS

在这里插入图片描述
二、切换成 project模式, 项目结构如下:
在这里插入图片描述
三、把我们刚才申请的appkey填写到 app/src/main/AndroidManifest.xml 中,因为开发的是安卓,所以appkey填安卓的:

在这里插入图片描述
四、把我们的签名证书放到app目录下,博主的签名证书名叫 uniapp.keystore
在这里插入图片描述
五、配置签名证书,在 app/build.gradlesigningConfigs 选项中:
在这里插入图片描述
六、右键 UniPlugin-Hello-AS,创建 Module
在这里插入图片描述七、填写插件模块信息:
在这里插入图片描述
八、配置 TCP-Socket/build.gradle,复制官方案例 uniplugin_module/build.gradle

apply plugin: 'com.android.library'

android {
    compileSdkVersion 29
    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'

    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }

}

repositories {
    flatDir {
        dirs 'libs'
    }
}

dependencies {
    compileOnly fileTree(dir: 'libs', include: ['*.jar'])

    compileOnly fileTree(dir: '../app/libs', include: ['uniapp-v8-release.aar'])

    compileOnly 'androidx.recyclerview:recyclerview:1.0.0'
    compileOnly 'androidx.legacy:legacy-support-v4:1.0.0'
    compileOnly 'androidx.appcompat:appcompat:1.0.0'
    implementation 'com.alibaba:fastjson:1.1.46.android'
    implementation 'com.facebook.fresco:fresco:1.13.0'

    /*implementation 'com.android.support:appcompat-v7:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'*/
}

九、删除不必要的官方案例,让项目更加简洁(可选):

在这里插入图片描述十、在TCP-Socket 模块下的 com/example/tcp/socket 创建类TcpModule

在这里插入图片描述
十一、TCP Scocket 通信功能实现,代码如下:

package com.example.tcp.socket;

import com.alibaba.fastjson.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.Socket;

import io.dcloud.feature.uniapp.annotation.UniJSMethod;
import io.dcloud.feature.uniapp.bridge.UniJSCallback;
import io.dcloud.feature.uniapp.common.UniModule;

public class TcpModule extends UniModule {
    int port = 6666;
    Socket socket = null;
    OutputStreamWriter outputStream = null;
    InputStreamReader inputStream = null;

    @UniJSMethod
    public void send(final JSONObject json, final UniJSCallback callback) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                String ip = json.getString("ip");
                String userId = json.getString("code");
                JSONObject res = new JSONObject();
                // 建立与服务器的连接
                try {
                    socket = new Socket(ip, port);
                    socket.setSoTimeout(1500);
                    //建立连接后获取输入输出流
                    outputStream = new OutputStreamWriter(socket.getOutputStream());
                    inputStream = new InputStreamReader(socket.getInputStream());
                } catch (Exception e) {
                    try {
                        socket.close();
                    } catch (IOException ioException) {
                        ioException.printStackTrace();
                    }
                    e.printStackTrace();
                    res.put("code", 400);
                    res.put("data", "系统异常, 请重启APP!");
                    callback.invoke(res);
                }
                BufferedReader socketReader = new BufferedReader((inputStream));
                BufferedWriter bufferedWriter = new BufferedWriter(outputStream);
                PrintWriter socketWriter = new PrintWriter(bufferedWriter, true);
                socketWriter.println(userId);
                try {
                    String s = socketReader.readLine();
                    res.put("code", 200);
                    res.put("data", s);
                    // 这里关闭连接, 不然回调以后,程序就不执行了,不能再 finally 里面关闭连接
                    socket.close();
                    callback.invoke(res);
                } catch (IOException e) {
                    res.put("code", 400);
                    res.put("data", "系统异常, 请重启APP!");
                    callback.invoke(res);
                }
            }
        }).start();
    }
}

注意:Android4.0 以后不允许在主线程进行网络连接,否则会出现 android.os.NetworkOnMainThreadException。因此,必须另起一个线程进行网络连接方面的操作。

十二、注册插件,在 app/src/main/assets/dcloud_uniplugins.json 文件中写入,如下:

在这里插入图片描述

{
  "nativePlugins": [
    {
      "plugins": [
        {
          "type": "module",
          "name": "TCP-Socket",
          "class": "com.example.tcp.socket.TcpModule"
        }
      ]
    }
  ]
}

五、HbuilderX 生成本地打包 TCP-Plugin

1、引入原生插件方式如下:

const TCP = uni.requireNativePlugin("TCP-Socket");

index.vue 代码编写如下:

<template>
	<view class="content">
		<image class="logo" src="/static/logo.png"></image>
		<view class="text-area">
			<text class="title">{{title}}</text>
		</view>
		<view>
			<button @click="clickBtn">点击发送消息</button>
		</view>
	</view>
</template>

<script>
	const TCP = uni.requireNativePlugin("TCP-Socket");
	export default {
		data() {
			return {
				title: 'TCP-Socket 插件测试'
			}
		},
		onLoad() {
		},
		methods: {
			clickBtn(){
				TCP.send({
					ip: '192.168.0.100', // 服务器ip
					code: 'd92863370904331f8a19cc116719bcd1' // 需要发送的数据
				}, e => {
					// 回调函数
					uni.showToast({
						title: '响应数据:' + JSON.stringify(e),
						icon: 'none'
					});
				})
			}
		}
	}
</script>
<style>
(...省略)
</style>

2、HbuilderX 生成本地打包资源:
在这里插入图片描述在这里插入图片描述


六、离线打包apk并进行真机调试

1、把生成的本地打包资源复制到 UniPlugin-Hello-AS 项目的 app/src/main/assets/apps 目录下:
在这里插入图片描述

2、配置 appid,在 UniPlugin-Hello-AS 项目的 app/src/main/assets/data/dcloud_control.xml 中配置:

在这里插入图片描述3、在 UniPlugin-Hello-AS 项目的 app/build.gradle 中对 TCP-Socket 插件引用:

在这里插入图片描述
4、测试,手机或者虚拟设备连接以后,点击运行进行测试:

在这里插入图片描述


七、打包生成原生插件 aar

在这里插入图片描述在这里插入图片描述


八、HbuilderX 引入TCP-Socket 打包的 aar 插件:

1、目录结构以及 package.json 编写如下:
在这里插入图片描述2、manifest.json 选择本地插件:
在这里插入图片描述


九、制作自定义调试基座

在这里插入图片描述在这里插入图片描述
打包自定义基座成功如下:
在这里插入图片描述


十、HbuilderX 真机调试测试插件

1、选择自定义调试基座:在这里插入图片描述

注意:如果出现已经制作自定义调试基座,但是没有运行基座选择这一选项的情况。请重新进行自定义基座打包,直到出现运行基座选择为止!

2、选择运行到 Android App 基座进行真机调试:
在这里插入图片描述在这里插入图片描述
测试成功!


原生插件开发调试打包全部完成!


十一、开发期间遇到的问题:

1、 [JS Framework] 当前运行的基座不包含原生插件[xxxx],请在manifest中配置该插件,重新制作包括该原生插件的自定义运行基座。
在这里插入图片描述问题分析:

1、 package.json 配置编写错误 (仔细进行对照).
2、 是否在manifest中配置 (这种睿智操作一般不会犯)
3、 原生插件是否使用自定义基座 (大部分是这个原因)
4、 自己开发原生插件是否有错误,未成功打包到基座 (原因是这个的概率很小,因为在 Android Studio 离线打包的时候我们调试通过了,说明插件没有问题。)


2、Android Studio 问题:Gradle task list not built during sync.Building the task list can impact Gradlesync performance on large projects.

解决方法如下:
在这里插入图片描述


完结~~, 撒花~~🎉🎉🎉🎉🎉

  • 22
    点赞
  • 81
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 17
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

m0rta1

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

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

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

打赏作者

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

抵扣说明:

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

余额充值