앱 개발 및 실행 (안드로이드)

앱 개발 환경 설정

넥사크로 안드로이드 앱은 안드로이드 앱 개발 도구인 안드로이드 스튜디오를 이용하여 개발하거나 넥사크로 제품군의 하나인 앱 빌더(App Builder)를 이용하여 개발할 수 있습니다. 이번 장에서는 안드로이드 스튜디오를 이용하여 앱을 개발하는 방법을 설명합니다. 기본적인 개발 절차는 일반적인 안드로이드 앱을 만드는 방법과 동일합니다.

이미 앱 개발 환경이 설정되어 있다면 앱 개발 환경 설정은 건너뛰어도 됩니다.

안드로이드 스튜디오 설치파일은 기본적으로 SDK 도구를 포함하고 있습니다. 아래 사이트에서 환경에 맞는 설치파일을 내려받을 수 있습니다.

https://developer.android.com/studio/index.html

이번 장에서는 안드로이드 스튜디오 4.1.1 버전 설치를 기준으로 작성했습니다.

앱 프로젝트 개발

앱 개발 환경이 설정되었다면 안드로이드 스튜디오를 실행해서 안드로이드 운영체제에서 동작하는 앱을 만들 수 있습니다. 앱 프로젝트를 진행하기 전에 넥사크로 스튜디오에서 생성한 모듈, 소스, 리소스 파일은 지정된 경로에 위치해야 합니다.

프로젝트 생성

1

기존에 열어놓은 프로젝트가 없다면 빠른 시작(Quick Start) 창에서 새로운 프로젝트를 생성합니다. 아래 그림에서 [Create New Project]를 선택합니다.

android_studio_3.0_open_project

다른 프로젝트 작업 중에 새로운 프로젝트를 생성하고자 한다면 아래 메뉴에서 생성할 수 있습니다.

File > New > New Project

2

안드로이드에서 제공하는 프로젝트 템플릿을 선택합니다.

선택하는 템플릿에 따라 구조, 화면 레이아웃, 파일 등의 구성이 달라집니다. 여기서는 넥사크로 앱을 담기 위한 기본적인 앱 형태면 충분하므로 Empty Activity를 선택한 다음 [Next] 버튼을 클릭합니다.

android_studio_add_an_activity_empty

3

앱의 이름, 패키지 이름 그리고 프로젝트의 경로를 설정합니다.

android_studio_create_project


항목

설명

1

Name

생성할 앱 이름을 입력합니다.

2

Package Name

패키지 이름을 입력합니다.

3

Save location

프로젝트가 저장될 위치를 지정합니다.

4

Language

개발 언어를 선택합니다. 기본 설정값은 kotlin이지만, 이번 장에서는 Java로 설정합니다.

5

Minimum API

최소 사양 SDK를 설정합니다. SDK 항목을 변경하면 하단에서 해당 버전 API 레벨의 디바이스 호환성에 관한 정보를 확인할 수 있습니다.

넥사크로에서 지원하는 안드로이드 최소 지원 사양은 5.0 버전입니다. 따라서 SDK 설정시 API 레벨 21 이상을 지원하는 SDK를 사용해야 합니다.

4

[Finish] 버튼을 클릭하면 프로젝트가 생성됩니다.

프로젝트 창이 열리면서 추가적으로 필요한 파일은 자동으로 내려받아 설치합니다.

5

안드로이드 스튜디오에 포함된 JDK를 설정합니다.

메뉴 [File > Project Structure]에서 JDK location 항목을 선택하고 "Embedded JDK"를 선택합니다.

넥사크로 라이브러리 설정

앱 개발시 사용할 수 있도록 제공되는 넥사크로 안드로이드 아카이브(AAR) 파일을 안드로이드 프로젝트에 설정합니다. 별도의 설정은 필요치 않으며 정해진 경로에 복사해주면 됩니다.

파일

경로

안드로이드 아카이브(nexacro.aar)

[프로젝트 경로]\app\libs\

라이브러리 파일을 실제 프로젝트 경로의 정해진 위치에 복사하려면 프로젝트 윈도우 상단에서 뷰 옵션을 [Project]로 변경합니다. 안드로이드 스튜디오를 설치하면 기본적으로 뷰 모드가 [Android]로 되어 있는데 이를 [Project]로 설정해야 모든 폴더 구조를 확인할 수 있습니다.

android_studio_project_explorer

1

nexacro.aar 파일은 [프로젝트 경로 > app > libs] 폴더로 복사합니다.

archive_library_설정

복사 작업은 파일탐색기에서 안드로이드 스튜디오로 바로 복사/붙여넣기가 가능합니다. 각각의 경로에 파일을 붙여넣기 할 때 아래 그림처럼 파일명과 경로를 변경할 수 있는 Copy 윈도우가 나타나는데 내용 변경없이 [OK] 버튼을 클릭합니다.

