一、ContentProvider简介
我们说Android应用程序的四个核心组件是:ActivityServiceBroadcastReceiver和ContentProvider。在Android中,应用程序彼此之间相互独立的,它们都运行在自己独立的虚拟机中。ContentProvider提供了程序之间共享数据的方法,一个程序可以使用ContentProvider定义一个URI,提供统一的操作接口,其他程序可以通过此URI访问指定的数据,进行数据的增、删、改、查。

ContentProvider作为应用程序之间唯一的共享数据的途径,ContentProvider主要的功能就是存储并检索数据以及向其他应用程序提供访问数据的接口。

Android系统为一些常见的数据类型(如音乐、视频、图像、手机通信录联系人信息等)内置了一系列的ContentProvider,这些都位于android.provider包下。持有特定的许可,可以在自己开发的应用程序中访问这些ContentProvider

共享的数据类型包括audiovideoimagespersonalcontactinformation等等详见android.provider

如果想让自己的数据共享,有两种方法:

1、创造自己的contentprovider

2、将数据添加到已存在的provider中(要有适当的权限)



让自己的数据和其他应用程序共享有两种方式:创建自己的ContentProvier(即继承自ContentProvider的子类)或者是将自己的数据添加到已有的ContentProvider中去,后者需要保证现有的ContentProvider和自己的数据类型相同且具有该ContentProvider的写入权限。对于ContentProvider,最重要的就是数据模型(datamodel)URI

1.数据模型
ContentProvider将其存储的数据以数据表的形式提供给访问者,在数据表中每一行为一条记录,每一列为具有特定类型和意义的数据。每一条数据记录都包括一个"_ID"数值字段,改字段唯一标识一条数据。

2.URI
URI,每一个ContentProvider都对外提供一个能够唯一标识自己数据集(dataset)的公开URI,如果一个ContentProvider管理多个数据集,其将会为每个数据集分配一个独立的URI。所有的ContentProviderURI都以"content://"开头,其中"content:"是用来标识数据是由ContentProvider管理的schema

在几乎所有的ContentProvider的操作中都会用到URI,因此一般来讲,如果是自己开发的ContentProvider,最好将URI定义为常量,这样在简化开发的同时也提高了代码的可维护性。

首先来介绍如何访问ContentProvider中的数据,访问ContentProvider中的数据主要通过ContentResolver对象,ContentResolver类提供了成员方法可以用来对ContentProvider中的数据进行查询、插入、修改和删除等操作。以查询为例,查询一个ContentProvider需要掌握如下的信息。

唯一标识ContentProviderURI
需要访问的数据字段名称。
该数据字段的数据类型

提示:如果需要访问特定的某条数据记录,只需该记录的ID即可。

查询ContentProvider的方法有两个:ContentResolverquery()Activity对象的managedQuery(),二者接收的参数均相同,返回的都是Cursor对象,唯一不同的是使用managedQuery方法可以让Activity来管理Cursor的生命周期。

被管理的Cursor会在Activity进入暂停状态的时候调用自己的deactivate方法自行卸载,而在Activity回到运行状态时会调用自己的requery方法重新查询生成的Cursor对象。如果一个未被管理的Cursor对象想被Activity管理,可以调用ActivitystartManagingCursor方法来实现。

下面讲解一下

如何查询一个contentprovider,

如何修改provider里边的数据,

如何创造自己的contentprovider.

Android系统会实例化所有的contentprovider,可以通过

ContentResolvercr=getContentResolver();直接获取对象实例

_ID

NUMBER

NUMBER_KEY

LABEL

NAME

TYPE

13

(425)5556677

4255556677

Kirklandoffice

BullyPulpit

TYPE_WORK

44

(212)555-1234

2125551234

NYapartment

AlanVain

TYPE_HOME

45

(212)555-6657

2125556657

Downtownoffice

AlanVain

TYPE_MOBILE

53

201.555.4433

2015554433

LoveNest

RexCars

TYPE_HOME



一个查询返回一个Cursor对象

URIs

每一个contentsprovider都回暴露一个公共的URI用来标识数据集

content://开头

如:

android.provider.Contacts.Phones.CONTENT_URI
android.provider.Contacts.Photos.CONTENT_URI

每一个ContentResolver方法都会以URI为第一个参数来指明使用哪一个数据集

