1. 투명 PNG 파일 만들기  를 먼저해야한다.

https://pixlr.com/editor/   이 싸이트를 이용한다. 
사용방법은 http://belitino.tistory.com/96 참고~

2. Button을 상속받은 비트맵 버튼을 만든다. (이미지 버튼은 좀 이상한듯) 

package com.company.mybitmapbutton;

import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Button;

/**
* Created by brad on 2015-09-14.
*/
public class MyBitmapButton extends Button {

private int normalButton = 0;
private int clickedButton = 0;


public MyBitmapButton(Context context) {
super(context);
}

public MyBitmapButton(Context context, AttributeSet attrs) {
super(context, attrs);
}


public void setImage(int normalImg, int clickedImg) {
this.normalButton = normalImg;
this.clickedButton = clickedImg;
super.setBackgroundResource(normalButton);
}

@Override
public boolean onTouchEvent(MotionEvent event) {

int action = event.getAction();

if(action == MotionEvent.ACTION_DOWN){
setBackgroundResource(clickedButton);
}else if(action == MotionEvent.ACTION_UP){
setBackgroundResource(normalButton);
}

return true;

}
}


3. 아래처럼 가져다가 사용한다.


plusButton = (MyBitmapButton) findViewById(R.id.button);
minusButton = (MyBitmapButton) findViewById(R.id.button2);
onOff = (MyBitmapButton) findViewById(R.id.button3);


plusButton.setImage(R.drawable.plus, R.drawable.plus);
minusButton.setImage(R.drawable.minus, R.drawable.minus);
onOff.setImage(R.drawable.on, R.drawable.off);





레퍼런스:

https://www.youtube.com/watch?v=P-A2m2KuTNU&index=55&list=PLG7te9eYUi7vXZf7O6Fd2YCnJlx5YG9qq







첫째. 웹과 상호통신을 하기위해 WebViewInterface 클래스 만들기 

public class WebViewInterface {

private WebView mWebView;
private Activity mContext;
private final Handler handler = new Handler();


public WebViewInterface(Activity activity, WebView view) {
mWebView = view;
mContext = activity;

}

@JavascriptInterface
public void callSettingsActivity(final String message) {
Toast.makeText(mContext, "settings in ...", Toast.LENGTH_LONG).show();

Intent intent = new Intent(mContext, SettingsActivity.class);
mContext.startActivity(intent);

}
}

- 웹에서 callSettingsActivity 함수를 호출하면 인텐트를 통해  SettingsActivity 를 시작한다.



둘째. 웹뷰와 인터페이스클래스와 연결해주기


mWebViewInterface = new WebViewInterface(MainActivity.this, webView);
webView.addJavascriptInterface(mWebViewInterface, "android");

 생성자로 this 넘기고 webView 를 넘겨준다. 

 - webView 에 인터페이스 객체의 alias 로  "android" 를 설정한다. 



셋째. 웹페이지 상에 자바스크립트 함수 만들기 

- TEST.HTML -

function  callActivity1(){

if(window.android){

    console.info("android");

    window.android.callSettingsActivity(number.value);

}

else{

console.info("web");

document.form.submit();

}

}

}

 - callActivity 함수가 호출되면 "android" 가 정의되있으면 네이티브앱쪽으로 호출하고, 아니면 웹서버로 호

   출하는 코드 


이 글의 요약 

-  로컬에 있는  웹 파일들을 불러와서 작업할때


웹뷰를 사용하는데 있어서 두드러지는 장점은 , 앱안에 필요한 웹 리소스들을 저장할수있다는것이다.

그것은 오프라인일때도 작업을 가능케 해주며, 로딩시간을 증진시킬것이다. 

HTML, JavaScript, CSS  를 assets 디렉토리 (src/main/assets 등에 만듬)로부터 가져와보자.

주의:   CSS 나 Javascript  를 참조할때 절대경로는  WebView 에서 작동하지 않는다.  다음과 같이 상대경로로  설정해야한다. ("/pages/somelink.html"  -> "./pages/index.html" )

아래와 같이 로딩하자.  ( 리모트에 있는 URL 을 읽어와서 작업하기 전에 전처리할것들을 이렇게 처리해도될듯)

mWebView.loadUrl("file:///android_asset/www/index.html");

