인스턴트 앱(Instant App) 샘플

진행할 항목

  1. 샘플 프로젝트 준비
  2. 인스턴트 앱 구조에 맞춰 프로젝트 변경
  3. 안드로이드 스튜디오에서 실행해보기
  4. Heroku 구성

1. 샘플 프로젝트 준비

Android Architecture Blueprints 프로젝트로 샘플 프로젝트 준비

git clone https://github.com/googlesamples/android-architecture.git
git checkout -t origin/todo-mvp

구글의 인스턴트 앱 예제를 보고 싶다면

https://github.com/googlesamples/android-instant-apps

2. 인스턴트 앱 구조에 맞춰 프로젝트 변경

가. 기존 app 모듈명을 base feature명으로 수정 (ex. base_module)

  • Project 창에서 모듈 선택 후 우클릭 Refactor - Rename - Rename module 선택 후 변경

  • app/build.gradle 수정 (apk_module 은 추가할 APK Module명) applicationId, applicationIdSuffix 항목 주석 처리 room schemaLocation 오류 수정을 위해 javaCompileOptions 추가

    // apply plugin: 'com.android.application'
    apply plugin: 'com.android.feature'
    ...
    android {
    	...
        baseFeature true
        ...
        defaultConfig {
            //applicationId "com.example.android.architecture.blueprints.todomvp"
            ...
            javaCompileOptions {
                annotationProcessorOptions {
                    arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
                }
            }
        }
        ...    
        // If you need to add more flavors, consider using flavor dimensions.
        productFlavors {
            mock {
                dimension "default"
                //applicationIdSuffix = ".mock"
            }
            prod {
                dimension "default"
            }
        }
        ...
    }
        
    dependencies {
        application project(":apk_module")
    }
  • base_module/src/main/AndroidManifest.xml 수정

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.android.architecture.blueprints.todoapp">
    ...
    </manifest>

나. APK Module 추가 (ex. apk_module)

  • Module 추가 (File > New > New Module)

  • Phone & Table Module 선택

  • 모듈명 기입.(ex. apk_module)

    앱 이름 및 최소 SDK 버전 등을 확인해야 함.

  • Add No Activity 선택.

  • values/strings.xml 에서 app_name 리소스 주석 처리

    <resources>
        <!--<string name="app_name">APK Module</string>-->
    </resources>
  • apk_module에 base_module/proguard-rules.pro와 proguardTest-rules.pro를 복사

  • apk_module/build.gradle 수정 buildTypes나 Flavors는 base_module에 있던 내용을 사용 base_module에서 주석처리한 applicationId, applicationIdSuffix 항목 사용

    android {
        ...
        defaultConfig {
            // applicationId "com.mycompany.apk_module"
            applicationId "com.example.android.architecture.blueprints.todomvp"
            ...
        }
        ...
        
        /*
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
        */
        buildTypes {
            debug {
                minifyEnabled true
                // Uses new built-in shrinker http://tools.android.com/tech-docs/new-build-system/built-in-shrinker
                useProguard false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro'
            }
    
            release {
                minifyEnabled true
                useProguard true
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                testProguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguardTest-rules.pro'
            }
        }
    
        flavorDimensions "default"
    
        // If you need to add more flavors, consider using flavor dimensions.
        productFlavors {
            mock {
                dimension "default"
                applicationIdSuffix = ".mock"
            }
            prod {
                dimension "default"
            }
        }
    
        // Remove mockRelease as it's not needed.
        android.variantFilter { variant ->
            if (variant.buildType.name == 'release'
                    && variant.getFlavors().get(0).name == 'mock') {
                variant.setIgnore(true)
            }
        }
    
        // Always show the result of every unit test, even if it passes.
        testOptions.unitTests.all {
            testLogging {
                events 'passed', 'skipped', 'failed', 'standardOut', 'standardError'
            }
        }
    }
    ...
    dependencies {
    	implementation project(':base_module')
    }
  • apk_module/src/main/AndroidManifest.xml 수정

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.nobasedev.apk_module">
    </manifest>
  • gradle sync 중 아래 에러 로그시

    Manifest merger failed : Attribute application@allowBackup value=(true) from AndroidManifest.xml:5:9-35
    	is also present at [:base_module] AndroidManifest.xml:28:9-36 value=(false).
    	Suggestion: add 'tools:replace="android:allowBackup"' to <application> element at AndroidManifest.xml:4:5-10:43 to override.

    apk_module/src/main/AndroidManifest.xml의 allowBackup 옵션을 base_module과 동일하게 설정

        <application
            android:allowBackup="false"
            ... />
  • gradle sync 후 apk_module 빌드 및 run 시켜서 동작 확인 아래와 같은 에러들이 발생할 경우 있음.

    Compilation failed; see the compiler error output for details.
    error: constant expression required

    보통 아래와 같은 구문에서 오류로 base_module로 변경된 부분들에서 주로 발생함. 이유는 안드로이드 리소스 id를 switch ~ case 문으로 비교 처리하는 부분인데, base_module로 변경되며 기존 application 모듈이 feature 모듈로 변경되었고, 이에 R.java 값들이 final로 선언되지 않기 때문에 if ~ else로 변경해야 함. (라이브러리 프로젝트에서 주로 발생하는 문제)

    switch문 앞에 커서를 놓고 alt + Enter 후 Replace 'switch' with 'if' 선택