so파일복사

2

메뉴 [File > Project Structure]에서 "Dependencies"를 선택하고 Modules 항목에서 "app"을 선택한 후 "+" 버튼을 클릭하고 "Jar Dependency" 항목을 선택합니다.

3

입력 창에서 aar 파일 경로를 입력하고 [OK] 버튼을 클릭합니다.

4

app > build.gradle 파일에 aar 파일이 추가된 것을 확인합니다.

리소스 설정

앱에서 사용할 로딩 이미지, 아이콘, 메시지, 레이아웃 등을 설정하는 단계입니다. 리소스 파일을 단말기의 화면 크기, 밀도에 따라 만든 후 적절한 폴더에 배치하면 안드로이드 시스템은 사용자에게 최적화된 뷰를 제공합니다.

기본 리소스는 넥사크로 안드로이드 아카이브(AAR) 파일에 포함되어 있습니다. 기본 리소스를 사용할 경우에는 별도 설정이 필요 없습니다.

리소스에 관한 더 상세한 내용은 안드로이드 매뉴얼을 참조하시기 바랍니다.

https://developer.android.com/guide/topics/resources/providing-resources

이미지 설정

앱에서 사용하는 이미지는 res 폴더 아래에 밀도에 따라 알맞은 폴더에 지정합니다. 예를 들어, 로딩 이미지는 drawable 폴더로, 앱 아이콘은 mipmap 폴더로 지정합니다. 적용할 이미지 파일은 앱에서 지원하는 해상도에 따라 폴더를 달리 설정해야 합니다.

항목

설명

ic_launcher.png

바탕화면에 설치되는 앱 아이콘 이미지입니다.

AndroidManifest.xml 파일에서 파일명을 변경할 수 있습니다.

splashimage_phone_landscape.png

안드로이드 폰 가로 방향 스플래시 이미지입니다(모두 소문자).

splashimage_phone_portrait.png

안드로이드 폰 세로 방향 스플래시 이미지입니다(모두 소문자).

splashimage_pad_landscape.png

안드로이드 태블릿 가로 방향 스플래시 이미지입니다(모두 소문자).

splashimage_pad_portrait.png

안드로이드 태블릿 세로 방향 스플래시 이미지입니다(모두 소문자).

런처 아이콘, 로딩 이미지 등을 별도로 지정하지 않는 경우에는 기본 설정이 적용되므로 앱 동작에는 아무런 문제가 없습니다.

문자열 설정

기본 문자열 설정은 넥사크로 안드로이드 아카이브(AAR) 파일에 포함된 strings.xml 파일 설정에 따라 적용됩니다. 기본 문자열 설정을 변경하려면 res 폴더 아래 values, values-ko, values-ja, values-zh 폴더에 strings.xml 파일을 만들고 변경할 항목만 설정할 수 있습니다.

아래 코드는 넥사크로 안드로이드 아카이브 파일에 포함된 strings.xml 파일 내용입니다. 배포 버전에 따라 내용은 달라질 수 있습니다.