1、查询一个ContentProvider

需要知道3样东西:

1URI

2)想查询的字段名称

3)数据类型

如果想查询特定的记录,还需要该记录的ID

可使用的方法:

ContentResolver.query()

Activity.managedQuery()

两者都返回Cursor对象

参数列表:CONTENT_URI,也可以加上ID

例如:content://.../23

有一些静态方法可以直接加进ID后返回一个URI

ContentUris,withAppendedId()

Uri.withAppendedPath()

例如,如果想查询一个Id23的联系人,可以这样构建查询


//UsetheContentUtilsmethodtoproducethebaseURIforthecontactwiht_ID==23
UrimyPerson=ContentUris.withAppendedId(People.CONTENT_URI,23);

//Alternativaly,uestheUrimethodtoproducethebadeURI
//Ittakesastringrathatthananinteger
UrimyPerson2=Uri.withAppendedPath(People.CONTENT_URI,"23");

//Thenqueryforthisspecificrecord
Cursorcur=managedQuery(myPerson,null,null,null,null);

其余参数含义:

1.URI

2.想要返回的字段名称,null就返回所有的字段

3.一个条件选择器或说拦截器,相当于SQL中的where

4.一个排序的条件,相当于SQL中的ORDERBY

下面是一个查询通讯录列表返回名字和相应号码的例子

//Formanarrayspecifyingwhichcolumnstoreturn
String[]projection=newString[]{
People._ID,
People._COUNT,
People.NAME,
People.NUMBER
};
//GetthebaseURIforthePeopletableintheContactscontentprovider
Uricontacts=People.CONTENT_URI;

//Makethequery
CursormanagedCursor=managedQuery(contacts,
projection,//Whichcolumnstoreturn
null,//Whichrowstoreturn(allrows)
null,//Selectionarguments(none)
People.NAME+"ASC"//Puttheresultsinascendingorderbyname
);

该查询返回的是一个Cursor对象,内容如下:

_ID

_COUNT

NAME

NUMBER

44

3

AlanVain

2125551234

13

3

BullyPulpit

4255556677

53

3

RexCars

2015554433



这个Cursor只能用作读取数据,如果想增加,修改或者删除的话

必须用ContentResolver对象

利用Cursor读取数据时必须知道字段的类型,通过

getString()

getInt()

getFloat()

如果直接用getString()读,Cursor将会把全部数据转为String类型返回

另:也可以通过index下标访问字段

下面例子读取名字和电话号码

privatevoidgetColumnData(Cursorcur){
//默认情况下游标位置不指向任何记录,所以通过判断移向第一个记录是否为空可以知道该Cursor是否为空
if(cur.moveToFirst()){
Stringname;
StringphoneNumber;
intnameColumn=cur.getColumnIndex(People.NAME);
intphoneColumn=cur.getColumnIndex(People.NUMBER);

StringimagePath;

do{
//Getthefieldvalues
name=cur.getString(nameColumn);
phoneNumber=cur.getString(phoneColumn);

//dosomethingwiththevalues

}while(cur.moveToNext());
}

如果一个查询要返回一个binarydata(二进制文件)如图片或声音文件,那文件必须是可以直接插进表或者表中只保持可以访问到该文件的路径(content:URI

一般来说,小型数据(20k50k)可以直接存入表中,可以通过Cursor.getBlod()获得,返回一个byte数组

如果是保存文件路径如content:URI就不可以直接访问或打开该路径,(因为有权限访问控制),不过,可以通过ContentResolver,openInputStream()去获得一个InputStream,然后就可以读取数据;

修改数据

增加新记录

增加新数据到原有记录

更新原有记录

删除记录

所有的modification必须通过使用ContentResolver方法有一些contentProvider对于写操作需要一些permission

如果没有申请该权限,就会失败

Addingrecords

1)把要增加的值保存在一个ContentValues

2)通过ContentResolver.insert()进行增加参数为providerURI以及要新增的ContentValueMap

返回一个URI,可以通过该URI获取一个Cursor的对象进一步做修改

ContentValuesvalues=newContentValues();
//Addbellsongtocontactsandmakehimafavourite
values.put(People.NAME,"bellsong");
//1=thenewcontactisaddedtofavourite
//0=thenewcontactisnotaddedtofavourite
values.put(People.STARRED,1);