다. Instant App Module 만들기

  • Module 추가 (File > New > New Module)

  • Instant App 선택

  • 모듈명 기입. (ex. instant_module)

  • instantapp 모듈의 build.gradle에 아래와 같이 추가

    apply plugin: 'com.android.instantapp'
    
    android {
        flavorDimensions "default"
        productFlavors {
            prod {
                dimension "default"
            }
        }
    }
    
    dependencies {
        implementation project(':base_module')
    }
  • Instant App에서 사용 가능한 권한 확인

    • ACCESS_COARSE_LOCATION
    • ACCESS_FINE_LOCATION
    • ACCESS_NETWORK_STATE
    • BILLING
    • CAMERA
    • INSTANT_APP_FOREGROUND_SERVICE (API level 26 or higher)
    • INTERNET
    • READ_PHONE_NUMBERS (API level 26 or higher)
    • RECORD_AUDIO
    • VIBRATE

3. 안드로이드 스튜디오에서 실행해보기

  • base_module 매니페스트에 인스턴트 앱 구동할 URL 정의 추가

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.android.architecture.blueprints.todoapp">
        ...
        
            <activity
                android:name="com.example.android.architecture.blueprints.todoapp.tasks.TasksActivity"
                android:theme="@style/AppTheme.OverlapSystemBar">
                ...
                <intent-filter android:autoVerify="true">
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data
                        android:scheme="http"
                        android:host="todoapp.com"
                        android:pathPrefix="/tasks" />
                    <data android:scheme="https" />
                </intent-filter>
            </activity>
        	...    
            <activity
                android:name="com.example.android.architecture.blueprints.todoapp.statistics.StatisticsActivity"
                android:parentActivityName=".tasks.TasksActivity"
                tools:ignore="UnusedAttribute">
                ...
                <intent-filter android:autoVerify="true">
                    <action android:name="android.intent.action.VIEW" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.BROWSABLE" />
                    <data
                        android:scheme="http"
                        android:host="todoapp.com"
                        android:pathPrefix="/stats" />
                    <data android:scheme="https" />
                </intent-filter>
            </activity>    
  • Run메뉴의 Edit Configurations... 선택

  • Android App의 instant_module 선택 후 Launch Options의 URL 항목 입력

  • Run 하여 동작 확인.

  • apk_module과 instant_module의 차이는 멀티태스킹 화면에서 확인 가능 (인스턴트 앱의 경우 앱 아이콘 우측 하단의 번개 표시됨)

