【android編程】第十二講-Service及其應用

第十二講Service及其應用


主要是ContentProvider和Service,這是Android中四組件中的兩個

ContentProvider

官方文檔:https://developer.android.com/reference/android/content/ContentProvider.html

參考鏈接:https://www.jianshu.com/p/f5ec75a9cfea

ContentResolver cr = getContentResolver():

每個Content Provider提供公共的URI (使用Uri類包裝)來唯一標識其數據集。管理多個數據集(多個表格)的Content Provider爲每個都提供了單獨的URI.所有爲provider提供的URI都以“content:/"作 爲前綴,content://"模式表示數據由Content Provider來管理。

Content Provider是Android應用程序的主要構建模塊之一,ContentProvider與Activity、Service、BroadcastReceiver齊名爲Android四大組件。可爲應用程序提供內容。 它們封裝數據並通過單個ContentResolver接口將其提供給應用程序。

僅當您需要在多個應用程序之間共享數據時,才需要內容提供程序。 例如,聯繫人數據由多個應用程序使用,並且必須存儲在ContentResolver中。 如果您不需要在多個應用程序之間共享數據,則可以直接通過SQLiteDatabase使用數據庫。 通過ContentResolver發出請求時,系統將檢查給定URI的權限,並將請求傳遞給在該權限下注冊的ContentResolver。 ContentResolver可以根據需要解釋其餘的URI。 UriMatcher類有助於解析URI。

通過ContentResolver發出請求時,系統將檢查給定URI的權限,並將請求傳遞給在該權限下注冊的ContentResolver。 ContentResolver可以根據需要解釋其餘的URI。 UriMatcher類有助於解析URI。

onCreate() which is called to initialize the provider
query(Uri, String[], Bundle, CancellationSignal) which returns data to the caller
insert(Uri, ContentValues) which inserts new data into the content provider
update(Uri, ContentValues, Bundle) which updates existing data in the content provider
delete(Uri, Bundle) which deletes data from the content provider
getType(Uri) which returns the MIME type of data in the content provider

結論:

ContentProvider一般爲存儲和獲取數據提供統一的接口,可以在不同的應用程序之間共享數據。

使用ContentProvider,主要有以下幾個理由:
1,ContentProvider提供了對底層數據存儲方式的抽象。比如下圖中,底層使用了SQLite數據庫,在用了ContentProvider封裝後,即使你把數據庫更換,也不會對上層數據使用層代碼產生影響。

2,Android框架中的一些類需要ContentProvider類型數據。如果你想讓你的數據可以使用在如SyncAdapter, Loader, CursorAdapter等類上,那麼你就需要爲你的數據做一層ContentProvider封裝。

3,第三個原因也是最主要的原因,是ContentProvider爲應用間的數據交互提供了一個安全的環境。它准許你把自己的應用數據根據需求開放給其他應用進行增、刪、改、查,而不用擔心直接開放數據庫權限而帶來的安全問題。

注意:

ContentProvider數據通過單個ContentResolver接口將其提供給應用程序

ContentResolver

Android爲我們提供了ContentResolver來統一管理與不同ContentProvider間的操作。

img

在Context.java的源碼中有一段

/** Return a ContentResolver instance for your application's package. */
 public abstract ContentResolver getContentResolver();

所以我們可以通過在所有繼承Context的類中通過調用getContentResolver()來獲得ContentResolver

通過ContentResolver發出請求時,系統將檢查給定URI的權限,並將請求傳遞給在該權限下注冊的ContentResolver。 ContentResolver可以根據需要解釋其餘的URI。 UriMatcher類有助於解析URI。

ContentProvider中的URI有固定格式,如下圖:

img

Authority:授權信息,用以區別不同的ContentProvider;
Path:表名,用以區分ContentProvider中不同的數據表;
Id:Id號,用以區別表中的不同數據;

URI組裝代碼示例:

public class TestContract {
    protected static final String CONTENT_AUTHORITY = "me.pengtao.contentprovidertest";
    protected static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

    protected static final String PATH_TEST = "test";
    public static final class TestEntry implements BaseColumns {

        public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_TEST).build();
        protected static Uri buildUri(long id) {
            return ContentUris.withAppendedId(CONTENT_URI, id);
        }
        protected static final String TABLE_NAME = "test";
        public static final String COLUMN_NAME = "name";
    }
}