Uriuri=getContentResolver().insert(People.CONTENT_URI,values);

Addingnewvalues

UriphoneUri=null;
UriemailUri=null;

//Addaphonenumberforbellsong
//BeginwiththeURIforthenewrecordjustreturnedbyinsert();
//itendswiththe_IDofthenewrecord,sowedon'thavetoaddtheIDourselves
//ThenappendthedesignationforthephonetabletothisURI
//andusetheresultingURItoinsertthephonenumber

phoneUri=Uri.withAppendedPath(uri,People.Phones.CONTENT_DIRECTORY);

values.clear();
values.put(People.Phones.TYPE,People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER,"1234567");
getContentResolver().insert(phoneUri,values);

//Nowaddanemailaddressinthesameway
emailUri=Uri.withAppendedPath(uri,People.ContactMethods.CONTENT_DIRECTORY);

values.clear();

//ContactMethods.KINDiduesdtodistinguishdifferentkindsofcontactmethods,suchasemail,etc
values.put(People.ContactMethods.KIND,Contacts.KIND_EMAIL);
values.put(People.ContactMethods.DATA,"[email protected]");
values.put(People.ContactMethods.TYPE,People.ContactMethods.TYPE_HOME);
getContentResolver().insert(emailUri,values);

YoucanplacesmallamountsofbinarydataintoatablebycallingtheversionofContentValues.put()thattakesabytearray.Thatwouldworkforasmallicon-likeimageorashortaudioclip,forexample.However,ifyouhavealargeamountofbinarydatatoadd,suchasaphotographoracompletesong,putacontent:URIforthedatainthetableandcallContentResolver.openOutputStream()withthefile'sURI.(Thatcausesthecontentprovidertostorethedatainafileandrecordthefilepathinahiddenfieldoftherecord.)

Inthisregard,theMediaStorecontentprovider,themainproviderthatdispensesimage,audio,andvideodata,employsaspecialconvention:ThesameURIthatisusedwithquery()ormanagedQuery()togetmeta-informationaboutthebinarydata(suchas,thecaptionofaphotographorthedateitwastaken)isusedwithopenInputStream()togetthedataitself.Similarly,thesameURIthatisusedwithinsert()toputmeta-informationintoaMediaStorerecordisusedwithopenOutputStream()toplacethebinarydatathere.Thefollowingcodesnippetillustratesthisconvention:

importandroid.provider.MediaStore.Images.Media;importandroid.content.ContentValues;importjava.io.OutputStream;

//SavethenameanddescriptionofanimageinaContentValuesmap.ContentValuesvalues=newContentValues(3);values.put(Media.DISPLAY_NAME,"road_trip_1");values.put(Media.DESCRIPTION,"Day1,triptoLosAngeles");values.put(Media.MIME_TYPE,"image/jpeg");

//Addanewrecordwithoutthebitmap,butwiththevaluesjustset.//insert()returnstheURIofthenewrecord.Uriuri=getContentResolver().insert(Media.EXTERNAL_CONTENT_URI,values);

//Nowgetahandletothefileforthatrecord,andsavethedataintoit.//Here,sourceBitmapisaBitmapobjectrepresentingthefiletosavetothedatabase.try{OutputStreamoutStream=getContentResolver().openOutputStream(uri);sourceBitmap.compress(Bitmap.CompressFormat.JPEG,50,outStream);outStream.close();}catch(Exceptione){Log.e(TAG,"exceptionwhilewritingimage",e);}

编写一个ContentProvider,必须先扩展android.content.ContentProvider并实现以下方法

query

insert

update

delete

getType

但是,要使这些方法生效,在实现它们之前必须进行大量的设置,以下介绍需要采取的各种步骤:

1)计划数据库、URI、列名称、创建元数据类来定义所有这些元素据的常量

2)扩展抽象类ContentProvider

3)实现方法queryinsertupdatedeletegetType

4)在描述文件中注册提供程序


1)计划数据库

这里借助一个包含一些图书的数据库,这个图书数据库仅包含一个books表,该表包括nameisbnauthor

BookPrividerMetaData类:

packagecom.kingsoft.NotePadList;


importandroid.net.Uri;
importandroid.provider.BaseColumns;