4. Heroku 구성

  • Heroku 회원가입 및 로그인

  • New - Create new app 선택

  • App name 지정 (ex. testinstant) 후 Create app버튼 클릭

  • Settings 탭 메뉴에서 Buildpacks 메뉴의 Add buildpakc 선택

  • nodejs 선택 후 Save changes 버튼 클릭

  • Deploy 탭 메뉴에서 Heroku Git 선택 후 하단의 설명대로 git 설정

  • Heroku CLI 설치 (https://devcenter.heroku.com/articles/heroku-command-line)

  • 로컬에 저장할 폴더 생성 (ex. heroku_todo_sample) 후 그 경로에서 cmd 띄움.

  • 아래 명령으로 로그인 시도 및 완료

    > heroku login
  • git 초기화

    > git init
    > heroku git:remote -a testinstant
  • nodejs 테스트 코드 작성

    • package.json

      {
        "name": "express-tutorial",
        "version": "1.0.0",
        "dependencies": 
        {
          "express": "~4.13.1",
          "ejs": "~2.4.1"    
        }
      }
    • Procfile

      web: node index.js
    • index.js

      var express = require('express')
      var app = express()
      
      app.set('port', (process.env.PORT || 5000))
      app.use(express.static(__dirname + '/public'))
      app.set('views', __dirname + '/views');
      app.set('view engine', 'ejs');
      app.engine('html', require('ejs').renderFile);
      
      app.get('/', function(request, response) {  
      	console.log("\n *index START* \n");
      	response.send('it\'s index');
      	console.log("\n *index EXIT* \n");
      })
      
      app.get('/.well-known/assetlinks.json',function(request, response){
      	//response.render('assetlinks.json');
      	var fs = require("fs");
      	console.log("\n *START* \n");
      	var content = fs.readFileSync("assetlinks.json");
      	response.setHeader('Content-Type', 'application/json');
      	response.send(content);
      	console.log("Output Content : \n"+ content);
      	console.log("\n *EXIT* \n");
      });
      
      app.listen(app.get('port'), function() {
        console.log("Node app is running at localhost:" + app.get('port'))
      })
  • assetlinks.json 만들기 참고 : https://developers.google.com/digital-asset-links/tools/generator 위 링크에서 Hosting site domainApp package nameApp package fingerprint (SHA256) 등을 입력하고 GENERATE STATEMENT 버튼을 누르면 아래와 같은 형태를 출력해주며, 그 내용을 assetlinks.json 파일로 만들면 됨.

    [{
      "relation": ["delegate_permission/common.handle_all_urls"],
      "target": {
        "namespace": "android_app",
        "package_name": "com.sample.app",
        "sha256_cert_fingerprints":
        ["75:D1:12:1A:85:EC:C7:2E...53:3E:84:E3:8C:0B"]
      }
    }]

    그리고 TEST STATEMENT 버튼을 눌러서 실제 서버에 json파일이 있는지 확인해볼 수 있음. (아래 과정 진행 후 테스트)

  • git commit 및 push (push시 Heroku에서 자동으로 deploy됨)

    > git add .
    > git commit -am "just do it"
    > git push heroku master

    위 과정 중 git push 안될 경우 (회사 보안으로 안되는 경우가 있는듯) 아래 명령 실행 후 token 복사

    > heroku auth:token
    ed2e5b99-97bc-...

    다시 git push heroku master 시도 후 Windows 보안 팝업이 뜨면 사용자 이름 없이 암호만 token 입력 후 확인하면 됨. 

  • 브라우저에서 아래 링크들 확인

  • base_module 매니페스트에서 정의한 URL 인텐트 필터들의 URL 주소를 수정함.

5. 주의할 점

Google Play 배포시 인스턴트 앱을 따로 배포함.

안드로이드 스튜디오의 Buid - Generate Signed APK... 메뉴에서 instant_module을 선택하여 생성함.

인스턴트 앱용 파일은 apk가 아니라 zip 형태로 나오며, 이를 Google Play 인스턴트 앱 배포 메뉴에서 올림.

실제 배포 요청시 base_module 매니페스트에 정의된 인텐트 필터의 URL 경로를 확인함.

해당 URL의 ./well-known/assetlinks.json이 확인 안된다면 배포가 안됨.

배포 후에도 단말마다 되는 경우가 있고, 안되는 경우가 있어 확인이 필요함.

인스턴트 앱(Instant App) 특정 웹 링크 연결

참고링크 :

https://developer.android.com/training/app-links/deep-linking

https://developer.android.com/training/app-links/verify-site-associations

https://developers.google.com/digital-asset-links/tools/generator

인스턴트 앱을 만들기는 했으나, 실제 Google Play 배포까지는 또 다른 문제들이 있음.

인스턴트 앱에서 사용할 URL의 실제 웹 링크 소유자 확인이 가능해야 등록이 가능함.

Google Digital Asset Link 참조 : https://developers.google.com/digital-asset-links/

앱 콘텐츠에 대한 딥 링크 만들기

링크 클릭시나 프로그래밍 요청으로 웹 URI 인텐트가 호출되면 안드로이드 시스템에서 아래 동작을 순차적으로 시도함.

  1. URI가 지정된 경우 URI 처리 가능한 사용자 기본 설정 앱을 실행
  2. URI를 처리할 수 있는 앱 실행
  3. 다이얼로그에서 선택 가능한 앱들에서 사용자가 선택

수신 링크를 위한 인텐트 필터 추가

<action> : ACTION_VIEW 인텐트 액션 지정.

<data> : 하나 이상의 <data> 태그 추가.

각 태그는 URI 형식을 나타내며, 최소한 android:scheme 속성이 포함되어야 함.

속성들을 이용하여 URI 유형을 구체화 할 수 있음. android:path, pathPattern, pathPrefix 등

<category> : BROWSABLE 카테고리 포함. Intent 필터가 웹 브라우저에서 접근하기 위해 필요함.

DEFAULT 카테고리도 포함. 앱이 암시적 인텐트에도 응답할 수 있음.

이 방법이 아니라면 인텐트가 앱 구성 요소명을 지정하는 경우에만 적용이 가능함.

<activity
    android:name="com.example.android.GizmosActivity"
    android:label="@string/title_gizmos" >
    <intent-filter android:label="@string/filter_view_http_gizmos">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "http://www.example.com/gizmos” -->
        <data android:scheme="http"
              android:host="www.example.com"
              android:pathPrefix="/gizmos" />
        <!-- note that the leading "/" is required for pathPrefix-->
    </intent-filter>
    <intent-filter android:label="@string/filter_view_example_gizmos">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <!-- Accepts URIs that begin with "example://gizmos” -->
        <data android:scheme="example"
              android:host="gizmos" />
    </intent-filter>
</activity>

위 2개의 인텐트 필터는 <data> 요소에 의해서만 서로 다름.

동일한 필터에 여러 개의 <data> 요소를 포함할 수 있지만 고유한 URL을 선언하려는 경우 별도의 필터로 만들어야 함.

아래의 경우 https://www.example.com 이나 app://open.my.app 만 지원하는 것처럼 보임.

하지만 실제로는 app://www.example.com 이나 https://open.my.app 형식도 지원하게 됨.

<intent-filter>
  ...
  <data android:scheme="https" android:host="www.example.com" />
  <data android:scheme="app" android:host="open.my.app" />
</intent-filter>

보다 자세한 내용은 developer 문서를...

수신 인텐트에서 데이터 읽기

시스템이 인텐트 필터를 통해 액티비티를 시작한다면, 인텐트가 제공하는 데이터를 사용할 수 있음.

getData()나 getAction() 메소드를 호출하여 사용함. 일반적으로 onCreate()나 onStart() 시기에 사용함.

딥 링크 테스트

adb(Android Debug Bridge)를 통해서 액티비티 매니저 툴로 인텐트 필터 URI 지정한 앱을 테스트할 수 있음.

$ adb shell am start -W -a android.intent.action.VIEW -d <URI> <PACKAGE>
$ adb shell am start -W -a android.intent.action.VIEW -d "example://gizmos" com.example.android

Android 앱 링크 확인

안드로이드 앱 링크는 웹 URL이 앱의 특정 컨텐츠로 바로 연결되도록 해주는 특별한 타입의 딥 링크임.

안드로이드 앱 링크를 앱에 추가하려면 HTTP URL을 사용해 인텐트 필터를 정의하고,

앱과 웹 사이트 URL을 모두 소유하고 있는지 확인이 필요함.

시스템이 사용자의 URL 소유권을 확인하면 자동으로 해당 URL 인텐트를 앱으로 라우팅함.

앱과 웹 소유권을 확인하기 위한 단계

딥 링크와 앱 링크의 차이점

딥 링크는 앱의 특정 활동을 직접 입력할 수 있도록 하는 인텐트 필터임.

이러한 링크 중 하나를 클릭하면 사용자가 주어진 URL을 사용할 수 있는 여러 앱 중 하나를 선택할 수 있음.

안드로이드 앱 링크는 웹에 속한 것으로 확인된 웹 URL을 기반으로 하는 딥 링크임.

이 중 하나를 클릭하면 앱이 즉시 열리며, 앱 선택 화면이 나타나지 않음.

사용자가 링크 처리에 대한 선호도를 변경할 수도 있음.

Deep linksApp links
Intent URL schemehttphttps, or a custom schemeRequires http or https
Intent actionAny actionRequires android.intent.action.VIEW
Intent categoryAny categoryRequires android.intent.category.BROWSABLE and android.intent.category.DEFAULT
Link verificationNoneRequires a Digital Asset Links file served on you website with HTTPS
User experienceMay show a disambiguation dialog for the user to select which app to open the linkNo dialog; your app opens to handle your website links
CompatibilityAll Android versionsAndroid 6.0 and higher

앱 링크 확인 요청하기

앱의 링크 처리 확인을 활성화하려면 앱 매니페스트에서 아래를 포함하는 웹 URL 인텐트 필터 중 하나에서 android:autoVerify="true" 설정을 함.

  • <action> : android.intent.action.VIEW
  • <category> : android.intent.category.BROWSABLE, android.intent.category.DEFAULT
  • <data> : http 또는 https
<activity ...>

    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="http" android:host="www.example.com" />
        <data android:scheme="https" />
    </intent-filter>

</activity>

android:autoVerify="true"가 인텐트 필터 중에 존재하는 앱일 경우,

Android 6.0 이상인 기기에 앱이 설치되면 시스템이 앱의 인텐트 필터에 있는 URL과 연결된 호스트를 확인함.

안드로이드 시스템은 위의 인텐트 필터의 각각의 고유한 호스트 이름에 대해 Digital Asset Links 파일을 쿼리함.

ex) https://hostname/.well-known/assetlinks.json