$r_title(values-ko\strings.xml)
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="AddManifest_Permission">Manifest파일에 권한 추가하거나 권한을 허용 하세요.</string>
    <string name="Additional_Permission">추가 권한 인증이 필요합니다.</string>
    <string name="AllowUse_Permission">해당 기능을 사용 하려면 권한을 허용 하세요.</string>
    <string name="Allow_Permission">엔진 업데이트의 정상적인 기능을 위해 해당 권한이 반드시 필요하니 권한을 허용하세요.</string>
    <string name="Allow_StoragePermission">어플리케이션 설정의 앱 권한에서 *저장공간* 권한을 허용하세요.</string>
    <string name="BeingUpdated">업데이트 중입니다.</string>
    <string name="Denied_Permission">엔진 업데이트 권한거부로 종료합니다.</string>
    <string name="Init_nexacro_engine">넥사크로 엔진을 초기화 합니다.</string>
    <string name="LoadingPleaseWait">로딩중입니다.. \n잠시만 기다려주세요...</string>
    <string name="cancel">아니오</string>
    <string name="check_license">라이센스 체크합니다.</string>
    <string name="checked">선택 됨</string>
    <string name="checkforupdates">업데이트 확인 중입니다.</string>
    <string name="click">클릭</string>
    <string name="copy">복사하기</string>
    <string name="cut">잘라내기</string>
    <string name="downloadingforupdates">업데이트 다운 중입니다.</string>
    <string name="editing_delete_description">%1$s 삭제됨</string>
    <string name="editing_description">입력상자 현재 수정 중 입니다. %1s</string>
    <string name="editing_insert_description">%1$s</string>
    <string name="execute_frameworkscript">프레임워크 스크립트 실행합니다.</string>
    <string name="executing_framework">어플리케이션을 로딩합니다.\n( 프레임워크 실행 )</string>
    <string name="exit">종료</string>
    <string name="fd_cancel">취소</string>
    <string name="fd_ok">확인</string>
    <string name="filter">필터</string>
    <string name="force_close">업데이트가 되었습니다.</string>
    <string name="home">홈</string>
    <string name="image_description">%1$s 이미지</string>
    <string name="installforupdates">업데이트 설치 중입니다.</string>
    <string name="link_description">%1$s 링크</string>
    <string name="list_description">%1$s 선택됨</string>
    <string name="listitem_description">%1$d 중에 %2$d, %3$s</string>
    <string name="listitem_selected_description">%1$d 중에 %2$d, 선택됨, %3$s</string>
    <string name="load_application">어플리케이션 로딩합니다.</string>
    <string name="load_bootstrap">어플리케이션을 로딩합니다.\n( 부트스트랩)</string>
    <string name="load_extendmodule">익스텐드 모듈 로딩합니다.</string>
    <string name="load_failapplication">어플리케이션 초기화 실패했습니다. 클릭 후 닫습니다.</string>
    <string name="load_frameworkmodule">프레임워크 모듈 로딩합니다.</string>
    <string name="loadingFail">초기 로딩이 실패하였습니다. \n재시작 해 주세요.</string>
    <string name="loading_application">어플리케이션을 로딩합니다.\n( 어플리케이션 로딩 )</string>
    <string name="move">이동</string>
    <string name="needupdate">업데이트가 있습니다. \n완료 후 재시작 해 주세요.</string>
    <string name="nofilename">파일명이 없습니다.</string>
    <string name="notexist">구동에 필요한 파일이 없습니다. \n재시작 해 주세요.</string>
    <string name="ok">예</string>
    <string name="paste">붙여넣기</string>
    <string name="preparing_framework">어플리케이션을 로딩합니다.\n( 프레임워크 준비중 )</string>
    <string name="selectall">전체선택</string>
    <string name="selectword">단어선택</string>
    <string name="spacebar">스페이스바</string>
    <string name="ssl_invalid">SSL 인증서가 올바르지 않습니다. 계속 진행하시겠습니까?</string>
    <string name="start_initialize">초기화 시작합니다.</string>
    <string name="unchecked">선택 안됨</string>
    <string name="updateFail">업데이트가 실패하였습니다. \n재시작 해 주세요.</string>
    <string name="upper">상위</string>
    <string name="wantreplace">동일한 파일이 이미 존재합니다. 바꾸시겠습니까?</string>
</resources>

문자열은 사용자의 상황에 맞게 수정할 수 있습니다. 그러나 위에 정의된 문자열 name은 앱에서 고정적으로 사용하므로 수정하지 않습니다.

영문 문자열 외 한국어나 일본어 등의 문자열을 추가하려면 res 폴더 아래 values-ko, values-ja 와 같은 식으로 폴더를 추가하고 strings.xml 파일을 추가합니다. 더 상세한 내용은 안드로이드 매뉴얼을 참조하시기 바랍니다.

https://developer.android.com/training/basics/supporting-devices/languages.html

레이아웃 설정

기본 레이아웃 설정은 넥사크로 안드로이드 아카이브(AAR) 파일에 포함된 nexacro_app.xml 파일 설정에 따라 적용됩니다. 레이아웃 설정을 변경하려면 layout 폴더에 nexacro_app.xml 파일을 만들어 설정합니다. 레이아웃 파일을 새로 추가하려면 [File > New > XML > Layout XML File]을 선택합니다.

아래 코드는 넥사크로 안드로이드 아카이브 파일에 포함된 nexacro_app.xml 파일 내용입니다. 배포 버전에 따라 내용은 달라질 수 있습니다.

$r_title(layout\nexacro_app.xml)

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nexacro_activity"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/transparent" >

    <com.nexacro.view.NexacroLayout
        android:id="@+id/nexacro_layout"
        android:background="@android:color/transparent"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    </com.nexacro.view.NexacroLayout>

</FrameLayout>

폰트 설정

Packing Update Type을 Update(Local+Server) 또는 Local로 선택한 경우에는 Resource 탭에서 설정한 UserFont를 사용할 수 있습니다. 하지만 Update Type을 Server로 선택한 경우에는 Resource 탭에서 설정한 UserFont를 사용할 수 없고 안드로이드 스튜디오 프로젝트에서 assets 폴더 하위에 fonts 폴더를 만들고 폰트 파일을 같이 빌드해야 합니다.