publicclassBookPrividerMetaData{
publicstaticfinalStringAUTHORITY="com.androidbook.privider.BookProvider";

publicstaticfinalStringDATABASE_NAME="book.db";
publicstaticfinalintDATABASE_VERSION=1;
publicstaticfinalStringBOOKS_TABLE_NAME="books";

privateBookPrividerMetaData(){}

//innerclassdescribingBookTable
publicstaticfinalclassBookTableMetaDataimplementsBaseColumns{
privateBookTableMetaData(){}
publicstaticfinalStringTABLE_NAME="books";

//uriandMIMEtypedefintions
publicstaticfinalUriCONTENT_URI=
Uri.parse("content://"+AUTHORITY+"/books");
publicstaticfinalStringCONTENT_TYPE=
"vnd.android.cursor.dir/vnd.androidbook.book";
publicstaticfinalStringCONTENT_ITEM_TYPE=
"vnd.android.cursor.item/vnd.androidbook.book";

publicstaticfinalStringDEFAULT_SORT_ORDER="modifiedDESC";

//AdditionColumnsstarthere
//Stringtype
publicstaticfinalStringBOOK_NAME="name";
//stringtype
publicstaticfinalStringBOOK_ISBN="isbn";
publicstaticfinalStringBOOK_AUTHOR="author";

publicstaticfinalStringCREATE_DATE="created";
publicstaticfinalStringMODIFIED_DATE="modified";
}
}

BookProviderMetaData类首先将其授权为com.androidbook.provider.BookProvider我们将此字符串在Android描述文件中注册该提供程序。此字符串形成了URI的前面部分

然后将它的一个表(books)定义为内部BookTableMetaData类。BooktTableMetaData类然后定义一个URI来标识一个图书集合。有了上一段的授权,图书集合的URI将类似于:

content://com.androidbook.provider.BookProvider/books

URI由以下常量标识

BookProviderMetaData.BookTableMetaData.Content_URI

元数据类BookTableMetaData也继承了BaseColumn类,后者提供了标准的_id字段,该字段表示行ID。有了这些元数据的定义,我们就准备好了实现提供程序了。


2、扩展ContentProvider

packagecom.kingsoft.NotePadList;


importjava.util.HashMap;


importandroid.content.ContentProvider;
importandroid.content.ContentValues;
importandroid.content.Context;
importandroid.content.UriMatcher;
importandroid.database.Cursor;
importandroid.database.sqlite.SQLiteDatabase;
importandroid.database.sqlite.SQLiteOpenHelper;
importandroid.net.Uri;
importandroid.util.Log;


importcom.kingsoft.NotePadList.BookPrividerMetaData.BookTableMetaData;


