우선 책을 보면서 느끼는 인상은 책을 상당히 가독성 있게, 읽고 싶어지는 느낌이 들도록 만들었다는 생각이 든다.

책의 구성 자체가 공부하려는 의지를 꺾는 경우가 있는가하면 최소한 이 책은 오히려 공부하고자 하는 의지를 북돋아주는 구성이 첫 번째 받은 인상이다.

 

이 책은 웹의 지식과 기술을 기반으로 사용하여 어떻게 다양한 멀티 플랫폼에서 동시에 구동될수 있는 앱을 만들 것이냐라는 주제를 풀어가는 책이다.

그러다보니 이러한 목적을 달성하기 위해서 사용되는 기술이 HTML5, CSS3로부터 시작해서 미디어 쿼리, JQuery Mobile, JavaScript, 폰갭, 워드프레스 등 한권의 책에서 다양한 웹과 앱의 기술들을 경험할 수 있을 뿐만 아니라 이러한 기술들을 어떻게 종합적으로 사용할 수 있는지를 보여주는 책이다.

 

요즘은 상황이 조금 다르지만 몇 해 전만해도 아이폰에 비해 안드로이드 개발자가 겪는 고충 중 하나는 다양한 화면 사이즈의 디바이스에 어떻게 UI를 잘 대응하느냐의 문제는 대표적인 골치거리 중 하나였다.

하이브리드 앱에서도 이같은 문제는 동일하게 직면하게 될텐데 더더욱 모바일 스크린, 데스크톱 스크린까지 아우러는 하이브리드는 이러한 문제가 더욱 가중되는 상황이다.

그런데 바로 이런 문제를 어떻게 대처할 것인지에 대해 CSS3의 미디어쿼리를 통해서 좋은 해법을 제시해 주고 있다. 즉 반응형 웹 디자인을 통해 어떻게 대응 할 것인지를 잘 다뤄주고 있다.

 

Chapter 11부터는 실전에서 써먹을 수 있는 프로젝트를 다루면서 필요한 기술들이 종합적으로 어떻게 쓰이는지를 보여주고 있다.

동영상 연결하기, 지도에 내 위치 표시하기 JQuery, JSON, 플리커, 워드프레스를 이용한 하이브리드앱을 만드는 과정을 하나씩 따라가면서 기술을 종합적으로 적용하는 실전적인 응용력을 배울 수 있을 것 같다.

그리고 폰갭을 통해서 안드로이드와 아이폰에서 작동하는 앱을 어떻게 만들어갈 것인지를 배울수 있는 좋은 참고서가 것 같다.

아무튼 시대적으로 멀티플랫폼에 대한 대비를 생각지 않을 수 없는 IT의 흐름은 하나의 대세인 것 같다. 또한 하이브리드의 기술의 진보로 화려함과 성능에서 보완(네이티브 앱에 비해 성능에서의 미흡함의 보완) 등으로 인해 이제 하이브리드 앱을 생각하는 사람이라면 특히 하이브리드 앱을 처음 접하거나 초보자들에게는 아주 유용한 참고서가 될 것 같다.

 


Handler를 이용해서 특정 시간 후에 특정 작업을 실행시키기


특정 시간 후에 특정한 작업이 실행되도록 하는 방법으로 AlarmManager를 이용할수 있지만 간단하게 처리할수 있는 방법으로는 Handler를 이용하면 더 쉽게 처리할수가 있다.

대표적으로 ProgressDialog를 특정 시간 경과 후 종료 시키는 방법을 구현한다면 아래와 같이 간단히 처리할수 있다.

아래 코드는 7초 후에 ProgressDialog를 중지시키는 코드이다.

실행하기 원하는 작업(코드)를 Runnable 인터페이스의 추상 메소드인 public void run() 안에 두면 run안의 코드가 특정 시간 경과후(여기서는 7초) 실행이 된다.

