WebView 샘플 앱 개발 개요

현재 project내 front-end개발 인원에 비해 Android 앱 개발 인원이 부족하여 안드로이드 앱 네이티브 기능이 필요하지 않은 부분은 협의하에 WebView로 구현하여 프로젝트 일정에 부담을 덜어내기로 합니다.

Android WebView란 ?

웹 애플리케이션 또는 웹페이지만 클라이언트 애플리케이션의 일부로 제공하려는 경우 [WebView](<https://developer.android.com/reference/android/webkit/WebView?hl=ko>)를 사용하면 됩니다. [WebView](<https://developer.android.com/reference/android/webkit/WebView?hl=ko>)  클래스는 Android의 [View](<https://developer.android.com/reference/android/view/View?hl=ko>)클래스의 확장으로, 웹페이지를 활동 레이아웃의 일부로 표시할 수 있게 해 줍니다. 탐색 컨트롤이나 주소 표시줄 등 완전히 개발된 웹브라우저의 기능은 전혀 포함되어 있지 않습니다.

[WebView](<https://developer.android.com/reference/android/webkit/WebView?hl=ko>)의 모든 작업은 기본적으로 웹페이지를 표시하는 것입니다.

앱에 WebView 추가

[WebView](<https://developer.android.com/reference/android/webkit/WebView?hl=ko>)를 앱에 추가하려면 활동 레이아웃에서 <WebView> 요소를 포함하거나 onCreate() 에서 전체 활동 창을 WebView로 설정하면 됩니다.

AndroidManifest.xml 파일의 manifest 태그 내에 아래와 같이 추가합니다.

<uses-permission android:name="android.permission.INTERNET" />

MainActivity에는 webView를 로드합니다. 테스트로 당근마켓의 홈페이지를 로드했습니다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val webView = findViewById(R.id.webview)
        webView.loadUrl("<https://www.daangn.com/>")

    }
}

activity_main.xml 파일에는 WebView를 추가합니다.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <WebView
        android:id="@+id/webview"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    </WebView>

</androidx.constraintlayout.widget.ConstraintLayout>

여기까지 설정 시 아래처럼 안드로이드 앱 실행 시 당근마켓의 홈 페이지를 표시합니다. 그러나 당웹뷰 내의 버튼을 클릭 시 앱 내에서 이동하지 않고 새로운 chrome 앱을 통해 페이지를 표시했습니다.

 

이번 포스팅에서는 웹뷰를 표시하는것 까지 확인하고, 다음 포스팅에서 앱 내에서 어떻게 웹 페이지를 이동할 수 있는지 알아보겠습니다.

최근 기획하고 있는 앱에서 푸쉬알림 기능이 필요하여 구현한 내용을 공유합니다.

Firebase Cloud Messaging 라이브러리를 이용하여 개발했으며, 샘플 앱의 전체 소스코드는 아래의 링크를 통해 확인해주세요.

https://github.com/gogoadl/android-fcm-demo

 

GitHub - gogoadl/android-fcm-demo: android firebase cloud message sample

android firebase cloud message sample. Contribute to gogoadl/android-fcm-demo development by creating an account on GitHub.

github.com

푸쉬알림이란?

푸시 알림은 사용자가 스마트폰 또는 다른 디바이스에 설치된, 브랜드 앱에서 직접 수신하는 실시간 알림 입니다. 기업은 푸시 메시지를 사용하여 현재 브랜드와 상호작용하지 않는 고객의 관심을 유도할 수 있습니다.

 

Android 앱에서는 FCM (Firebase Cloud Messaging) 라이브러리를 통해 쉽게 푸쉬알림 기능을 구현할 수 있습니다. 지금부터 FCM의 기본적인 설정과 FCM을 통해 안드로이드 앱으로 푸쉬알림을 전송하는 기능을 구현해봅니다.

Firebase 콘솔 설정

먼저Firebase 콘솔로 이동하여 프로젝트를 생성해줍니다.

로그인

 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

저는 아래처럼 프로젝트 이름을 설정했습니다.

애널리틱스 설정은 기본적으로 사용 설정이 권장됩니다. 저는 이대로 계속 버튼을 눌러 진행했습니다.

Google 애널리틱스 구성 역시 기본 설정으로 구성 후 프로젝트 만들기 버튼을 선택합니다.

프로젝트가 생성된 후 아래 안드로이드 로고 이미지를 선택하여 안드로이드 앱을 추가합니다.

이후 아래와 같은 창이 보이면 Android 패키지 이름 항목에 테스트용으로 생성한 앱의 패키지명을 추가해줍니다. 패키지명을 잘 모르시겠다면 Android 프로젝트의 AndroidManifest.xml 파일의 package 속성을 사용해주시면 됩니다. 앱 닉네임과 디버그 서명 인증서는 skip 합니다.

이후 앱 등록 시 아래와 같은 화면이 나옵니다. 설명에 따라 다운로드한 파일을 앱 모듈 루트 디렉토리에 위치시켜 줍니다.

FirebaseSDK 추가도 동일하게 상세한 설명을 참고하여 세팅을 완료합니다.

 

궁금한점이나 잘못된 부분이 있다면 댓글로 알려주시면 감사하겠습니다!

 

Reference

https://firebase.google.com/docs/cloud-messaging?hl=ko

 

Firebase 클라우드 메시징

Firebase 클라우드 메시징(FCM)은 무료로 메시지를 안정적으로 전송할 수 있는 크로스 플랫폼 메시징 솔루션입니다.

firebase.google.com

https://business.adobe.com/kr/glossary/push-notifications.html

 

푸시 알림이란 무엇입니까? | Adobe 용어 설명

푸시 알림은 스마트폰 또는 다른 디바이스에 설치된 앱에서 실시간으로 제공하는 알림입니다. 자세한 내용을 살펴보십시오.

business.adobe.com

 

BroadcastReceiver ?

Android 시스템 및 기타 Android 앱에서 브로드캐스트 메시지를 보내거나 받을 수 있습니다 . 이러한 브로드캐스트는 관심 있는 이벤트가 발생할 때 전송됩니다. 예를 들어 Android 시스템은 시스템이 부팅되거나 기기가 충전을 시작하는 등 다양한 시스템 이벤트가 발생할 때 브로드캐스트를 보냅니다. 예를 들어 앱은 사용자 지정 브로드캐스트를 전송하여 다른 앱에 관심이 있을 수 있는 항목(예: 일부 새 데이터가 다운로드됨)을 알릴 수도 있습니다.

BroadcastReceiver 등록 방법

// BroadcastReceiver class
public class MyBroadcastReceiver extends BroadcastReceiver {
        private static final String TAG = "MyBroadcastReceiver";
        @Override
        public void onReceive(Context context, Intent intent) {
            StringBuilder sb = new StringBuilder();
            sb.append("Action: " + intent.getAction() + "\\n");
            sb.append("URI: " + intent.toUri(Intent.URI_INTENT_SCHEME).toString() + "\\n");
            String log = sb.toString();
            Log.d(TAG, log);
            Toast.makeText(context, log, Toast.LENGTH_LONG).show();
        }
    }
// activity
BroadcastReceiver br = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter(APP_SPECIFIC_BROADCAST);

registerReceiver(context, br, filter);

unregisterReceiver(br);

LocalBroadcastManager

[LocalBroadcastManager.sendBroadcast](<https://developer.android.com/reference/androidx/localbroadcastmanager/content/LocalBroadcastManager#sendBroadcast(android.content.Intent)>)메서드는 발신자와 동일한 앱에 있는 수신자에게 브로드캐스트를 보냅니다. 앱 간에 브로드캐스트를 보낼 필요가 없으면 로컬 브로드캐스트를 사용하세요. 구현이 훨씬 더 효율적이며(프로세스 간 통신이 필요하지 않음) 브로드캐스트를 수신하거나 보낼 수 있는 다른 앱과 관련된 보안 문제에 대해 걱정할 필요가 없습니다.

브로드캐스트 수신기를 내보내고 장치의 다른 앱에서 볼 수 있는지 여부를 선택합니다. 이 수신기가 시스템이나 다른 앱(귀하가 소유한 다른 앱 포함)에서 보낸 브로드캐스트를 수신하는 경우 android:exported="true"

<receiver android:name=".MyBroadcastReceiver" android:exported="true">

 

Reference

https://developer.android.com/guide/components/broadcasts?hl=ko 

 

브로드캐스트 개요  |  Android 개발자  |  Android Developers

브로드캐스트 개요 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Android 앱은 Android 시스템 및 기타 Android 앱에서 게시-구독 디자인 패턴과 유사한 브로드캐

developer.android.com

 

 

Android 장치에 블루투스 마우스 장치를 연결하면 화면에 마우스 커서가 표시됩니다.

이 커서를 Android 7.0 이상부터 커서를 숨기거나, 모양을 바꿀 수 있게 되었습니다.

필자의 경우 마우스 커서를 숨기는 기능에 사용했었는데, 이때 사용한 코드를 공유합니다.

블루투스 마우스 커서의 경우 setPointerIcon 메서드를 호출한 뷰 계층에서는 마우스 커서 모양이 변경되는것으로 보이며,

시스템 UI위에서는 기본 마우스 커서가 보이게 됩니다.

아래는 제가 사용했던 코드 일부를 공유합니다.
@Override
public boolean dispatchGenericMotionEvent(MotionEvent motionEvent) {
    int index = motionEvent.getActionIndex();

    if (motionEvent.getToolType(index) == MotionEvent.TOOL_TYPE_MOUSE) {
        if (rootLayout != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            PointerIcon pointerIcon = PointerIcon.getSystemIcon(this, -1);
            rootLayout.setPointerIcon(pointerIcon);
        }
    }
    return super.dispatchGenericMotionEvent(motionEvent);
}

 

SDK 외부 배포 시 발생한 오류이며 배포한 SDK의 proguard 옵션 설정으로 오류 해결 가능하다.

 

오류의 원인은 proguard 설정에서 패키지명, 클래스명이 난독화되면서 a.a.a.... 처럼 알파벳 순서대로 패키지명이 생성된다. 이 때 내가 배포한 SDK를 사용하는 유저가 사용하는 다른 라이브러리에서도 이러한 기본 난독화 패키지명을 사용할 경우 두 SDK의 패키지명, 클래스명이 모두 a.a... 등으로 표시되면서 클래스를 찾지 못하게 된다.

 

이 때 프로가드에서 flattenpackagehierarchy 옵션을 통해 기본 패키지명 앞에 식별가능한 prefix 패키지명을 붙일 수 있다.

-flattenpackagehierarchy "com.hyeonwoo.handsome"

이 프로가드 설정은 consumer-rule이 아닌 proguard-rule에 적용해야 한다.

 

Kakao Android SDK에서 해당 설정이 적용되지 않은것을 발견해 이슈 리포트를 했으나 내부에서 이슈 파악 후 수정된 버전을 배포준비중이라고 했다. 내가 도움이 되었다면 더 기뻤겠지만, 해당 이슈를 kakao 에서도 버그로 인식하는 것으로 봐서 올바른 수정사항이라고 어느정도 확신을 가질 수 있을 것 같다.

 

https://devtalk.kakao.com/t/android-sdk-com-kakao-sdk2-11-0/124909

 

Android SDK com.kakao.sdk:v2-friend:2.11.0에서 발생가능한 문제에 대해 문의드립니다

SDK 버전 : com.kakao.sdk:v2-friend:2.11.0 SDK Proguard 설정으로인해 발생가능한 문제가 있어보여 확인 부탁드립니다. External Libraries 에서 해당 라이브러리 jar 파일을 확인해봤는데요. 패키지명이 기본적으

devtalk.kakao.com

 

개요

안드로이드 스튜디오 IDE에서 쉽게 아이콘 이미지를 얻어오는 방법을 찾아서 포스팅합니다.  

저같은 경우 주로 사용되는 픽토그램 이미지를 https://material.io/ 사이트를 자주 이용했었는데요, IDE 내에서도 쉽게 이미지 리소스를 검색하고 프로젝트에 적용할 수 있는 좋은 방법이 있습니다.

 

Material Design

Build beautiful, usable products faster. Material Design is an adaptable system—backed by open-source code—that helps teams build high quality digital experiences.

material.io

 

먼저 안드로이드 스튜디오 상단 바의 Tools > Resource Manager 탭을 선택하여 하단의 이미지와 같이 Resource Manager 탭을 띄워줍니다.

 

이후 Resource Manager 탭 하단의 + 버튼을 선택하여 Vector Asset 탭을 선택해줍니다.

선택 시 아래와 같이 Asset Studio 라는 윈도우창이 뜨게됩니다.

Asset Type : Clip Art 타입을 선택해줍니다.

Name : 아이콘 이름을 설정합니다.

Clip Art :  이미지 선택 시 아래의 Select Icon 윈도우 창이 뜨면서 필요한 이미지를 선택할 수 있습니다.

Size : 이미지의 사이즈를 조정합니다.

Color : 원하는 색상으로 이미지를 변환 할 수 있습니다.

Opacity : 이미지의 투명도를 설정할 수 있습니다.

위와 같이 아이콘을 설정 및 필요한 이미지로 가공 후 Next 버튼을 눌러줍니다. 이후 저장 위치를 확인 후 Finish 버튼을 눌러줍니다.

 

이후 아래의 이미지처럼 저장한 이미지를 불러와서 사용할 수 있습니다.

간단하게 IDE 자체적으로 제공하는 기능으로 이미지 리소스를 다운로드 하는 방법을 알아봤습니다. 

같은 안드로이드 개발자 분들께 도움이 되었으면 좋겠습니다 ^0^

 

AAR 라이브러리 개요

아래는 Android Developers 공식 문서에 소개된 AAR 라이브러리에 대한 설명입니다.

 

Android 라이브러리는 구조적으로 Android 앱 모듈과 동일합니다. Android 라이브러리에는 소스 코드, 리소스 파일, Android 매니페스트를 비롯하여 앱을 빌드하는 데 필요한 모든 항목이 포함될 수 있습니다.

하지만, 이 라이브러리는 기기에서 실행되는 APK로 컴파일되는 대신 Android 앱 모듈의 종속 항목으로 사용할 수 있는 Android 보관 파일(AAR)로 컴파일됩니다. JAR 파일과 달리, AAR 파일은 Android 애플리케이션에 다음과 같은 기능을 제공합니다.

 

AAR 파일에는 Android 리소스 및 매니페스트 파일이 포함될 수 있습니다. 이 파일에서는 자바 클래스 및 메서드 외에 레이아웃 및 드로어블과 같은 공유 리소스를 번들로 구성할 수 있습니다.

AAR 파일은 앱 모듈의 C/C++ 코드에서 사용할 C/C++ 라이브러리를 포함할 수 있습니다.


라이브러리 모듈은 다음과 같은 상황에 유용합니다.

활동, 서비스, UI 레이아웃 등 일부 구성요소를 동일하게 사용하는 여러 앱을 빌드하는 경우

여러 APK 변형(예: 무료 및 유료 버전)에 포함되는 앱을 빌드하며 두 버전에서 모두 동일한 핵심 구성요소가 필요한 경우

 

AAR 라이브러리는 안드로이드 개발자라면 build.gradle 파일에서 implementation (또는 api) 키워드를 통해 의존성에 포함하여 자주 사용하게 되는데, 필자가 소개하고자 하는 내용은 흔히 사용되는 오픈소스 라이브러리가 아닌 좀더 private한? 라이브러리를 구성했던 방법에 대해서 말하고자 합니다.

 

아래의 사진처럼 우리는 다양한 AAR 라이브러리를 사용하고 있습니다!

 

AAR 라이브러리 모듈 생성

  1. File > New > New Module을 클릭합니다.

  1. Create New Module 창이 표시되면 Android Library, Next를 차례로 클릭합니다.
  2. 라이브러리에 이름을 지정하고 라이브러리의 코드를 위한 최소 SDK 버전을 선택한 후 Finish를 클릭합니다.

라이브러리가 생성되었다면, build.gradle 파일을 확인해보자.

기존 어플리케이션의 경우 어플리케이션으로 표시되는 것과 달리, 라이브러리로 표시되는 것을 확인할 수 있다.

 

//  어플리케이션을 라이브러리로 변환하여 사용하고 싶은 경우, 이 값을 위의 사진처럼 라이브러리로 변경하여 사용할 수 있다.
id 'com.android.application'

 

ExoPlayer2의 PlayerControlView가 일정시간이 지난 후 사라지는 현상이 있었다.

 

원인은 매우 간단했는데, 버그로 판단해서 버전도 변경해보고 여러가지 시도를 했었다.

 

xml 파일의 PlayerControllerView에 아래의 코드를 추가시켜준 후 문제가 발생하지 않았다.

 

        app:show_timeout="0"

 

Reference : https://stackoverflow.com/questions/51579006/exoplayer-show-playercontrolview-throughout-playback-of-audio-clip

안드로이드 앱 개발 중 JNI측에서 발생한 크래시의 경우 크래시가 발생하면서 앱이 종료되고 다시 시작되는데, 이 때 크래시가 발생한 순간의 로그를 안드로이드 스튜디오 로그캣에서 확인할 수 없을 때가 있다.

 

이 로그를 확인하는 방법을 간단하게 알아보자.

 

1. 로그를 추출할 기기가 연결하기

2. adb shell bugreport > [로그저장위치][log.txt]

 

 텍스트파일을 확인하면 JNI측에서 발생한 로그를 확인할 수 있다.

 

 

기존 안드로이드 프로젝트에서 free, paid Version 구성 또는 인앱 결제 모델, 구독 결제 모델을 나누는 방법으로 좋은 Flavor에 대해서 알아보겠습니다 ㅎㅅㅎ

 

Flavor란 무엇일까?

Flavor이란 Gradle에서 제공하는 옵션으로, Flavor를 통해 한 프로젝트에서 여러개의 빌드 버전을 생성할 수 있습니다.  필자는 안드로이드 개발 중 구독, 인앱 결제를 제외하고 같은 기능을 수행하는 프로젝트를 병합하기 위해 사용했습니다.

 

Flavor를 통해 여러 빌드를 생성하는 방법

 

 

applicationIdSuffix는 프로젝트 패키지 마지막에 설정될 이름을 설정할 수 있습니다. 이 옵션을 통해 구글 플레이에 각각 다른 패키지 명으로 앱을 추가할 수 있습니다.

manifestPlaceHolder는 AndroidManifest.xml 파일에 변수를 주입할 수 있습니다. 필자의 경우 앱 이름을 manifest에 앱 이름을 설정하는데 사용했습니다.

 

여기까지 설정되었다면 gradle Sync Now를 실행하면 아래와 같이 Build Variants에 Flavor 마다 Debug, Release가 생성된것을 확인할 수 있습니다.

그렇다면 각각 free, pro 버전을 분기하는 방법에 대해 알아보겠습니다.

 

만약 pro 버전과 free 버전을 나누는 부분이라면 pro 버전의 경우 광고가 표시되지 않고, free 버전의 경우 사용시 광고가 표시되는 등의 기능을 제외하고는 모두 같은 기능을 공유하게 될 것입니다.

제가 테스트했던 두가지 방법을 소개해드립니다.

 

1. 버전마다 디렉토리 생성 하여 클래스 파일로 분기하기

우선 프로젝트 구조를 Android 에서 Project로 변경합니다.

 

이후 디렉토리 구조를 위와같이 생성해줍니다.

build.gradle에 선언된 id와 같은 구조로 생성되어야 합니다. (이거때메 조금 헤매었음;;)

저는 free 버전을 main으로 사용하고, pro 버전을 새로 생성하여 테스트 했습니다.

 

pro버전의 AndroidManifest.xml 파일의 불필요한 코드를 제거해줍니다.

pro 버전이지만 패키지 이름이 com.hyeonwoo.free 인것을 주의해주셔야 합니다.

pro로 바꾸면 오류남여;

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

    <application>
        <activity android:name="com.hyeonwoo.pro.MainActivity">
        </activity>
    </application>

</manifest>

 

이후 각각 flavors에 따라 xml 파일에 표시되는 문자를 변경하여 테스트 해보시면 됩니다.

 

2. 하나의 소스코드로 BuildConfig에 따라 분기하기

분기할 코드의 양이 적다면 이 방법이 더 편리할 수 있어보입니다.

 

위의 디렉토리 구조 생성을 스킵하시고 build.gradle로 이동합니다.

flavorDimensions "version"
    productFlavors {
        free {
            applicationIdSuffix ".free"
            manifestPlaceholders = [appLabel: "free Version"]
            buildConfigField "boolean", "PRO_VERSION", 'false'
        }
        pro {
            applicationIdSuffix ".pro"
            manifestPlaceholders = [appLabel: "[pro Version"]
            buildConfigField "boolean", "PRO_VERSION", 'true'
        }
    }

 

buildConfigField를 사용하여 PRO_VERSION 이라는 boolean 타입 변수를 생성해줬습니다.

이제 한 액티비티에서 빌드 버전에 따라 분기할 수 있습니다.

package com.hyeonwoo.free;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

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

        if (BuildConfig.PRO_VERSION) {
            Log.d("MainActivity", "Pro Version");
        } else {
            Log.d("MainActivity", "Free Version");
        }
    }
}

빌드스크립트를 사용하시는 경우 각 빌드에따라 signingConfig도 설정하실 수 있읍니당 ㅎㅅㅎ 

마지막으로 데모코드는 제 깃헙에서 확인하실 수 있습니다.

 

https://github.com/gogoadl/Android_FlavorsDemo

 

GitHub - gogoadl/Android_FlavorsDemo: Flavors 적용 데모입니다.

Flavors 적용 데모입니다. Contribute to gogoadl/Android_FlavorsDemo development by creating an account on GitHub.

github.com

질문이나 추가하실 사항이 있다면 댓글남겨주세요!

+ Recent posts