2012년 5월 10일 목요일

Content Providers 01

Content Providers

  • 콘텐트 프로바이더는 구조적 데이터 집합에 대합 접근을 관리한다.
    데이터를 캡슐화하고 데이터 보안을 정의하는 메카니즘을 제공한다.
    콘텐트 프로바이더는 하나의 프로세스에서 데이터를 다른 프로세스의 코드와 연결한다.

  • Content Provider의 데이터를 접근할 때 프로바이더를 클라이언트로 통신하기 위해 어플의 Context 안에 ContentResolver 개체를 사용한다.
    ContentResolver 개체는 ContentProvider 인스턴스인 프로바이더 개체와 통신한다.
    프로바이더 개체는 클라이언트로부터 데이터 요구를 수신하고 요구된 액션을 수행한 뒤 결과를 리턴한다.

Content Provider Basics

  • 콘텐트 프로바이더는 데이터의 중앙 저장소에 대한 접근를 관리한다.
    애플러케이션는 프로바이더 클라이언트 개체를 사용하여 프로바이더를 접근하는데 콘텐트 프로바이더를 이용한다.

  • 프로바이더와 프로바이더 클라이언트는 프로세스간 통신(IPC)과 안전한 데이터 접근을 다루는 표준 인터페이스를 제공한다.

  • 요약
  • 콘텐트 프로바이더의 작동방식
    콘텐트 프로바이더로부터 데이터를 복원하는 API
    콘텐트 프로바이더에 데이터를 삽입, 갱신, 삭제하는 API
    프로바이더와 동작을 수생하는 다른 API

Overview

  • 콘텐트 프로바이더는 관계형 데이터베이스의 테이블과 유사한 하나이상의 테이블을 외부 어플에 데이터를 표현한다.
    row은 프로바이더가 모은 데이터의 특정 타입 인스턴스를 대표하고 column은 각각의 데이터의 개별 부분을 이룬다.

  • 예제

  • wordapp idfrequencylocale_ID
    mapreduceuser1100en_US1
    precompileruser14200fr_FR2
    appletuser2225fr_CA3
    constuser1255pt_BR4
    intuser5100en_UK5

프로바이더에 접근하기

  • 어플은 ContentResolver 클라이언트 개체로 콘텐트 프로바이더로부터 데이터에 접근한다.
    ContentResolver 메서드는 기본적으로 CRUD (create, retrieve, update, delete)을 가진다.

  • 클라이언트 어플 프로세스의 ContentResolver 개체와 프로바이더를 소유한 어플의 ContentProvider 개체는 자동적으로 프로세스간 통신을 다룬다.
    ContentProvider는 데이터의 저장소와 테이블로써 데이터의 외부 표현간에 추상적 레이어로 작동한다.

  • 예제
    사용자 사전 프로바이더로부터 단어와 로케일 관련 리스트를 얻기 위해 ContentResolver.query()를 호출한다.
    query() 메서드는 사용자 사전 프로바이더에 정의된 ContentProvider.query()를 호출한다.

  • // Queries the user dictionary and returns results
    mCursor = getContentResolver().query(
        UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
        mProjection,                        // The columns to return for each row
        mSelectionClause                    // Selection criteria
        mSelectionArgs,                     // Selection criteria
        mSortOrder);                        // The sort order for the returned rows
    

  • query(Uri, projection, selection, selectionArgs, sortOrder)에 대한 인수들은 SQL SELECT 구문과 다음과 매칭된다.

  • query() argumentSELECT keyword/parameterNotes
    UriFROM table_nameUri는 table_name 이름의 프로바이더와 맵핑된다.
    projectioncol, col, col, ... projection은 복원된 각 행을 위해 포함되어야 할 열들의 배열이다.
    selectionWHERE col=value selection은 행들을 선택하는 것의 기준을 지정한다.
    selectionArgs

    sortOrderORDER BY col, col, ...

Content URIs

  • content URI는 프로바이더의 데이터를 확인하는 URI이다.
    Content URI들은 전체 프로바이더의 기호 이름(authority)과 테이블을 가리키는 이름(path)을 포함한다.

  • content://user_dictionary/words

  • user_dictionary프로바이더의 authority
    words테이블 경로
    content:scheme

  • 프로바이더는 URI의 뒷 부분에 ID값을 추가함으로써 테이블의 하나 row를 접근할 수 있게 한다.
    예를 들어 사용자 사전에서 _ID가 4인 row를 얻기 위해 다음과 같이 content URI를 사용한다.

  • Uri singleUri=ContentUri.withAppendedId(UserDictionary.Words.CONTENT_URI, 4) ;

