AWS Lambda結合Android應用的初次嘗試

Android - 從零開始的AWS Lambda


和傳統的應用程序不同,用Lambda函數,結合其他微服務框架一起使用,可以把一個原本很複雜、冗餘的應用分解成一個個小的部分。每個框架之間只需要專注於做自己的事情,相互之間是松耦合的。這增加了開發程序的可能性、靈活性。

但是,要從頭自己開始學習還是有點點困難的。所以這次記錄了一下從頭開始學習Lambda函+Cognito與Android配合使用的過程。

“AWS Lambda 是一項計算服務,可使您無需預配置或管理服務器即可運行代碼。”
所以,我們只需要關注於代碼本身,由AWS Lambda 管理提供內存、CPU、網絡和其他資源均衡的計算機羣。

實現了啥

​ 創建一個簡單的 Android 移動應用程序,通過在本地應用程序中輸入字符串並提交,從 Amazon Cognito 身份池檢索 AWS 憑證,並使用包含請求數據的事件,調用 Lambda 函數處理請求,記錄Cloud Watch日誌並向前端返回響應,返回字符串。
在這裏插入圖片描述

​ 如果這些字連在一起就看不懂了:其實就是輸入兩個字符串,提交之後觸發了Lambda函數;觸發成功,返回toast,由Lambda函數記錄調用的日誌。
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-96rMMpZo-1590072707661)(C:\Users\ASUS\Desktop\STEP - 1.assets\image-20200521215306843.png)]
在這裏插入圖片描述

STEP - 1 創建AWS IAM用戶並授予管理員權限

AWS建議 不要使用 AWS 賬戶根用戶執行任務,而是應爲需要管理員訪問權限的每個人創建新的 IAM 用戶。

所以,擁有IAM用戶是訪問AWS服務的先決條件。

想要通過控制檯創建用戶,可以下載AWS CLI

1. 進入[AWS IAM console - 用戶 - 添加用戶]

在這裏插入圖片描述

2. 添加用戶

a. 用戶名即之後用於訪問服務的用戶名

b. 因爲之後要通過CLI訪問,所以務必要選擇編程訪問。選擇AWS控制檯訪問的話,之後可以通過 IAM 控制檯控制面板中找到賬戶的登錄 URL進行登錄。

https://account-ID-or-alias.signin.aws.amazon.com/console

在這裏插入圖片描述

3. 創建組

篩選策略中,選擇AdministratorAccess,即賦予組用戶管理員權限。
在這裏插入圖片描述

4. (可選)輸入可跟蹤用戶信息的鍵值對

本次實驗暫時用不到。接下來的步驟確認信息無誤後,擁有管理員權限的IAM用戶即可生成。
在這裏插入圖片描述

5. 返回[AWS IAM console - 用戶]

查看創建好的用戶,記錄用戶ARN,用於之後身份驗證服務。arn是AWS資源的唯一標識。
在這裏插入圖片描述

6. 選擇[安全證書 - 創建訪問密鑰]

把密鑰下載下來,之後登錄AWS CLI需要用。
在這裏插入圖片描述

STEP - 2 創建AWS IAM執行角色

要讓AWS Lambda對我們的資源進行訪問、操作,以實現具體功能,就要授予其相對應的權限。

IAM 角色就是信任的實體授予權限的安全方法。實體可以是其他賬戶的IAM用戶、AWS的服務、EC2上的程序代碼等。

1. 打開[AWS IAM console - 用戶 - 角色]

選擇創建角色,使用案例選擇Lambda。
在這裏插入圖片描述

2. 確保當前角色擁有CloudWatch Logs日誌寫入的權限

這裏選擇AWSLambdaBasicExecutionRole
在這裏插入圖片描述

3. 角色名稱鍵入lambda-android-role

在這裏插入圖片描述
這樣,擁有basic權限的角色就配置好了。

STEP - 3 創建函數

接下來的操作會用到shell,編寫一個簡單的觸發器,在本地打包創建成lambda函數並提交。

  • PS😋也可以選擇在AWS Lambda的網頁端中,通過現成的模板或創作設置想要的函數。 也支持在本地寫完Lambda函數,打包之後上傳。

    [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-495OgRET-1590072707671)(C:\Users\ASUS\Desktop\STEP - 1.assets\image-20200521185723392.png)]

在這裏,我選擇用WSL 2(Windows Subsystem for Linux 2) 進行腳本編寫創建,如果使用Linux/MacOS直接調用默認shell就可以了。

