해당 포스트는 "안드로이드 통신+보안 프로그래밍" 책의 내용을 요약한 것이다.



※ 웹뷰 클래스

: 웹브라우저의 핵심 엔진은 웹키트이다. 웹키트는 크기 웹코어와 자바 스크립트 코어로 구성되어 있다. 웹코어는 HTML 번역, 레이아웃 처리를 수행하고 자바 스크립트 코어는 자바 스크립트를 분석하고 실행하는 기능을 담당한다. WebView 클래스와 WebViewClient 클래스를 통해 웹코어의 역할을 수행할 수 있다.

mWebView = (WebView) findViewById(R.id.webview);
mWebView.loadUrl("http://www.google.com");

위와 같이 하면 구글 페이지가 보인다. 하지만 해당 액티비티가 앱 내부에서 실행된 웹뷰가 아닌 스마트폰에서 기본으로 제공되는 웹브라우저 화면으로 보이게 된다. 앱 내부에서 웹뷰를 보일려면 다음과 같이 WebView객체와 WebViewClient 객체를 연결해줘야 한다.

mWebView.setWebViewClient(new HelloWebViewClient());
mWebView.loadUrl("http://www.google.com");
private class HelloWebVeiwClient extends WebViewClient{
   @Ovverride
   public booolean shouldOverrideUrlLoading(WebView view, String url){
     view.loadUrl(url);
     return true;
   }
}

WebViewClient 클래스는 웹뷰에 어떤 세부적인 작업을 할 지에 대해 지시를 내리는 기능을 한다. WebViewClient클래스의 메서드들을 살펴보자

- public boolean shouldOverrideUrlLoading(WebView view, String url) : HTML 문서 내에 존재하는 다른 리소스 URL의 로드 방식 , 즉 스마트폰 기본 브라우저에 해당 URL를 로드할 지 앱 내의 웹뷰에 로드할 지 기술한다.

- public void onPageStarted(WebView view, String url, Bitmap favicon) : 서버에서 제공한 HTML 문서를 웹뷰에 처음 로드하기 전에 제일 먼저 호출되는 콜백 메서드이다.

- public void onLoadResource(WebView view, String url) : onPageStarted() 메서드 호출 후 현재 화면에 로드하는 URL이 무엇인지 알려주는 기능을 하는 메서드이다.

- public void onPageFinished(WebView view, String url) : 서버에서 제공한 HTML 문서를 웹뷰 화면에 출력하는 작업이 완료되었을 때 호출되는 콜백메서드이다.

위 코드와 같이 웹뷰 내에 웹브라우저를 로딩해도 자바 스크립트가 실행되지 않았기 때문에 정상적으로 보이진 않는다. 따라서 웹뷰에 자바 스크립트를 사용하려면 다음과 같이 해야 한다.

mWebView.getSettings().setJavaScriptEnabled(true);

ex) 웹뷰에서 HTML를 로딩할 때 진행 상황을 프로그래스바로 나타내는 예제

public class Mywebview extends Activity {
    private WebView mWebView;
    private ProgressBar mProgressBar;
    final Activity activity = this;
    final private String DEFAULT_URL = "http://www.google.com";
   
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.android_main);
        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
        mWebView = (WebView) findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new HelloWebViewClient());
        mWebView.loadUrl(DEFAULT_URL);
        mWebView.setWebChromeClient(new WebChromeClient() {
            @Override
            public void onProgressChanged(WebView view, int progress) {
                //progress의 범위는 0부터 10000까지다.
  mProgressBar.setProgress(progress*100);
            }
        });
    }
   
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
     //백키를 누르면 액티비티를 종료하지 않고 이전 페이지로 돌아간다.
        if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
            mWebView.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
   
    private class HelloWebViewClient extends WebViewClient {
        @SuppressWarnings("deprecation")
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            view.loadUrl(url);
            return true;
        }
        @TargetApi(Build.VERSION_CODES.N)
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
      ///HTML 문서 내의 URL을 클릭할 시 웹뷰에 로드한다.
            view.loadUrl(request.getUrl().toString());
            return true;
        }
        @SuppressWarnings("deprecation")
        @Override
        public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
            Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
          }
        @TargetApi(android.os.Build.VERSION_CODES.M)
        @Override
        public void onReceivedError(WebView view, WebResourceRequest req, WebResourceError err) {
            Toast.makeText(activity, "Oh no! " + err.getErrorCode() + " " + err.getDescription().toString() +
                                       " " + req.getUrl().toString(), Toast.LENGTH_SHORT).show();
        }
    } 
}