프로바이더에서 데이터를 얻기

  • 프로바이더로부터 데이터를 얻기위해 다음과 같은 기본 단계를 따른다.

    1. 프로바이더로부터 읽기 접근 권한을 요청한다.

    2. 프로바이더에게 쿼리를 보내는 코드를 정의한다.

읽기 접근 권한을 요청하기

  • 권한은 실행시에 요청될 수 없고 manifest 파일안에서 엘러먼트와 프로바이더에 의해 정의되는 권한을 이용한다.

  • 어플 설치시 이 권한 요청은 수락된다.

  • 사용자 사전 프로바이더 예제에서 manifest 파일안에 android.permission.READ_USER_DICTIONARY 권한을 정의한다. 그리고 어플은 프로바이더로부터 읽기 위해 이 권한을 요청해야 한다.

쿼리를 생성하기

  • 사용자 사전 프로바이더 예제에서 쿼리 작성

  • // A "projection" defines the columns that will be returned for each row
    String[] mProjection =
    {
        UserDictionary.Words._ID,    // Contract class constant for the _ID column name
        UserDictionary.Words.WORD,   // Contract class constant for the word column name
        UserDictionary.Words.LOCALE  // Contract class constant for the locale column name
    };
    
    // Defines a string to contain the selection clause
    String mSelectionClause = null;
    
    // Initializes an array to contain selection arguments
    String[] mSelectionArgs = {""};
    

  • 다음 코드는 ContentResolver.query()를 사용하는 법을 보여준다.
    프로바이더 클라이언트 쿼리는 SQL 쿼리와 유사하고 리턴할 column들의 집합과 선택기준의 집합, 정렬순서를 포함한다. 쿼리가 리턴해야 하는 column들의 집합은 proejction이다.

    검색할 row를 지정하는 표현은 selection 구문과 selection 인수로 나뉜다.
    selection 구문은 논리/부울 표현과 column 이름들,값들로 이루어진 조합이다.
    값 대신 '?' 대체 매개변수를 지정하면 쿼리 메서드는 selection 인수 배열로부터 값을 검색한다.

    다음 예제에서 단어를 입력하지 않으면 selectin 구문은 null이고 쿼리는 프로바이더에서 모든 단어들을 리턴한다.
    사용자가 단어를 입력하면 selection 구문은 UserDictionary.Words.Word +"=?"로 지정되고 selection 인수 배열의 첫번째 원소는 사용자가 입력한 단어로 지정된다.

  • /*
     * This defines a one-element String array to contain the selection argument.
     */
    String[] mSelectionArgs = {""};
    
    // Gets a word from the UI
    mSearchString = mSearchWord.getText().toString();
    
    // Remember to insert code here to check for invalid or malicious input.
    
    // If the word is the empty string, gets everything
    if (TextUtils.isEmpty(mSearchString)) {
        // Setting the selection clause to null will return all words
        mSelectionClause = null;
        mSelectionArgs[0] = "";
    
    } else {
        // Constructs a selection clause that matches the word that the user entered.
        mSelectionClause = " = ?";
    
        // Moves the user's input string to the selection arguments.
        mSelectionArgs[0] = mSearchString;
    
    }
    
    // Does a query against the table and returns a Cursor object
    mCursor = getContentResolver().query(
        UserDictionary.Words.CONTENT_URI,  // The content URI of the words table
        mProjection,                       // The columns to return for each row
        mSelectionClause                   // Either null, or the word the user entered
        mSelectionArgs,                    // Either empty, or the string the user entered
        mSortOrder);                       // The sort order for the returned rows
    
    // Some providers return null if an error occurs, others throw an exception
    if (null == mCursor) {
        /*
         * Insert code here to handle the error. Be sure not to use the cursor! You may want to
         * call android.util.Log.e() to log this error.
         *
         */
    // If the Cursor is empty, the provider found no matches
    } else if (mCursor.getCount() < 1) {
    
        /*
         * Insert code here to notify the user that the search was unsuccessful. This isn't necessarily
         * an error. You may want to offer the user the option to insert a new row, or re-type the
         * search term.
         */
    
    } else {
        // Insert code here to do something with the results
    
    }

  • 이 쿼리는 다음의 SQL 문장과 대응되고 cotract class constants 대신 실제 column 이름들이 사용된다.
  • SELECT _ID, word, frequency, locale FROM words WHERE word = ORDER BY word ASC;