여러 호스트를 위한 앱 링크 지원시

시스템이 매니페스트의 모든 호스트에 대해 일치하는 Digital Asset Links 파일을 찾은 경우에만 앱이 지정된 URL 패턴의 기본 처리기로 설정됨.

<application>

  <activity android:name=”MainActivity”>
    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="http" android:host="www.example.com" />
      <data android:scheme="https" />
    </intent-filter>
  </activity>
  <activity android:name=”SecondActivity”>
    <intent-filter>
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="https" android:host="www.example.net" />
    </intent-filter>
  </activity>

</application>

위의 경우 https://www.example.com/.well-known/assetlinks.json 과 https://www.example.net/.well-known/assetlinks.json이 없을 경우 앱 인텐트 필터 확인이 실패함.

여러 서브 도메인을 위한 앱 링크 지원시

Digital Asset Links 프로토콜은 인텐트 필터의 서브 도메인을 유니크한 개별 호스트로 취급함.

서브 도메인이 호스트가 다를 경우 각 도메인에 유효한 assetlinks.json을 가져야 함.

<application>
  <activity android:name=”MainActivity”>
    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="https" android:host="www.example.com" />
      <data android:scheme="https" android:host="mobile.example.com" />
    </intent-filter>
  </activity>
</application>

위의 경우 https://www.example.com/.well-known/assetlinks.json 와 https://mobile.example.com/.well-known/assetlinks.json가있어야 함.