assets 폴더에 폰트 파일을 배포하는 방식은 21.0.0.1200 이후 버전부터 지원하는 기능입니다.

안드로이드 스튜디오에서 폰트를 설정하는 절차는 아래와 같습니다.

1

넥사크로 스튜디오에서 UserFont 파일을 생성합니다.

UserFont 파일 생성과 관련된 내용은 UserFont 파일 생성 항목을 참고하세요.

2

xfont 파일에 local("Font Name") 형식으로 스크립트를 작성합니다.

@font-face {
  font-family : 'NOTO';
  font-style : normal;
  src : local("NotoSansCJKkr-Regular");
}

@font-face {
  font-family : 'NOTO';
  font-style : normal;
  src : local("NotoSansCJKkr-Bold");
}

3

안드로이드 스튜디오 프로젝트 assets 폴더 하위에 fonts 폴더를 만들고 적용하고자 하는 폰트 파일을 가져다 놓습니다.

4

안드로이드 스튜디오에서 앱을 빌드합니다.

Update Type을 Update(Local+Server) 또는 Local로 선택한 경우 assets\fonts 폴더에도 폰트 파일을 가져다 놓고 Resource에도 같은 폰트 파일을 설정하면 assets\fonts 폴더의 파일이 먼저 적용되고 Resource에서 설정한 파일이 처리되기 때문에 불필요하게 두 번 같은 파일이 처리됩니다.

중복된 작업이 처리되지 않도록 유의해주세요.

Config 설정

기본 Config 설정은 넥사크로 안드로이드 아카이브(AAR) 파일에 포함된 nexacro_config.xml 파일 설정에 따라 적용됩니다. Config 설정을 변경하려면 xml 폴더에 nexacro_config.xml 파일을 만들어 설정합니다.

아래 코드는 넥사크로 안드로이드 아카이브 파일에 포함된 nexacro_config.xml 파일 내용입니다. 배포 버전에 따라 내용은 달라질 수 있습니다.

$r_title(xml\nexacro_config.xml)

<?xml version="1.0" encoding="UTF-8"?>
<nexacro-config>
    <application style="default" dialog-position="bottom" file-logging="true" quiet="false" loglevel="debug" tracemode="none" traceduration="-1"/>
    <notification enable="true" handler="com.nexacro.notification.DefaultHandler"/>
    <xpush-server request-missing-message="false" sender-id="1234567890"/>
    <updator cancelable="true" force="true" restart="false" errormsg="true" quiet="false" failpass="true"/>
    <log filepath="%SD_CARD%nreLog.log" filesize="408960" backupfilecount="5"/>
</nexacro-config>

nexacro_config.xml에 설정할 수 있는 기능은 다음과 같습니다.

기능

속성

설정 값

설명

application

dialog-position

"top" | "center" | "bottom"

앱 업데이트 진행 정보를 표시할 팝업 위치를 설정합니다.

file-logging

"true" | "false"

로딩 에러 정보를 파일로 저장할지 설정합니다.

"true"로 설정시 외부저장소(context.getExternalCacheDir()) > 내부저장소(context.getCacheDir()) 순서로 파일 저장을 시도합니다.

파일은 [외부/내부저장소 경로]/logs/yyyy_MM_dd.txt 형태로 저장됩니다.

quiet

"true" | "false"

앱 실행 팝업을 표시할지 설정합니다.

"true"로 설정시 "Loading Application" 실행 메시지가 출력되지 않습니다.

updator

force

"true" | "false"

start_android.json에 업데이트 파일 정보 존재시 팝업으로 표시할지 설정합니다.

"true"로 설정시 팝업 알림없이 강제로 업데이트를 진행합니다.

cancelable

"true" | "false"

업데이트 파일 존재시 팝업에 업데이트 취소 버튼을 표시할지 설정합니다.

취소 버튼을 클릭하면 업데이트를 진행하지 않고 앱을 실행합니다.

errormsg

"true" | "false"

앱 로딩에 실패했을 때 에러 정보를 팝업으로 표시할지 설정합니다.

quiet

"true" | "false"

업데이트 팝업을 표시할지 설정합니다.

"true"로 설정해도 업데이트 파일 존재시 진행 단계는 표시됩니다.

failpass

"true" | "false"

업데이트 오류를 무시할지 설정합니다.

xpush-server

request-missing-message

"true" | "false"

xpush 서버로 미수신 메시지를 자동으로 요청할지 설정합니다.

notification

enable

"true" | "false"

알림기능을 사용할지 설정합니다.

handler

“[함수 이름]”

알림 수신시 메시지 혹은 데이터 처리를 위한 함수를 설정합니다.