즉 ProgressDialog의 객체인 pDialog를 show() 한 후에 


      ProgressDialog pDialog = new ProgressDialog(this, 

                                                              AlertDialog.THEME_DEVICE_DEFAULT_LIGHT);

      pDialog.setCancelable(false);

      pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);

      pDialog.setMessage("주변의 BLE 장치를 검색 중입니다.");

      pDialog.show();


      mHandler.postDelayed(new Runnable() {

          @Override

          public void run() {

              pDialog.dismiss();

          }

      }, 7000);



Handler 클래스의 해당 메소드를 보면 다음과 같이 되어 있다.


public final boolean postDelayed (Runnable r, long delayMillis)

⇒ Causes the Runnable r to be added to the message queue, to be run after the specified amount of time elapses. The runnable will be run on the thread to which this handler is attached.


r : The Runnable that will be executed.

delayMillis : The delay (in milliseconds) until the Runnable will be executed.


Runnable 객체를 message queue에 추가한 후에 delayMillis 시간 경과 후에 Runnalbe 객체 r을 실행시킨다. 이렇게 처리하면 Thread로는 처리 불가능한 UI 관련 작업도 처리할수 있다.


r : 실행할 코드를 담고 있는 Runnable 객체. Runnable의 추상 메소드 public void run()안에 있는 코드를 실행한다.

delayMillis : 여기에 설정된 시간만큼 경과 후에 r을 실행한다.





현재의 디바이스가 BLE(Bluetooth Low Energy)를 지원하는지 확인하는 법


일단은 Manifest파일에 아래의 permission을 추가해야한다.


    <uses-permission android:name="android.permission.BLUETOOTH"/>


    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>


    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>


BLE가 지원되는 여부를 파악하기 위해서는 PackageManager의 도움을 받으면 된다.

PackageManager의 객체는 Context 클래스에 있는 아래 메소드로부터 객체를 획득할수 있다.


public abstract PackageManager getPackageManager ()


그리고 PackageManager 클래스에 있는 아래 메소드를 통해 BLE 지원 여부를 확인할 수 있다.


public abstract boolean hasSystemFeature (String name)


아래는 코드 조각이다.


        PackageManager pkgMan = getPackageManager();

        if (pkgMan.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {

            Toast.makeText(this, "BLE가 지원되는 디바이스 입니다.", 1).show();

        } else {

            Toast.makeText(this, "BLE가 지원되지 않습니다.", 1).show();

        }






Android Studio에서 화면 넓게 사용하는 Tip


※ Android Studio 1.5 버전을 중심으로


요즘의 노트북들은 세로 화면보다는 가로 화면이 넓은 형태인데 eclipse를 사용하다가 Android Studio를 사용해 보면 화면이 좁다는 느낌을 갖게된다. 

이러한 불편함을 해소할수 있는 기능이 Android Studio에서는 아주 잘 제공이 되고 있다. 어떤 면에서는 감탄스럽다.


(1) 불필요한 몇 가지 Bar 표시 안되게

Status Bar와 Navigation Bar 표시 안되게

아래 그림과 같은 Status bar와 Navigation Bar는 굳이 없어도 될만한 정보이다. 이 2개의 bar를 표시되지 않게 할려면 View 메뉴에서 Status Bar와 Navigation Bar의 체크 상태를 해제하면 된다.




(2) Distraction Free Mode 사용하기

아래 이미지에서와 같이 오직 메뉴만 보여지는 상태로 코딩할수 있는 화면이다.

View - Enter Distraciton Free Mode를 클릭하면 아주 넓은 화면상태에서 여유롭게 코딩을 할수가 있다. 이 상태를 해제시는 다시 View메뉴로 들어가서 Exit Distraction Free Mode를 클릭하면된다. 





(3) Full Screen 사용하기
아래 이미지와 같이 Tool 버튼들과 각 클래스 탭만으로 보여주는 화면 상태가 Full Screen 상태이다. 
View - Enter Full Screen를 클릭하면 사용할수 있는 화면이다.
이때 만일 메뉴들을 보기 원하면 F10키나 마우스를 화면 최상단으로 옮기면 숨겨져 있는 메뉴가 다시 나타나게된다.
이 상태의 해제는 View - Exit Full Screen을 클릭하면된다.



(4) Presentation Mode 사용하기
아래 이미지와 같이 코딩할수 있는 공간 외에는 다른 아무것도 화면에 보이지 않는 상태이다. 글자 크기도 큼지막하게 시원한 화면에서 코딩할수 있다. 물론 이 화면은 프리젠테이션을 목적으로 하는 화면이다. 
여기서 메뉴를 볼려면 F10이나 마우스를 화면 최상단으로 옮기면 메뉴가 다시 보여지게된다.





adb shell을 이용한 안드로이드 앱 apk 삭제하기


DOS 창에서 안드로이드 SDK가 설치된 폴더의 

platform-tools 폴더로 이동해서 


adb uninstall 패키지명


ex)C:\....\platform-tools>adb uninstall com.test.myapp



