Android執行個體剖析筆記notePad

來源:互聯網
上載者:User

開卷語

      俗話說,“熟讀唐詩三百首,不會作詩也會吟”。最近收集了很多Android的範例程式碼,從這些代碼的閱讀和實驗中學習到很多知識,從而產生寫這個系列的打算,目標就是一步步跟著執行個體進行動手實作,真正從“做”中體會和學習Android開發。
      本文是這個系列的第一篇,目標是Android內建的一個範常式序:記事本,將分為四篇文章進行詳細介紹。

預備知識

      搭建開發環境,嘗試編寫”Hello World”,瞭解Android的基本概念,熟悉Android的API(官方文檔中都有,不贅述)。

程式

      先來簡單瞭解下程式啟動並執行效果

程式進入點

   類似於win32程式裡的WinMain函數,Android自然也有它的程式進入點。它通過在AndroidManifest.xml檔案中配置來指明,可以看到名為NotesList的activity節點下有這樣一個intent-filter,其action為android.intent.action.MAIN,

Category指定為 android.intent.category.LAUNCHER,這就指明了這個activity是作為入口activity,系統尋找到它後,就會建立這個activity執行個體來運行,若未發現就不啟動(你可以把MAIN改名字試試)。

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

NotesList詳解

      就從進入點所在的activity(見圖1)開始,可以看到這個activity最重要的功能就是顯示日誌列表。這個程式的日誌都存放在Sqlite資料庫中,因此需要讀取出所有的日誌記錄並顯示。

      先來看兩個重要的私人資料,第一個PROJECTION欄位指明了“日誌列表“所關注的資料庫中的欄位(即只需要ID和Title就可以了)。

   private static final String[] PROJECTION = new String[] {
            Notes._ID, // 0
            Notes.TITLE, // 1
    };

     第二個欄位COLUMN_INDEX_TITLE指明title欄位在資料表中的索引。

private static final int COLUMN_INDEX_TITLE = 1;

然後就進入第一個調用的函數onCreate。

        Intent intent = getIntent();
        if (intent.getData() == null) 
        {
            intent.setData(Notes.CONTENT_URI);
        }

      因為NotesList這個activity是系統調用的,此時的intent是不帶資料和操作類型的,系統只是在其中指明了目標組件是Notelist,所以這裡把”content:// com.google.provider.NotePad/notes”儲存到intent裡面,這個URI地址指明了資料庫中的資料表名(參見以後的NotePadProvider類),也就是儲存日誌的資料表notes。

        Cursor cursor = managedQuery(getIntent().getData(), PROJECTION, null, null, Notes.DEFAULT_SORT_ORDER);

      然後調用managedQuery函數查詢出所有的日誌資訊,這裡第一個參數就是上面設定的” content:// com.google.provider.NotePad/notes”這個URI,即notes資料表。PROJECTION欄位指明了結果中所需要的欄位,Notes.DEFAULT_SORT_ORDER 指明了結果的定序。實際上managedQuery並沒有直接去查詢資料庫,而是通過Content Provider來完成實際的資料庫操作,這樣就實現了邏輯層和資料庫層的分離。

 SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.noteslist_item, cursor,
                new String[] { Notes.TITLE }, new int[] { android.R.id.text1 });
        setListAdapter(adapter);

      查詢出日誌列表後,構造一個CursorAdapter,並將其作為List View的資料來源,從而在介面上顯示出日誌列表。可以看到,第二個參數是R.layout.noteslist_item,開啟對應的noteslist_item.xml檔案,

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/text1"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight"
    android:textAppearance="?android:attr/textAppearanceLarge"
    android:gravity="center_vertical"
    android:paddingLeft="5dip"
    android:singleLine="true"
/>

      就是用來顯示一條日誌記錄的TextView,最後兩個欄位指明了實際的欄位對應關係,通過這個TextView來顯示一條日誌記錄的title欄位。

處理“選擇日誌”事件

      既然有了“日誌列表”,就自然要考慮如何處理某一條日誌的單擊事件,這通過重載onListItemClick方法來完成,

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Uri uri = ContentUris.withAppendedId(getIntent().getData(), id);
        
        String action = getIntent().getAction();
        if (Intent.ACTION_PICK.equals(action) || Intent.ACTION_GET_CONTENT.equals(action)) {
            // The caller is waiting for us to return a note selected by
            // the user.  The have clicked on one, so return it now.
            setResult(RESULT_OK, new Intent().setData(uri));
        } else {
            // Launch activity to view/edit the currently selected item
            startActivity(new Intent(Intent.ACTION_EDIT, uri));
        }
    }

      首先通過”content:// com.google.provider.NotePad/notes”和日誌的id 號拼接得到選中日誌的真正URI,然後建立一個新的Intent,其操作類型為Intent.ACTION_EDIT,資料域指出待編輯的日誌URI(這裡只分析else塊)。