기본 값은 “com.nexacro.notification.DefaultHandler” 입니다.

file-logging 속성의 [저장소 경로] 위치에 대한 자세한 정보는 아래 링크를 참고하세요.


https://developer.android.com/reference/android/content/Context.html#getExternalCacheDir()

https://developer.android.com/reference/android/content/Context.html#getCacheDir()

빌드 환경 설정

안드로이드 프로젝트를 생성하면서 기본으로 만들어진 MainActivity.java 파일과 AndroidManifest.xml 파일을 넥사크로 환경에 맞게 수정합니다.

MainActivity.java

프로젝트 생성 시 액티비티 이름을 지정해주는데 별도로 수정하지 않는다면 MainActivity.java 라는 이름으로 생성됩니다. [프로젝트 > app > src > main > java] 폴더에서 지정된 패키지 이름 아래에 MainActivity.java 파일을 선택하고 아래와 같이 수정해줍니다.

$r_title(MainActivity.java)


package com.example.helloandroid;

import com.nexacro.NexacroResourceManager;
import com.nexacro.NexacroUpdatorActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends NexacroUpdatorActivity {

    public MainActivity() {
        super();

        setBootstrapURL("http://[URL]/start_android.json");
        setProjectURL("http://[URL]/");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        NexacroResourceManager.createInstance(this);
        NexacroResourceManager.getInstance().setDirect(false);

        Intent intent = getIntent();
        if(intent != null) {
            String bootstrapURL = intent.getStringExtra("bootstrapURL");
            String projectUrl = intent.getStringExtra("projectUrl");
            if(bootstrapURL != null) {
                setBootstrapURL(bootstrapURL);
                setProjectURL(projectUrl);
            }
        }

        super.onCreate(savedInstanceState);
    }

    @Override
    public void setContentView(View view) {
        super.setContentView(view);
    }

}

위의 코드에서 굵은 글씨로 표시된 부분은 사용자가 환경에 맞게 수정해야 합니다.

15~16번 줄은 부트스트랩 주소와 프로젝트 소스의 주소를 설정하는 부분입니다. setBootstrapURL 메소드의 인수는 넥사크로 스튜디오에서 만든 start_android.json 파일이 배치된 서버 URL을 지정하고 setProjectURL 메소드의 인수는 해당 프로젝트 소스가 제너레이트된 경로를 지정합니다. 제너레이트 소스의 경로 끝에는 '/'를 붙여줍니다.

15 setBootstrapURL("http://192.168.0.1:8080/nexacro/_android_/start_android.json");
16 setProjectURL("http://192.168.0.1:8080/nexacro/_android_/");

22~23번 줄은 앱의 Update Type에 따라 수정합니다. Update Type은 넥사크로 스튜디오에서 Packing(Archive&Update)시 설정하는데 Update Type이 'Server'인 경우에는 23번 줄의 setDirect 메소드의 인수를 아래와 같이 'true'로 설정합니다. Update Type이 'Update(Local+Server)' 혹은 'Local' 타입인 경우에는 'false'로 설정합니다.

22 NexacroResourceManager.createInstance(this);
23 NexacroResourceManager.getInstance().setDirect(true);

부트스트랩 URL, 프로젝트 URL 그리고 리소스 매니저를 잘못 설정하면 앱이 정상적으로 동작하지 않을 수 있습니다.

onCreate 내에서 setScreenid 메소드를 사용하면 screenid를 원하는 값으로 지정할 수 있습니다.

public void onCreate(Bundle savedInstanceState) {
    NexacroResourceManager.createInstance(this);
    NexacroResourceManager.getInstance().setScreenid("Tablet_screen");
}

AndroidManifest.xml

AndroidManifest.xml 파일은 [프로젝트 > app > src > main]에 위치하며 앱에 대한 기본적인 정보와 특정 기능 실행 시 필요한 정보를 담고 있습니다. 기본적으로 필요한 정보는 아래 코드를 참고해 작성합니다. 아래 내용 중 굵게 표시된 package, MainActivity 에 대한 정보는 프로젝트 설정에 따라 지정해야 합니다.

$r_title(AndroidManifest.xml)


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.helloandroid">

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <!-- network access Permission -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

	<!-- storage access permission -->
	<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.READ_INTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE" />

    <application android:label="@string/app_name"
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:theme="@style/AppTheme"
        android:hasCode="true"
        android:hardwareAccelerated="true"
        android:requestLegacyExternalStorage="true"
        android:usesCleartextTraffic="true">

        <activity
            android:name="com.example.helloandroid.MainActivity"
            android:label="@string/app_name"
            android:theme="@style/Theme.AppCompat.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name="com.nexacro.NexacroActivity"
            android:alwaysRetainTaskState="true"
            android:configChanges="orientation|keyboardHidden|screenSize"
            android:launchMode="singleTop"
            android:theme="@android:style/Theme.NoTitleBar"
            android:windowSoftInputMode="adjustResize" >
        </activity>
    </application>