其實後來我看開發指南,Windows自帶的Power Shell也是可以的,被我弄複雜了;但是Power Shell長得醜,算了不虧。
方法有很多,只要是shell能編寫程序就OK。

1. 創建index.js文件

Lambda支持很多語言,如:javascript, java, python, c# … 這裏按照官方給的教程,我用的是node.js,很簡潔。

  • 函數第一個參數event是來自調用程序(即我們的APP上的操作)信息對象。
  • 第二個參數contexts是包含有關調用、函數和執行環境的對象,這裏將會獲取日誌流的名稱,然後將其返回到調用方。
  • 第三個參數callback 是一個回調函數,可以用它對調用程序(APP)進行響應(response)。

這裏我們簡單定義了,調用程序中的函數,在console中記錄事件並返回給客戶端一個字符串的操作。

exports.handler = function(event, context, callback) {
   console.log("Received event: ", event);
   var data = {
       "greetings": "Hello, " + event.firstName + " " + event.lastName + "."
   };
   callback(null, data);
}

2. 打包文件

把文件打包成壓縮包,並下載aws命令行。

zip function.zip index.js

sudo apt install awscli

在這裏插入圖片描述
在這裏插入圖片描述

如果是第一次使用AWS CLI,那麼就要設置用戶。

aws config

按照指令,輸入STEP - 1中下載好[用戶]-[安全證書]中的key,以及服務的地域等信息。
在這裏插入圖片描述

3. 使用 create-function 命令創建 Lambda 函數

  • handler的名字之所以叫index.handler是因爲這是index.js文件中的handler函數。(好像是這樣的)
  • lambda role arn可以直接在AWS CLI中查看,也可以在IAM role中查看。
  • 這裏創建的函數名稱叫AndroidBackendLambdaFunction
aws lambda create-function --function-name [lambda函數名] \ --zip-file 
fileb://[目錄路徑]/function.zip --handler index.handler --runtime nodejs12.x \
--role [lambda role arn]

在這裏插入圖片描述

同時,可以在AWS Lambda網頁端查看到函數了。
在這裏插入圖片描述

STEP - 4 創建Amazon Cognito 身份池

Amazon Cognito 提供用戶池和身份池。

用戶池是爲應用程序提供註冊和登錄選項的用戶目錄,身份池提供 AWS 憑證以向用戶授予對其他 AWS 服務的訪問權限。

這裏用身份池,並授予其執行Lambda AndroidBackendLambdaFunction 函數的權限。

1. 打開 Amazon Cognito 控制檯

2. 新建身份池

身份池名爲 JavaFunctionAndroidEventHandlerPool ,並棄用未經驗證的訪問權限(即允許遊客登錄應用程序,觸發該函數)。
在這裏插入圖片描述
在未經身份驗證(unauthenticated identities)的角色中,輸入

{
   "Version":"2012-10-17",
   "Statement":[
      {
         "Effect":"Allow",
         "Action":[
            "mobileanalytics:PutEvents",
            "cognito-sync:*"
         ],
         "Resource":[
            "*"
         ]
      },
      {
         "Effect":"Allow",
         "Action":[
            "lambda:invokefunction"
         ],
         "Resource":[
            "arn:aws:lambda:us-east-1:[account-id]:function:AndroidBackendLambdaFunction"
         ]
      }
   ]
}

Resource對應的就是AndroidBackendLambdaFunction的arn。[account-id]即IAM用戶的id。
在這裏插入圖片描述
創建成功後,系統會提示保存身份池id等信息,記錄下來,下一步將會用到.

STEP - 5 創建Android程序

做了這麼多,終於可以開始創建應用了,其實應用在這個demo裏面就顯得比較簡單。

值得注意的是,Lambda函數最好在API 28以上運行。在新建APP的時候,注意API版本。

如果在創建工程之後,同步(sync)失敗,(可能報app compact xxx29.+之類的錯),有以下幾種解決方法:

  1. 檢查project structure - Android SDK版本和build.gradle(module: app)中的sdk版本是否不一致.
  2. 檢查project structure -Update,是否版本過低. 建議升級較新的Android Studio版本. 我就是因爲版本比較舊,所以不能用29的包.

1. 創建一個Hello World工程

  • 創建的工程名爲AndroidEventGenerator

  • build.gradle (Module:app) 文件的 dependencies 部分中添加以下內容

    implementation 'com.amazonaws:aws-android-sdk-core:2.2.+'
    implementation 'com.amazonaws:aws-android-sdk-lambda:2.2.+'
    implementation "commons-logging:commons-logging:1.2"
  • AndroidManifest.xml中,添加連接 Internet的權限
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.androideventgenerator">
	...
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

