【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上才能启动
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章