악의적인 입력로부터 보호하기

  • '?' 매개변수와 selection 인수들의 개별 배열을 selection 구문에 사용한다.
    이럼으로써 사용자 입력은 SQL 문장의 부분으로 해석되지 않고 쿼리에 직접 연결된다.


  • // Constructs a selection clause with a replaceable parameter
    String mSelectionClause = "var = ?";


  • selection 인수들의 배열을 성정한다.

  • // Defines an array to contain the selection arguments
    String[] selectionArgs = {""};


쿼리 결과를 표현하기

  • ContentResolver.query() 클라이언트 메서드는 Cursor를 리턴한다.
    Cursor는 selection 기준과 합치하는 row들을 위한 쿼리 proejction에 의해 지정된 column들을 담고 있다.
    Cursor 메서드를 사용하여 row들을 반복 적용할 수 있고 각 column의 데이터 타입과 데이터을 판단, 얻어오며 결과들의 다른 속성들을 검사할 수 있다.

  • selection criteria와 합치하는 Row들이 없으면 프로바이더는 Cursor.getCount()가 0인 비어있는 Cursor 개체를 리턴한다.


  • Cursor는 Row들의 하나 리스트이기 때문에 Cursor의 내용을 표현할 때 SimpleCursorAdapter를 통하여 ListView와 연결하는 것이 좋다.

  • 예제

  • // Defines a list of columns to retrieve from the Cursor and load into an output row
    String[] mWordListColumns =
    {
        UserDictionary.Words.WORD,   // Contract class constant containing the word column name
        UserDictionary.Words.LOCALE  // Contract class constant containing the locale column name
    };
    
    // Defines a list of View IDs that will receive the Cursor columns for each row
    int[] mWordListItems = { R.id.dictWord, R.id.locale};
    
    // Creates a new SimpleCursorAdapter
    mCursorAdapter = new SimpleCursorAdapter(
        getApplicationContext(),               // The application's Context object
        R.layout.wordlistrow,                  // A layout in XML for one row in the ListView
        mCursor,                               // The result from the query
        mWordListColumns,                      // A string array of column names in the cursor
        mWordListItems,                        // An integer array of view IDs in the row layout
        0);                                    // Flags (usually none are needed)
    
    // Sets the adapter for the ListView
    mWordList.setAdapter(mCursorAdapter);
    


쿼리 결과로부터 데이터를 가져오기

  • Cursor로부터 Row들을 반복 적용

  • // Determine the column index of the column named "word"
    int index = mCursor.getColumnIndex(UserDictionary.Words.WORD);
    
    /*
     * Only executes if the cursor is valid. The User Dictionary Provider returns null if
     * an internal error occurs. Other providers may throw an Exception instead of returning null.
     */
    
    if (mCursor != null) {
        /*
         * Moves to the next row in the cursor. Before the first movement in the cursor, the
         * "row pointer" is -1, and if you try to retrieve data at that position you will get an
         * exception.
         */
        while (mCursor.moveToNext()) {
    
            // Gets the value from the column.
            newWord = mCursor.getString(index);
    
            // Insert code here to process the retrieved word.
    
            ...
    
            // end of while loop
        }
    } else {
    
        // Insert code here to report an error if the cursor is null or the provider threw an exception.
    }
    