2. 寫代碼

  • 爲了Lambda函數能夠正確響應,創建和之前index.js中實現相同方法、參數的RequestClassResponseClass
package com.example.androideventgenerator;

public class RequestClass {
    String firstName;
    String lastName;

    public RequestClass(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public RequestClass() {
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}
package com.example.androideventgenerator;

public class ResponseClass {
    String greetings;

    public ResponseClass(String greetings) {
        this.greetings = greetings;
    }

    public ResponseClass() {
    }

    public String getGreetings() {
        return greetings;
    }

    public void setGreetings(String greetings) {
        this.greetings = greetings;
    }
}
  • MyInterface

    用annotation@LambdaFunction對接口函數進行映射,注意這個映射的函數名應該一致,參數是request。

package com.example.androideventgenerator;

import com.amazonaws.mobileconnectors.lambdainvoker.LambdaFunction;

public interface MyInterface {
    /**
     * Invoke the Lambda function "AndroidBackendLambdaFunction".
     * The function name is the method name.
     */
    @LambdaFunction
    ResponseClass AndroidBackendLambdaFunction(RequestClass request);

}
  • MainActivity

    代碼要注意的地方我寫在註釋裏。。好長啊

package com.example.androideventgenerator;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.amazonaws.auth.CognitoCachingCredentialsProvider;
import com.amazonaws.mobileconnectors.lambdainvoker.LambdaFunctionException;
import com.amazonaws.mobileconnectors.lambdainvoker.LambdaInvokerFactory;
import com.amazonaws.regions.Regions;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    EditText editText1, editText2;
    Button button;
    RequestClass request;
    MyInterface myInterface;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 創建提供Cognito服務的對象實例,這句在創建身份池的時候AWS已經提供了
        CognitoCachingCredentialsProvider cognitoProvider = new CognitoCachingCredentialsProvider(
       //把XXXXXXXXX改成創建的身份池id
                this.getApplicationContext(), "us-east-1:XXXXXXXXX", Regions.US_EAST_1);
        
        // 創建調用Lambda函數的工廠代理對象
        //第三個參數是 a AWS credentials provider
        LambdaInvokerFactory factory = new LambdaInvokerFactory(this.getApplicationContext(),
                Regions.US_EAST_1, cognitoProvider);
		//用工廠和默認的binder(第二個參數)創建一個Lambda對象實例
        myInterface = factory.build(MyInterface.class);
        editText1 = findViewById(R.id.editText);
        editText2 = findViewById(R.id.editText2);
        button = findViewById(R.id.button);
        
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String firstName = editText1.getText().toString();
                String LastName = editText2.getText().toString();
                request = new RequestClass(firstName, LastName);
                // The Lambda function invocation results in a network call.
                // Make sure it is not called from the main thread.
                //執行一個異步的請求
                new myAsyncTask().execute(request);
            }
        });


    }
	//爲了防止內存泄漏,不要把AsyncTask放在主線程中
    class myAsyncTask extends AsyncTask<RequestClass, Void, ResponseClass> {

        @Override
        protected ResponseClass doInBackground(RequestClass... params) {
            // invoke "echo" method. In case it fails, it will throw a
            // LambdaFunctionException.
            try {
                //******調用Lambda函數,返回request********//
                return myInterface.AndroidBackendLambdaFunction(params[0]);
            } catch (LambdaFunctionException lfe) {
                Log.e("Tag", "Failed to invoke echo", lfe);
                return null;
            }
        }

        @Override
        protected void onPostExecute(ResponseClass result) {
            if (result == null) {
                return;
            }
            /* 執行完onBackgoundTask方法後,執行onPostExecute方法 !
            ** 如果onBackgoundTask return回來的結果不爲空,即Lambda函數調用成功的話!
            ** 就do a toast !
            */
            Toast.makeText(MainActivity.this, result.getGreetings(), Toast.LENGTH_LONG).show();
        }
    }
}

3. 結果

雖然我的佈局有點醜,但是可以看出,點擊SUBMIT之後,輸入的字符串在應用內被響應了。
[外鏈圖片轉存中...(img-o00olEZx-1590072707677)]

同時,在AWS CloudWatch或AWS Lambda監控的CloudWatch中,都可以查看到Lambda函數記錄的日誌內容。
在這裏插入圖片描述

補充:調用函數的收費時間等信息也是可以看到的。
可以看出,我們只寫了Lambda功能代碼,沒有部署過物理資源,而實際調用物理資源的時間也微乎其微。
在這裏插入圖片描述

參考: AWS Lambda 開發人員指南

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章