從上面代碼我們可以看到,我們創建了一個
content://me.pengtao.contentprovidertest/test的uri,並且開了一個靜態方法,用以在有新數據產生時根據id生成新的uri。

一個栗子

首先我們創建一個自己的TestProvider繼承ContentProvider。默認該Provider需要實現如下六個方法,onCreate(), query(Uri, String[], String, String[], String),insert(Uri, ContentValues), update(Uri, ContentValues, String, String[]), delete(Uri, String, String[]), getType(Uri),方法介紹在上面。

實現insert和query方法爲例

private final static int TEST = 100;

static UriMatcher buildUriMatcher() {
    final UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    final String authority = TestContract.CONTENT_AUTHORITY;
    matcher.addURI(authority, TestContract.PATH_TEST, TEST);
    return matcher;
}

@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
    final SQLiteDatabase db = mOpenHelper.getReadableDatabase();

    Cursor cursor = null;
    switch ( buildUriMatcher().match(uri)) {
        case TEST:
            cursor = db.query(TestContract.TestEntry.TABLE_NAME, projection, selection, selectionArgs, sortOrder, null, null);
            break;
    }

    return cursor;
}

@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
    final SQLiteDatabase db = mOpenHelper.getWritableDatabase();
    Uri returnUri;
    long _id;
    switch ( buildUriMatcher().match(uri)) {
        case TEST:
            _id = db.insert(TestContract.TestEntry.TABLE_NAME, null, values);
            if ( _id > 0 )
                returnUri = TestContract.TestEntry.buildUri(_id);
            else
                throw new android.database.SQLException("Failed to insert row into " + uri);
            break;
        default:
            throw new android.database.SQLException("Unknown uri: " + uri);
    }
    return returnUri;
}

此例中我們可以看到,我們根據path的不同,來區別對不同的數據庫表進行操作,從而完成uri與具體數據庫間的映射關係。

因爲ContentProvider作爲四大組件之一,所以還需要在AndroidManifest.xml中註冊一下。

<provider    
    android:authorities="me.pengtao.contentprovidertest"  
    android:name=".provider.TestProvider" />

然後你就可以使用getContentResolver()方法來對該ContentProvider進行操作了,ContentResolver對應ContentProvider也有insert,query,delete等方法,詳情請參考:
http://developer.android.com/reference/android/content/ContentResolver.html

此處因爲我們只實現了ContentProvider的query和insert的方法,所以我們可以進行插入和查詢處理。如下我們可以在某個Activity中進行如下操作,先插入一個數據peng,然後再從從表中讀取第一行數據中的第二個字段的值。

ContentValues contentValues = new ContentValues();
contentValues.put(TestContract.TestEntry.COLUMN_NAME, "peng");
contentValues.put(TestContract.TestEntry._ID, System.currentTimeMillis());
getContentResolver().insert(TestContract.TestEntry.CONTENT_URI, contentValues);

Cursor cursor = getContentResolver().query(TestContract.TestEntry.CONTENT_URI, null, null, null, null);

try {
    Log.e("ContentProviderTest", "total data number = " + cursor.getCount());
    cursor.moveToFirst();
    Log.e("ContentProviderTest", "total data number = " + cursor.getString(1));
} finally {
    cursor.close();
}

數據共享

以上例子中創建的ContentProvider只能在本應用內訪問,那如何讓其他應用也可以訪問此應用中的數據呢,一種方法是向此應用設置一個android:sharedUserId,然後需要訪問此數據的應用也設置同一個sharedUserId,具有同樣的sharedUserId的應用間可以共享數據。

但此種方法不夠安全,也無法做到對不同數據進行不同讀寫權限的管理,下面我們就來詳細介紹下ContentProvider中的數據共享規則。

首先我們先介紹下,共享數據所涉及到的幾個重要標籤:
android:exported 設置此provider是否可以被其他應用使用。
android:readPermission 該provider的讀權限的標識
android:writePermission 該provider的寫權限標識
android:permission provider讀寫權限標識
android:grantUriPermissions 臨時權限標識,true時,意味着該provider下所有數據均可被臨時使用;false時,則反之,但可以通過設置<grant-uri-permission>標籤來指定哪些路徑可以被臨時使用。這麼說可能還是不容易理解,我們舉個例子,比如你開發了一個郵箱應用,其中含有附件需要第三方應用打開,但第三方應用又沒有向你申請該附件的讀權限,但如果你設置了此標籤,則可以在start第三方應用時,傳入FLAG_GRANT_READ_URI_PERMISSIONFLAG_GRANT_WRITE_URI_PERMISSION來讓第三方應用臨時具有讀寫該數據的權限。