</manifest>

targetsdk 버전을 28 이상으로 지정한 경우에 HTTP 통신을 유지하기 위해서는 usesCleartextTraffic 값을 true로 지정해주어야 합니다.

해당 설정이 없을 경우 default 값이 false로 적용되어 HTTP 통신이 허용되지 않습니다.

https://developer.android.com/guide/topics/manifest/application-element#usesCleartextTraffic


<application

android:allowBackup="true"

android:usesCleartextTraffic="true"

AndroidManifest.xml > 추가 권한 설정

배포할 앱의 기능에 따라 필요한 권한 및 기타 정보를 추가로 지정합니다. 예를 들어, 카메라 기능을 사용한다면 아래와 같은 항목이 추가되어야 합니다.

...
<uses-permission android:name="android.permission.CAMERA" />
<activity android:name="com.nexacro.deviceAPI.CameraListener"
	android:screenOrientation="landscape">
	<intent-filter>
   		<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus"
	android:required="false"/>
...

다음은 많이 사용되는 기능의 권한 설정 코드입니다. 필요한 항목만 AndroidManifest.xml 파일에 추가합니다. 이 외에 안드로이드에서 정의한 모든 권한 목록을 보려면 Manifest.permission을 참조하시기 바랍니다.

기능에 필요한 권한을 설정하지 않으면 앱 실행시 해당 권한이 없다는 메시지와 함께 기능이 동작하지 않습니다.

<!-- AudioPlayer, AudioRecorder -->
<uses-permission android:name="android.permission.RECORD_AUDIO" /> 
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- Call -->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!-- Contact -->
<uses-permission android:name="android.permission.READ_CONTACTS" /> 
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<!-- Camera -->
<uses-permission android:name="android.permission.CAMERA" /> 
<activity android:name="com.nexacro.deviceAPI.CameraListener" android:screenOrientation="landscape">
	<intent-filter>
    	<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false"/>
<!-- Geolocation -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<!-- Map -->
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
<uses-library android:name="com.google.android.maps" /> 
<meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="user value" />

구글맵을 사용하려면 API KEY 값을 받아서 입력해야 합니다. 자세한 내용은 다음 링크를 참조하십시오.

https://developers.google.com/maps/documentation/android/start#obtain_a_google_maps_api_key

<!-- SMS -->
<uses-permission android:name="android.permission.SEND_SMS" /> 
<uses-permission android:name="android.permission.RECEIVE_SMS" /> 
<uses-permission android:name="android.permission.READ_SMS" /> 
<uses-permission android:name="android.permission.WRITE_SMS" />

<receiver android:name="com.nexacro.deviceAPI.SmsRecv">
	<intent-filter>
		<action android:name="android.provider.Telephony.SMS_RECEIVED" />
	</intent-filter>
	<intent-filter>
		<action android:name="android.provider.Telephony.WAP_PUSH_RECEIVED" />
		<data android:mimeType="application/vnd.wap.mms-message" />
	</intent-filter>
</receiver>
<!-- Vibrator -->
<uses-permission android:name="android.permission.VIBRATE" />
<!-- Bluetooth -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

<service
	android:name="com.nexacro.deviceAPI.BluetoothLEAdapterService"
	android:enabled="true"
	android:exported="true">
</service>
<!-- ExternalAPI -->
<uses-permission android:name="android.permission.GET_TASKS" />
<!-- Etc -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />

AndroidManifest.xml > FCM 설정

About FCM messages

https://firebase.google.com/docs/cloud-messaging/concept-options

<!-- Adding NexacroNotificationService -->
<service android:name="com.nexacro.notification.NexacroNotificationService">
	<intent-filter>
		<action android:name="com.google.firebase.MESSAGING_EVENT" />
	</intent-filter>
</service>

<!-- Adding NexacroActivity intent-filter -->
<activity android:name="com.nexacro.NexacroActivity"
	android:launchMode="singleTask"
	android:noHistory="false"
	android:hardwareAccelerated="true"
	android:windowSoftInputMode="adjustResize"
	android:configChanges="orientation|keyboard|keyboardHidden|screenSize|navigation|uiMode">
	<intent-filter>
		<action android:name="OPEN_NEXACRO_ACTIVITY_EXAMPLE" />
		<category android:name="android.intent.category.DEFAULT" />
	</intent-filter>
</activity>

