星闪聊天软件,正式发布,让Android手机体验星闪科技。 原创 精华
我们的上篇文章:点我访问
今天来写的这篇文章,是来告诉51CTO的小伙伴们,经过一整天的完善,我把聊天的功能做得很棒很棒了。
解决了之前开发时遇到的各种问题,这些后面我都会讲到。也同时,修复了不少串口输出的Bug。
作为一款星闪聊天软件,星闪,NearLink,正是我们要凸显的一大特色。它是华为工程师们自立自强创造出的一大创新杰作,开创了我们中国原生的新一代近距离无线连接技术,从陪跑跟随全球脚步,到领跑超越蓝牙。
那么我们这款软件,也是在这个情况下,我认为的一款:足以让开发者和用户,都能接受,都能愿意参与使用体验开发的聊天APP,它的优点就是让任意设备(只要能和CH340芯片正常通讯)都能外挂一款星闪开发板,进行双向SLE通讯。
所以,在接下来的时间里,我将会慢慢的讲述我一个人,怎么敲出这款APP的每一行代码。我把这篇文章当成一个特别棒的教学资料,公开给大家,也是给我自己做的一个最近总结。
让我们来到C代码,我们使用小熊派厂商的星闪开发板,因为优点就是看重了他们对鸿蒙社区的贡献(在Gitee上,他们的项目,提交了不少,这一次星闪开发板的资料也是给力很多,上手简单)和这一次他们对星闪开发板的开源工作。
代码地址:点我前往我的仓库
static void ssaps_server_write_request_cbk(uint8_t server_id, uint16_t conn_id, ssaps_req_write_cb_t *write_cb_para,
errcode_t status)
{
osal_printk("%s ssaps write request callback cbk server_id:%x, conn_id:%x, handle:%x, status:%x\r\n",
SLE_UART_SERVER_LOG, server_id, conn_id, write_cb_para->handle, status);
/*
osal_printk("\n");
osal_printk("Let's start chatting, This is the content of the client: %x", write_cb_para->value);
*/
osal_printk("\n Let's start chatting, This is the content of the client:");
if ((write_cb_para->length > 0) && write_cb_para->value) {
uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)write_cb_para->value, write_cb_para->length, 0);
}
osal_printk("\n");
}
我在小熊派的SLE_UART Demo上,添加了标注客户端服务端发送文本的标记,这很简单,因为我对文本处理的负担都放在了Android软件上。这里是服务端接收客户端的代码,下面是客户端接收服务端的代码。
void sle_uart_notification_cb(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
errcode_t status)
{
unused(client_id);
unused(conn_id);
unused(status);
//osal_printk("\n sle uart recived data : %s\r\n", data->data);
//uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)(data->data), data->data_len, 0);
osal_printk("\n");
//osal_printk("Let's start chatting, This is the content of the server: %s", data->data);
osal_printk("\n Let's start chatting, This is the content of the server:");
uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)(data->data), data->data_len, 0);
osal_printk("\n");
}
void sle_uart_indication_cb(uint8_t client_id, uint16_t conn_id, ssapc_handle_value_t *data,
errcode_t status)
{
unused(client_id);
unused(conn_id);
unused(status);
//osal_printk("\n sle uart recived data : %s\r\n", data->data);
//uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)(data->data), data->data_len, 0);
osal_printk("\n");
//osal_printk("Let's start chatting, This is the content of the server: %s", data->data);
osal_printk("\n Let's start chatting, This is the content of the server:");
uapi_uart_write(CONFIG_SLE_UART_BUS, (uint8_t *)(data->data), data->data_len, 0);
osal_printk("\n");
}
C的代码,我就改动这么多。那么,C的开发工作结束后,我是第一时间启动了Android前端后端的开发。那个时候,我对软件的规划是:++先做出UI,再做出CH34X芯片通信,因为那时候我看到Github上有不少资料,但是后来我后悔了。最后就是做功能。++
就是这样的规划,让我后来开发时遇到了太多太多的麻烦,如下:
- Github上开源的相关CH34X Java代码,质量参差不齐,不利于整体项目开源。
- 沁恒厂商对相关的资料老旧,很多相关的代码我是通过海外平台Stackoverflow和相关开源文档翻译后才得到确切资料的。
- Android上做聊天软件,本人还是第一次做,初版的代码有点杂乱无序,主函数里还写了不少无关注释,这让我上下翻阅代码浪费了很多时间。
- 还有很多很多…
足够庆幸的是,我也是在最近俩天内把NLChat这款APP做出来了。
这是完善后的聊天页面,我在今天25号,成功的修好了相关代码的问题,这也是这几天开发的重点中的重点。我们不是做什么Demo,是真的要做一款文字通讯软件,核心就得是处理文本。
那么,接下来就是我今天对CH34xReadData该函数的大改后的情况:
private void CH34xReadData() {
//先播报星闪软件情况,已经UART接入星闪网络,再好好的处理字符
HhandlerI.sendEmptyMessage(10);
StringBuffer stringBuffer = new StringBuffer();
MainAPP.CH34X.setReadListener(bytes -> {
//字节转文本
String string = StringUtils.needProcess().bytesToString(bytes);
Log.v(TAG, "长度:bytes.length="+ bytes.length + "\t内容:" + string);
//进行文本处理
String processedString = CH34xProcessingForReadData(string);
stringBuffer.append(processedString);
//处理完再打印到UI上
runOnUiThread(() -> {
NearLinkServerText.append(processedString);
if (NearLinkServerText.length() > 2048) {
String str = NearLinkServerText.getText().toString().substring(NearLinkServerText.getText().length() - 1024, NearLinkServerText.getText().length());
NearLinkServerText.setText("");
NearLinkServerText.append(str);
}
});
});
}
private StringBuilder buffer = new StringBuilder();
@SuppressLint("SimpleDateFormat")
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS");
private String CH34xProcessingForReadData(String string) {
buffer.append(string);
String result = buffer.toString();
int endIndex = result.indexOf("\n");
if (endIndex != -1) {
String completeFirstData = result.substring(0, endIndex + 1);
String completeSecondData = "";
Log.v(TAG, "长度:completeFirstData.length="+ completeFirstData.length() + "\t内容:" + completeFirstData);
buffer.delete(0, endIndex + 1);
//去掉特定的前缀字符串,然后返回(聊天内容),只有当消息包含特定的前缀时才处理
if (completeFirstData.contains(ChatUtils.getPrefixServer()) || completeFirstData.contains(ChatUtils.getPrefixClient())) {
if (completeFirstData.startsWith(ChatUtils.getPrefixServer())) {
completeSecondData = completeFirstData.replace(ChatUtils.getPrefixServer(), "").trim();
Log.v(TAG, "长度:completeSecondData.length="+ completeSecondData.length() + "\t内容:" + completeSecondData);
} else if (completeFirstData.startsWith(ChatUtils.getPrefixClient())) {
completeSecondData = completeFirstData.replace(ChatUtils.getPrefixClient(), "").trim();
Log.v(TAG, "长度:completeSecondData.length="+ completeSecondData.length() + "\t内容:" + completeSecondData);
}
//添加时间戳
String timestamp = dateFormat.format(new java.util.Date());
completeSecondData = timestamp + " - " + completeSecondData;
//确保消息以换行符结尾
if (!completeSecondData.endsWith("\n")) {
completeSecondData += "\n";
}
return completeSecondData;
}
} else {
return "";
}
return "";
}
其中一行MainAPP.CH34X.setReadListener(...)
是我们的写进CH34X这个Lib里面的ReadThread,本身就是一个Thread线程。当收到了串口反馈的文本后,不管是什么文本,都会以bytes字节形式输出。
这下就很简单了,字节转文本,进行文本处理,处理完后再打印在UI上,这些工作写出来很简单,做起来花的时间真的很长,首先我在自己反编译后翻译的CH34X库里写的数据包为32,我还得需要做一个StringBuffer持续写入成一个完整字节数据,再做处理。++这个问题我是今天25号才排查出来的。++
处理的代码就是去掉后缀Enter后,就是该怎么去掉我们在C代码上面的那么长的标注呢?我的做法是这样的:我们用一个所谓的白名单制度,只要是如下的字符串接收进Android APP里,软件再开始处理去掉前缀,字符串是长这样的:
public class ChatUtils {
//这里设置的是跟C代码相关的,白名单获取聊天文本,当这些文本出现在串口通讯里面的时候,提取这String后者即可,期间过滤掉前者和大量串口log。
private static final String PREFIX_SERVER = " Let's start chatting, This is the content of the server:";
private static final String PREFIX_CLIENT = " Let's start chatting, This is the content of the client:";
//对方为星闪服务端
public static String getPrefixServer() {
return PREFIX_SERVER;
}
//对方为星闪客户端
public static String getPrefixClient() {
return PREFIX_CLIENT;
}
}
这里用static静态了,直接写个getter函数是可以直接调用的,只要匹配上了,就可以把聊天句子前缀内容给去掉了,换成代码里安排好的时间戳,就可以打印在UI上了。
这俩天的工作,不仅仅这些,还有UI上我们也能在连接开发板前,设置好串口数据了哦。
Java代码如下:
resources = getApplicationContext().getResources();
UartSettingsBaud = resources.getStringArray(R.array.listBaud);
UartSettingsData = resources.getStringArray(R.array.listData);
UartSettingsStop = resources.getStringArray(R.array.listStop);
UartSettingsParity = resources.getStringArray(R.array.listParity);
UartSettingsParityII = resources.getStringArray(R.array.listParityNum);
RadioButtonBaud4800 = findViewById(id.rbBaud4800);
RadioButtonBaud4800.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
if (isChecked) {
wchUartSettings.setBaudRate(Integer.parseInt(UartSettingsBaud[0]));
SnackBarToastForDebug("已设置波特率" + wchUartSettings.getBaudRate() + "!","设置成功",0,Snackbar.LENGTH_SHORT);
} else {
if (CheckBoxUartWarn.isChecked())
Toast.makeText(MainActivity.this,"更改波特率中",Toast.LENGTH_SHORT).show();
}
}
});
.......
public WCHUartSettings() {}
public static WCHUartSettings needGetData() { return getData.thing; }
protected static class getData { private static final WCHUartSettings thing = new WCHUartSettings(); }
public int getBaudRate() {
return baudRate;
}
public void setBaudRate(int baudRate) {
this.baudRate = baudRate;
}
public byte getDataBit() {
return dataBit;
}
public void setDataBit(byte dataBit) {
this.dataBit = dataBit;
}
public byte getStopBit() {
return stopBit;
}
public void setStopBit(byte stopBit) {
this.stopBit = stopBit;
}
public byte getParity() {
return parity;
}
public void setParity(byte parity) {
this.parity = parity;
}
代码介绍的最后,我得来讲讲我反编译翻译后的CH34X仓库,CH34X由驱动库、线程读和安卓广播组成。驱动库的代码尤为重要,粗略如下:
public int resumeUsbList() {} //检查权限,初始化重启后等等情况都是用这个函数解决检查权限
public boolean uartInit() {} //设置CH34X芯片
public boolean setConfig(int baudRate, byte dataBits, byte stopBits, byte parity, byte flowControl) {}
//设置串口接口的波特率、数据位、停止位、奇偶校验位以及流控。
public int writeData(byte[] data, int dataLength) {} //发送数据
public int writeData(byte[] data, int dataLength, int timeOut) {} //也是发送数据,包括了timeOut超时检查
public void closeDevice() {} //关闭USB设备,第一次启动Android APP时和销毁程序时用得到
public void connectionDevice(UsbDevice usbDevice) {} //创建新的USB设备、USB接口、声明连接;启动接收(读)的线程
private int controlTransfer(int i, int i2, int i3) {} //配置使用USB的UART设置,该函数常被UartInit()调用
//尤其是这个controlTransfer函数,光是这个函数我翻阅了不少资料,代码里面都有注释引导链接前往。
最后的最后,也是一件利于大家的好事,我开启了Gitee仓库的地址点我前往,为了方便那些不便前往GitHub的开发者。
第一篇我们约定的大家,还记得嘛?
我估计我自己还会花时间对C的源码做修改,同时在安卓上一定要做好文本提炼工作后,用SQLite做本地数据库保存,确保不会丢失聊天数据的同时,还能写时间戳保存。
接下来的几天的开发工作,我们都会围绕这些去做了,确保功能越做越多,越来越好用。让更多人愿意接触星闪开发板,愿意支持我们的NLChat开发工作。
希望51CTO的大家,能多多支持一下我们,有条件的可以前往Github给我们多多Star,您们的支持是我坚持开发最大的动力。让我们下一篇文章再见。
很有用的文章,感谢作者开源。
不错不错,必须支持!
谢谢OvO
大大支持一下
感觉很厉害
一个人开发666