호스트 명에 와일드카드(ex. *.example.com)를 선언한 경우 루트 호스트에 assethosts.json 파일을 게시해야 함.

<application>
  <activity android:name=”MainActivity”>
    <intent-filter android:autoVerify="true">
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:scheme="https" android:host="*.example.com" />
    </intent-filter>
  </activity>
</application>

위의 경우 https://example.com/.well-known/assetlinks.json 가 있어야 함.

웹 사이트 연결 선언

Digital Asset Links JSON 파일은 웹 사이트와 연결된 앱을 표시하고 앱 URL 인텐트를 확인하기 위해 게시함.

JSON 파일은 아래 필드들을 사용.

  • package_name : 앱의 build.gradle 에 선언된 앱 ID

  • sha256_cert_fingerprints : 앱의 서명 인증서의 SHA256 fingerprints. 아래 명령으로 얻을 수 있음.

    $ keytool -list -v -keystore my-release-key.keystore

    이 필드는 여러 fingerprints를 지원하며, 디버그와 프로덕션 빌드와 같은 버전의 앱을 지원하는데 사용함.

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

웹 사이트를 여러 앱과 연결시

한 assetlinks.json 파일에서 여러 앱과 연결을 선언할 수 있음.

인스턴트 앱의 경우 각 웹사이트 도메인에 하나의 인스턴트 앱만 지정 가능

[{
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.puppies.app",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
  },
  {
  "relation": ["delegate_permission/common.handle_all_urls"],
  "target": {
    "namespace": "android_app",
    "package_name": "com.example.monkeys.app",
    "sha256_cert_fingerprints":
    ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
  }
}]

서로 다른 앱이 동일한 웹 호스트에서 여러 리소스에 대한 링크 처리 가능함.

ex) app1은 https://example.com/articles에 대한 인텐트 필터를 선언하고 app2는 https://example.com/videos에 대한 인텐트 필터를 선언 할 수 있음.

여러 웹 사이트를 단일 앱과 연결

여러 웹 사이트는 각가의 assetlinks.json 파일에서 동일한 앱을 선언할 수 있음.