FCM 서비스를 이용하는 경우에는 build.gradle 설정에 아래 항목을 추가해주어야 합니다. 안드로이드 스튜디오에서 자동으로 설정되지 않는다면 확인 후 추가해주고 Firebase 콘솔에서 생성한 "google-services.json" 파일을 모듈 경로에 복사합니다.

Android 프로젝트에 Firebase 추가

https://firebase.google.com/docs/android/setup

buildscript {
	// Adding repositories 
	repositories {
		google()  
		jcenter()
	}
	dependencies {
		classpath 'com.android.tools.build:gradle:3.2.1'
		// NOTE: Do not place your application dependencies here; they belong
		// in the individual module build.gradle files
		// Adding google service
		classpath 'com.google.gms:google-services:4.0.1'
	}
}

allprojects {
	// Adding repositories 
	repositories {
		google()
		jcenter()
	}
}

task clean(type: Delete) {
	delete rootProject.buildDir
}

dependencies {
	implementation fileTree(include: ['*.jar'], dir: 'libs')
	implementation 'com.android.support:appcompat-v7:27.1.1'
	implementation 'com.android.support:support-v4:27.1.1'
	implementation 'com.google.android.gms:play-services-maps:16.0.0'
	implementation 'com.google.android.gms:play-services-location:16.0.0'
	implementation 'com.google.firebase:firebase-core:16.0.6'
	implementation 'com.google.firebase:firebase-messaging:17.3.4'
	compile 'com.android.support:multidex:1.0.3'
	implementation 'com.android.support:design:27.1.1'
	implementation files('libs/nexacro.aar')
}
apply plugin: 'com.google.gms.google-services'

targetsdk 버전을 28 이상으로 지정한 경우에는 play-services 버전도 같이 수정해주어야 합니다.

implementation 'com.google.android.gms:play-services-maps:16.1.0'

implementation 'com.google.android.gms:play-services-location:16.1.0'

AndroidManifest.xml > ImagePicker, Camera, FileUpload, FileDailog 설정

targetsdk 버전을 24 이상으로 지정하고 ImagePicker, FileDialog, FileUpload, Camera 컴포넌트를 사용하는 경우(Camera 컴포넌트는 targetsdk 버전 29 이상)에는 res/xml 폴더안에 file_paths.xml 파일을 생성하고 AndroidManifest.xml 파일과 build.gradle 파일에 아래 항목을 추가해주어야 합니다.

WebBrowser 컴포넌트에 연결된 HTML 콘텐츠에서 Input 태그의 capture 속성을 사용할 경우 파일에 접근하기 위해 같은 설정이 필요합니다.

$r_title(file_paths.xml)
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-cache-path name="cache" path="."/>
    <external-files-path name="files" path="./"/>
</paths>
$r_title(AndroidManifest.xml / FileProvider)
<provider
	android:name="android.support.v4.content.FileProvider"
	android:authorities="${applicationId}.fileprovider"
	android:exported="false"
	android:grantUriPermissions="true">
	<meta-data
		android:name="android.support.FILE_PROVIDER_PATHS"
		android:resource="@xml/file_paths"/>
</provider>

<!-- AndroidX -->
<provider
	android:name="androidx.core.content.FileProvider"
	android:authorities="${applicationId}.fileprovider"
	android:exported="false"
	android:grantUriPermissions="true">
	<meta-data
		android:name="android.support.FILE_PROVIDER_PATHS"
		android:resource="@xml/file_paths"/>
</provider>
$r_title(build.gradle / exifinterface)

dependencies {
    implementation 'com.android.support:exifinterface:28.0.0'
	//AndroidX
	implementation 'androidx.exifinterface:exifinterface:1.3.2'
}

네트워크 연결 없는 앱 실행 설정

넥사크로 스튜디오에서 앱을 패킹할 때 Update Type을 Local이나 Update(Local+Server)으로 설정하면 네트워크 연결 없이도 앱을 실행 할 수 있습니다. 일반적으로 네트워크 연결이 필요 없는 경우에는 Local 타입을 사용하고, 평소에는 서버와 통신이 필요하지만 연결에 문제가 생길 시에도 앱이 동작해야 할 경우에는 Update(Local+Server) 타입을 사용합니다. 이처럼 네트워크 연결이 필요 없거나 문제 발생 시 앱 단독으로 실행이 가능하게 하려면 앱 빌드 시 실행에 필요한 파일을 적재하여 배포해야 합니다.

Update Type 설정값이 Update(Local+Server)인 경우 네트워크가 연결되어 있지 않으면 앱을 실행할 수 없습니다. 앱 로딩 시 부트스트랩 URL을 확인하고 연결이 실패하면 앱을 실행하지 않습니다.