publicclassBookProviderextendsContentProvider{
//CreateaProjectionMapforColumns
//Projectionmapsaresimiliarto"as"constructinansql
//statementwherebyyoucanrenamethecolumns

privatestaticHashMap<String,String>sBooksProjectionMap;
static{
sBooksProjectionMap=newHashMap<String,String>();
sBooksProjectionMap.put(BookTableMetaData._ID,BookTableMetaData._ID);

//nameisbnauthor
sBooksProjectionMap.put(BookTableMetaData.BOOK_NAME,BookTableMetaData.BOOK_NAME);
sBooksProjectionMap.put(BookTableMetaData.BOOK_ISBN,BookTableMetaData.BOOK_ISBN);
sBooksProjectionMap.put(BookTableMetaData.BOOK_AUTHOR,BookTableMetaData.BOOK_AUTHOR);

//createddata,modifieddate
sBooksProjectionMap.put(BookTableMetaData.CREATE_DATE,BookTableMetaData.CREATE_DATE);
sBooksProjectionMap.put(BookTableMetaData.MODIFIED_DATE,BookTableMetaData.MODIFIED_DATE);
}

//Provideamechanismtoidentifyalltheincominguripatterns
privatestaticUriMatchersUriMatcher;
privatestaticfinalintINCOMING_BOOK_COLLECTION_URI_INDICATOR=1;
privatestaticfinalintINCOMING_SINGLE_BOOK_URI_INDICATOR=2;
static{
sUriMatcher=newUriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(BookPrividerMetaData.AUTHORITY,"books",INCOMING_BOOK_COLLECTION_URI_INDICATOR);
sUriMatcher.addURI(BookPrividerMetaData.AUTHORITY,"books/#",INCOMING_SINGLE_BOOK_URI_INDICATOR);
}

//DealwithOnCreatecallback

privateDatabaseHelpermOpenHelper;


@Override
publicintdelete(Uriuri,Stringselection,String[]selectionArgs){
//TODOAuto-generatedmethodstub
return0;
}


@Override
publicStringgetType(Uriuri){
//TODOAuto-generatedmethodstub
returnnull;
}


@Override
publicUriinsert(Uriuri,ContentValuesvalues){
//TODOAuto-generatedmethodstub
returnnull;
}


@Override
publicbooleanonCreate(){
//TODOAuto-generatedmethodstub
mOpenHelper=newDatabaseHelper(getContext());
returntrue;
}


@Override
publicCursorquery(Uriuri,String[]projection,Stringselection,
String[]selectionArgs,StringsortOrder){
//TODOAuto-generatedmethodstub
returnnull;
}


@Override
publicintupdate(Uriuri,ContentValuesvalues,Stringselection,
String[]selectionArgs){
//TODOAuto-generatedmethodstub
return0;
}

privatestaticclassDatabaseHelperextendsSQLiteOpenHelper{



publicDatabaseHelper(Contextcontext){
super(context,BookPrividerMetaData.DATABASE_NAME,null,BookPrividerMetaData.DATABASE_VERSION);
//TODOAuto-generatedconstructorstub
}


@Override
publicvoidonCreate(SQLiteDatabasedb){
//TODOAuto-generatedmethodstub
db.execSQL("CREATE TABLE"+BookTableMetaData.TABLE_NAME+"("
+BookPrividerMetaData.BookTableMetaData._ID
+"INTEGERPRIMARYKEY,"
+BookTableMetaData.BOOK_NAME+"TEXT"
+BookTableMetaData.BOOK_ISBN+"TEXT"
+BookTableMetaData.BOOK_AUTHOR+"TEXT"
+BookTableMetaData.CREATE_DATE+"INTEGER"
+BookTableMetaData.MODIFIED_DATE+"INTEGER"
+");"

);

}


@Override
publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){
//TODOAuto-generatedmethodstub
Log.w("Tag","Upgratindatabsefromversion"+oldVersion+"to"+newVersion+",whichwilldesrotyallolddata");
db.execSQL("DROPTABLEIFEXITS"+BookTableMetaData.TABLE_NAME);
onCreate(db);
}

}


}


3、履行MIME类型契约

必须实现getType()方法,以便返回URIMIME类型

@Override
publicStringgetType(Uriuri){
//TODOAuto-generatedmethodstub
switch(sUriMatcher.match(uri)){
caseINCOMING_BOOK_COLLECTION_URI_INDICATOR:
returnBookTableMetaData.CONTENT_TYPE;
caseINCOMING_SINGLE_BOOK_URI_INDICATOR:
returnBookTableMetaData.CONTENT_ITEM_TYPE;
default:
thrownewIllegalArgumentException("UnknowURI"+uri);
}
}



4、实现query()

5、实现insert()

6、实现update()

7、实现delete()

8、使用UriMatcher来解析URI

9、实现投影映射

10、提供注册程序

<providerandroid:name="BooksProvider"

android:authorities="com.androidbook.provider.BookProvider"/>




实战一:

使用现成的ContantProvider,遍历联系人列表中的联系人和号码

packagecom.kingsoft.contentprovider;

importandroid.app.Activity;
importandroid.content.ContentResolver;
importandroid.content.ContentUris;
importandroid.content.ContentValues;
importandroid.database.Cursor;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.provider.Contacts;
importandroid.provider.ContactsContract;
importandroid.provider.Contacts.People;
importandroid.view.View;
importandroid.view.View.OnClickListener;
importandroid.widget.Button;
importandroid.widget.Toast;

