한 액티비티에서 다른 액티비티를 실행하려면 액티비티 실행을 요청하는 도구가 필요하다. 안드로이드는 브로드캐스팅과 인텐트(Intent)를 제공하는데, 브로드캐스팅은 모든 객체에게 뿌리는 방송과 같은 것이며 인텐트는 이 방송에 실려 전달되는 메시지에 해당된다.
인텐트는 명시적 인텐트와 묵시적 인텐트가 있다. 실행할 액티비티를 직접적으로 정확하게 가리키는 것을 명시적 인텐트라고 하고, 필요한 기능만 설명한 채 직접적으로 액티비티를 가리키지 않는 것을 암시적 인텐트라고 한다.
명시적 인텐트가 가장 빈번히 사용되는 경우는 하나의 패키지 안의 한 액티비티가 또 다른 액티비티를 호출할 때이다. 이것이 가능한 이유는 너무나도 당연한 일이지만 호출하는 쪽에서 호출할 액티비티를 임포트할 수 있기때문이다. 명시적 인텐트를 사용하는 법은 어렵지 않다. 현재 액티비티의 컨텍스트와 실행할 액티비티 클래스를 인텐트 생성자의 인자로 사용하여 인텐트 인스턴스 하나를 생성한 후에 startActivy() 메서드에 생성한 인텐트를 인자로 넘겨서 호출하면 된다.
startActivityForResult() 메서드가 실행되고 지정한 액티비티가 실행된 후에 실행된 액티비티가 다시 종료되면 액티비티의 onActionResult() 메서드가 호출될 것이다.
@Override public void onActivityResult(int requestCode, int resultCode, Intent data){ // 실행한 액티비티가 종료된 후에 필요한 코드를 이곳에 추가합니다. }
Intent 클래스 생성자의 첫 번째 인자는 현재 액티비티의 컨텍스트이고 두 번째 인자는 실행할 액티비티의 클래스이다.
암시적 인텐트는 앞에서 설명한 것처럼 실행 주제를 정확하게 서술하지 않고 자신의 목적만으로 적합한 액티비티를 실행하는 것이다. 암시적 인텐트를 사용하기 위해서는 두 가지가 필요하다. 하나는 실행하고자 하는 기능을 나타내는 액션(action)이며, 다른 하나는 액션과 함께 액티비티에 넘겨줄 데이터(data)입니다. 액션은 String 객체로 표현되며 데이터는 Uri 클래스의 인스턴스 이다. 예를 들어 가장 많이 사용되는 전화의 경우 액션으로 Intent.ACTION_CALL을, 데이터로 Uri.parse("tel:01012345678") 과 같은 Uri 값을 사용한다. 이를 코드로 구현하면 아래와 같다.
...(생략)... /* 전화를 걸기 위한 인텐트를 생성합니다. */ Intent intent = new Intent(Intent.ACTION_CALL); /* 전화를 Uri 인스턴스로 변환합니다. */ intent.setData(Uri.parse("tel:01012345678")); /* 생성된 인텐트를 사용해 액티비티를 실행합니다. */ startActivity(intent); ...(생략)...
전화를 거는 액티비티를 실행하기 위해 Intent.ACTION_CALL 액션을 사용하고 전화번호를 인자로 넘겨줘야 한다는 사실을 어떻게 알 수 있을까? 암시적 인텐트를 사용하기 위해서는 실행될 대상이 어떤 조건의 인텐트를 받는지 문서상으로 알고 있어야 한다. 안드로이드의 기본 애플리케이션 가운데 전화, 브라우저, 맵 등은 액티비티 공유를 위해 필요한 액션과 데이터를 문서로 공유하고 있다.
구글의 개발자 사이트의 http://developer.android.com/guide/appendix/g-app-intents.html 로 들어가서 Intents List : Invoking Google Applications on Android Devices(인텐트 목록: 안드로이드 기기에서 구글의 애플리케이션 호출하기)를 읽어보면 액션과 필요한 데이터를 확인할 수 있다.
액티비티 생명주기(Activity Life Cycle) 는 총 6단계를 거치며, 상황에 따라 1단계가 더 추가될 수 있다. 6단계는 시간의 순서에 따라 생성, 시작, 활성화, 비활성화, 중비, 소멸이며, 애플리케이션이 비활성화되었다가 활성화되는 순간 재시작을 거치게 되어 사실상 7단계로 구성된다. (재시작되지 않은채 소멸될수 있다. )
처음 실행시 OnCreate(), onStart(), onResume()순으로 빠르게 호출이 실행된다. 이것은 액티비티가 처음으로 실행될 때이며 테스크가 백그라운드로 숨었다가 다시 실행될 때에는 onStart() 나 onResume() 메서드를 호출하고, onCreate()는 생략된다. onPause()는 액티비티가 비활성화되어 화면에서 사라지기 직전을 나타낸다. onStop() 은 액티비티가 화면상에서 완전히 사라져버릴때 호출된다. onPause() 뒤에 불리며 시스템메모리가 부족한 경우 onStop()은 호출되지 않는다.
대부분의 경우 액티비티가 화면상에서 사라지는 순간 onPause(), onStop()순으로 연속 호출되어 두 메서드 사이의 경계를 구분하기가 어려울 수 있는데, onStop()의 호출없이 onPause() 메서드만 호출되었다가 활성화되었을때 onResume()으로 돌아가는 경우는 현재 액티비티 앞에 반투명 액티비티가 존재하는 경우이다.
로그캣 안드로이드에서는 app의 로그를 관리하기 위해 "로그캣"이라는 도구를 제공한다. 액티비티의 생명주기를 직접 눈으로 확인하기 위해 각 콜백 메서드가 호출되는 순간 화면상에 로그를 뿌려준다. 로그캣은 DDMS와 이클립스 양쪽에서 모두 사용할 수 있다. 보기 : [Windows] > [Show View] > [Others] 에서 LogCat 을 선택하면 이클립스 하단에 LogCat 이라는 창이 추가된다.
각 옵션들은 다음 같은 기준으로 로그를 필터링한다. ▶ V : 장황하다는 Verbose의 약자로 상세 정보를 출력하는 데 사용한다. ▶ D : Debug를 의미하며 말 그대로 디버깅 정보를 출력하는데 사용된다. ▶ I : Information의 약자로 위험성이나 오류가 아닌 단순 정보를 출력하는데 사용된다. ▶ W : Warning의 약자로 경고를 출력하는 데 사용된다. 경고는 오류는 아니지만 개발자가 체크해야 할 정보로 분류됩니다. ▶ E : Error, 애플리케이션 동작에 있어 심각한 오류를 출력할 때 사용된다.
위의 우선순위는 플랫폼이 알아서 판단하고 분류해 주는 것은 아니다. 각 우선순위는 개발자가 모듈이나 애플리케이션 작성 시 한 중의 로그 출력 코드를 추가할 때, 이 로그는 어느 정도의 위험성을 가지는지 판단하여 그에 맞는 레벨의 로그출력 메서드를 사용함으로써 결정된다.
LogFilter
LogFilter 윈도우는 프로세스 아이디(pid)와 로그 태그를 기준으로 로그를 분류한다. Filter Name 은 생성할 필터의 이름 , by Log Tag 는 분류할 로그의 태그 정보를 입력하는 곳. by pid 는 특정 프로세스의 출력만 보는것으로 프로세스 아이디를 입력한다. by Log Level 은 위의 다섯가지 우선순위 옵션과 동일한 기능이다.
public class AALCActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Log.i("AALCActivity", "onCreate"); } }
모바일 브라우저 종류 체크 - 요즘 모바일 기기가 완전 대세라서 브라우저 호환성 부터 시작해서 이슈가 엄청나다. 아이폰, 아이패드, 갤럭시S, 갤럭시 탭 으로 축약되는데. 기기가 너무 상이하니 적응하기 어려움이 이루 말할 수 없다.
"navigator.userAgent" 라는 에이전트명을 호출하는 스크립트가 있다.
사이트에 접속된 Device 의 userAgent 정보를 확인해서 해당 페이지의 모바일페이지로 이동을 시키든지, 내용을 변경하든지. 알아서들.
만약 기기를 못찾으면 혹시 모르니 아래의 키워드를 소문자로 변환해보시라. var s = "iPhone"; // 문자열을 대문자로 변환 s.toUpperCase();
// 문자열을 소문자로 변환
s.toLowerCase();
<script type="text/javascript"> var mobileKeyWords = new Array('iPhone', 'iPod', 'BlackBerry', 'Android', 'Windows CE', 'LG', 'MOT', 'SAMSUNG', 'SonyEricsson'); for (var word in mobileKeyWords){ if (navigator.userAgent.match(mobileKeyWords[word]) != null){ location.href = "보내고 싶은 모바일 경로"; break; } } </script>
SQLite (1) 데이터베이스의 정의 : Android에서 데이터베이스로 접근하려면, SQLiteOpenHelper 클래스를 상속한 데이터베이스 헬퍼를 정의하고 이것을 이용한다. 이 헬퍼는 데이터베이스의 생성 및 업그레이드를 관리한다. SQLiteOpeHelper 클래스를 상혹한 클래느는 다음의 메소드를 오버라이드한다.
오버라이드하는 메소드
메소드의 호출
onCreate() 메소드 onUpgrade() 메소드
데이터베이스 생성 시 호출. 데이터베이스 업그레이드 시 호출.
(2) 데이터베이스 헬퍼 생성자 SQLiteOpenHepler 클래스
SQLiteOpenHelper(Context context, String fileName, SQLiteDatabase.CursorFactory factory, int version)
기능 : SQLiteOpenHelper 클래스의 생성자 인수 : context 컨텍스트 fileName 데이터베이스 파일명
factory 팩토리
version 버전
데이터베이스 이름에는 저장할 곳의 파일명을 지정합니다. /data/data/패키지명/databases/데이터베이스 파일명 ( test.db) 에 저장됩니다. 팩토리는 사용하지 않으면 null을 지정하고, 버전은 1을 지정.
(3) 데이터베이스의 생성 : onCreate() 메소드를 호출 SQLiteOpenHelper 클래스
void onCreate(SQLiteDatabase db)
기능 : 데이터베이스 생성 시 호출 인수 : db 데이터베이스 객체
onCreate() 메소드 내에서는 SQL명령으로 테이블을 생성하고 있습니다. test 라는 테이블명에서 id 와 info 라는 컬럼을 갖고 있는 테이블을 생성하는 SQL 명령은 아래와 같습니다. 컬럼의 형은 문자열이므로 컬럼명 뒤에 text라고 기술하고, id 는 다른 ID 와 중복되지 않는 기본키이므로 primary key 라고 추가합니다. create table id not exists text(id text primary key, info text)
SQL명령을 실행하려면 SQLtestDatabase 클래스의 execSQL()을 사용합니다. SQLiteDatabase 클래스
void execSQL(String Sql)
기능 : SQL 명령의 실행 인수 : sql SQL명령
(4) 데이터베이스의 업그레이드 : 데이터베이스는 버전업 시 데이터베이스 구조를 변환하지 않으면 업그레이드가 안 되는 경우가 있습니다. 필요로 하는 버전에 이상이 있을 경우 이 onUpdate() 메소드가 불리므로, 이 메소드 내에서 구버전 번호와 신버전 번호의 정보로부터 데이터베이스 구조의 변환을 실행합니다. SQLiteOpenHelper 클래스
void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
기능 : 데이터베이스를 업그레이드한 때 인수 : db 데이터베이스 객체 oldVersion 구버전 번호
newVersion 신버전 번호
다음으로, SQL명령으로 테이블을 삭제한 후 onCreate() 메소드를 호출하여 테이블을 생성합니다. test 라는 이름의 테이블을 삭제할 SQL명령은 다음과 같습니다. drop table if exists test
(5) 데이터베이스 객체의 취득 : 메인이 되는 SQLiteEx 클래스의 개체에서는 먼저 정의한 데이터베이스 헬퍼를 이용하는 SQLiteDatabase 형 데이터베이스객체를 구합니다. 프로그램 내에서는 이 객체를 사용해서 데이터베이스의 조작을 실행합니다. 데이터베이스 객체를 구하려면 DBHelper 객첼르 생성한 후 getWritableDatabase() 메소드를 호출합니다. SQLiteOpenHelper 클래스
SQLiteDatabase getWritableDatabase()
기능 : 데이터베이스 객체의 취득 인수 :데이터베이스 객체
(6) 데이터베이스의 쓰기 : 데이터베이스의 쓰기를 실행하려면 Content Values 클래스로 갱신하는 레코드 정보를 작성합니다. Content Values 객체를 생성한 후 put() 메소드로 컬럼명과 값을 세트로 추가합니다. ContentValues 클래스
void put(String colName, String value)
기능 : 레코드 정보에 컬럼명과 값 추가 인수 : colName 컬럼명 value 값
이 프로그램에서는 다음의 칼럼을 추가하고 있습니다.
컬러명
값
id info
0 텍스트 박스의 문자열
데이터 객체의 update() 메소드에 이 레코드 정보를 건네주는 것에 의해서, id[0]을 갖는 레코드의 info가 텍스트 박스의 문자로 덧쓰게 됩니다. SQLiteDatabase 클래스
int update(String tableName, ContentValues values, String where, String[] whereArgs)
기능 : 테이블 레코드의 갱신 인수 : tableName 테이블명 values 레코드 정보
where where 파라미터
whereArgs where 파라미터 반환값 : 갱신한 레코드 수
반환값 레코드 수가 0이 될 때는 id[0] 이 존재하지 않는다고 간주하고, insert() 메소드로 테이블에 레코드를 삽입한다. SQLiteDatabase 클래스
long insert(String tableName, String nullColumnHack, ContentValues values)
기능 : 테이블의 레코드 추가 인수 : tableName 테이블명 nullColumnHack NULL값
values 레코드 정보
반환값 : 레코드 ID
(7) 데이터베이스로부터 읽기 : 데이터베이스로부터 읽기를 실행하려면 데이터베이스 객체의 query() 메소드를 사용합니다. SQLiteDatabase 클래스
기능 : 데이터베이스로부터 읽기 인수 : tableName 테이블명 columns 컬럼명의 배열 selection selection parameter selectArgs selection parameter factor groupBy groupBy parameter having having parameter orderBy orderBy parameter limit limit parameter
반환값 : Cursor 객체
selection, groupBy, having, orderBy, limit 파라미터는 SQLite의 query 명령 파라미터입니다. 이번에는 id가 0 인것만을 취득하고 싶기 때문에 selection 파라미터에 "id=0" 을 지정하고 있습니다. 반환값은 Cursor 객체로 구해지는데, Cursor 객체는 내부적으로 다수의 레코드를 저장하고 있습니다. getCount() 메소드로 레코드 수, getColumnCount() 메소드로 컬럼 수를 구할 수 있습니다.
Cursor 클래스
int getCount()
기능 : 레코드 수 구하기
반환값 : 레코드 수
Cursor 클래스
int getColumnCount()
기능 : 컬럼 수 구하기
반환값 :컬럼 수
Cursor 객체는, 자장하는 레코드 안의 조작대상을 지시하는 정보(커서)를 갖습니다. 커서에서 선두의 레코드를 지시하는 때에는 moveToFirst() 메소드를 부릅니다. Cursor 클래스
boolean moveToFirst()
기능 : 커서에서 선두 레코드를 지시한다.
반환값 : 성공 또는 실패
그리고 나서, getString() 메소드 등을 부르는 것으로 레코드 내의 컬럼이 지시하는 값을 수하는 것이 가능합니다. Cursor 클래스
String getString(int colIdx)
기능 : String 형 값 구하기 인수 : colIdx 컬럼의 위치
반환값 : String 형의 값
double getDouble(int colIdx)
기능 : double 형 값 구하기
인수 : colIdx 컬럼의 위치
반환값 : double 형의 값
float getFloat(int colIdx)
기능 : float 형 값 구하기
인수 : colIdx 컬럼의 위치
반환값 : float 형의 값
int getInt(int colIdx)
기능 : int 형 값 구하기
인수 : colIdx 컬럼의 위치
반환값 : int 형의 값
long getLong(int colIdx)
기능 : long 형 값 구하기
인수 : colIdx 컬럼의 위치
반환값 : long 형의 값
short getShort(int colIdx)
기능 : short 형 값 구하기
인수 : colIdx 컬럼의 위치
반환값 : short 형의 값
여기에서는 컬럼1(0부터 시작하므로 2열 째) 값을 String 형으로 얻고 있습니다. 마지막으로, Cursor 객체의 close() 메소드를 호출하여 Cursor 를 종료합니다.
public class SQLiteEx extends Activity implements View.OnClickListener{ private final static String DB_NAME = "test.db"; // DB name private final static String DB_TABLE = "test"; // table name private final static int DB_VERSION = 1; // Version private EditText editText; private Button btnWrite; private Button btnRead; private SQLiteDatabase db;
// 초기화 @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); requestWindowFeature(Window.FEATURE_NO_TITLE); // 레이아웃의 생성 LinearLayout layout = new LinearLayout(this); layout.setBackgroundColor(Color.rgb(255, 255, 255)); layout.setOrientation(LinearLayout.VERTICAL); setContentView(layout); // 텍스트 박스의 생성 editText = new EditText(this); editText.setText("", EditText.BufferType.NORMAL); setLLParams(editText, 240, 50); layout.addView(editText); // 쓰기 버튼의 생성 btnWrite = new Button(this); btnWrite.setText("쓰기"); btnWrite.setOnClickListener(this); setLLParams(btnWrite); layout.addView(btnWrite); // 일기 버튼의 생성 btnRead = new Button(this); btnRead.setText("읽 기"); btnRead.setOnClickListener(this); setLLParams(btnRead); layout.addView(btnRead); // 데이터베이스 객체 구하기 (5) DBHelper dbHelper = new DBHelper(this); db = dbHelper.getWritableDatabase(); } // 버튼 클릭 이벤트 처리 public void onClick(View v){ if ( v == btnWrite) { try { String str = editText.getText().toString(); writeDB(str); } catch (Exception e) { showDialog(this, "Error", "Write Error"); } } else if ( v == btnRead) { try { String str = readDB(); editText.setText(str); } catch (Exception e) { showDialog(this, "Error", "Read Error"); } } }
자바스크립트는 현재 점점 빨라지고 있고 또한 강력해지고 있다. 특히 각종 모바일 디바이스가 보급되면서 모바일 웹에 대한 관심이 커졌고, 이에 따라 자바스크립트 엔진의 성능 역시 발전하고 있다. 팜프리는 이미 모든 애플리케이션을 자바스크립트를 사용해 만들 수 있도록 했고, 아이폰 및 안드로이드에서도 여러 가지 프레임워크를 통해 네이티브 애플리케이션과 같은 UI 및 성능을 보이는 웹 애플리케이션 개발이 가능해졌다. 하지만 이는 모드 클라이언트 사이트에서의 자바스크립트, 쉽게 얘기해서 브라우저에서 동작하는 자바 스크립트를 말한다.
jQuery.js 등의 스크립트 라이브러리를 요즘 많이 사용하고 있는데, node.js 는 서버사이드에서 강력한 성능을 발휘할 수 있는 서버사이드 자바스크립트 라이브러리라고 할 수 있다. 우리는 node.js 를 통해서 자바스크립트의 미래를 짐작해 볼수 있을 것이다.
기존에는 서버사이드에서 스크립트를 돌려서 모든 연산을 수행하고 그 결과값으로 HTML을 만들어서 클라이언트에게 전송했다. 클라이언트는 HTML을 랜더링해서 사용자에게 보여주는 역활을 했고, 자바스크립트는 클라이언트측에서 자잘한 UI기능을 수행하며 감초역활을 했는데, 현재는 서버와 클라이언트의 역활이 상당히 많이 달라졌다.
과거 서버에서의 역활들이 클라이언트로 만히 이동을했다. jQuery등을 사용해서 UI플로우 및 여러 인터렉션과 각종 로직의 상당 부분을 클라이언트 사이드에서 처리한다. 그리고, 요즘 흔히 오픈API라고 불리는 API를 제공하여 서버의 데이터에 대한 접관과 처리를 클라이언트가 제오할수 있도록 했다.
node.js는 구글이 개발해서 현재 크롬 브라우저에 탑재되어 있는 자바스크립트 엔진인 V8을 기반으로 만들어진 서버사이트 스크립트 이다. 오히려 V8의 기능을 자바스크립트를 통해 사용 할수 있도록 만들어진 wrapper라고 말하는 것이 더 정확한 표현일 것이다. node.js 는 V8이 내부적으로 특정 기능을 수행하는 과정에서 유저가 등록한 callback function 을 호출해 주도록 한다.
창시자 Ryan Dahl이 발표한 design goal을 정리하면, - Function은 직접 I/O에 연결되지 않는다. - 저수준(low-level)을 지향하며, 스트리밍으로 모든 것을 처리하되, 데이터 버퍼일을 강제하지 않는다. - TCP, DNS, HTTP 등의 중요한 프로토콜에 대해 빌트인 된 형대로 지원된다. - 다양한 HTTP 기능들을 지원한다. - 클라이언트 사이트 자바스크립트 프로그래밍과 비슷하며, 동시에 과거의 유닉스 시스템 프로그래밍과도 친숙하다.