패키지 명을 알려면 DOS 창에서

adb shell

# cd /data/data

# ls를 하면

설치된 패키지들이 죽~ 나온다.

여기서 삭제하기 원하는 패키지(앱)를 확인하면 된다.



Android을 보다 깊고 폭 넓게 공부할려면 두 가지를 해볼 필요가 있다.


① 구글에서 제공하는 API Guides

http://developer.android.com ⇒ 메뉴 중 Develop ⇒ API Guides

여기에서 안드로이드로 할수 있는 전 분야에 대해 배울수 있다. 

Web App을 어떻게 만들것인지, Bluetooth 관련, 구글 맵과 센서 사용관련, 미디어와 카메라 관련, 애니메이션 동작 관련 등 주옥같은 정보들이 담겨있다.


② 구글에서 제공하는 Sample 소스

Android SDK를 다운 받으면 sdk폴더 아래에 samples 폴더가 있는데 이 폴더에는 안드로이드 각 버전별 샘플 소스들이 있다. 이또한 영양가 높은 샘플들이다. 이 소스들을 분석해 보면 배우는 것들이 많을 것이다.

구글의 Sample 소스를 다운 받을려면 Android SDK 폴더에 보면 SDK Manager.exe라는 실행파일을 실행하면 샘플 소스들을 각 버젼별로 다운 받을수 있다.




'생각의 파편들' 카테고리의 다른 글

HTML5에 대한 생각 조각  (0) 2015.11.02


안드로이드 스튜디오(Android Studio)에서 구글 샘플 소스 import 하기


※ Android Studio 1.5 버전을 기준


File - New - Import Sample... - Import Sample이라는 창이 뜨고 여기서 원하는 sample 소스를 import 하면된다.







Sample 소스가 import가 완료되면 위의 그림과 같이 선택할 수 있는 몇 가지 동작에 대한 간략한 소개 화면이 뜬다.

여기서 Alt+1(숫자 1)을 누르면 화면 좌측에 드디어 project 및 파일 구조를 보여주는 view가 보여진다. 

여기서부터 이제 샘플 소스를 주무를 수 있게 된다.





Android 카메라 영상이나 사진이 어두울 때 밝게 하는 방법


카메라로부터의 영상과 사진을 밝게 하는 방법으로는 노출(exposure) 값을 조정하는 방법을 통해서 원하는 기능을 구현할수 있다.

Camera.Parameters 클래스에는 카메라에 다양한 옵션들을 설정할수 있는 기능과 해당 값들이 마련되어 있다.

카메라의 zoom 조절, color에 대한 effect 설정, 초점 관련 설정, white balancing 설정, 그리고 노출정도에 대한 설정 등이다.

이 중에서 영상(사진)의 밝기를 결정하는 첫번째가 노출 값을 조정하는 것이고 또 다른 하나는 영상 밝기에 직접적인 영향을 주는 것은 아니나 

white balance를 통해서 일정부분 결과를 만들어낼수 있다. 

밝기 문제를 해결하는 핵심은 노출(exposure)의 값을 변경하는 것이다.

Camera.Parameters 클래스의 아래 메소드를 통해서 간단히 해결할수 있다.


public void setExposureCompensation (int value)