知道了這些標籤用法後,讓我們改寫下AndroidManifest.xml,讓ContentProvider可以被其他應用查詢。

聲明一個permission

<permission android:name="me.pengtao.READ" android:protectionLevel="normal"/>

然後改變provider標籤爲

<provider
    android:authorities="me.pengtao.contentprovidertest"
    android:name=".provider.TestProvider"
    android:readPermission="me.pengtao.READ"
    android:exported="true">
</provider>

則在其他應用中可以使用以下權限來對TestProvider進行訪問。

<uses-permission android:name="me.pengtao.READ"/>

有人可能又想問,如果我的provider裏面包含了不同的數據表,我希望對不同的數據表有不同的權限操作,要如何做呢?Android爲這種場景提供了provider的子標籤<path-permission>,path-permission包括了以下幾個標籤。

<path-permission android:path="string"
                 android:pathPrefix="string"
                 android:pathPattern="string"
                 android:permission="string"
                 android:readPermission="string"
                 android:writePermission="string" />

可以對不同path設置不同的權限規則

ContentProvider示例

根據ContentProvider的概念完成一個讀取手機聯繫人的程序,要求讀出手機存儲的所有聯繫人的姓名和電話號,並使用listView顯示。

獲取權限

<uses-permission android:name="android.permission.READ_CONTACTS"/>
public class MainActivity extends AppCompatActivity {
    private  String[] COLUMNS = {
            ContactsContract.Contacts._ID,ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER
            };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //檢測程序是否開啓權限
        if(checkSelfPermission(Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED){
            requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},1);//沒有權限需要動態獲取
        }
        Cursor cursor =getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null); //查詢記錄
        assert cursor != null;
        int idIndex = cursor.getColumnIndex(COLUMNS[0]);//獲得ID所對應的素引值
        int displayNameIndex = cursor.getColumnIndex(COLUMNS[1]);//獲得NAME所對應的索引值
        List<String> items = new ArrayList<String>();
        for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {// 迭代全部記錄
            int id = cursor.getInt(idIndex);
            String displayName = cursor.getString(displayNameIndex);
            Cursor phone = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="+id,null,null); //查詢記錄
            while (phone.moveToNext()){
                String phoneNumber = phone.getString(phone.getColumnIndex(COLUMNS[2]));
                items.add("id:"+ id + "\t姓名:"+ displayName+"\t電話:"+phoneNumber);
            }
        }
        ListView list = (ListView) findViewById(R.id.list);//獲得列表控件
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.list_item, items);
        list.setAdapter(adapter);
    }
}

聯繫人數據庫重要的有以下幾個表:

https://developer.android.com/guide/topics/providers/content-provider-basics#java

   1. contacts
   2. raw_contacts
   3. data
   4. mimetypes

[ContentResolver.query()](https://developer.android.com/reference/android/content/ContentResolver#query(android.net.Uri, java.lang.String[], android.os.Bundle, android.os.CancellationSignal))方法

// Queries the user dictionary and returns results
cursor = getContentResolver().query(
    UserDictionary.Words.CONTENT_URI,   // The content URI of the words table
    projection,                        // The columns to return for each row
    selectionClause,                   // Selection criteria
    selectionArgs,                     // Selection criteria
    sortOrder);                        // The sort order for the returned rows

Query() compared to SQL query.

query() argument SELECT keyword/parameter Notes
Uri FROM *table_name* Uri maps to the table in the provider named table_name.
projection *col,col,col,...* projection is an array of columns that should be included for each row retrieved.
selection WHERE *col* = *value* selection specifies the criteria for selecting rows.
selectionArgs (No exact equivalent. Selection arguments replace ? placeholders in the selection clause.)
sortOrder ORDER BY *col,col,...* sortOrder specifies the order in which rows appear in the returned Cursor.

ContactsContract

ContactsContract.Contacts和ContactsContract.CommonDataKinds

子類ContactsContract.Contacts是一張表,代表了所有聯繫人的統計信息。帶有聯繫人ID(—ID),查詢鍵(LOOKUP_KEY),聯繫人的姓名(DISPLAY_NAME_PRIMARY),頭像的id(PHOTO_ID)以及類別的id等等。

我們可以通過以下的方法取得所有聯繫人的表的Cursor對象:
1)ContentResolver contentResolver=getContentResolver();//獲取 ContentResolver對象查詢在ContentProvider裏定義的共享對象;