Content Provider Permissions

  • 프로바이더 어플은 다른 어플들이 프로바이더의 데이터를 접근하기 위해 필요한 권한을 지정할 수 있다.
    이 권한들은 어플이 어떤 데이터를 접근하는 지를 사용자가 인지하는 것을 확인해준다.
    최종 사용자들은 어플 설치시 요구된 권한들 볼 수 있다.


  • 예제의 사용자 사전 프로바이더는 데이터를 검색하기 위해 android.permission.READ_USER_DICTIONARY 권한을 요구한다.
    프로바이더는 데이터에 대한 삽입, 갱신, 삭제(inserting, updating, deleting)들을 위한 별도의 android.permission.WRITE_USER_DICTIONARY 권한을 가진다.


  • 프로바이더에 접근하는 데 필요한 권한을 얻기 위해 어플은 manifest 파일안에 엘러먼트로 요청한다.
    Android Package Manager가 어플을 설치할 때 어플이 요구한 모든 권한들을 동의해야 한다.


  • <uses-permission android:name="android.permission.READ_USER_DICTIONARY">
    

데이터 삽입, 갱신, 삭제

  • 프로바이더에서 데이터를 검색하는 방식과 같이 데이터를 수정하기 위해 프로바이더 클라이언트와 프로바이더의 ContentProvider간의 상호동작을 이용한다.

데이터 삽입(Inserting data)

  • ContentResolver.insert()는 프로바이더에 새로운 레코드를 입력하고 content URI를 리턴한다.

  • // Defines a new Uri object that receives the result of the insertion
    Uri mNewUri;
    
    ...
    
    // Defines an object to contain the new values to insert
    ContentValues mNewValues = new ContentValues();
    
    /*
     * Sets the values of each column and inserts the word. The arguments to the "put"
     * method are "column name" and "value"
     */
    mNewValues.put(UserDictionary.Words.APP_ID, "example.user");
    mNewValues.put(UserDictionary.Words.LOCALE, "en_US");
    mNewValues.put(UserDictionary.Words.WORD, "insert");
    mNewValues.put(UserDictionary.Words.FREQUENCY, "100");
    
    mNewUri = getContentResolver().insert(
        UserDictionary.Word.CONTENT_URI,   // the user dictionary content URI
        mNewValues                          // the values to insert
    );
    


2012년 5월 8일 화요일

Intents and Intent Filters - 03

Note Pad Example

  • 노트 패드 예제는 사용자가 노트 리스트를 탐색하고 리스트내의 개별 아이템의 세부내용을 볼 수 있게 한다. 그리고 사용자는 리스트에 새로운 아이템을 추가할 수 있게 한다.

  • Note Pad 어플은 각각 최소한 하나 이상의 인텐트 필터를 가진 세 개의 액티비티를 manifest 파일에 선언한다. 그것은 노트 데이터를 관리하는 콘텐트 프로바이더를 추가로 선언한다.

  • <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.example.android.notepad">
        <application android:icon="@drawable/app_notes"
                     android:label="@string/app_name" >
    
            <provider android:name="NotePadProvider"
                      android:authorities="com.google.provider.NotePad" />
    
            <activity android:name="NotesList" android:label="@string/title_notes_list">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.VIEW" />
                    <action android:name="android.intent.action.EDIT" />
                    <action android:name="android.intent.action.PICK" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.GET_CONTENT" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
                </intent-filter>
            </activity>
            
            <activity android:name="NoteEditor"
                      android:theme="@android:style/Theme.Light"
                      android:label="@string/title_note" >
                <intent-filter android:label="@string/resolve_edit">
                    <action android:name="android.intent.action.VIEW" />
                    <action android:name="android.intent.action.EDIT" />
                    <action android:name="com.android.notepad.action.EDIT_NOTE" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
                </intent-filter>
                <intent-filter>
                    <action android:name="android.intent.action.INSERT" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
                </intent-filter>
            </activity>
            
            <activity android:name="TitleEditor" 
                      android:label="@string/title_edit_title"
                      android:theme="@android:style/Theme.Dialog">
                <intent-filter android:label="@string/resolve_title">
                    <action android:name="com.android.notepad.action.EDIT_TITLE" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="android.intent.category.ALTERNATIVE" />
                    <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
                    <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
                </intent-filter>
            </activity>
            
        </application>
    </manifest>

  • NoteList 액티비티는 일련의 노트에 대해 수행하며 어플의 최초 인터페이스로 작용한다.

  • <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    

    이 필터는 노트패드 어플의 엔트리 포인트로 선언되게 한다.

  • <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <action android:name="android.intent.action.EDIT" />
        <action android:name="android.intent.action.PICK" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="vnd.android.cursor.dir/vnd.google.note" />
    </intent-filter>
    
    디렉토리를 탐색(VIEW), 편집(EDIT)하고 디렉토리내의 특정 노트를 선택(PICK)할 수 있게 한다.
    데이터 엘러먼트의 mimeType 속성은 액션들이 수행할 데이터 종류를 지정한다.
    이것은 액티비티가 노트패드 데이터(vnd.google.note)를 보유한 콘텐트 프로바이더로부터
    0개 이상의 아이템들(vnd.android.cursor.dir)에 대해 Cursor를 얻을 수 있음을 보인다.
  • <intent-filter>
        <action android:name="android.intent.action.GET_CONTENT" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>
  • 이 액티비티의 두번째 목적은 새로운 노트를 만들고 노트 디렉토리에 삽입하는 것이다.
    action : android.intent.action.VIEW
    data : content://com.google.provider.NotePad/notes/ID
    액티비티로 하여금 ID로 지정된 노트의 내용을 표현하게 한다.

    action : android.intent.action.INSERT
    data : content://com.google.provider.NotePad/notes
    액티비티가 새로운 빈 노트를 content://com.google.provider.NotePad/notes에 만들고 편집하게 한다.

  • TitleEditor는 노트의 제목을 편집한다.

  • <intent-filter android:label="@string/resolve_title">
        <action android:name="com.android.notepad.action.EDIT_TITLE" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.ALTERNATIVE" />
        <category android:name="android.intent.category.SELECTED_ALTERNATIVE" />
        <data android:mimeType="vnd.android.cursor.item/vnd.google.note" />
    </intent-filter>

    ALTERNATIVE와 SELECTED_ALTERNATIVE는 옵션의 메뉴로 표현될 수 있는 액티비티를 지정한다.

    action:com.android.notepad.action.EDIT_TITLE
    data:content://com.google.provider.NotePad/notes/ID
    노트 ID와 관련된 타이틀을 표시하고, 편집한다.

