当前位置:   article > 正文

鸿蒙Serverless云函数搭建,告别传统后端?_serverless还需要后端吗

serverless还需要后端吗

[本文正在参加星光计划3.0-夏日挑战赛]

在6月19号,我在深圳分会场参与了“消失的服务器”HSD线下活动,HSD线下活动主要讲解了华为Serverless如何帮助开发者无服务器构建应用。在后面的限时Codelabs中,要求利用云函数做一个猜数字的安卓应用或者鸿蒙应用。我选择了后者,碍于没有提前做准备,集成云函数环境耗费了太多时间,也踩了很多坑,导致最后没能完成。本文将从零开始集成云函数服务,在云端完成猜数字的函数,Serverless服务或许是正在被引导的一种方向。

0. Serverless

关于Serverless服务,在华为开发者论坛上有一篇博文讲得非常详细,读者可仔细阅读。
Serverless服务帮您无服务器构建应用
那么Serverless到底有哪些优点,能帮我们做哪些事情呢?

  • 成本可控化
    在传统的开发模式中,一款APP从开发到上架,其中的业务分析,架构设计,商业运营,后期运维,这期间有非常多的沟通时间,成本,较长的开发周期。Serverless云服务,恰好能够提供这样一系列的针对性的云服务,让原先未知的不可控的成本开销,变得似乎可以量化。
  • 专注业务开发
    Serverless提供认证服务、云服务、云数据库、云托管、云存储以及Serverless模板等构建能力,统一构建自动弹性伸缩的后端服务。具有跨平台、上限快、低成本、免运维等特点。那么这样,大多数后端逻辑如果都基于Serverless开发,那么只需要聚焦业务代码的开发即可,关于后端的管理和运维都交给了云服务。

我粗浅的理解,一个前端开发者也能借助Serverless的简易便捷性,在低学习成本下,也能快速从前到后构建一个APP。

1.准备工作

1.3 业务分析

我们需要利用云函数,在云端判断猜测是否正确,应用端只负责接收用户传入的猜测的数字和传递随机数,数字传入云函数,云函数判断是大了,还是小了,还是猜对了,并且返回应用端。
说白了,这个判断在云端完成,HarmonyOS应用侧只负责编写页面,接收数据,返回结果,不做任何判断的事情。

1.2 创建应用,创建项目

  • 创建一个应用,并且在AppGallery Connect创建项目,注意包名一致哦!
  • 在HarmonyOS侧的云函数集成方式,目前只能在JAVA侧,不过由于HarmonyOS存在JS,JAVA的混合开发模式,编程语言倒不是问题。

2.集成云函数服务

这个环节,也是我踩坑比较多的环节,导致最后没能完成!

2.2 添加配置文件

2.2.1 我们进入到前文创建好的项目页面

image.png
选择下载配置文件,不包含密匙

配置文件中默认会包含AGC为应用分配的客户端密钥和API密钥信息,但是经过尝试存在一定BUG。所以我们选择不包含密匙,在应用启动时候再分配密匙。

2.2.2 添加进入HarmonyOS项目工程的应用级目录

image.png

2.2.3 添加依赖

进入应用级的build.gradle文件。
image.png
添加依赖, implementation ‘com.huawei.agconnect:agconnect-function-harmony:1.3.1.300’

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar', '*.har'])
    testImplementation 'junit:junit:4.13.1'
    ohosTestImplementation 'com.huawei.ohos.testkit:runner:2.0.0.200'
    /*****************添加云函数依赖*********************/
    implementation 'com.huawei.agconnect:agconnect-function-harmony:1.3.1.300'
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们可以看到关键词,function这里对应的就是云函数的服务,还有云数据库之类的服务也有对应的关键词,但是似乎存在一定的bug,并且服务版本较低,相对提供给安卓的服务还是有一定差距。

2.3 启动应用时,分配密匙

前面我们在工程文件中放入了不含密匙的配置文件,我们需要在应用启动时候分配密匙。

  • 我们先把前文的配置文件复制一份到resources/rawfile/目录下
    image.png
  • 我们进入JAVA文件夹中,打开MyApplication进行编写。
package com.example.random;

import com.huawei.agconnect.AGConnectInstance;
import com.huawei.agconnect.AGConnectOptionsBuilder;
import ohos.aafwk.ability.AbilityPackage;
import ohos.global.resource.RawFileEntry;
import ohos.global.resource.Resource;
import ohos.global.resource.ResourceManager;