앱 실행에 필요한 파일은 모듈, 소스, 리소스 그리고 start_android.json으로 넥사크로 스튜디오에서 Packing(Archive&Update)을 수행하여 얻을 수 있습니다. 이 파일을 안드로이드 프로젝트에서 애셋(Asset)의 archive 폴더에 적재한 후 앱을 빌드하면 네트워크 연결 없이도 앱을 실행할 수 있습니다.

넥사크로 스튜디오에서 Packing(Archive&Update)시 Update Type을 Local로 설정한 때도 안드로이드 프로젝트의 MainActivity.java에서 부트스트랩 URL은 반드시 설정해야 합니다.

예를 들면, 다음과 같이 가상의 주소나 실제 사용하지 않는 임의의 주소로 설정해도 무방합니다.

setBootstrapURL("http://192.168.0.1/_android_/start_android.json");

setProjectURL("http://192.168.0.1/_android_/");

1

안드로이드 스튜디오 프로젝트에서 assets 폴더를 생성합니다.

애셋은 안드로이드 앱에서 사용할 수 있는 논리적인 저장 공간입니다. 안드로이드 프로젝트에서 assets 폴더를 만들고 앱에서 사용할 파일을 복사한 후 APK를 생성하면 앱 실행 시 그 파일을 사용할 수 있습니다. 애셋은 읽기 전용 공간으로 사용되므로 앱 실행 중 변경되지 않을 파일을 이곳에 놓고 사용합니다.

먼저 프로젝트의 app을 선택하고 마우스 오른쪽 버튼을 클릭합니다. 다음과 같이 콘텍스트 메뉴에서 [New > Folder > Assets Folder]를 선택하여 [프로젝트 > app > src > main] 하위에 Assets 폴더를 생성합니다.

create_assets_1

2

assets 폴더 하위에 archive 폴더를 생성합니다.

[프로젝트 > app > src > main > assets] 폴더를 선택한 후 [New > Directory] 메뉴를 사용해 archive 폴더를 assets 폴더 하위에 생성합니다.

3

모듈, 소스, 리소스 파일을 assets/archive 폴더에 복사합니다.

모듈, 소스, 리소스 파일을 [프로젝트 > app > src > main > assets > archive] 폴더로 복사합니다. 윈도우 탐색기에서 파일을 선택한 후 안드로이드 스튜디오로 복사/붙여넣기 합니다.

copy_archive_files

4

폴더 및 파일이 프로젝트에 정상적으로 추가되었는지 확인합니다.

안드로이드 스튜디오의 프로젝트 창에서 assets/archive 폴더 및 파일들이 정상적으로 추가되었는지 확인합니다.

asset_경로

빌드

앱 테스트

  1. 안드로이드 단말기를 PC에 USB로 연결해 바로 테스트할 수 있습니다. 단말기를 직접 연결하는 경우에는 단말기에 개발자 옵션인 [USB 디버깅] 항목을 설정하고 단말기에 맞는 드라이버를 설치해주어야 합니다.

USB 디버깅 옵션 선택 방법은 아래 정보를 참고하세요.

https://developer.android.com/studio/debug/dev-options

  1. 안드로이드 스튜디오에서 Run 메뉴를 수행합니다. Run 메뉴는 APK를 생성 및 단말기에 설치하고 실행까지 할 수 있는 기능입니다. 실행 명령을 내리면 어느 단말기에서 실행할지 선택할 수 있습니다.

Run > Run 'App'

넥서스 원(Nexus One) 또는 옵티머스 원(Optimus One) 같은 경우 내장 메모리에 사용자가 쓸 수 있는 공간이 없어 외장 메모리 카드를 추가하지 않았다면 앱이 정상적으로 설치되지 않습니다.

안드로이드 가상 디바이스를 이용한 테스트는 지원하지 않습니다.

select_deployment_target

앱 실행 로그는 안드로이드 스튜디오 하단의 Logcat을 통해 확인할 수 있습니다.

logcat

설치 파일 생성

완성된 앱을 배포하기 위해서는 앱을 인식할 수 있는 keystore를 생성하고 서명된 AAB, APK 파일을 생성해야 합니다. keystore 파일은 최초 1회 생성하며 이후 앱이 업데이트되었을때는 최초 생성된 keystore 파일을 사용합니다.

keystore 생성 및 Signed AAB, APK 를 생성하는 방법은 구글의 웹페이지를 참조하십시오.

http://developer.android.com/guide/publishing/app-signing.html

상단 메뉴에서 [Build > Generate Singed Bundle/APK] 항목을 선택합니다.

generate_signed_apk_menu

기본 설정은 AAB 파일 생성입니다. 필요한 경우 APK 항목을 선택하고 APK 파일을 생성할 수 있습니다.