2012년 5월 7일 월요일

Intents and Intent Filters 02

Intent Resolution

  • 인텐트는 두가지 그룹으로 나뉜다.
  • Explicit intents 컴퍼넌트 이름으로 타겟 컴퍼넌트를 지정한다. 외연적 인텐트은 하위 서비스나 자매 액티비티 실행 같은 어플 내부 메세지로 사용된다.
    Implicit intents 컴퍼넌트 이름을 지정하지 않고 다른 어플의 컴퍼넌트를 활성화시키는 데 사용된다.
  • Implicit Intents는 인텐트를 처리할 최적의 컴퍼넌트를 찾기 위해 인텐트 개체의 내용을 Intent filter와 비교한다.
  • 인텐트 필터는 컴퍼넌트의 능력을 알리고 그것이 다룰 수 있는 인텐트를 제한한다.
  • 인텐트 필터를 갖춘 컴퍼넌트는 explicit/implicit intents를 둘 다 처리할 수 있지만, 그렇지 않을 경우 explicit intents만 수신할 수 있다.
  • 인텐트는 action, data(uri/data type), category), category등의 세가지 측면에서 인텐트 필터로 테스트된다.

Intent filters

  • 시스템에게 어떤 implicit intents를 다룰 수 있는지 알리기 위해 액티비티, 서비스, 브로드캐스트 리시버는 하나 이상의 인텐트 필터를 가질 수 있다.
  • 필터는 컴포넌트가 수신하고자 하는 일련의 인텐트 집합을 기술한다.
Action test
  • manifest 파일에 의 하위 엘러먼트 로 액션들을 나열한다.
  • <intent-filter . . . >
        <action android:name="com.example.project.SHOW_CURRENT" />
        <action android:name="com.example.project.SHOW_RECENT" />
        <action android:name="com.example.project.SHOW_PENDING" />
        . . .
    </intent-filter>
Category test
  • <intent-filter> 엘러먼트는 action 엘러먼트와 같이 category를 하위 엘러먼트로 가진다.
  • <intent-filter . . . >
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        . . .
    </intent-filter>
  • 액션과 카테고리를 위한 상수 대신 문자열이 사용된다. CATEGORY_BROWSABLE에는 android.intent.category.BROWSABLE, ACTION_EDIT에는 android.intent.action.EDIT를 사용한다.