Intent深度剖析

那麼,上面這句startActivity(new Intent(Intent.ACTION_EDIT, uri))執行後會發生什麼事情呢?這時候Android系統就跳出來接管了,它會根據intent中的資訊找到對應的activity,在這裡找到的是NoteEditor這個activity,然後建立這個activity的執行個體並運行。

那麼,Android又是如何找到NoteEditor這個對應的activity的呢?這就是intent發揮作用的時刻了。

new Intent(Intent.ACTION_EDIT, uri)

這裡的Intent.ACTION_EDIT=” android.intent.action.EDIT”,另外通過設定斷點,我們看下這裡的uri值:

      可以看到選中的日誌條目的URI是:content://com.google.provider.NotePad/notes/1

然後我們再來看下Androidmanfest.xml,其中有這個provider

<provider android:name="NotePadProvider"
            android:authorities="com.google.provider.NotePad"
        />

      發現沒有?它也有com.google.provider.NotePad,這個是content://com.google.provider.NotePad/notes/1的一部分,同時

    <activity android:name="NoteEditor"
            android:theme="@android:style/Theme.Light"
            android:label="@string/title_note"
            android:screenOrientation="sensor"
            android:configChanges="keyboardHidden|orientation"
        >
            <!-- This filter says that we can view or edit the data of
                 a single 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>
            <!-- This filter says that we can create a new note inside
                 of a directory of notes. -->
            <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>

      上面第一個intent-filter中有一個action 名為android.intent.action.EDIT,而前面我們建立的Intent也正好是

Intent.ACTION_EDIT=” android.intent.action.EDIT”,想必大家已經明白是怎麼回事了吧。

下面就進入activity選擇機制了:

系統從intent中擷取道uri,得到了content://com.google.provider.NotePad/notes/1,去掉開始的content:標識,得到com.google.provider.NotePad/notes/1,然後擷取前面的com.google.provider.NotePad,然後就到Androidmanfest.xml中找到authorities為com.google.provider.NotePad的provider,這個就是後面要講的contentprovider,然後就載入這個content provider。

        <provider android:name="NotePadProvider"
            android:authorities="com.google.provider.NotePad"
        />

在這裡是NotePadProvider,然後調用NotePadProvider的gettype函數,並把上述URI傳給這個函數,函數返回URI所對應的類型(這裡返回Notes.CONTENT_ITEM_TYPE,代表一條日誌記錄,而CONTENT_ITEM_TYPE = " vnd.android.cursor.item/vnd.google.note ")。

   @Override
    public String getType(Uri uri) {
        switch (sUriMatcher.match(uri)) {
        case NOTES:
            return Notes.CONTENT_TYPE;
        case NOTE_ID:
            return Notes.CONTENT_ITEM_TYPE;
        default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
}

      上面的sUriMatcher.match是用來檢測uri是否能夠被處理,而sUriMatcher.match(uri)傳回值其實是由

        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
        sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);

決定的。

然後系統使用獲得的" vnd.android.cursor.item/vnd.google.note "和”android.intent.action.EDIT”到androidmanfest.xml中去找匹配的activity.

  <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>

正好NoteEditor這個activity的intent-filter滿足上述條件,這樣就找到了NoteEditor。於是系統載入這個類並執行個體化,運行,然後就到了NoteEditor的OnCreate函數中(見後續文章)。

 

小技巧

1,在命令列中使用”adb shell”命令進入系統中,然後”cd app”進入應用程式所在目錄,”rm XXX”就可以刪除你指定的apk,從而去掉其在系統頂層介面佔據的表徵圖,若兩次”cd data”則可以進入應用程式使用的資料目錄,你的資料可以儲存在這裡,例如Notepad就是把其資料庫放在它的databases目錄下,名為note_pad.db.

2,第一次啟動模擬器會比較慢,但以後就別關閉模擬器了,修改代碼,調試都不需要再次啟動的,直接修改後run或debug就是。

作者:phinecos(洞庭散人)
出處:http://phinecos.cnblogs.com/
本文著作權歸作者和部落格園共有,歡迎轉載,但請保留此段聲明,並在文章頁面明顯位置給出原文串連

作者:洞庭散人

出處:http://phinecos.cnblogs.com/    

本部落格遵從Creative Commons Attribution 3.0 License,若用於非商業目的,您可以自由轉載,但請保留原作者資訊和文章連結URL。
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.