( assets 안에 www 만들었어도, 링크는 저렇게'android_asset'  해야한다.)


shouldOverrideUrlLoading  를 요렇게 하면 로컬페이지가 아닐경우 브라우저를 오픈한다. 

public class MyAppWebViewClient extends WebViewClient {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if(Uri.parse(url).getHost().length() == 0) {
            return false;
        }

        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        view.getContext().startActivity(intent);
        return true;
    }

}




레퍼런스 :

https://developer.chrome.com/multidevice/webview/gettingstarted  



이 글의 요약 

-  디바이스의 돌아가기버튼 (back 버튼) 을 클릭했을때, 이전 웹 페이지로 돌아가고싶을때 


WebView 는  canGoBack  함수를 가지고있는데, 이것을 가지고 요리하면 됩니다. 코드의 굵은 부분을 보시면됨.

public class MainActivity extends Activity {
   
 private WebView mWebView;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
     ...
 }
   
    @Override
    public void onBackPressed() {
        if(mWebView.canGoBack()) {
            mWebView.goBack();
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
       ...
    }
    
}




레퍼런스 :

https://developer.chrome.com/multidevice/webview/gettingstarted  

이 글의 요약 

-  원하는 URL 만 웹뷰로 보여주고, 나머지는 모바일 브라우저를  새창으로 띄우고 싶을때 



webView = (WebView) findViewById(R.id.webview);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("http://www.html5rocks.com/");

안드로이드에 webview 를 추가한후에 이것만 코딩한후에  실행시켜보면 , 의도와는 다르게 

앱 내부의 webview 에서 페이지가 뜨는것이아니라, 외부 브라우저를 통해서 띄우려고 할것인데..

이벤트가 일어나는 순서는 :

  1. WebView 는 리모트서버로부터 원래  URL 로딩을 시도하고, 새로운  URL 로 리다이렉트를 갖는다.
  2. WebView 는 시스템이 URL 에 대해 뷰 인텐트를 핸들링할수있는지 체크하고 만약 그렇다면 시스템은 URL 네비게이션을 핸들링한다. 그렇지 않으면  웹뷰는 내부적으로 네비게이트할것이다. (즉, 디바이스에 브라우저가 없다면)
  3. 시스템은 사용자의 http:// URL 을 핸들링할수있는 더 나은 어플리케이션을 선택한후 (즉, 사용자 디폴트 브라우저) 만약, 브라우저가 많으면 아래처럼 선택창을 띄운다.

browser selection dialog

만약 , 어플리케이션의 내부의 웹뷰를 통해서 보고싶으면 WebView 로 부터의 다양한 이벤트를 핸들링할수있 WebViewClient 를 오버라이딩해야한다. 

아래 같은 기본 WebViewClient 구현은 웹뷰에서 어떤 URL 이나 열수있게 한다.

// 브라우저 대신해 WEBVIEW 로 열기위해 강제로 링크하고 리다이렉트한다.

webView.setWebViewClient(new MyAppWebViewClient());

public class MyAppWebViewClient extends WebViewClient {

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url){
         return false;
}
}


return true 로 하면 처리하지 않는다. (웹뷰에 아무것도 나타나지 않음) 


그럼 다른 싸이트 말고, 원하는 싸이트만 열수있게 하려면 어떻게 해야하는가?

shouldOverrideUrlLoading  메소드를 오버라이딩하면 된다. 

아래와 같이 코드를 추가해보자.


  1. public class MyAppWebViewClient extends WebViewClient {
            
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                if(Uri.parse(url).getHost().endsWith("html5rocks.com")) {
                    return false;
                }
                 
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                view.getContext().startActivity(intent);
                return true;
            }
        }

    URL 이 html5rocks.com 으로 끝나는것은 내부 웹뷰에서 처리한다는 뜻이다.  리턴 false 는 그 자신이 처리한다는 뜻이고, 만약 다른 URL 이라면 새로운 안드로이드 Intent 를 사용해서 새로운 액티비티 (설치된 모바일 브라우저) 를 호출하게된다.



레퍼런스 :

https://developer.chrome.com/multidevice/webview/gettingstarted  

안드로이드 어플리케이션의 타이틀 바를 제거하고 싶을 때 아래의 두가지 방법중 하나를 사용한다.