Data test
  • 액션과 카테고리처럼 데이타 역시 인텐트의 하위 엘러먼트로 포함된다.
  • <intent-filter . . . >
        <data android:mimeType="video/mpeg" android:scheme="http" . . . /> 
        <data android:mimeType="audio/mpeg" android:scheme="http" . . . />
        . . .
    </intent-filter>
  • 데이터 엘러먼트는 URI와 데이터 타입(MIME 미디어 타입)을 지정한다.
  • URI는 scheme, host, port 부분들로 이루어 진다.
  • scheme://host:port/path (ex. content://com.example.project:200/folder/subfolder/etc)
  • 데이터 패스 규칙
  • Intent object filter result
    URI data type URI data type
    xx xxpass
    ox oxpass
    xo xopass
    oo oopass
  • 인텐트가 하나 이상의 액티비티 또는 서비스의 필터를 통과하면 사용자는 어떤 컴퍼넌트를 활성화할 것인지 질의받는다. 대상이 존재하지 않는다면 예외가 발생한다.
Common cases
  • 컴퍼넌트는 파일 또는 콘텐트 프로바이더에 로컬 데이터를 얻는 경우 content:/file: scheme를 지정함이 없이 데이터 타입만을 나열한다.
  • 예를 들어 콘텐트 프로바이더으로부터 이미지 데이터를 얻는 컴퍼넌트를 다음과 같이 지정할 수 있다.
  • <data android:mimeType="image/*" />
  • 일반적인 경우 스킴과 데이터 타입을 다음과 같이 지정하여 컴퍼넌트가 네트워크에서 비디오 데이터를 구할 수 있게 한다.
  • <data android:scheme="http" android:type="video/*" />
  • 어플을 초기화하는 액티비티들은 "android.intent.action.MAIN" action를 포함하는 필터들을 가진다. 그들이 어플 론처로 나타난다면 "android.intent.category.LAUNCHER" category 역시 지정한다.
  • <intent-filter . . . >
        <action android:name="code android.intent.action.MAIN" />
        <category android:name="code android.intent.category.LAUNCHER" />
    </intent-filter>
Using intent matching
  • 인텐트는 인텐트 필터에 대칭되어 비교될 뿐만 아니라 장치내 컴퍼넌트들에 대한 것들 탐색하게 한다.
  • PackageManager는 특정 인텐트를 수용할 수 잇는 모든 컴퍼넌트를 리턴하는 query..() 메서들과 인텐트에 반응하는 최적의 컴퍼넌트를 결정하는 resolve...() 메서드들을 가진다.
  • queryIntentActivities() 인텐트에 대해 인수로 받아 그것에 대해 수행할 수 있는 모든 액티비티 리스트를 반환
    queryIntentServices()
    queryBroadcastReceivers()

Intents and Intent Filters 01


액티비티, 서비스, 브로드캐스트 리시버등의 컴퍼넌트들은 Intents로 불리는 메세지에 의해 활성화된다.
인텐트 메세지 장치는 동일한 앱의 컴포넌트들 또는 다른 앱의 컴포넌트들과 late runtime binding를 가능케 한다.
인텐트는 연산되어야 할 추상적인 명세를 지닌 데이터 구조이다.
예를 들어 브로드캐스트는 이미 발생되거나 알려져야 할 무엇인가를 나타낸다.

컴포넌트 유형별 인턴트 전달 메카니즘

  • 인텐트를 인수로 전달되어 액티비티로 하여금 다음과 같은 역활의 메서드를 수행한다.
  • Context.startActivity()새로운 액티비티를 시작한다.
    Activity.startActivityForResult()기존의 액티비티로 하여금 무언가를 지령한다.
    Activity.setResult()startActvityForResult()를 호출한 액티비티에 정보를 전달한다.
  • 다음과 같은 서비스 관련 메서드는 인텐트를 인수로 가지고 각각의 기능을 한다.
  • 브로드캐스트 메서드에 전달된 인텐트 개체들은 관심이 있는 모든 브로드캐스트 리시버들에게 전달된다.

Intent Object

컴퍼넌트가 수행해야 할 Action과 관련 데이터를 지니는 또는 안드로이드 시스템이 알아야 하는 해당 인텐트를 처리할 컴퍼넌트 카테고리와 타겟 액티비티를 실행하는 명령을 기술한  것을 포함하는 한 단위의 정보이다.

