본문 바로가기

Android/WebView

[WebView] WebView 사용 방법 - 1. WebView 적용 및 JavaScriptInterface의 사용.

728x90

Webview에 대한 공부를 진행하던 도중,

JavaScriptInterface를 통해 webView에서 호출되는 함수를 처리하는 방법에 대해 알게 되었다.

 

따라서 JavaScriptInterface를 사용하는 방법과 더불어 webView에 대한 기본적인 사용 방법에 대해 작성해보고자 한다.


 

webView를 사용하기 위해서는 별 다른 작업은 필요하지 않고 단순히 xml에 WebView를 선언하고, 해당 webView에서 URL을 호출하면 일반적인 웹과 같이 사용할 수 있게 된다.

 

따라서, 맨 처음으로

WebViewActivity를 생성하고, 그곳에서 사용할 layout에 webView 선언을 해준다.

 

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

</androidx.constraintlayout.widget.ConstraintLayout>

 

 

그 후에

 

webView!!.loadUrl("https://heegs.tistory.com")

 

이처럼 보여주고자 하는 URL을 넣어 loadUrl 을 수행시키면 새로운 인터넷이 열리면서 해당 페이지로 넘어가게 된다.

 

하지만, 우리가 원하는 것은 새로운 창이 뜨면서 이동하는 것이 아니라 앱 내부에서 웹뷰가 보이는 상태를 원한다.

따라서, WebViewActivity에서 webView에 대한 설정을 추가해주어 우리가 원하는 상태로 만들어 주도록 하자.

 

webView?.run { 
    webViewClient = WebViewClient()
    webChromeClient = WebChromeClient()
    setLayerType(View.LAYER_TYPE_HARDWARE, null)

    settings.run { // 세부 세팅 등록
        setSupportMultipleWindows(false)
        loadWithOverviewMode = true
        useWideViewPort = true
        setSupportZoom(false)
        builtInZoomControls = false
        cacheMode = WebSettings.LOAD_NO_CACHE
    }
}

 

새 창을 띄우지 않게 하는 것 외에 기본적인 설정을 해보았다.

 

각 옵션에 대한 설명을 하자면 다음과 같다.

 

  • webViewClient : 새 창이 뜨지 않도록 생성. WebView에 Load에 대한 CallBack을 받는다.
  • webChromeClient : Chrome에서도 사용 및 이벤트를 받을 수 있도록 설정.
  • setLayerType : WebView가 보일 layout type을 설정
  • settings : WebView에 대한 설정을 할 수 있다.
  • setSupportMultipleWindows : 멀티 윈도우 사용 여부
  • loadWithOverviewMode : 메타 태그 사용 여부
  • useWideViewPort : 화면 사이즈 맞추기 사용 여부
  • setSupportZoom : 줌 허용 여부
  • builtInZoomControls : 화면 확대 축소 여부
  • cacheMode :  WebView의 캐시 허용 여부

 

이것들 외에도 다양한 webView 설정 옵션이 있으니, 필요한 옵션이 있다면 찾아보고 설정하면 될 것이다.

 

여기서 우리가 앞서 발생했던 이슈를 해결하기 위해서 필요한 작업은,  webViewClient에 새로운 객체를 생성하여 넣어주는 작업이다.

이와같이 설정 후에 다시 url을 호출해보자.

 

 

정상적으로 앱 내에서 webView가 호출되는 것을 볼 수 있을 것이다.

 

 

* 22.04.07 추가

webChromeClient의 경우, Alert 등 event를 받기 위해서는 반드시 선언해야 한다.

 

* 22.04.08 추가

 

settings.run { // 세부 세팅 등록
    
    ...
    
    domStorageEnabled = true // Dom Storage API 사용 여부
}

해당 부분을 추가해주어야 다양한 이벤트를 정상적으로 받을 수 있다.

예로 들어, 필자가 연결해둔 블로그로 이동을 한 후 가장 하단으로 내렸을 때 더 많은 게시글이 있음에도 불구하고 이전 글이 로드되지 않는다.

확인을 해보니 domStorageEnabled 값이 default로 false 값이기 때문에 해당 값을 true로 설정해 주어야 다양한 이벤트가 정상적으로 동작한다고 한다.

Dom Storage란, Web Storage를 말하는 것인데 해당 기능을 false로 했을 때 어째서 받지 못하는 이벤트가 있는지는 확인하지 못하였지만, true로 설정을 해두어야 정상 동작한다고 하니 해당 값을 반드시 추가해주도록 하자.


간단하게 웹뷰를 호출해 보았으니, JavaScriptInterface를 사용하는 방법에 대해 알아볼 차례이다.

 

JavaScriptInterface를 사용하기 위해서는 앞서 작성했던 webView의 세팅에 추가적으로 옵션을 넣어주어야 한다.

 

addJavascriptInterface(javaScriptInterface!!, "JavaScriptInterface") // 해당 webView에서 호출 가능한 JSInterface 설정

settings.run { // 세부 세팅 등록
    javaScriptEnabled = true // 웹 페이지 JS 호출 허용
}

 

여기서 사용하는 javaScriptInterface는 필자가 따로 선언한 부분으로, javaScriptInterface를 통해 직접 호출되는 부분이다.

addJavaScriptInterface에 해당 부분을 넣음으로써, 이 webView에서는 따로 선언한 javaScriptInterface를 JavaScriptInterface로 사용하겠다는 의미가 된다.

 

settings에서 추가하는 javaScriptEnabled = true를 통해 webView에서 javaScript를 사용하겠다는 설정을 해준다.

 