1. 프로그램 코드로 삽입


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);

    requestWindowFeature(Window.FEATURE_NO_TITLE);   를 SetContentView '위에' 써주어야한다.



2. manifest 파일 수정


<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
<activity

    android:theme="@android:style/Theme.NoTitleBar.Fullscreen"   추가 


유의사항으로는 

public class MainActivity extends AppCompatActivity {

이거 에러난다.

public class MainActivity extends FragmentActivity {

FragmentActivity 를 상속받으면 OK~


네트워크를 사용하는 대부분의 안드로이드 앱은 HTTP 을 사용해서 데이터를 주고 받는다. 

안드로이드는 두가지 HTTP 클라이언트 클래스를 가지고있는데, HttpURLConnection  과 Apache HTTP Client 이다. 

둘다 HTTPS 를 지원하며, 스트리밍 업로드, 다운로드 와 타임아웃 설정 , IPv6 및 커넥션 풀링등을 지원한다.


Apache HTTP Client

DefaultHttpClient 와 그것의 형제인 AndroidHttpClient 는 웹 브라우저에 맞춰진  HTTP 클라이언트 확장이다. 다양하고 유연

한 API 들을 가지고있는데 안정되고 거의 버그가 없다. 그러나 그런 커다란 API 사이즈는 하위호환성을 깨지 않고 개선 시키기

가 어려우며 안드로이드 팀은 그것을 적극적으로 사용하지 않는다.




HttpURLConnection

HttpURLConnection 는 대부분의 어플리케이션에 적합한 일반적인 목적의 가벼운 HTTP 클라이언트이다. 이 클래스는 조금 

기초적인 기반을 가지고있지만, 안정적으로 향상시킬수있기 쉽게 되어있다. 프로요 버전 이전에 HttpURLConnection 은 약간의 

버그를 가지고있었다. 특히 readable InputStream 에서 close() 를 부르는것은 커넥션 풀을 맛가게 할수 있었다.


private void disableConnectionReuseIfNecessary() {

   
// HTTP connection reuse which was buggy pre-froyo
   
if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {
       
System.setProperty("http.keepAlive", "false"); // HTTP 커넥션 재사용은 HTTP 의 KeepAlive 옵션의 역할
   
}
} KeepAlive 란? http://brad2014.tistory.com/341


진저브레드 버전에서는 transparent response compression  를 추가했다. HttpURLConnection 는 자동적으로 이 헤더를 요청

에 추가하고 돌아오는 응답을 핸들링할것이다.


Accept-Encoding: gzip


만약 응답 압축이 문제가 있을때  class documentation  를 보면 어떻게 비활성화 시킬수있는지 나와있다.

HTTP 의 Content-Length 헤더는 압축된 사이즈를 돌려주기 때문에 비 압축 데이터의 버퍼 크기를 그것으로 잡으면 에러가 생길

수 있다. 대신해서  InputStream.read() 이 -1 을 리턴할때까지 읽어야한다. 

우리는 또한 진저브레드 버전에서 여러가지 HTTPS 향상을 이루어냈는데 HttpsURLConnection  는 SNI(Server Name 

Indication) 을 가지고 접속 시도를 한다. 그것은 IP 주소를 공유하기위해 multiple HTTPS 호스트를 허용한다. 또한 세션 티켓들

과 압축을 활성화 할수있다. 커넥션 실패일때 자동적으로 재시도한다. 이것은 오래된것에 대한 호환성을 깨지 않고  최신의 서버

에 접속할때 HttpsURLConnection 를 효율적이게 만든다.


아이스크림 샌드위치에서 응답캐쉬를 추가했고,  HTTP 요청은 3가지방법중 하나로  이루어진다.


- 완전한 캐쉬 응답은 로컬 스토리지로부터 직접적으로 서비스된다. 

- 상황에 따라서 캐쉬된 응답은 최신의 것이라는것이 웹서버에 의해 검증된다. 클라이언트는 “Give me /foo.png if it changed 

   since yesterday”  같은 요청을 보내며 , 서버는 업데이트된 내용을 보내주거나 304 Not Modified 상태를 보내준다.."컨텐츠

