Android - Hybrid App

오픈소스 비즈니스 컨설팅
이동: 둘러보기, 검색

스마트폰 OS인 Android에서 Hybrid App 관련 사항을 정리 한다.

하이브리드 앱

제가 생각하는 하이브리드 앱은 모바일 앱과 모바일 웹(HTML5)이 서로의 장점을 살려서 모바일 환경을 더욱 풍요롭게 하는 것을 의미 합니다. Android에서는 하이브리드 앱을 구현하기 위해서 WebView를 사용 합니다.

권한 설정

  • AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE" />

화면 설정

  • /res/layout/~.xml
<WebView 
    android:id="@+id/webview"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    />

Web 화면

  • HTML Page
  • ratio : 0.75, 1.0, 1.5
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio: 1.0)" href="mdpi.css" />
<style type="text/css" media="screen">
@media screen and (-webkit-device-pixel-ratio: 1.0) {
}
</style>

<script type="text/javascript">
if (window.devicePixelRatio == 1.0) {
}
</script>

Activity 구현

  • Activity
private WebView browser = null;
private Activity activity = null;

public void onCreate(Bundle savedInstanceState)
{ 
    WebSettings setting = null;

    super.onCreate(savedInstanceState);
    setContentView(R.layout.smarthybrid);

    activity = this;
    browser = (WebView) findViewById(R.id.website);
    setting = browser.getSettings();
    setting.setJavaScriptEnabled(true);
    browser.setWebViewClient(new ProxyWebViewClient());
    browser.setWebChromeClient(new ProxyWebChromeClient());

    //--- 프로젝트의 /assets 폴더에 test.html 파일을 추가
    //--- browser.loadUrl("file:///android_asset/test.html");
    //--- browser.loadData("<meta http-equiv='Content-Type' content='text/html; charset=utf-8' />한글", "text/html", "utf-8");
    browser.loadUrl(website);
    //--- byte[] post = EncodingUtils.getBytes("a=b&c=d&e=f", "BASE64");
    //--- browser.postUrl("http://example.com/myform/", post);
}

public boolean onKeyDown(int keyCode, KeyEvent event) { 
    if ((keyCode == KeyEvent.KEYCODE_BACK) && browser.canGoBack()) { 
        browser.goBack(); 
        return true; 
    } 
    return super.onKeyDown(keyCode, event);
}

private class ProxyWebViewClient extends WebViewClient {
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("tel:")) {
            Intent i = new Intent(Intent.ACTION_CALL, Uri.parse(url));
            startActivity(i);
       } else {
            view.loadUrl(url);
       }
       return true;
    }

    public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
        Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
    }
}

private class ProxyWebChromeClient extends WebChromeClient {
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        Toast.makeText(activity, message, 3000).show();
        result.confirm();
        return true;
    }
}
  • WebView에서 HTML5 사용을 위한 WebSettings 설정
   	WebSettings setting = null;

       browser = (WebView) findViewById(R.id.website);
       setting = browser.getSettings();
       setting.setJavaScriptEnabled(true);		//--- JavaScript 허용
       setting.setGeolocationEnabled(true);	//--- 위치 정보 사용 허용
       setting.setJavaScriptCanOpenWindowsAutomatically(true); 
       setting.setLoadsImagesAutomatically(true);
       setting.setPluginsEnabled(true);
       setting.setLightTouchEnabled(true); 
       setting.setSupportZoom(true);
       setting.setBuiltInZoomControls(true);

       //--- HTML5에서 Database를 설정 한다. 
       setting.setDatabaseEnabled(true);		//--- HTML5에서 Database 허용
       //--- Database를 위해 WebChromeClient.onExceededDatabaseQuota(~) 함수도 같이 설정할 것
       setting.setDatabasePath("/data/data/com.jopenbusiness.android.smarthybrid/database");

       //--- HTML5에서 DOM Storage를 설정 한다.
       setting.setDomStorageEnabled(true);		//--- HTML5 DOM Storage 허용
       
       //--- HTML5에서 Cache 설정을 한다.
       //--- Database를 위해 WebChromeClient.onReachedMaxAppCacheSize(~) 함수도 같이 설정할 것
       setting.setAppCacheMaxSize(1024 * 1024 * 8);	//--- Default Cache Size를 8 MB로 설정
       setting.setAppCachePath("/data/data/com.jopenbusiness.android.smarthybrid/cache");
       setting.setAppCacheEnabled(true);
       setting.setAllowFileAccess(true);
       setting.setCacheMode(WebSettings.LOAD_DEFAULT);

Cookie 동기화 처리

  • Activity에서 Cookie 동기화 처리
private WebView browser = null;

public void onCreate(Bundle savedInstanceState)
{ 
    CookieSyncManager.createInstance(this);
}

public void onStart() { 
    super.onStart(); 

    browser = (WebView) findViewById(R.id.website);
    browser.setWebViewClient(new ProxyWebViewClient());
    CookieSyncManager.createInstance(this);
}  

protected void onResume()
{
    super.onResume();
    CookieSyncManager.getInstance().startSync();
} 

protected void onPause()
{
    super.onPause();
    CookieSyncManager.getInstance().stopSync();
}

private class ProxyWebViewClient extends WebViewClient {
    public void onPageFinished(WebView view, String url) {
        CookieSyncManager.getInstance().sync();
    }
}

Progress Bar 표시

앱의 타이틀바에 웹페이지 로딩 진행 상태(Progress Bar)를 표시 한다.

  • Activity에 다음을 추가 한다.
private WebView browser = null;
private Activity activity = null;

public void onCreate(Bundle savedInstanceState)
{ 
    super.onCreate(savedInstanceState);
    //--- setContentView(~) 앞에 다음 라인을 추가 한다.
    getWindow().requestWindowFeature(Window.FEATURE_PROGRESS);         
    setContentView(R.layout.main);

    activity = this;
    this.setTitle("앱 타이틀");

    browser = (WebView) findViewById(R.id.website);
    browser.setWebChromeClient(new ProxyWebChromeClient());
}

public class ProxyWebChromeClient extends WebChromeClient {
    public void onProgressChanged(WebView view, int newProgress) {
        activity.setTitle("로딩 ...");
        activity.setProgress(newProgress * 100);

        if (newProgress == 100) {
            activity.setTitle("앱 타이틀);
        }
    }
}

앱과 웹의 통신

  • Activity에서 다음과 같이 구현 한다.
import  android.os.Handler;
 
private WebView browser = null;
private final Handler handler = new Handler();

public void onCreate(Bundle savedInstanceState) {
    WebSettings setting = null;

    browser = (WebView) findViewById(R.id.website);
    setting = browser.getSettings();
    setting.setJavaScriptEnabled(true);
    browser.addJavascriptInterface(new Hybrid(), "hybrid");
}

private class Hybrid {
    public void setMessage(final String arg) {
        handler.post(new Runnable() {
            public void run() {
                Log.d("Hybrid", "setMessage("+arg+")");
                mTextView.setText(arg);
            }
        });
    }
} 
//--- JavaScript에서 App 호출
window.hybrid.setMessage(msg);

//--- App에서 JavaScript 호출
browser.loadUrl("javascript:setMessage('message')");

화면 회전 금지

  • WebView 화면이 회전되지 않도록 하려면, 화면 회전에서 "화면이 회전해도 Activity가 다시 로딩되지 않도록 하는 방법"을 참조 하세요.

WebView의 HTML5 지원

  • Android 2.2의 WebView의 HTML5 지원 점수

Android22 html5test.png

참고 문헌

  • Android 매뉴얼