인텐트 구성

Component Name
인텐트를 처리할 컴퍼넌트의 이름이며 그 이름 형식은 컴퍼넌트 클래스 이름과 패키지 이름을 결합한 형태이다.
클래스 이름은 생략 가능하며 그 경우에 안드로이드는 인텐트의 다른 정보를 활용하여 타겟을 찾는다.
컴퍼넌트 이름은 setComponent(), setClass(), setClassName()으로 지정되고 getComponentName()로 접근된다.
-Action
수행되어야 액션을 가리키는 문자열
Context.startService()서비스를 초기화 또는 새로운 명령을 전달한다.
Context.bindService()호출 컴퍼넌트와 타겟 서비스간 연결을 성립시킨다.
Constant Target Component Action
ACTION_CALL activity 전화를 초기화
ACTION_EDIT activity 편집할 데이타를 표시
ACTION_MAIN activity 데이터입력과 출력없이 태스크의 초기 액티비티로써 시작
ACTION_SYNC activity 서버의 데이터를 모바일 장치와 동기화
ACTION_BATTERY_LOW broadcast receiver 배터리 잔량이 낮다는 경고
ACTION_HEADSET_PLUG broadcast receiver 헤드셋이 장치에 연결 또는 분리됨을 표시
ACTION_SCREEN_ON broadcast receiver 스크린이 켜졌음을 표시
ACTION_TIMEZONE_CHANGED broadcast receiver 타임존 설정이 변경되었음을 표시


인텐트 개체의 액션은 setAction()에 의해 지정되고 getAction() 의해 접근한다.

Data
  • 작용될 데이터의 URI와 데이터의 MIME 타입
  • 각각의 액션들은 각각 타입의 데이터명세와 쌍을 이룬다.
  • Action fieldData field
    ACTION_EDIT편집을 위해 표시할 다큐멘트의 URI
    ACTION_CALL전화번호를 나타내는 tel:URI
    ACTION_VIEW다운로드 또는 표현될 http:URI
  • 인텐트와 그것을 다룰 수 있는 컴퍼넌트를 매칭할 때 해당 URI와 데이터 타입을 아는 것이 중요하다.
  • 많은 경우, 데이터 타입은 URI로부터 추론될 수 있다. 예를 들어 content: URI는 데이터가 장치내 위치하며 Content Provider에 의해 관리된다는 것을 의미한다.
  • 타입은 인텐트 개체에 대해 명시적으로 지정될 수 있다.
    setData()URI 데이터를 지정
    setType()MIME 타입을 지정
    setDataAndType()
    getData()
    getType()
Category
  • 인텐트를 처리해야 할 컴퍼넌트의 종류에 대한 추가적인 정보를 담은 문자열
  • Category Constants
  • ConstantMeaning
    CATEGORY_BROWSABLE 이미지와 이메일 표시위한 브라우저
    CATEGORY_GADGET 해당 액티비티가 다른 액티비티에 임베디드될 수 있다.
    CATEGORY_HOME 액티비티가 홈스크린을 출력한다
    CATEGORY_LAUNCHER 태스크의 초기 액티비티가 될 수 있으며 최상위 어플 런처에 배치된다.
    CATEGORY_PREFERENCE 타겟 액티비티가 Preference Panel이다.
  • Method
  • addCategory()
    removeCategory()
    getCategories
Extras
  • 인텐트를 처리할 컴퍼넌트에 전달되어야 할 추가적 정보의 Key-value pair
  • ActionExtra설명
    ACTION_TIMEZONE_CHANGEDtime-zone
    ACTION_HEADSET_PLUGstate, name
    SHOW_COLOR
Flags

2012년 5월 6일 일요일

액티비티 라이프사이클 관리


사용자가 앱을 실행, 나오기, 다시 접근할 때
액티비티 인스턴스는 라이프사이클의 각각의 상태에
놓인다.

액티비티가 처음으로 시작할 때 시스템의 전면으로 나와
사용자의 사용자 포커스를 받는다.
이 과정에서 안드로이드 시스템은 사용자에 의해 설정된 유저 인터페이스
와 다른 컴퍼넌트들의 메스드를 호출한다.
사용자는 다른 액티비티 또는 다른 앱을 실행할 경우
해당 액티비티는 백그라운드로 이동하여 해당 액티비티의 또 다른
라이프 사이클 함수 그룹을 호출하여 처리한다.