   가 그대로니깐 다운로드될 필요없다라는~"

- 캐쉬되지 않은 응답은 웹으로부터 서비스되며 이 응답은 후에 응답캐쉬에 저장될것이다.


리플렉션을 사용하여 디바이스상에 HTTP 응답캐쉬를 활성화하는 샘플. 이 샘플은 이전 버전에는 영향없이 아이스크림 샌드위치

버전부터 응답캐쉬를 작동시킨다. 


private void enableHttpResponseCache() {

   
try {
       
long httpCacheSize = 10 * 1024 * 1024; // 10 MiB
       
File httpCacheDir = new File(getCacheDir(), "http");
       
Class.forName("android.net.http.HttpResponseCache").getMethod("install", File.class, long.class)
           
.invoke(null, httpCacheDir, httpCacheSize);
   
} catch (Exception httpResponseCacheNotAvailable) {
   
}
}

* HTTP 응답상의 케쉬헤더를 세팅하기위해 웹서버를 설정해야한다. 



그래서 뭐가 좋은건데? 


apache HTTP client 는  Eclair and Froyo 에서 아주 작은 버그를 가지고있고 저 버전을 사용할땐 베스트이다.

진저브레드에서는 HttpURLConnection  가 베스트이다. 그것의 단순함과 작은 사이즈는 안드로이드에 딱이다.

Transparent comppression 과 응답캐싱은 네트워크 부하를 줄이고 속도를 향상시키며 베터리에도 좋다. 

새로운 어플리케이션은 HttpURLConnection   로 하자. 

http://roundhere.tistory.com/73 펌 


안드로이드 서비스(Services)


안드로이드 서비스는 U.I 없이 백그라운드에서 실행되는 기능을 말합니다.

예를들어 네트워크 통신,음악재생,I/O 작업등의 작업등이 해당된다.


* 서비스 타입

Started 타입

이 서비스는 startService() 호출하면 시작됩니다.

한번 시작되면 백그라운드에서 무기한으로 실행됩니다. 보통은 작업이 완료되면 스스로 종료됩니다.

예를들어 파일 다운로드, 음악재생 등이 있습니다.

Bound 타입

bindService() 호출후에 시작됩니다. 클라이언트와 서버 와 같이 동작합니다.

액티비티는 서비스에게 어떤 요청을 하고 서비스는 결과값을 반환합니다.

Bound 서비스는 여러 액티비티와 연결될 수 있습니다.


* 주의할점

서비스는 메인 스레드에서 실행됩니다. (서비스는 쓰레드가 아님, 별도의 프로세스도 아님)  

서비스가 CPU 자원을 많이 소모하는 작업이라면 서비스안에 스레드를 생성해서 작업하는게 좋습니다.

앱이 실행중일때만 필요한 기능이라면 스레드를 사용하는게 맞고 

앱이 실행중이지 않을때 실행되어야 한다면 서비스를 이용해야 한다.




안드로이드 어플은 액티비티, 서비스, 리시버들이 서로 엮이며 돌아가게 설계되는 경우가 보통이다.  액티비티는 보통 하나의 화면에 대응되어 사용자가 어플과 인터액션하고 서비스나 리시버 같은 다른 컴포넌트를 시작/중지시키는 그런 일을 한다.  "서비스는 UI에서 인터액션을 하면서 동시에 백그라운드로 음악듣기와 같은 것을 구현할 때 유용한 메커니즘이다" 라고 한다.  이 말은 보통의 스레드 활용을 연상시킨다.  그러면 안드로이드 서비스는 스레드와 어떤 관계인가?   


안드로이드에서 View를 포함하는 UI 오브젝트들은 자신을 콘트롤하는 스레드가 하나이어야 하기 때문에 안드로이드는 UI 오브젝트들이 UI 스레드에서만 돌게 만들었다.  그래서 UI스레드가 아닌 스레드들이 UI 오브젝트를 콘트롤하려면 Handler, Activity.runOnUIThread(Runnable), View.post(Runnable)나 AsyncTask를 써야 한다 (참고-1, 참고-2).  이런 스레드 활용 방법은 JAVA와 같은 개념이기에 심플하다.  


안드로이드 서비스에서 액티비티의 UI 오브젝트에 접근하려면 통상 그 서비스에서 intent를 날리고, 액티비티내에는 BroadcasterReceiver 인스턴스 객체를 임베드하고 onReceive() implementation에서 UI 오브젝트를 접근하는 형태를 이용한다.  이 구조는 스레드 만들면서 Runnable job을 던져주고, 적당한 때 스레드 스타트 하는 것보다 더 비동기적이다.  


서비스는 별도의 스레드인가?  아니다.  여기 첫머리에 분명 "서비스는 스레드가 아니다" 라 한다.  그럼 서비스는 무엇이냐?  존재 이유는?  위에서 말하기를, 


- 어플리케이션이 시스템에게 "여기 백그라운드에서 수행해야 할 일이 있거든" 알려주기 위해서란다.  그래서 manifest에 나타낸다.  그런데 서비스가 단순 백그라운드 작업 처리용도라면 통상적인 별도 스레드 만들어서 녀석에게 필요한 백그라운드 일을 던져 주면 되잖아.  뭐가 다르지?  아, 안드로이드 서비스는 별도의 라이프사이클이 있지.  Context.startService로 시작한 서비스는 액티비티가 죽던 살던 어찌 되던지 한번 돌기 시작하면 stopSelf()나 Context.stopService로 중지시키기 전까지는 여간해서는 계속 돈다.  반면 Context.bindService로 구동/바인딩 된 서비스는 바인딩된 서비스 클라이언트가 더 이상 존재하지 않으면 시스템이 서비스를 없앨수 있다 한다.  서비스도 언제든 죽을 수 있다 하지만, 일단 돌기 시작한 서비스는 stopService() 같은 것으로 명확히 중지시키기 전까지는 (상대적으로) 계속 돈다고 생각할 수 있나?  


서비스가 부모 어플리케이션이나 프로세스 상태와 무관하게 유지되는 백그라운드 작업처리 장치임에 비해 안드로이드 스레드는 자바 경우와 같이 부모 프로세스가 살아있는 동안에 runnable, running과 blocked 상태를 뱅글뱅글 거쳐 일을 마치면 dead 상태에 들어가는 단순한 흐름을 갖고 있다.  


- A facility for an application to expose some of its functionality to other applications.  (서비스를 갖고 있는) 어플리케이션이 다른 어플리케이션에게 자신의 기능을 공표할 수 있는 기능.  아니 이게 무슨 말인감?  클래스들의 모든 public 메소드가 다른 클래스에게 "나 이런 것 할 수 있으니 필요하면 불러 써" 하는 것 아닌감?  다시 잘 보자...  음, 어플리케이션간에 알려주는 관계라...  AIDL 서비스 얘기같다.


이곳에서 안드로이드가 멀티태스킹 지원을 어떻게 하려고 하는 지 읽어보니 안드로이드의 서비스, 스레드, 백그라운드 작업, 프로세스, 어플리케이션간의 관계에 대해 좀 더 이해가 깊어진다.  음. 안드로이드에서 일단 실행된 어플리케이션은 사용자가 죽이더라도 완전히 퇴출시키지 않도록 하고, 그러면서 (어플리케이션 바꾸기) 할 때 스왑공간이 부족하니 어플리케이션이 사용하는 메모리를 타이트하게 관리하도록 했다고.  이처럼 상충되는 요구조건은 사용자에게 보다 seamless한 UX를 제공하기 위해서란다.   


복잡하게 생각하지 말고 대충 생각해야 겠다.  


1. UI 스레드에서 시간이 요하는 태스크 처리가 필요하고, 계속 현재 UI 스레드가 foreground에서 놀고 있을 가능성이 많으면 간단히 별도 스레드를 만들거나 AsyncTask로 처리한다.  


2. 만약 부모 스레드가 더 이상 foreground가 아닐때에나, 그 스레드를 소유한 어플이 중지되었거나 관계없이 백그라운드에서 서비스가 계속 살아있으면서 일을 해야 하면 서비스로 구현한다.  그리고는 서비스내에서 스레드를 만들어 서비스가 수행해야 하는 작업을 스레드가 담당하도록 한다.  작업량이 많거나 작거나 관계없이 별도 스레드에서 하도록 한다.  안전하게...   만약 어플리케이션의 UI 스레드가 돌고 있다고 생각되면 Handler 클래스를 이용해도 되고, 잘 모르겠으면 BroadcastReceiver 클래스를 이용하여 어플리케이션 UI를 관장하는 스레드에게 접근한다.  BroadcastReceiver 클래스는 돌고 있지 않던 프로세스도 깨울 수 있다.    



+ Recent posts