여기서 매개인자 value에 들어갈 값의 범위는 디바이스들 마다 각기 상이하다. 따라서 이 값을 결정하는 메소드가 2가지 제공이 된다.

getMinExposureCompensation()의 값에서 getMaxExposureCompensation()까지의 값이 value에 들어갈 값이다.

보틍은 -n ~ +n까지가 되는데 0이면 노출을 적용하지 않는 것이고 음수이면 기본 밝기보다 더 어두워지고 양수 값이면 기본 밝기 보다 더 밝아진다.


여기서 주의 할 것은 이렇게 설정된 각종 parameter 값들이 적용되는 시점은 android.hardware.Camera 클래스의 setParameters()가 실행될 때이다.


public void setParameters (Camera.Parameters params)


그런데 주의해야할 것은 이 메소드 실행 전에 각종 parameter 값들을 설정시 어떤 parameter 값들은 적용이 안되는 디바이스들이 있다. 이건 디바이스마다 상이하다.

만일 설정되어 있는 parameter 중에서 특정 디바이스에 적용이 안되는 값이 설정되어 있으면 setParameters (Camera.Parameters params) 메소드 실행시 앱이 강제 종료되는 수가 있다. 

예를 들어 아래의 parameter들은 갤S4에서는 강제종료된다. 


parameter.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT); 

parameter.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);


그런데 이렇게 강제 종료되지 않도록 Exception 처리를 해 두면 앱은 강제 종료가 되지 않지만 setParameters (Camera.Parameters params)가 정상 실행되지 않았기 때문에

위에서 설정했던 parameter들이 전혀 적용이 되지 않는 상황이 벌어진다. 왜 적용이 안되는가 혼란에 빠질수가 있다.

아래의 코드가 그 예이다.


try {

parameters.setPreviewFrameRate(10);

parameters.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT); 

parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);


//위의 두 parameter때문에 여기서 exception 발생하고 이 메소드 실행 안됨

//따라서 앞에서 설정했던 모든 parameter들이 하나도 적용이 안된다.

mCamera.setParameters(parameters); 

} catch (Exception ex) {

Toast.makeText(mContext, "setParameters failed~", 1).show();

}


이 문제에 대한 해법은 아래와 같이 처리해 줘야 된다.


//public List<String> getSupportedSceneModes ()

List<String> sceneModeList = parameters.getSupportedSceneModes();


//public List<String> getSupportedFocusModes ()

List<String> focusModeList = parameters.getSupportedFocusModes();


try {

parameters.setPreviewFrameRate(10); 

if (sceneModeList != null && 

                       sceneModeList.contains(Camera.Parameters.SCENE_MODE_PORTRAIT)) {

parameters.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT); 

}

if (focusModeList != null && 

                      focusModeList.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)) {

parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);

}

mCamera.setParameters(parameters);

} catch (Exception ex) {

Toast.makeText(mContext, "setParameters failed~", 1).show();

}


아래는 코드 조각이다.


public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {

if (mCamera == null) return;

Camera.Parameters parameters = mCamera.getParameters();


//WHITE_BALANCE_FLUORESCENT 

//WHITE_BALANCE_DAYLIGHT 

//WHITE_BALANCE_CLOUDY_DAYLIGHT 

//WHITE_BALANCE_INCANDESCENT 

//WHITE_BALANCE_SHADE : 약간 어둡다.

//WHITE_BALANCE_WARM_FLUORESCENT : 약간 어둡다.

parameters.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_CLOUDY_DAYLIGHT); 

int maxExpo = parameters.getMaxExposureCompensation();

parameters.setExposureCompensation(maxExpo); 


List<String> sceneModeList = parameters.getSupportedSceneModes();

List<String> focusModeList = parameters.getSupportedFocusModes();

try {

parameters.setPreviewFrameRate(10); 

if (sceneModeList != null &&  

                              sceneModeList.contains(Camera.Parameters.SCENE_MODE_PORTRAIT)) {

parameters.setSceneMode(Camera.Parameters.SCENE_MODE_PORTRAIT); 

}

if (focusModeList != null && 

                            focusModeList.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO)){

 parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);

}