2)Cursor cursor=contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
//根據URI對象ContactsContract.Contacts.CONTENT_URI查詢所有聯繫人;

從Cursor對象裏我們關鍵是要取得聯繫人的_id。通過它,再通過ContactsContract.CommonDataKinds的各個子類查詢該_id聯繫人的電話(ContactsContract.CommonDataKinds.Phone),email(ContactsContract.CommonDataKinds.Email)等等。
以取得該聯繫人所有電話爲例:
1)int idFieldIndex=cursor.getColumnIndex(ContactsContract.Contacts._ID);
int id=cursor.getInt(idFieldIndex);//根據列名取得該聯繫人的id;
2)Cursor phonecursor=contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"=?", new String[]{Integer.toString(id)}, null);
//再類ContactsContract.CommonDataKinds.Phone中根據查詢相應id聯繫人的所有電話;
類似地可以ContactsContract.CommonDataKinds的不同的子類查詢不同的內容。android文檔告訴我們推薦使用ContactsContract.Contacts.LOOKUP_KEY代替ContactsContract.Contacts._ID。

Service

Started (啓動) :當應用程序組件(例如Activity) 通過調用startService()方法啓動服務時,服務處於“started"狀態。一 旦啓動, 服務能在後臺無限期運行,即使啓動它的組件已經被銷燬.I通常,啓動服務執行單個操作並且不會向調用者返回結果。例如,它可能通過網絡下載或者上傳文件。如果操作完成,服務需要停止自身。

Bound (綁定) :當應用程序組件通過調用bindService()方法綁定到服務時,服務處於“bound"狀態。綁定服務提供客戶端-服務器接口以允許組件與服務交互、發送請求、獲得結果、甚至使用進程間通信(IPC)跨進程完成這些操作。僅當其他應用程序組件與之綁定時,綁定服務才運行。多個組件可以一次綁定到一個服務上,但是當它們都解綁定時,服務被銷燬。

啓動且綁定:服務既可以是啓動服務,也允許綁定。此時需要同時實現以下回調方法:**onStartCommand()**和 onBind()。系統不會在所有客戶端都取消綁定時銷燬服務。爲此,必須通過調用 stopSelf()stopService() 顯式停止服務

無論應用是處於啓動狀態還是綁定狀態,或者處於啓動且綁定狀態,任何應用組件均可像使用 Activity 那樣通過調用 Intent 來使用服務(即使此服務來自另一應用),也可以通過清單文件將服務聲明爲私有服務,阻止其他應用訪問

要使用服務,必須繼承Service類(或者Service類的現有子類),在子類中重寫某些回調方法,以處理服務生命週期的某些關鍵方面並提供一種機制將組件綁定到服務

Service類中重要方法

爲了創建服務,開發人員需要創建Service類(或其子類)的子類:在實現類中,需要重寫一些處理服務生命週期重要方面的回調方法並根據需要提供組件綁定到服務的機制。需要重寫的重要回調方法如下:

onStartCommand()
當組件通過調用 startService() 請求啓動服務時,系統將調用此方法(如果是綁定服務則不會調用此方法)。一旦執行此方法,服務即會啓動並可在後臺無限期運行。 在指定任務完成後,通過調用 stopSelf()stopService() 來停止服務

onBind()
當一個組件想通過調用 bindService() 與服務綁定時,系統將調用此方法(如果是啓動服務則不會調用此方法)。在此方法的實現中,必須通過返回 IBinder 提供一個接口,供客戶端用來與服務進行通信

onCreate()
首次創建服務時,系統將調用此方法來執行初始化操作(在調用 onStartCommand()onBind() 之前)。如果在啓動或綁定之前Service已在運行,則不會調用此方法

onDestroy()
當服務不再使用且將被銷燬時,系統將調用此方法,這是服務接收的最後一個調用,在此方法中應清理佔用的資源