public class MyApplication extends AbilityPackage {
    @Override
    public void onInitialize() {
        super.onInitialize();
        in();
    }
    private void in(){
        //添加如下代码
        try {
            AGConnectOptionsBuilder builder = new AGConnectOptionsBuilder();
            ResourceManager resourceManager = getResourceManager();
            // agconnect-services.json 文件路径
            RawFileEntry rawFileEntry =  resourceManager.getRawFileEntry("resources/rawfile/agconnect-services.json");
            Resource resource = rawFileEntry.openRawFile();
            builder.setInputStream(resource );
            // 如果您的json文件中不存在client_id、client_secret和api_key参数,需通过以下接口设置
            builder.setClientId("865143442517281664");
            builder.setClientSecret("6CDCF90B48310A1A80DF1B818848133BF3AE1EA8A61BE09C628894B262E7FC93");
            builder.setApiKey("DAEDADSYJyrB0DwxGwAReXl8nyYuCugvsdM4iCRmgf+jZhm+vzyPf+ViiznkGCTSI51Tv9JzxVpQlTjJuddZnqu9VuQ3nymhk8M7MA==");
            AGConnectInstance.initialize(this, builder);
        } catch (Exception e) {
            e.printStackTrace();
        }
//TODO: 添加代码结束
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

注意:
代码中所需要的,client_id、client_secret和api_key参数分别来自于AGC构建的对应的项目页面上。

image.png

2.4 添加权限!!!!!

进入config.json文件
在"abilities"下添加

      {
        "permissions": [
          "com.huawei.agconnect.core.DataAbilityShellProvider.PROVIDER"
        ],
        "name": "com.huawei.agconnect.core.provider.AGConnectInitializeAbility",
        "type": "data",
         //com.yzj.card 此处为自己工程的包名
        "uri": "dataability://com.yzj.card.AGConnectInitializeAbility"
      }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

至此,应用侧环境配置完成,下面为项目编写猜数字的云函数。

3.编写云函数

3.1 创建云函数

  • 我们在我的项目页面,左侧栏上找到云函数,点击进入。
    image.png
  • 点击,立即开通。
    image.png
  • 点击,创建函数,进入到创建函数页面。
    这里的函数名称自拟,我们可以看到这里提供了在线编辑函数,或者上传其他编程语言的压缩包。这里我们在线进行Javascript编程。
    image.png

3.2 编写云函数

我们进行在线的云函数编写。
image.png

  • 业务代码
let myHandler = function(event, context, callback, logger) 
  
{ 
    var res = new context.HTTPResponse(context.env, { 
            "res-type":"context.env", 
        "faas-content-type":"json", 
},"application/json", "200"); 
     //定义一个全局变量,接收用户传来的数据
    var guess_num; 
      //定义一个全局变量,接收应用端生成的随机数
    var random_num;
    //random_num = Math.ceil(Math.random()*100); 
    if (event.body) { 
        //如果传过来的是一个对象 那么解析,获取对应数据。
        var _body = JSON.parse(event.body); 
        //设置随机数
        random_num =_body.random_num;
       //当前猜测的数据 
       guess_num = _body.guess_num; 
    } else { 
        guess_num = event.guess_num; 
    } 
    //定义一个变量 用来返回结果
    var result="";
    //执行猜测函数
    result = randNumgame(guess_num);
    res = result; 
    //返回结果
    context.callback(res); 
  
    function randNumgame (inputNum) { 
        var resultString; 
    //判断猜测数据是否合法
    if (!isNumber(inputNum)) { 
        resultString = "input is not a number"; 
    } else { 
        var remainder = inputNum; 
         //大于正确数据
        if(remainder>random_num)
            resultString="大了";
        //等于正确数据
        else if(remainder ==random_num)
            resultString = "正确"
        else
            resultString = "小了"
    } 
    //返回字符串结果
    return resultString; 
} 
    //判断数据是否合法
    function isNumber (input) { 
    if (parseInt(input).toString == "NaN") { 
        return false; 
    } else { 
        return true; 
    } 
} 
} 
  
; 
  
module.exports.myHandler =myHandler;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 函数创建完成后,点击右上角保存保存
    image.png

3.3 添加触发器

  • 选择触发器,点击添加触发器
    image.png
  • 创建成功后,查看信息,找到函数的标识符号
    image.png
    这个名称非常重要,是我们进入函数的标识!

至此,云函数已经编写配置完成了,接下来是应用的简单编写。

4. HarmonyOS侧应用

4.1 启动云函数功能

启动云函数功能,只需要实例化AGConnectFunction即可

private AGConnectFunction function;
function = AGConnectFunction.getInstance();
  • 1
  • 2

我们对云函数的调用都是通过function实现的

4.2 调用云函数

 private void getWeek(int num) {

        HashMap<String,Integer> map = new HashMap();
        //两个对象,分别对应JS云函数中的 random_num  和 guess_num
        map.put("random_num",random_num);
        map.put("guess_num", num);
        /*
        注意! 这里的“guess-test” 就是我们在触发器配置信息里面的标识
         */
        function2.wrap("guess-$latest").call(map)
                .addOnCompleteListener(new OnHarmonyCompleteListener<FunctionResult>() {
                    @Override
                    public void onComplete(HarmonyTask<FunctionResult> task) {
                        if (task.isSuccessful()) {
                            //获取返回值,对应JS云函数中的res
                            String value = task.getResult().getValue();
                            HiLog.info(LABEL,value);
                            tx.setText(value);
                            //如果正确 再刷新随机数
                            if(value=="正确"){
                                random_num=new Random().nextInt(100);
                            }

                        } else {
                            Exception e = task.getException();
                            if (e instanceof AGCFunctionException) {
                                AGCFunctionException functionException = (AGCFunctionException) e;
                                int errCode = functionException.getCode();
                                String message = functionException.getMessage();
                                HiLog.error(LABEL,message);
                            }
                            // ...
                        }
                    }
                });
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

4.3 简单Demo

  • 界面
    微信图片_20220622135518.jpg
  • MainAbilitySlice
package com.example.random.slice;

import com.example.random.ResourceTable;
import com.huawei.agconnect.AGConnectInstance;
import com.huawei.agconnect.AGConnectOptionsBuilder;
import com.huawei.agconnect.auth.AGConnectAuthCredential;
import com.huawei.agconnect.function.AGCFunctionException;
import com.huawei.agconnect.function.AGConnectFunction;
import com.huawei.agconnect.function.FunctionResult;
import com.huawei.hmf.tasks.HarmonyTask;
import com.huawei.hmf.tasks.OnHarmonyCompleteListener;
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.agp.components.Button;
import ohos.agp.components.Component;
import ohos.agp.components.Text;
import ohos.agp.components.TextField;
import ohos.global.resource.RawFileEntry;
import ohos.global.resource.Resource;
import ohos.global.resource.ResourceManager;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;
import ohos.utils.zson.ZSONObject;

import java.util.HashMap;
import java.util.Random;

public class MainAbilitySlice extends AbilitySlice {
    private AGConnectFunction function2 ;
    private TextField textField;
    private Text tx;
    private Button btn;
    private int random_num;
    private static final HiLogLabel LABEL = new HiLogLabel(0, 0, "=>JAVA:" );
    @Override
    public void onStart(Intent intent) {

        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        //启动AGC服务
        start();
        //生成随机数
        Random r = new Random();
        random_num = r.nextInt(100);
        //测试控件
        textField = (TextField) findComponentById(ResourceTable.Id_tf);
        btn = (Button) findComponentById(ResourceTable.Id_btn);
        tx= (Text) findComponentById(ResourceTable.Id_tx);

        //绑定事件,点击一次进行数字猜测
        btn.setClickedListener(new Component.ClickedListener() {
            @Override
            public void onClick(Component component) {
                getWeek(Integer.parseInt(textField.getText()));
                HiLog.info(LABEL, String.valueOf(Integer.parseInt(textField.getText())));
            }

        });

    }

    private void start(){
        //启动函数
        try {
            function2= AGConnectFunction.getInstance();
        } catch (Exception e) {
            e.printStackTrace();

        }
    }
    private void getWeek(int num) {

        HashMap<String,Integer> map = new HashMap();
        //两个对象,分别对应JS云函数中的 random_num  和 guess_num
        map.put("random_num",random_num);
        map.put("guess_num", num);
        /*
        注意! 这里的“guess-test” 就是我们在触发器配置信息里面的标识
         */
        function2.wrap("guess-$latest").call(map)
                .addOnCompleteListener(new OnHarmonyCompleteListener<FunctionResult>() {
                    @Override
                    public void onComplete(HarmonyTask<FunctionResult> task) {
                        if (task.isSuccessful()) {
                            //获取返回值,对应JS云函数中的res
                            String value = task.getResult().getValue();
                            HiLog.info(LABEL,value);
                            tx.setText(value);
                            //如果正确 再刷新随机数
                            if(value=="正确"){
                                random_num=new Random().nextInt(100);
                            }

                        } else {
                            Exception e = task.getException();
                            if (e instanceof AGCFunctionException) {
                                AGCFunctionException functionException = (AGCFunctionException) e;
                                int errCode = functionException.getCode();
                                String message = functionException.getMessage();
                                HiLog.error(LABEL,message);
                            }
                            // ...
                        }
                    }
                });
    }
    @Override
    public void onActive() {
        super.onActive();
    }

    @Override
    public void onForeground(Intent intent) {
        super.onForeground(intent);
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

5. 结果

530ae113ff5b7b0f2ab0e5051158920d2022622142152.gif

关于codelab的内容其实是非常简单的,只是在环境配置上走了不少弯路,尤其是权限的赋予非常重要。在Serverless的加持下,开发人员能够专注于核心业务的开发,一些运维后端的处理可以交由云端托管。目前Serverless的全套服务在安卓上有非常多的案例,也比较好适配。鸿蒙开发上似乎资料不多,案例较少,但未来Serverless势必会全套适配鸿蒙开发。届时,必大有可为。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/102587
推荐阅读
相关标签
  

闽ICP备14008679号