액티비티 시작하기

main() 메서드에서 개시되는 다른 프로그래밍 패러다임과 달리
안드로이드는 라이프 사이클의 특정 단계에 대응되는 콜백함수를
호출함으로써 액티비티 인스턴스의 코드를 초기화한다.

라이프사이클 콜백 이해하기

onCreate() -> Created -- onStart() -> Started(visible) -- onResume() -> Resumed(visible)

Resumed(visible) -- onPause() -> Paused(partially visible)
Paused -- onResume() -> Resumed
Paused -- onStop() -> Stopped(hidden)

Stopped -- onStart() -> Started
Stopped -- onDestroy() -> Destroyed

액티비티 라이프사이클 함수들의 역활
-사용자가 앱을 사용중 전화 수신시 또는 다른 앱으로 전환될 경우 종료되지 않게 한다.
-시스템 자원의 효율적 관리
-사용자 어플을 떠나서 다시 복귀해 어플의 전개과정을 이어간다.
-스크린의 랜드스케이프와 포트레이트 회전시 어플의 크래시 방지와 프로그래스를 잃지 않게 함.

액티비티의 정적 상태
-Resumed
: 액티비티가 전면에 배치되고 사용자와 상호작용할 수 있음
-Paused
: 액티비티가 다른 액티비티에 의해 부분적으로 가려지며 사용자와 상호작용이
중지된다.
-Stopped
: 액티비티는 완전히 숨겨져 백그라운드로 놓인며 해당 인스턴스와 멤버변수같은 상태정보들이 저장된다.

론처 액티비티 지정
사용자가 홈화면에서 앱아이콘을 클릭하면 시스템은 론처 또는 메인으로 지정된
액티비티의 onCreate()를 호출한다.
메인 액티비티는 프로젝트의 루트 디렉토리에서 AndoridManifest.xml에서 정의하며
MAIN action과 LAUNCHER category를 포함하는 <intent-filter>를 선언함으로써
이루어진다.

<activity android:name=".MainActivity" android:label="@string/app_name">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity

새 인스턴스 생성

TextView mTextView; // Member variable for text view in the layout

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

    // Set the user interface layout for this Activity
    // The layout file is defined in the project res/layout/main_activity.xml file
    setContentView(R.layout.main_activity);
   
    // Initialize member TextView so we can manipulate it later
    mTextView = (TextView) findViewById(R.id.text_message);
   
    // Make sure we're running on Honeycomb or higher to use ActionBar APIs
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
        // For the main activity, make sure the app icon in the action bar
        // does not behave as a button
        ActionBar actionBar = getActionBar();
        actionBar.setHomeButtonEnabled(false);
    }
}

액티비티 종료하기

@Override
public void onDestroy() {
    super.onDestroy();  // Always call the superclass
   
    // Stop method tracing that the activity started during onCreate()
    android.os.Debug.stopMethodTracing();
}

액티비티의 중지와 재개

@Override
public void onPause() {
    super.onPause();  // Always call the superclass method first

    // Release the Camera because we don't need it when paused
    // and other activities might need to use it.
    if (mCamera != null) {
        mCamera.release()
        mCamera = null;
    }
}

액티비티 정지와 재시작
-시나리오
사용자가 Recent Apps window를 열어 다른 앱으로 이동한다. 현재의 사용자 앱은
전면에 있는 것은 stop된다.

@Override
protected void onStop() {
    super.onStop();  // Always call the superclass method first

    // Save the note's current draft, because the activity is stopping
    // and we want to be sure the current note progress isn't lost.
    ContentValues values = new ContentValues();
    values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
    values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());

    getContentResolver().update(
            mUri,    // The URI for the note to update.
            values,  // The map of column names and new values to apply to them.
            null,    // No SELECT criteria are used.
            null     // No WHERE columns are used.
            );
}

@Override
public void onResume() {
    super.onResume();  // Always call the superclass method first

    // Get the Camera instance as the activity achieves full user focus
    if (mCamera == null) {
        initializeCamera(); // Local method to handle camera init
    }
}