위의 두 가지 설정을 추가하게 되면, 해당  webView에서 javaScript를 사용할 수 있으며, javaScriptInterface를 통하여 해당 호출이 들어오게 되는 것이다.

 

그러면, 위에 따로 생성했던 javaScriptInterface를 보자.

 

class JavaScriptInterface {
    var repository: JavaScriptRepository? = null

    @JavascriptInterface
    fun showText(arg: String) {
        Log.d("interfaceTest", "showShareView text ? $arg")
        repository?.showText(arg)
    }

    @JavascriptInterface
    fun otherUrl(arg: String) {
        Log.d("interfaceTest", "otherUrl text ? $arg")
        repository?.otherUrl(arg)
    }
}

 

@JavascriptInterface라는 어노테이션을 함수 위에 선언함으로써, webView에서 호출이 가능하도록 함수를 선언할 수 있다.

위의 코드를 예로 들자면,  webView에서 showText, otherUrl이라는 함수를 호출할 때 데이터를 인자로 넣어서 호출하게 되면 App에서 해당 함수가 호출되어 로그를 찍고, repository.~ 함수를 이어서 호출하게 되는 것이다.

 

 

필자는 Repository를 따로 선언하여 JavascriptInterface를 통해 함수가 호출되었을 때 별도의 작업을 진행하기 위해 선언하였지만, 별도의 Depth를 추가로 선언하지 않고 호출받은 부분에서 필요한 처리를 진행해도 상관없다.

 

interface JavaScriptRepository {
    fun showText(arg: String)
    fun otherUrl(arg: String)
}

 

 Repository라는 의미에 맞춰서 Interface로 선언된 부분이고, 필자는 해당 부분의 구현부를 ViewModel에서 진행하였다.

 

class WebViewModel : BaseViewModel(), JavaScriptRepository

 

와 같이 ViewModel선언 시 JavaScriptResotiory를 상속 받아서 해당 부분을 구현해주면 된다.

 

verride fun showText(arg: String) {
    Log.d("javaScript", "insert JavaScript showText\n$arg")
}

override fun otherUrl(arg: String) {
    Log.d("javaScript", "insert JavaScript otherText\n$arg")
    viewModelScope.launch {
        webView!!.loadUrl(arg)
    }
}

 

필자는 간단하게 로그를 찍고, otherUrl에서는 받은 인자를 사용하여 페이지를 변경하는 코드를 작성하였다.

loadUrl을 호출하는 부분이 viewModel에서 선언되어있기 때문에 CoroutineScope가 아닌 viewModelScope를 사용하였다는 것을 참고하길 바란다.

 

그러면 이제 webView에서 해당 함수를 호출해주도록 해야 하는데,

아주 간단하게 html 파일을 넣어서 웹뷰처럼 보이는 화면을 만들어보도록 하자.

 

 

다음과 같은 스텝으로 assets 디렉터리를 생성한 후에 html 파일을 생성하도록 하자.

 

 

sample_web.html 코드는 다음과 같이 작성해주었다.

 

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="initial-scale=1.0">
    <meta charset="utf-8">
</head>
<body>
<p>
    Sample WebView test
    send JavaScript button : insertData1
    send JavaScript2 button : insertData2 and change WebVew 2
</p>
<input type="button" value="send JavaScript" onClick="showText('insertData1')"/>
<script type="text/javascript">
    function showText(data) {
        JavaScriptInterface.showText(data);
    }
</script>
<input type="button" value="send JavaScript2" onClick="otherUrl('https://heegs.tistory.com')"/>
<script type="text/javascript">
    function otherUrl(data) {
        JavaScriptInterface.otherUrl(data);
    }
</script>
</body>
</html>

 

간단하게 <p> 태그에는 화면에서 보여질 본문 부분이고,

input 태그는 type에 맞게 버튼이, script는 해당 버튼을 눌렀을 때 동작하는 함수라고 생각하면 된다.

 

작성한 html 코드를 webView를 통해 실행시키도록 코드를 수정해보자.

 

fun openSampleWebView() {
    webView!!.loadUrl("file:///android_asset/sample_web.html")
}

 

loadUrl로 사용이 가능한 경로는 웹 페이지의 URL뿐 아니라 앱 내 파일 경로 또한 사용이 가능하다.

따라서, 필자와 동일한 위치에 html 파일을 작성했으면 다음과 같은 경로를 통해 호출하면 된다.

 

해당 html파일을 실행하게 되면,

 

 

다음과 같은 화면을 확인할 수 있고, 버튼을 눌러서 javaScriptInterface가 정상적으로 동작하는지 확인해보자.

 

 

첫 번째 버튼을 눌렀을 때 다음과 같이 html에 입력한 텍스트가 넘어오는 것을 확인할 수 있고,

 

 

두 번째 버튼을 누르면 다음과 같이 페이지가 변경되는 것을 확인할 수 있다.


WebView를 적용함에 있어서, JavaScriptInterface가 어떻게 적용되고 사용되는지에 대해 알아보았다.

이전에 진행한 프로젝트에서도 webView를 사용했었는데,

명확하게 인지하지 못하고 작성된 코드를 따라서 작성하였기 때문에 공부할 겸 글을 작성하며 정리해보았다.

 

html 파일을 사용하여 다양한 테스트가 가능할 것으로 보아, 추후에 필요한 기능이 있다면 해당 파일을 수정하여 테스트를 진행하면 될 것으로 보인다.

 

해당 게시글에 사용한 예제는 다음 Github에 올려두었다.

https://github.com/HeeGyeong/CleanArchitectureSample

 

GitHub - HeeGyeong/CleanArchitectureSample

Contribute to HeeGyeong/CleanArchitectureSample development by creating an account on GitHub.

github.com

 

728x90