publicclassContentProviderActivityextendsActivity{
/**Calledwhentheactivityisfirstcreated.*/
@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Buttonb1=(Button)findViewById(R.id.Button1);
b1.setOnClickListener(newOnClickListener(){

@Override
publicvoidonClick(Viewv){
//TODOAuto-generatedmethodstub
ContentResolvercontentResolver=getContentResolver();

//获得所有联系人
Cursorcursor=contentResolver.query(ContactsContract.Contacts.CONTENT_URI,null,null,null,null);

//循环遍历
if(cursor.moveToFirst()){
intidColumn=cursor.getColumnIndex(ContactsContract.Contacts._ID);
intdisplayNameColumn=cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);

do{
//获得联系人id
StringcontactsId=cursor.getString(idColumn);
//获得联系人姓名
StringdisdisPlayName=cursor.getString(displayNameColumn);
Toast.makeText(ContentProviderActivity.this,"联系人姓名:"+disdisPlayName,Toast.LENGTH_LONG).show();

//查看联系人有多少个电话号码,如果没有返回0
intphoneCount=cursor.getInt(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));

if(phoneCount>0){
//获得联系人的电话号码列表
CursorphonesCursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone._ID+"="+contactsId,null,null);

if(phonesCursor.moveToFirst()){
do{
//遍历所有电话号码
StringphoneNumber=phonesCursor.getString(phonesCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Toast.makeText(ContentProviderActivity.this,"联系人电话:"+phoneNumber,Toast.LENGTH_LONG).show();
}while(phonesCursor.moveToNext());
}
}
}while(cursor.moveToNext());
}
}
});
}

}

main.xml

<?xmlversion="1.0"encoding="utf-8"?>
<LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">

<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="读取联系人信息"/>
<Button
android:id="@+id/Button1"
android:layout_width="80dip"
android:layout_height="wrap_content"
android:text="确认"
/>

</LinearLayout>

AndroidManifest.xml

<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>

实战二:另外一种方法获得联系人

packagecn.mycontent;

importandroid.app.Activity;
importandroid.content.ContentResolver;
importandroid.database.Cursor;
importandroid.graphics.Color;
importandroid.net.Uri;
importandroid.os.Bundle;
importandroid.provider.BaseColumns;
importandroid.provider.ContactsContract;
importandroid.widget.TextView;

publicclassActivity01extendsActivity{
//查询ContentProvider时希望返回的列
String[]columns={
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts._ID,
//People._ID,
//People.NAME
};

UricontactUri=ContactsContract.Contacts.CONTENT_URI;
TextViewtv;
//UricontaUri=Contacts.People.CONTENT_URI;

@Override
publicvoidonCreate(BundlesavedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
tv=(TextView)findViewById(R.id.tv);
Stringresult=getQueryData();
tv.setTextColor(Color.GREEN);
tv.setTextSize(20.0f);
tv.setText("ID\t名字\n"+result);

}
//获取联系人列表的信息,返回String对象
publicStringgetQueryData(){
Stringresult="";
//获取ContentResolver对象
ContentResolverresolver=getContentResolver();
Cursorcursor=resolver.query(contactUri,columns,null,null,null);
//获得_ID字段的索引
intidIndex=cursor.getColumnIndex(BaseColumns._ID);
//获得Name字段的索引
intnameIndex=cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME);
//遍历Cursor提取数据
for(cursor.moveToFirst();(!cursor.isAfterLast());cursor.moveToNext()){
result=result+cursor.getString(idIndex)+"\t";
result=result+cursor.getString(nameIndex)+"\t\n";
}
cursor.close();
returnresult;
}
}

AndroidManifest.xml

<uses-permissionandroid:name="android.permission.READ_CONTACTS"/>

更多相关文章

  1. [android]数据库SQLiteDatabase简介
  2. Android学习笔记-ProgressBar和ListView使用方法(二)
  3. Android getResources().getConfiguration()方法的作用
  4. Android数据库应用(《Android开发入门与实战》选摘)
  5. Android中使用Gson解析JSON数据,以及把JSON数据映射成一个对象
  6. Storm——Android SQLite数据库管理类库
  7. Android 监听ContentProvider中数据的变化 Android 监听ContentP
  8. Android 存储字符串数据到txt文件
  9. android 数据持久化——File

随机推荐

  1. 金九银十中,看看这31道Android面试题
  2. 窥探支付宝钱包android客户端的屏幕加密
  3. Android(安卓)Hook Activity 的几种姿势
  4. android实现省市区三级联动 citypicker
  5. [有梦想的IT人] Android优秀的动画库
  6. 十分钟学会kotlin实现Android(安卓)MVP模
  7. unity通过android adb查看真机日志
  8. android中xml tools属性详解
  9. Atom E6xx 系列明年推出,將支援 Android(
  10. Android(安卓)Studio开发(一)模拟微信页面