아래는 app1과 example.com 및 example.net의 연결을 선언하는 예제임.

  1. https://www.example.com/.well-known/assetlinks.json

[{ "relation": ["delegate_permission/common.handle_all_urls"], "target": { "namespace": "android_app", "package_name": "com.mycompany.app1", "sha256_cert_fingerprints": ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"] } }]

https://www.example.net/.well-known/assetlinks.json 

```json
[{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
  "namespace": "android_app",
  "package_name": "com.mycompany.app1",
  "sha256_cert_fingerprints":  ["14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:16:A0:83:42:E6:1D:BE:A8:8A:04:96:B2:3F:CF:44:E5"]
}
}]

JSON 확인 파일 게시

아래 위치에 JSON 확인 파일이 게시해야 함.

https://domain.name/.well-known/assetlinks.json

  • assetlinks.json 파일의 content-type을 application/json 형식으로 제공.
  • assetlinks.json 파일은 앱의 인텐트 필터가 HTTPS를 데이터 스킴으로 선언하는지 여부와 관계없이 HTTPS 연결을 통해 액세스할 수 있어야 함.
  • assetlinks.json 파일은 리다이렉션(301 또는 302 리다이렉션 아님) 없이 액세스할 수 있어야 함. 로봇(robots.txt) 에 assetlinks.json파일 크롤링을 허용해야 함.
  • 앱 링크가 여러 호스트 도메인을 지원하는 경우 각 도메인에 assetlinks.json 파일을 게시해야 함.
  • 매니페스트 파일에 dev/test URL이 있는 앱을 공개하지 말 것. 예를 들어 공개 키를 사용하여 액세스 할 수 없는 경우(VPN에서만 액세스 할 수 있는 URL 등) 이러한 경우 빌드 variants를 구성하여 dev 빌드에 대한 다른 매니페스트 파일 생성.

앱 링크 테스트

앱 연결 기능 구현시 시스템이 앱과 웹 사이트를 연결하고 URL 요청을 처리할 수 있는지 확인.

기존 명령문 파일을 테스트 하려면 Statement List Generator and Tester 툴을 사용.

https://developers.google.com/digital-asset-links/tools/generator

확인한 호스트 리스트 확인

앱에서 확인해야 하는 관련 호스트 목록을 확인.

인텐트 필터에 다음 속성 및 요소가 포함된 모든 URL 목록을 만듬.

  • android:scheme 속성 값이 http나 https인 것
  • android:host 속성이 도메인 URL 패턴과 같이 있는 것
  • android.intent.action.VIEW 카테고리 요소
  • android.intent.category.BROWSABLE 카테고리 요소

이 목록을 사용하여 각 호스트 및 하위 도메인에 Digital Asset Links JSON 파일이 제공되는지 확인.

Digital Asset Links 파일 확인

각 웹 사이트에 대해 Digital Asset Links API를 사용하여 JSON 파일이 제대로 호스팅되고 있는지 확인.

https://digitalassetlinks.googleapis.com/v1/statements:list?source.web.site=https://<<domain.name:optional_port>>&relation=delegate_permission/common.handle_all_urls

URL 인텐트 테스트

앱과 연결할 웹 사이트 목록 확인 후 호스팅된 JSON 파일이 유효하다는 것을 확인 후 기기에 앱 설치.

비동기 확인 프로세스 완료시까지 최소 20초 대기.

아래 명령을 사용하여 시스템에서 앱을 확인하고 올바른 처리 설정됐는지 확인.

adb shell am start -a android.intent.action.VIEW -c android.intent.category.BROWSABLE -d "http://<<domain.name:optional_port>>"

링크 정책 확인

테스트 과정으로 링크 처리를 위한 현재 시스템 설정을 확인할 수 있음.

아래 명령을 사용하여 연결된 장치의 모든 앱에 대한 기존 링크 처리 정책 리스트를 가져옴.

adb shell dumpsys package domain-preferred-apps

또는 아래와 같이 함

adb shell dumpsys package d

시스템이 확인 프레세스를 완료할 수 있도록 앱 설치 후 적어도 20초 정도 필요할 수 있음

위 명령은 장치에 정의된 각 사용자 또는 프로필의 목록을 반환하며, 아래 형식의 헤더가 있음.

App linkages for user 0:

위 헤더에 따른 내용은 아래와 같이 나열되며, 도메인과 연결된 앱이 표시됨.

Package: com.android.vending
Domains: play.google.com market.android.com
Status: always : 200000002
  • Package : 매니페스트에 선언된 패키지 명으로 연결된 앱 표시
  • Domains : 앱이 웹 링크를 처리하는 호스트의 전체 목록 표시. 빈 칸으로 구분.
  • Status : 앱의 현재 링크 처리 설정 표시. 인증을 통과했고, 매니페스트에 android:autoVerify=true 가 포함된 앱은 always를 표시함. 이 이후의 16진수는 시스템의 사용자 앱 연결 환경설정에 대한 기록이며, 이 값이 확인이 성공했는지 여부를 나타내지는 않음.

테스트

앱의 인텐트 필터를 지정하고 앱 링크 기준을 충족하는 모든 웹 사이트로 앱을 인증할 수 있어야 함.

<application>

    <activity android:name=”MainActivity”>
        <intent-filter android:autoVerify="true">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" android:host="www.example.com" />
            <data android:scheme="https" android:host="mobile.example.com" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" android:host="www.example2.com" />
        </intent-filter>
    </activity>

    <activity android:name=”SecondActivity”>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="https" android:host="account.example.com" />
        </intent-filter>
    </activity>

      <activity android:name=”ThirdActivity”>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <data android:scheme="https" android:host="map.example.com" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data android:scheme="market" android:host="example.com" />
        </intent-filter>
      </activity>

</application>

플랫폼에서 위 목록에서 확인하려는 호스트 목록은 아래와 같음.

확인하지 않으려는 호스트 목록은 아래와 같음.

  • map.example.com (android.intent.category.BROWSABLE 없음)
  • market://example.com (http나 https 스킴이 없음.)


인스턴트 앱(Instant App)

참고링크 :

https://codelabs.developers.google.com/codelabs/android-instant-apps/index.html

https://developer.android.com/topic/google-play-instant/getting-started/create-base-feature-module

https://developer.android.com/topic/google-play-instant/getting-started/convert-to-instant-app

https://developer.android.com/topic/google-play-instant/guides/config-splits

인스턴트 앱이란?

https://developer.android.com/topic/instant-apps/overview?hl=ko

구글 I/O 2017에서 공개된 것으로 설치없이 앱의 일부 기능을 사용해보는 것.

Play Store, Google 검색, SNS 및 링크를 공유하는 모든 곳에서 인스턴트 앱을 사용할 수 있다 함.

Android 6.0 (API 23) 이상에서만 실행됨.

자주 묻는 질문(FAQ)

필요한 것들

앱을 인스턴트 앱에 맞춰 feature 모듈과 APK용 / Instant App용 모듈로 구성해야 함.

인스턴트 앱은 링크 기반으로 진입점을 작업하며, 인증을 위한 웹 도메인이 필요함.

(간단히 로컬 테스트는 실제 도메인 없어도 가능하나, 구글 플레이 배포시는 필요함.)

인스턴트 앱 구조 맞추기

기존 앱을 인스턴트 앱 구조에 맞춰 변경해보자.

기존 프로젝트의 기본 모듈을 base feature로 수정 후 이를 참조하여 APK, Instant App용 모듈을 추가한다.

기존 앱 base feature module로 변경

  1. 기존 app 모듈명을 base feature명으로 수정 (ex. base_module)

  2. base_module의 gradle 파일을 열어 아래와 같이 수정

    // apply plugin: 'com.android.application'
    apply plugin: 'com.android.feature'
    ...
    android {
        ...
        baseFeature true
        ...
    }

APK Module 만들기

  1. Module 추가 (File > New > New Module)

  2. Phone & Table Module 선택

  3. 모듈명 기입.(ex. apk_module)

    앱 이름 및 최소 SDK 버전 등을 확인해야 함.

  4. Add No Activity 선택.

Base Module과 APK Module 연결

  1. base_module의 gradle 파일을 열어 아래와 같이 수정 (applicationIdSuffix 가 있다면 주석 처리)

    android {
        ...
        defaultConfig {
            ...
            //applicationId "com.mycompany.example"
            ...
        }
    }
    ...
    dependencies {    
        ...
        application project(":apk_module")
        ...
    }
  2. apk_module의 gradle 파일을 열어 아래와 같이 수정

    android {
        ...
        defaultConfig {
            // applicationId "com.mycompany.example.apk_module"
            applicationId "com.mycompany.example"
            ...
        }
        ...
        dependencies {
            implementation project(':base_module')
        }
    }
  3. base_module의 AndroidManifest.xml 파일을 열어 아래와 같이 수정

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              package="com.mycompany.example">
    ...
    </manifest>
  4. apk_module의 AndroidManifest.xml 파일을 열어 아래와 같이 수정

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.mycompany.example.app">
    </manifest>

Instant App Module 만들기

  1. Module 추가 (File > New > New Module)

  2. Instant App 선택

  3. 모듈명 기입. (ex. instantapp)

  4. instantapp 모듈의 build.gradle에 아래와 같이 추가

    dependencies {
        implementation project(':base_module')
    }
  5. Instant App에서 사용 가능한 권한 확인

    • ACCESS_COARSE_LOCATION
    • ACCESS_FINE_LOCATION
    • ACCESS_NETWORK_STATE
    • BILLING
    • CAMERA
    • INSTANT_APP_FOREGROUND_SERVICE (API level 26 or higher)
    • INTERNET
    • READ_PHONE_NUMBERS (API level 26 or higher)
    • RECORD_AUDIO
    • VIBRATE

Google Play에서 "Try Now" 을 위한 처리 추가

  1. base_module의 기본 액티비티 사용

  2. base_module의 기본 URL 생성

    <activity android:name="com.example.base_module.MainActivity" ... >
        <meta-data android:name="default-url"
                   android:value="https://example.com/welcome" />
    </activity>

인스턴트 앱 접근을 위한 URL 제공

  1. base_module의 AndroidManifest.xml 파일에서 액티비티에 URL 매핑 적용

    HTTP와 HTTPS 모두 지원해야 함

    <activity android:name="com.example.base_module.MainActivity" ... >
        <intent-filter android:autoVerify="true">
            <action android:name="android.intent.action.VIEW" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.BROWSABLE" />
            <data
                android:scheme="http"
                android:host="example.com"
                android:pathPrefix="/welcome" />
            <data android:scheme="https" />
        </intent-filter>
    </activity>
  2. 인스턴트 앱 실행시 진입할 액티비티의 해당 URL에 autoVerify 옵션 추가

    <activity android:name="com.example.base_module.MainActivity" ... >
         <intent-filter android:autoVerify="true">
             ...
         </intent-filter>
    </activity>

인스턴트 앱 테스트

  1. Android Studio 의 "Run" -> "Edit Configurations"
  2. instantapp 모듈 선택
  3. "General" 탭의 "Launch Options"에서 "Launch" 값을 "URL" 선택
  4. "General" 탭의 "Launch Options"에서 "URL" 값을 실행할 액티비티의 매핑 URL로 입력
  5. 멀티태스킹 화면 등에서 아이콘에 번개표시로 인스턴트 앱 확인 

인스턴트 앱 링크 연결

Android 앱 링크를 설정하여 앱에서 직접 링크의 특정 콘텐츠로 이동하는 용도로 Deep Link와 App Link가 사용됨.

Deep Link

앱의 특정 콘텐츠로 바로 연결하는 URL. 인텐트 필터를 추가하여 인텐트에서 데이터를 추출하여 처리함.

App Link

Android 6.0 (API 23) 이상에서 앱이 주어진 유형의 링크의 기본 처리기로 지정되도록 허용함.

  • 앱 링크는 소유하고 웹 사이트 도메인에 연결되는 HTTP URL을 사용하므로 다른 앱이 링크 사용 불가 (Google 웹 사이트 연결 방법 중 하나를 통해 도메인 소유권 확인 필요)
  • 웹 사이트와 앱의 동일한 콘텐츠에 대해 하나의 HTTP URL을 사용. 앱이 없을 경우 웹 사이트로 이동됨.
  • 인스턴트 앱 지원을 하기 위해 앱 링크 설정
  • 모바일 브라우저, Google 검색 앱, Android 화면 검색, Google Assistant를 통해 Google 에서 URL을 클릭하여 열 수 있음.

Android App Link 추가하기

  1. 특정 컨텐츠에 대한 딥링크를 앱에 생성. (위 내용 중 "인스턴트 앱 접근을 위한 URL 제공" 참조)
  2. 딥링크 확인 추가. 인스턴트 앱에서 사용할 URL의 실제 웹 링크 소유자 확인. (Google Search Console에서 소유자 확인한 사이트에 Digital Asset Links JSON 파일이 있어야 함.) (Developers 문서에는 위와 같지만, Google Search Console에 대한 부분은 안해도 인스턴트 앱이 되긴 했음.) 참조 : https://developer.android.com/training/app-links/


+ Recent posts