僅當內存過低必須回收系統資源以供前臺 Activity 使用時,系統纔會強制停止服務。如果將服務綁定到前臺 Activity,則它不太可能會終止,如果將服務聲明爲在前臺運行,則它幾乎永遠不會終止。或者,如果服務已啓動並要長時間運行,則系統會隨着時間的推移降低服務在後臺任務列表中的位置,而服務也將隨之變得非常容易被終止。如果服務是啓動服務,則必須將其設計爲能夠妥善處理系統對它的重啓。 如果系統終止服務,那麼一旦資源變得再次可用,系統便會重啓服務(這還取決於 onStartCommand() 的返回值)

Service的聲明

類似Activity和其他組件,開發人員必須在應用程序配置文件中聲明全
部的Service.爲了聲明Service,需要向<application>標籤中增加
<service>子標籤,<service>子標籤的語法如下。

<service android:description="string resource"
         android:directBootAware=["true" | "false"]
         android:enabled=["true" | "false"]
         android:exported=["true" | "false"]
         android:foregroundServiceType=["connectedDevice" | "dataSync" |
                                        "location" | "mediaPlayback" | "mediaProjection" |
                                        "phoneCall"]
         android:icon="drawable resource"
         android:isolatedProcess=["true" | "false"]
         android:label="string resource"
         android:name="string"
         android:permission="string"
         android:process="string" >
    . . .
</service>

android:name 屬性是唯一必需的屬性,用於指定服務的類名,還可將其他屬性包括在 < service > 元素中以定義一些特性

爲了確保應用的安全性,最好始終使用顯式 Intent 啓動或綁定 Service,且不要爲服務聲明 Intent 過濾器。 啓動哪個服務存在一定的不確定性,而如果對這種不確定性的考量非常有必要,則可爲服務提供 Intent 過濾器並從 Intent 中排除相應的組件名稱,但隨後必須使用 setPackage() 方法設置 Intent 的軟件包,這樣可以充分消除目標服務的不確定性

此外,還可以通過添加 android:exported 屬性並將其設置爲 “false”,確保服務僅適用於本應用。這可以有效阻止其他應用啓動本應用內的服務,即便在使用顯式 Intent 時也是如此

屬性 說明
description 對服務進行描述,屬性值應爲對字符串資源的引用,以便進行本地化
directBootAware 設置是否可以在用戶解鎖設備之前運行,默認值爲“false”
enabled 設置是否可以由系統來實例化服務。< application >元素有自己的enabled屬性,適用於包括服務在內的所有應用程序組件。要啓用服務,< application >和< service >屬性必須都爲“true”(默認情況下都爲true)。如果其中一個是“false”,則服務被禁用
exported 設置其他應用程序的組件是否可以調用本服務或與其交互,如果可以,則爲“true”。當值爲“false”時,只有同一個應用程序或具有相同用戶ID的應用程序的組件可以啓動該服務或綁定到該服務。該屬性的默認值取決於服務是否包含Intent filters。沒有任何過濾器意味着它只能通過指定其確切的類名來調用,這意味着該服務僅用於應用程序內部使用(因爲其他人不知道類名)。所以在這種情況下,默認值爲“false”。 另一方面,如果存在至少一個過濾器,意味着該服務打算供外部使用,因此默認值爲“true”
icon 服務的圖標,屬性值應是對drawable資源的引用。如果未設置,則將使用應用程序圖標
isolatedProcess 設置該服務是否作爲一個單獨的進程運行,如果設置爲true,此服務將在與系統其餘部分隔離的特殊進程下運行,並且沒有自己的權限,與它唯一的通信是通過服務API(綁定和啓動)
label 可以向用戶顯示的服務的名稱,屬性值應是對字符串資源的引用
name 服務類的完全限定名
permission 設定組件必須具有的權限,得以啓動服務或綁定服務。如果startService(),bindService()或stopService()的調用者沒有被授予此權限,則該方法將不會工作,並且Intent對象不會傳遞到服務中
process 用來運行服務的進程的名稱。通常,應用程序的所有組件都運行在應用程序創建的默認進程中,它與應用程序包名具有相同的名稱。 < application >元素的process屬性可以爲所有組件設置不同的默認值,但組件可以使用自己的進程屬性覆蓋默認值,從而允許跨多個進程擴展應用程序

啓動Service

啓動服務由組件通過調用 startService() 啓動,服務啓動之後,其生命週期即獨立於啓動它的組件,並且可以在後臺無限期地運行,即使啓動服務的組件已被銷燬也不受影響。因此,服務應通過調用 stopSelf() 來自行停止運行,或者由另一個組件調用 stopService() 來停止