mCamera.setParameters(parameters);

} catch (Exception ex) {

Toast.makeText(mContext, "setParameters failed~", 1).show();

}

mCamera.startPreview();






Intent에 primitive 데이터들(int, float, String...)을 실어서 다른 Activity로 전송하는 것은 쉽게 구현이 가능하다. 그러나 객체 자체를 넘기는 것은 그냥은 안된다.

이를 위해 필요한 클래스가 ParcelParcelable이다.


  • Parcel

⇒ Container for a message (data and object references) that can be sent through an IBinder. 

Parcel은 객체를 IBinder를 통해서 다른 Process, 다른 Activity에 실어나르기 위한 컨테이너 역할을 한다.

한 마디로 Parcel에 객체(클래스)를 담아서 이 Parcel을 Intent에 실어서 보내면 된다.

예를 들어 SomeObject라는 클래스가 Parcel에 담겨 있다면 다음과 같은 코드가 가능하다.


SomeObject so = new SomeObject(arg1, arg2);

Intent it = new Intent(packageName, packageName.호출할ClassName);

it.putExtra("SOME_KEY", so);

startActivity(it);


이렇게 특정 Process나 Activity로 객체 so를 전송할 수 있다.

이렇게 전송하기 위해서는 SomeObject를 직렬화 해야 되는데 객체 직렬화 처리를 해 주는 interface가 Parcelable이다.

Intent에 담아 전송하고자 하는 클래스가 Parcelable를 implements해 주면 해당 클래스의 직렬화는 Parcelable이 담당해 준다.


  • Parcelable

⇒ Interface for classes whose instances can be written to and restored from a Parcel. 

직렬화해서 보낼 객체를 Parcel에 담거나 Parcel에 담겨진 직렬화된 객체를 복원하는 역할을 해주는 interface이다.


객체를 Intent에 담아서 전송하기 위해 처리해 주어야 할 가장 중심 작업은 2가지이다.

(1) 객체(class)의 멤버 변수를 Parcel에 담는 행위(전송하는 쪽에서 필요한 기능)

 -. public void writeToParcel(Parcel dest, int flags)에서 객체의 데이터를 Parcel에 담는 행위


