鸿蒙结合SpringBoot实现简易消息通知APP
本项目为学校鸿蒙课程的课程实践设计,本身有一定的Web开发基础,但是从来没有学过移动APP开发,上了这门课之后感觉处处跟Web开发不同,所以想试着结合移动端和SpringBoot给初学者一种快速实现网络连接应用的途径,以供自己学习和大家参考,我也是个鸿蒙开发新手,如果觉得我做的不好大家轻喷哈!
一. 开发环境
本人日常使用macOS系统开发,但是不知道为什么在macOS里DevEco Studio开启鸿蒙虚拟机会非常卡顿(我的MacBookPro虽然不是顶配但是也有32G内存和i7处理器应该不是配置问题),所以这次开发环境使用了Windows机器。DevEcoStudio版本3.0.0.993,SDK7版本。因为设计SpringBoot所以需要还需要IDEA,版本为2022.2(Ultimate Edition)。JDK8。鸿蒙虚拟机使用了自带的本地虚拟机,因为远程的很卡(如果本地虚拟机提示不支持当前CPU可以看一下我之前的帖子鸿蒙DevEco本地模拟器提示不支持的CPU解决办法_•ө•的博客-CSDN博客)。
二.环境搭建
2.1鸿蒙项目搭建
这里SDK版本要选择7要不然使用Java语言。
2.2 服务器端搭建
因为消息通知服务的消息是从服务器发布的,需要使用SpringBoot搭建服务端。直接使用SpringBoot构造器创建,注意最好提前勾选需要的,必须勾选SpringWeb,当然也可以从maven导入,但是这样更省事,顺便把lombok,MyBatis和MySQL也勾上。
至此,项目初始化完成。
三.重点代码
本文不会详细的讲解鸿蒙的基础组件和SpringBoot基础。仅挑几个我觉得比较重要的展示。
3.1 项目原理
本项目的主要原理是用SpringBoot作为服务端,然后APP端使用HttpURLConnection来建立双端链接,通过URL来进行文本的上传和下载,如果将服务端部署到服务器上即可实现脱机云端连接。
3.2 鸿蒙端代码编写
因为HttpURLConnection连接比较麻烦,可以选择使用封装一个Net工具类。比如:
注意,网络连接行为不可以放在主线程!有可能会导致主线程阻塞让整个程序直接崩溃。
这里我使用了ZZR老师编写的ZZR网络工具类,功能比自己编写工具类更加齐全。
用法如图:
除此之外还需要一个登录界面和一个二级界面用来容纳组件
这里我简单做了一个:
输入密码如果成功会进入二级界面(SecondSilence),如果密码错误则会提示密码错误。
注意!如果要访问网络需要进行两个设置
左侧的是允许使用HTTP链接访问,如果你想要访问的链接是HTTPS链接则可以不设置这个,右侧则是启用网络访问权限。
配置完之后,我们就可以测试了。可以先写一个demo,测试能否访问成功。
3.3 服务端编写
服务端是一个标准的SpringBoot程序,不再详细说明,主要是对外暴露的两个端口
一个是/message/get 一个是/message/update/更新内容。
3.4 服务端部署
这个我们需要调用的API要发布到公网才能被模拟器访问,本地的服务器可能可以访问到本地部署的服务,但是我并没有找到怎么访问,所以就直接发布到了公网上。
部署需要一台公网服务器,大家可以自行准备,我用的是腾讯的服务器。
首先把项目打包
打包完成后,可以看到一个jar包。将这个jar包上传
上传完毕 在服务器中上传
之后使用命令部署jar包,如果需要后台运行命令nohup java -jar xxxx.jar &
前台运行可以直接java -jar xxx.jar
运行完毕,我们测试是否可以在浏览器上访问。
测试成功。
四.双端结合
在slice里绑定一个Onclick方法,在方法里进行访问和调用即可。
五.效果展示
这样我们就可以实现一个在线的,自己的通知本了!
是不是非常简单捏~本帖只是提出一种结合的思想,简单做了个小demo,实际上可以借助HTTP、HTTPS链接可以完成更复杂的在线功能。我们以后继续共同学习探索!
中原工学院 软件学院
ZYY
附件 主要代码
public class MainAbilitySlice extends AbilitySlice {
Text text = null;
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_main);
text = (Text)findComponentById(ResourceTable.Id_t1);
Button but1 = (Button) findComponentById(ResourceTable.Id_bt1);
TextField textField1 = (TextField) findComponentById(ResourceTable.Id_tf1);
TextField textField2 = (TextField) findComponentById(ResourceTable.Id_tf2);
but1.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent intent1 = new Intent();
if(textField1.getText().equals("zyy") && textField2.getText().equals("zyy123")){
present(new SecondSlice(),intent1);
}else{
text.setTextColor(Color.RED);
text.setText("账号或密码错误");
}
}
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
public class SecondSlice extends AbilitySlice {
@Override
public void onStart(Intent intent) {
super.onStart(intent);
super.setUIContent(ResourceTable.Layout_ability_second);
Button button1 = (Button) findComponentById(ResourceTable.Id_second_bt1);
Button button2 = (Button) findComponentById(ResourceTable.Id_second_bt2);
Button button3 = (Button) findComponentById(ResourceTable.Id_second_bt3);
TextField textField1 = (TextField) findComponentById(ResourceTable.Id_second_tf1);
ZZRHttp.get("http://你的ip地址:8080/message/get", new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {
textField1.setText("异常"+s);
}
@Override
public void onResponse(String s) {
textField1.setText(s);
}
});
button1.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent intent1 = new Intent();
present(new MainAbilitySlice(),intent1);
}
});
button2.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
ZZRHttp.get("http://你的ip地址:8080/message/get", new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {
textField1.setText("异常"+s);
}
@Override
public void onResponse(String s) {
SimpleDateFormat a = new SimpleDateFormat("yyyy年MM月dd日HH时mm分ss秒");
Date nowTime = new Date();
String time = a.format(nowTime);
textField1.setText(s+"上次刷新时间:"+ time);
}
});
}
});
button3.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
String url = "http://你的ip地址:8080/message/update/"+textField1.getText();
ZZRHttp.get(url, new ZZRCallBack.CallBackString() {
@Override
public void onFailure(int i, String s) {
textField1.setText("上传失败!请重试");
}
@Override
public void onResponse(String s) {
textField1.setText("上传成功!刷新后就可以看到您刚才上传的内容了。");
}
});
}
});
}
@Override
public void onActive() {
super.onActive();
}
@Override
public void onForeground(Intent intent) {
super.onForeground(intent);
}
}
public class NetUtils {
public static String NetUtil(String URL,String Method){
try{
java.net.URL url = new URL(URL);
//得到connection对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == 200){
//如果返回值正常,数据在网络中是以流的形式得到服务端返回的数据
//调用getInputStream()方法获得服务器返回的输入流
//对输入流进行读取,获取响应信息
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuffer res = new StringBuffer("");
while ((line = reader.readLine()) != null) {//循环从流中读取
res.append(line);
}
//关闭流
reader.close();
connection.disconnect();
return res.toString();
}else{
connection.disconnect();
return "连接异常";
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "发生错误!";
}
}
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:alignment="center"
ohos:orientation="vertical">
<Text
ohos:id="$+id:t1"
ohos:height="match_content"
ohos:width="match_content"
ohos:layout_alignment="horizontal_center"
ohos:text="欢迎登录"
ohos:text_size="52vp"
/>
<TextField
ohos:id="$+id:tf1"
ohos:height="40vp"
ohos:width="300vp"
ohos:background_element="#FF69FBFF"
ohos:margin="10px"
ohos:text_size="30vp"
/>
<TextField
ohos:id="$+id:tf2"
ohos:height="40vp"
ohos:width="300vp"
ohos:background_element="#FF69FBFF"
ohos:text_size="30vp"
/>
<Button
ohos:id="$+id:bt1"
ohos:height="60vp"
ohos:width="200vp"
ohos:background_element="#FF8484FF"
ohos:text="登录"
ohos:text_size="60vp"
ohos:margin="20px"
/>
</DirectionalLayout>
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
xmlns:ohos="http://schemas.huawei.com/res/ohos"
ohos:height="match_parent"
ohos:width="match_parent"
ohos:orientation="vertical">
<Text
ohos:id="$+id:second_t1"
ohos:height="match_content"
ohos:width="match_content"
ohos:text="请查看通知"
ohos:text_size="60vp"
/>
<TextField
ohos:id="$+id:second_tf1"
ohos:height="500vp"
ohos:width="410vp"
ohos:margin="10vp"
ohos:background_element="#FF69FBFF"
ohos:text_size="30vp"
ohos:text="如果出现此提示,则可能出现服务器异常,请联系管理员或稍后再试!"
/>
<Button
ohos:id="$+id:second_bt1"
ohos:height="60vp"
ohos:width="200vp"
ohos:background_element="#FF8484FF"
ohos:text="退出登录"
ohos:text_size="30vp"
ohos:margin="20px"
/>
<Button
ohos:id="$+id:second_bt2"
ohos:height="60vp"
ohos:width="200vp"
ohos:background_element="#FF8484FF"
ohos:text="刷新通知"
ohos:text_size="30vp"
ohos:margin="20px"
/>
<Button
ohos:id="$+id:second_bt3"
ohos:height="60vp"
ohos:width="200vp"
ohos:background_element="#FF8484FF"
ohos:text="上传我写的通知"
ohos:text_size="30vp"
ohos:margin="20px"
/>
</DirectionalLayout>
{
"app": {
"bundleName": "com.zyy",
"vendor": "example",
"version": {
"code": 1000000,
"name": "1.0.0"
}
},
"deviceConfig": {
"default": {
"network": {
"cleartextTraffic": true
}
}
},
"module": {
"package": "com.zyy",
"name": ".MyApplication",
"mainAbility": "com.zyy.MainAbility",
"deviceType": [
"phone",
"tablet",
"tv",
"wearable",
"car"
],
"distro": {
"deliveryWithInstall": true,
"moduleName": "entry",
"moduleType": "entry",
"installationFree": false
},
"abilities": [
{
"skills": [
{
"entities": [
"entity.system.home"
],
"actions": [
"action.system.home"
]
}
],
"name": "com.zyy.MainAbility",
"description": "$string:mainability_description",
"icon": "$media:icon",
"label": "$string:entry_MainAbility",
"launchType": "standard",
"orientation": "unspecified",
"visible": true,
"type": "page"
}
],
"reqPermissions": [
{
"name": "ohos.permission.INTERNET"
}
]
}
}
apply plugin: 'com.huawei.ohos.hap'
apply plugin: 'com.huawei.ohos.decctest'
//For instructions on signature configuration, see https://developer.harmonyos.com/cn/docs/documentation/doc-guides/ide_debug_device-0000001053822404#section1112183053510
ohos {
compileSdkVersion 6
defaultConfig {
compatibleSdkVersion 6
}
buildTypes {
release {
proguardOpt {
proguardEnabled false
rulesFiles 'proguard-rules.pro'
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
testImplementation 'junit:junit:4.13.1'
ohosTestImplementation 'com.huawei.ohos.testkit:runner:2.0.0.400'
implementation 'com.zzrv5.zzrhttp:ZZRHttp:1.0.1'
}
decc {
supportType = ['html', 'xml']
}