위 코드에서 WebChromeClient 클래스를 사용했다. 해당 클래스는 자바 스크립트 코어와 연동하여 자바 스크립트 실행 결과와 자바 스크립트가 요구하는 데이터를 제공해주는 창구 역할을 수행한다. onProgressChanged 콜백 메서드는 웹 페이지 로딩 진행 상황을 나타내준다. WebChromeClient 클래스로 웹뷰에서 자바스크립트 로그 메시지를 띄우거나 파일을 선택하거나 하는 데 사용할 수 있다. onReceivedError 메서드는 웹페이지를 수신하는 과정에서 에러가 발생했을 때 호출된다.

 

 

※ 웹뷰에서 자바 스크립트 사용하기

public class JavaScript extends Activity {
       @Override
  public void onCreate(Bundle icicle) {
            super.onCreate(icicle);
            setContentView(R.layout.main);
            WebView wv = (WebView) findViewById(R.id.webview);
            wv.getSettings().setJavaScriptEnabled(true);
            wv.loadUrl("file:///android_asset/demo.html");
        }
}

안드로이드 웹뷰에서 자바 스크립트를 사용하려면 loadUrl() 메서드로 자바스크립트 파일을 읽으면 된다. URL file:///android_asset/demo.html 은 /assets 디렉터리의 demo.html 자바스크립트 파일을 말한다. 만약 /raw 디렉터리를 표시하려면 file///android_res/raw, 외장 저장 장치를 표현하려면 file///sdcard로 표시해야 한다.

 

ex) 웹뷰에서 자바 스크립트를 사용한 예제

: 코드부터 보고 설명하겠다.

public class JavaScript2 extends Activity {
    @Override
 public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        WebView mWebView = (WebView) findViewById(R.id.webview);
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setSaveFormData(false);
        webSettings.setJavaScriptEnabled(true);
        webSettings.setDefaultTextEncodingName("utf-8");
        mWebView.addJavascriptInterface(new JavaScriptInterface(this), "Android");
        mWebView.loadUrl("file:///android_asset/demo.html");
        
    }
   
    public class JavaScriptInterface {
     Context mContext;
     JavaScriptInterface(Context c) {
      mContext = c;
     }
        @JavascriptInterface
     public void showToast(String toast) {
      Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
     }
    }
}
#assets 디렉터리의 demo.html
<html>
<input type="button" value="Say hello" onClick="showAndroidToast('Hello Android!')" />

<script language="javascript" type="text/javascript">
    function showAndroidToast(toast) {
            Android.showToast(toast);
    }
</script>
</html>

 

안드로이드 코드에서 setSaveFormData(boolean)는 자바 스크립트를 사용하여 문서를 저장하는 기능을 허용할 지 설정한다. 저장을 허용한다면 해킹에 악용되 웹뷰의 기능이 변경될 수 있다. 하지만 캐시를 사용할 시에는 true로 꼭 해줘야 한다. addJavascriptInterface() 메서드는 자바 스크립트 내부에서 안드로이드 클래스에 있는 메서드를 호출할 수 있도록 해준다. 위 코드에서 html 코드를 보면 Android.showToast(toast)로 안드로이드 메서드가 호출된 걸 볼 수 있다. 또한 JavaScriptInterface 객체가 addJavascriptInterface의 두 번째 매개변수 이름으로 자바 스크립트 내부에서 사용되는 걸 알 수 있다. addJavascriptInterface 메서드는 경우에 따라 문제가 발생할 여지가 많아 사용하지 않는 게 좋다.

+ Recent posts