public class FacialFeatures implements Parcelable{

private int pinNumber; //직렬화 할 실제 데이터

private byte[] faceFeatures; //직렬화 할 실제 데이터

public FacialFeatures(int pin_num, byte[] facialFT){

pinNumber = pin_num;

faceFeatures = facialFT;

}

@Override

public int describeContents() {

return 0;

}

//아래 메소드에서 직렬화할 객체의 데이터를 Parcel에 담는다.

//송신할 때 플랫폼의 IPC에서 이 메소드 자동 호출

@Override

public void writeToParcel(Parcel dest, int flags) {

dest.writeInt(pinNumber); //Parcel에 객체의 데이터 담기

dest.writeByteArray(faceFeatures); //Parcel에 객체의 데이터 담기

}



(2) Parcel에 담겨진 객체를 읽어내는 행위(수신쪽에서 필요한 기능)

-. Intent에 담아 다른 Process나 다른 Activity로 넘길 클래스에 CREATRO라는 static 멤버 변수를 선언해 주어야 한다. CREATOR의 데이터 type은 Parcelable.Creator 타입인데 Parcelable.Creator는 interface이므로 2개의 추상 메소드를 재정의 해 주어야 한다.

수신쪽에서 CREATOR라는 변수를 이용해서 Parcel에 담겨진 객체를 읽어낸다. 


public static Parcelable.Creator<FacialFeatures> CREATOR = new Parcelable.Creator(){

//아래 메소드가 Parcel에 담겨져 있는 객체 데이터를 읽는 기능 수행

//수신시 플랫폼의 IPC에서 이 메소드 자동 호출

@Override

public FacialFeatures createFromParcel(Parcel source) {

int _pinNum = source.readInt();

byte[] _faceFeatures = source.createByteArray();

return new FacialFeatures(_pinNum, _faceFeatures);

}

@Override

public FacialFeatures[] newArray(int size){

return new FacialFeatures[size];

}

};


정리하면 객체를 Intent에 담아서 다른 Process나 다른 Activity에 전송할려면 해당 객체를 직렬화 해 주어야 하는데 이 기능을 담당해 주는 Interface가 Parcelable이다.

객체를 직렬화 하기 위해서는 2가지를 처리해 주어야 하는데

(1) public void writeToParcel(Parcel dest, int flags)에서 해당 객체의 데이터를 Parcel에 담기

(2) Parcelable.Creator 타입의 static 변수 CREATOR를 재 정의하는 메소드 public T createFromParcel (Parcel source)에서 Parcel에 담겨져 있는 객체의 데이터를 읽는 작업을 처리해 주면 객체를 Intent에 담아서 전송하는 것이 가능하다.


그런데 이러한 실행 중 다음과 같은 에러를 종종 만나게 된다. 


java.lang.RuntimeException: bad array lengths


이 에러의 원인은 객체를 직렬화하면 객체의 데이터(멤버 변수)가 순서대로 일렬로 정렬되는데 해당 데이터의 범위를 벗어났을 경우 통상적으로 발생하는 에러이다.

특히 직렬화 할 데이터 중에 배열이 포함되어 있을 때 다음과 같은 실수를 범하기 쉽고 이때 발생하는 문제이다.


public static Parcelable.Creator<FacialFeatures> CREATOR = new Parcelable.Creator(){

@Override

public FacialFeatures createFromParcel(Parcel source) {

int _pinNum = source.readInt();

//public final byte[] createByteArray ()

//==>Read and return a byte[] object from the parcel.

//createByteArray()가 그냥 크기만 Parcel에 있는 byte 배열과 동일한 사이즈를 가진

//비어 있는 array를 만드는 것이 아니라 다음 2가지를 동시에 처리한다.

// (1) Parcel에 있는 배열의 크기와 동일한 크기의 배열을 만들고

// (2) Parcel로부터 배열의 내용까지 read해서 = 좌측에 대입시키는 것까지를 처리한다.

//따라서 createByteArray()를 하고 나면 직렬화 순서상 그 다음 순서로 position이 

//넘어 간다. 따라서 createByteArray() 후에 readByteArray(byte[] b)를 수행하면

//readByteArray()에서 아래와 같은 에러가 발생하고 앱이 crash된다.

//Caused by: java.lang.RuntimeException: bad array lengths

byte[] _faceFeatures = source.createByteArray();

//위의 createByteArray()에서 이미 배열을 읽었기 때문에 data position이

//이미 배열의 위치를 지나가 버렸다.

//따라서 readByteArray()를 하면 bad array lengths가 발생하는 것이다.

//따라서 readByteArray()로 읽든지 아니면 createByteArray()읽든지 둘 중 어느 

//하나만 있으면 된다.

// source.readByteArray(_faceFeatures);

return new FacialFeatures(_pinNum, _faceFeatures);

}

@Override

public FacialFeatures[] newArray(int size){

return new FacialFeatures[size];

}

};




구글 제공 API 데모(샘플) 소스 이클립스에서 import 하는 법


File - New - Project - Build Target의 API 버전을 선택하면 해당 API 데모 소스들이 전부 목록으로 뜬다.

여기서 원하는 샘플 소스를 선택하면 된다.



구글의 안드로이드 개발자 사이트에서 제공해주는 샘플 소스가 있는데 이런 것들은 완전한 프로젝트 형태로 주어지는 것이 아니다. 

따라서 일반적인 프로젝트 import식으로는 안된다.


File - New - Project - Android 하위 항목의 Android Project from Existing Code - Root Directory 항목에 샘플 소스가 있는 위치(디렉토리) 지정, Copy projects into workspace 항목 체크 - Finish 클릭


※ 일반적인 완성된 프로젝트 import하는 법

File - import - General 하위 항목의 Existing Projects into Workspace - Next - Select root directory 항목에 import할 프로젝트 위치 지정 - Copy projects into workspace 항목 체크 - Finish



+ Recent posts