• <noscript id="e0iig"><kbd id="e0iig"></kbd></noscript>
  • <td id="e0iig"></td>
  • <option id="e0iig"></option>
  • <noscript id="e0iig"><source id="e0iig"></source></noscript>
  • (譯)從Content Provider中獲取數據

    標簽: android

    原文:https://developer.android.google.cn/guide/topics/providers/content-provider-basics.html

    Content Provider基礎

    Content Provider管理對中央數據倉庫的訪問,它是Android應用的一部分,通常會提供自己的UI來使用數據。然而,Content Provider主要是為了給其他應用使用而設計的,這些應用可以通過Provider客戶端程序訪問內容提供者。Provider和Provider客戶端共同提供了一個穩定的、標準的訪問數據的接口,并且也很好地解決了進程間通信和安全訪問數據的問題。
    一般來說,你會在這兩種場合下使用Content Provider:想要訪問其他程序中一個已有的Content Provider,或是想要在應用中創建一個新的Content Provider來和其他應用分享數據。
    本文主要講述了以下幾點:

    • Content Provider是如何工作的。
    • 從Content Provider中獲取數據的API。
    • 插入、更新、刪除Content Provider中的數據的API。
    • 其他能夠讓你更加方便地使用Content Provider的API。

    概述

    Content Provider利用若干張表來向外部應用提供數據,這些表的形式類似于關系型數據庫中的表。表中的每行代表Provider收集的某類數據的一個實例,每列代表數據的一個部分。
    Content Provider通過眾多的API和構建管理到應用的數據存儲層的訪問,包括:

    • 與其他應用分享你的應用中的數據。
    • 向控件發送數據。
    • 利用搜索框架,通過SearchRecentSuggestionsProvider向你的應用返回定制的搜索建議。
    • 通過實現AbstractThreadedSyncAdapter同步你的服務器和應用的數據。
    • 通過CursorLoader為你的UI加載數據。

    Content Provider與其他構件的關系

    訪問一個Content Provider

    當你想要訪問Content Provider中的數據時,你需要使用你的應用的Context中的ContentResolver對象,它可以作為一個客戶端來與Provider進行交流。和ContentResolver對象進行交流的是ContentProvider的實現類的一個實例。這個Provider實例從客戶端接收數據請求,執行被請求的行為,并最終返回結果。ContentResolver類提供了對永久存儲的基礎的增刪改查的方法。
    使用CursorLoader執行異步查詢是從UI訪問 ContentProvider對象的一個常用模式。UI中的Activity或者Fragment通過CursorLoader執行查詢操作,并最終利用ContentResolver獲得ContentProvider對象。這讓你的UI在查詢過程中也能和用戶交互。這個模式涉及到一系列的對象,如下圖所示:
    各個對象以及存儲空間之間的交互
    注意:想要訪問Provider,你的應用通常需要在manifest文件中聲明相應的權限。
    用戶詞典是Android平臺的一個內嵌的Provider,它存儲了用戶想要保存的一些非常規的單詞。
    表1:

    word app id frequency locale _ID
    mapreduce user1 100 en_US 1
    precompiler user14 200 fr_FR 2
    applet user2 225 fr_CA 3
    const user1 255 pt_BR 4
    int user5 100 en_UK 5

    表1中,每行代表一個單詞,這個單詞可能不能在標準字典中找到。每列代表這個單詞的某些方面的信息,比如首次遇到的地點。每列的頭是它們在Content Provider中的列名。這個Provider中,_ID列充當了主鍵的角色,它是Provider自動保持的。
    為了從用戶詞典中獲取單詞和它們的地點,你需要調用ContentResolver.query()。這個方法會調用用戶詞典Provider定義的ContentProvider.query()方法。下面是使用ContentResolver.query()的一個示例:

    // 查詢用戶詞典并返回結果
    mCursor = getContentResolver().query(
        UserDictionary.Words.CONTENT_URI,   // 單詞表的內容URI
        mProjection,                        // 需要返回的列
        mSelectionClause                    // 選取準則
        mSelectionArgs,                     // 選取準則
        mSortOrder);                        // 行間的排序方法

    下面介紹 query(Uri,projection,selection,selectionArgs,sortOrder) 方法的參數是如何和SQL SELECT語句匹配的:
    這里寫圖片描述

    內容URI

    內容URI是一個標識Provider中的數據的URI,它包括整個Provider的符號名(授權機構)、指向的表的名稱(路徑)。當你調用客戶端方法訪問Provider中的一張表時,內容URI是一個必要的參數。
    在前面的代碼中,CONTENT_URI常量包含了用戶詞典的內容URI。ContentResolver對象會分析出URI的授權機構,并用它來解析出對應的Provider。之后,ContentResolver就能將查詢分發到正確的Provider處。
    ContentProvider通過內容URI的路徑部分選擇需要訪問的表。Provider一般會為每張表準備一個路徑。
    在前面的代碼中,用戶詞典完整的URI為:

    content://user_dictionary/words

    user_dictionary代表授權機構,words代表路徑。content://代表協議,用于標明這是一個內容URI。
    許多的Provider允許你通過在URI的最后追加一個ID值來訪問某個單獨的行。比如,如果你想要訪問_ID==4的行,可以這么做:

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

    注意:Uri與Uri.Builder類包含了一些用于構建形式良好的Uri對象的方法。 ContentUris類包含了為URI追加ID的方法,比如上面的withAppendedId()。

    從Provider中獲取數據

    這部分描述了如何從Provider中獲取數據,同樣使用用戶詞典的例子。
    注意:為了保持簡潔,這部分的代碼片段都在UI線程中調用了 ContentResolver.query()方法。在實際的程序中,你應當在一個單獨的線程中執行異步查詢。一種實現的方法是使用CursorLoader類。并且,這里演示用的代碼都只是一些片段,不是一個完整的應用。
    想要從Provider中獲取數據,需要以下兩個步驟:

    1. 獲取Provider的讀取訪問權限;
    2. 構建在Provider中進行查詢的代碼。

    獲取讀取權限

    為了從Provider中獲取數據,你的應用需要該Provider的讀取訪問權限。這個權限不能在運行時獲取,只能在Manifest文件中通過<user-permission>元素確定。當用戶安裝你的應用時,就表示它們同意這個權限要求。
    為了找到你需要使用的Provider所需的權限的名稱,你需要去查看這個Provider的文檔。例如,用戶詞典Provider需要的權限名為android.permission.READ_USER_DICTIONARY,如果你想要讀取用戶詞典的內容,就必須在Manifest文件中聲明該權限。

    構建查詢代碼

    獲取數據的下一步是構建查詢代碼。下面的代碼片段定義了一些用于訪問用戶詞典的變量:

    // 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()。Provider的客戶端查詢代碼和SQL語句非常接近,它包含了需要被返回的列的集合、選取準則和排序方法。
    需要被返回的列的集合也稱為投影(projection),對應mProjection變量。
    SELECT從句和SELECT參數定義了需要被返回的行的特征。SELECT從句是一系列邏輯和布爾表達式、列名稱和值(mSelectionClause)的組合。如果你指定了占位符‘?’而不是具體值,查詢方法會從SELECT參數數組(mSelectionArgs)中獲得值。
    下面的片段中,如果用戶沒有輸入一個單詞,SELECT從句就會被設為null,并且此次查詢會返回用戶詞典中的所有單詞。如果用戶輸入了一個單詞,SELECT從句就會被設為UserDictionary.Words.WORD + ” = ?” ,并且SELECT參數數組的第一個元素會被設為用戶輸入的單詞。

    /*
     * 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 = UserDictionary.Words.WORD + " = ?";
    
        // 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語句:

    SELECT _ID, word, locale
    FROM words
    WHERE word = <userinput> 
    ORDER BY word ASC;

    防止惡意輸入

    如果該Provider使用一個SQL數據庫來管理數據,那么將不受信任的數據包含在原始SQL語句中會導致SQL注入。
    考慮這樣的SELECT從句:

    // Constructs a selection clause by concatenating the user's input to the column name
    String mSelectionClause =  "var = " + mUserInput;

    如果這么做,用戶將擁有將惡意的SQL語句和你的SQL語句串聯起來的能力。比如,用戶可以輸入“nothing; DROP TABLE ;”作為mUserInput的值,這會導致最終的SQL語句變為“var = nothing; DROP TABLE ;”。由于SELECT從句會被作為SQL語句對待,這可能會導致Provider刪除數據庫中的所有表(除非Provider設置了防止SQL注入的方法)。
    為了避免這個問題,需要在SELECT子句中使用“?”占位符,并提供一個單獨的SELECT參數列表。這樣做的話,用戶的輸入會和查詢綁定起來,而不會被視為SQL語句的一部分。由于不會被當成SQL語句,自然也就避免了SQL注入問題。上面的例子可以用這樣的SELECT子句代替:

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

    SELECT參數數組:

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

    可以這樣來向SELECT參數數組中添加值:

    // Sets the selection argument to the user's input
    selectionArgs[0] = mUserInput;

    使用SELECT子句搭配SELECT參數的方法要比直接指定SELECT語句
    好,即使Provider使用的不是SQL數據庫。

    展示查詢結果

    客戶端方法ContentResolver.query()會返回一個Cursor,它包含投影指定的列以及滿足選取準則的行。Cursor提供了對各行各列的隨機訪問。使用Cursor提供的方法,你可以遍歷結果的所有行,確定每列的數據類型,從各列中獲得數據,并且檢查結果的其他屬性。一些Cursor實現能夠在Provider的數據改變時自動更新,或者在Cursor發生變化時觸發某些觀察者中的方法。
    注意:一些Provider可能會限制對它的某些列的訪問,基于進行查詢的對象。
    如果沒有任何行能滿足選取準則的要求,那么Provider會返回一個空Cursor對象,調用它的getCount()方法會返回0。
    如果出現內部錯誤,那么查詢的結果會根據具體的Provider而定。可能會返回null或者拋出異常。
    由于Cursor通常包含許多行,可以利用ListView來展示它的內部數據,通過SimpleCursorAdapter。
    接著上面的代碼片段,下面的片段中創建了一個包含查詢結果的 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向ListView提供詩句,查詢中必須包含_ID列,ListView沒有把它展示出來。這也是許多Provider提供了_ID列的原因。

    從查詢結果中獲取數據

    除了簡單地將查詢結果展示出來之外,你也可以在其他任務中使用它們。比如,你可以從用戶詞典中獲取單詞拼寫,并在其他Provider中進行查詢。為了這么做,你需要遍歷Cursor的所有行:

    // 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.
    }

    Cursor對象實現了一系列的get方法,用于從該對象中獲取不同類型的數據。上面的代碼中使用的是getString(),你也可以用getType()來獲取某列的數據類型。

    Content Provider權限

    Provider所屬的應用可以指定一系列的權限,其他應用在獲取數據之前必須要獲取這些權限。這些權限保證用戶能夠知道某個應用正在獲取什么類型的數據。用戶會在安裝應用時了解到應用需要哪些權限。
    如果Provider沒有聲明任何權限,那么除了它的內部構件之外,任何應用都無法獲得它的內部數據。
    比如,想要從用戶詞典中獲取數據的話,需要 android.permission.READ_USER_DICTIONARY權限,而想要進行增刪改操作的話還需要 android.permission.WRITE_USER_DICTIONARY權限。
    權限需要在Manifest文件中聲明:

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

    這些權限會在安裝應用時向用戶申請。除非用戶全部同意,否則安裝不會繼續進行。

    版權聲明:本文為swt369原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
    本文鏈接:https://blog.csdn.net/swt369/article/details/78140004

    智能推薦

    安卓學習筆記之Content provider

    安卓四大組件之 content provider 用途 跨程序共享數據(為其他應用程序提供訪問數據的接口) 利用Content Provider對數據進行封裝,有利于脫離對數據庫的依賴性(解耦)。改變底層數據庫,而上層數據查詢不用改變。 什么是Content Provider? 使用安卓存儲有五種方式,分別是 文件 網絡存儲 數據庫 content provider sharedpreferenc...

    Beego ———— 從瀏覽器中獲取string數據

    創建一個controller文件并在路由中注冊 然后重啟就可以獲取到string了      ...

    如何從cookie中獲取到接口中的數據

    幾個注意問題: 如何找到自己想要的cookie的name呢? 打開瀏覽器的調試工具(以Chrome為例):里面的Application里面就能找你網頁所用到的cookie列表  如何配合ajax請求使用呢? 這里你不用管cookie里的值到底是什么,也不用編譯。直接原封不動的轉發,根據你后臺的數據結構調用就可以了。這就是cookie,但它所能傳輸的數據大小是有限的(不能超過4k),而且因...

    HTML中常用操作關于:頁面跳轉,空格

    1.頁面跳轉 2.空格的代替符...

    freemarker + ItextRender 根據模板生成PDF文件

    1. 制作模板 2. 獲取模板,并將所獲取的數據加載生成html文件 2. 生成PDF文件 其中由兩個地方需要注意,都是關于獲取文件路徑的問題,由于項目部署的時候是打包成jar包形式,所以在開發過程中時直接安照傳統的獲取方法沒有一點文件,但是當打包后部署,總是出錯。于是參考網上文章,先將文件讀出來到項目的臨時目錄下,然后再按正常方式加載該臨時文件; 還有一個問題至今沒有解決,就是關于生成PDF文件...

    猜你喜歡

    電腦空間不夠了?教你一個小秒招快速清理 Docker 占用的磁盤空間!

    Docker 很占用空間,每當我們運行容器、拉取鏡像、部署應用、構建自己的鏡像時,我們的磁盤空間會被大量占用。 如果你也被這個問題所困擾,咱們就一起看一下 Docker 是如何使用磁盤空間的,以及如何回收。 docker 占用的空間可以通過下面的命令查看: TYPE 列出了docker 使用磁盤的 4 種類型: Images:所有鏡像占用的空間,包括拉取下來的鏡像,和本地構建的。 Con...

    requests實現全自動PPT模板

    http://www.1ppt.com/moban/ 可以免費的下載PPT模板,當然如果要人工一個個下,還是挺麻煩的,我們可以利用requests輕松下載 訪問這個主頁,我們可以看到下面的樣式 點每一個PPT模板的圖片,我們可以進入到詳細的信息頁面,翻到下面,我們可以看到對應的下載地址 點擊這個下載的按鈕,我們便可以下載對應的PPT壓縮包 那我們就開始做吧 首先,查看網頁的源代碼,我們可以看到每一...

    Linux C系統編程-線程互斥鎖(四)

    互斥鎖 互斥鎖也是屬于線程之間處理同步互斥方式,有上鎖/解鎖兩種狀態。 互斥鎖函數接口 1)初始化互斥鎖 pthread_mutex_init() man 3 pthread_mutex_init (找不到的情況下首先 sudo apt-get install glibc-doc sudo apt-get install manpages-posix-dev) 動態初始化 int pthread_...

    統計學習方法 - 樸素貝葉斯

    引入問題:一機器在良好狀態生產合格產品幾率是 90%,在故障狀態生產合格產品幾率是 30%,機器良好的概率是 75%。若一日第一件產品是合格品,那么此日機器良好的概率是多少。 貝葉斯模型 生成模型與判別模型 判別模型,即要判斷這個東西到底是哪一類,也就是要求y,那就用給定的x去預測。 生成模型,是要生成一個模型,那就是誰根據什么生成了模型,誰就是類別y,根據的內容就是x 以上述例子,判斷一個生產出...

    精品国产乱码久久久久久蜜桃不卡