可以通過擴展兩個類來創建啓動服務:

  • Service
    這是所有服務的父類。擴展此類時,如果要執行耗時操作,必須創建一個用於執行操作的新線程,因爲默認情況下服務將運行於UI線程
  • IntentService
    這是 Service 的子類,它使用工作線程逐一處理所有啓動請求。如果應用不需要同時處理多個請求,這是最好的選擇。IntentService只需實現構造函數onHandleIntent() 方法即可,onHandleIntent()方法會接收每個啓動請求的 Intent

參考鏈接:https://www.jianshu.com/p/95ec2a23f300

https://www.runoob.com/android/android-services.html

Service示例

在manifest中application加入

<service android:name=".MyService"

MyService.java

public class MyService extends Service {
    @Nullable
    private final String TAG = "MyService";
    /** 標識服務如果被殺死之後的行爲 */
    int mStartMode;

    /** 當服務被創建時調用. */
    public void onCreate() {
        Log.e(TAG, "Service is Created!");
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "Service is running!");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "Service is Destoryed!");
        super.onDestroy();
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind");
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

Activity

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void startservice(View view) {
        Intent intent = new Intent(this, MyService.class);
//        Bundle bundle = new Bundle();
//        intent.putExtras(bundle);
        startService(intent);
    }

    public void stopservice(View view) {
        Intent intent = new Intent(this, MyService.class);
//        Bundle bundle = new Bundle();
//        intent.putExtras(bundle);
        stopService(intent);
    }
}

題目記錄

  1. ConentProvider
  • A.是Android不同應用程序間共享數據的方式之一0.00/1.00
  • B.創建了程序共同訪問的公共存儲區
  • C.是Android中不同應用程序間共享數據的唯一方式
  • D.是Android中不同應用程序間傳遞私有數據的唯一方式
  1. ContentProvider
  • A.保存和獲取數據並使其對所有的應用程序可見
  • B.保存和獲取數據並使其對特定的應用程序可見
  • C.保存和獲取數據並創建一個公共存儲區,以便其它程序訪問
  • D.保存和獲取數據並創建一個私有存儲區供某些程序訪問0.00/1.00
  1. ContentProvider
  • A.是個單例
  • B.要爲每個使用它的程序創建一個對象
  • C.被多個程序使用時要創建公共存儲區0.00
  • D.要創建多個公共存儲供多個程序使用
  1. ContentProvider內部如何保存數據
  • A.由訪問的程序確定
  • B.由設計者決定
  • C.Android的規範中已經指定0.00
  • D.由公共存儲區的性質確定0.00
  1. 用戶存取ContentProvider的數據是通過
  • A.通過ContentResolver對象1.00/1.00
  • B.直接訪問數據
  • C.直接訪問公共存儲區
  • D.直接訪問ContentPovider對象
  1. ContentProvider的數據模型是
  • A.是一個根據數據的特點自定義的數據結構
  • B.是一個根據應用自定義的數據結構
  • C.是一個樹形數據結構
  • D.基於數據庫模型的簡單表格1.00/1.00
  1. 對ContentProveder的查詢
  • A.返回一個Cursor對象1.00/1.00
  • B.返回一個ContentValues對象
  • C.直接返回查詢的值
  • D.返回一個自定的集合對象
  1. URI的含義是:
  • A.統一資源標識
  • B.統一資源定位
  • C.統一資源Intent
  • D.統一資源接口
  1. ContentProvider的URI的前綴是
  • A.無前綴
  • B.com.
  • C.https://
  • D.content://1.00/1.00
  1. 使用ContentProvider的時候
  • A.直接訪問,並返回Cursor
  • B.必須先連接SQLite數據庫
  • C.需要在AndroidManifest.xml中進行配置1.00/1.00
  • D.先創建公共數據存儲區
  1. Service的特點是
  • A.運行於某個Activity之中
  • B.不提供用戶界面
  • C.不能使用其它組件
  • D.可能隨時被回收
  1. Service的特點之一是:
  • A.能長時間在後臺運行1.00/1.00
  • B.有可以關閉用戶界面
  • C.一定依賴於一個Activity才能運行
  • D.不能和其它程序交換數據
  1. Service啓動時
  • A.可以通過Intent啓動1.00/1.00
  • B.可以設定是否被系統回收
  • C.可是設置是否打開界面
  • D.必須綁定到某個Acitvity上才能啓動
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章