源码地址:
https://github.com/StarsAaron/ContentProviderTestDemo/tree/master
ContentProvider 使用
ContentProvider 是不同应用程序之间进行数据交换的标准 API,它以 Uri 的形式对外提供数据,其他应用程序使用 ContentResolver 通过 Uri 去访问操作指定数据。
开发一个 ContentProvider 的步骤:
定义一个继承 ContentProvider 的子类。
通常需重写如下方法——
- public boolean onCreate() :当其他程序首次访问 ContentProvider 时该方法被回调。
public Uri insert(Uri uri, ContentValues values) :插入数据 values。
public int delete(Uri uri, String selection, String[] selectionArgs) :删除 select 条件所匹配的全部记录。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) :修改 select 条件所匹配的全部记录。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) :查询出 select 条件所匹配的全部记录。参数 projection 指定数据列名。
public String getType(Uri uri) :返回当前 Uri 所代表的数据的 MIME 类型。
- 返回值由两种字符串开头
- vnd.android.cursor.dir/ :对应数据可能包括多条记录。
- vnd.android.cursor.item/ :对应数据只包含一条记录。
- 返回值由两种字符串开头
在 AndroidManifest.xml 文件中注册该 ContentProvider(注册时需为之绑定一个 Uri)。只要为
<application
android:icon="@drawable/ic_launcher">
<!-- 注册一个ContentProvider -->
<provider
android:exported="true"
android:name=".FirstProvider"
android:authorities="org.crazyit.providers.firstprovider"
<!-- 额外参数 -->
android:permission="com.harvic.contentProviderBlog"
android:readPermission="com.harvic.contentProviderBlog.read"
android:writePermission="com.harvic.cotentProviderBlog.write">
</provider>
</application>
各属性说明:
- name :指定该 ContentProvider 的实现类的类名。
- authorities :指定该 ContentProvider 对应的 Uri。
- exported:这个属性用于指示该服务是否能被其他程序应用组件调用或跟他交互。
取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。
- readPermission:使用Content Provider的查询功能所必需的权限,即使用ContentProvider里的query()函数的权限。
- writePermission:使用ContentProvider的修改功能所必须的权限,即使用ContentProvider的insert()、update()、delete()函数的权限。
- permission:客户端读、写 Content Provider 中的数据所必需的权限名称。 本属性为一次性设置读和写权限提供了快捷途径。 不过,readPermission和writePermission属性优先于本设置。 如果同时设置了readPermission属性,则其将控制对 Content Provider 的读取。 如果设置了writePermission属性,则其也将控制对 Content Provider 数据的修改。也就是说如果只设置permission权限,那么拥有这个权限的应用就可以实现对这里的ContentProvider进行读写;如果同时设置了permission和readPermission那么具有readPermission权限的应用才可以读,拥有permission权限的才能写!也就是说只拥有permission权限是不能读的,因为readPermission的优先级要高于permission;如果同时设置了readPermission、writePermission、permission那么permission就无效了。
示例:
public class FirstProvider extends ContentProvider{
// 第一次创建该ContentProvider时调用该方法
@Override
public boolean onCreate(){
System.out.println("===onCreate方法被调用===");
return true;
}
// 该方法的返回值代表了该ContentProvider所提供数据的MIME类型
@Override
public String getType(Uri uri){
System.out.println("~~getType方法被调用~~");
return null;
}
// 实现查询方法,该方法应该返回查询得到的Cursor
@Override
public Cursor query(Uri uri, String[] projection, String where,
String[] whereArgs, String sortOrder){
System.out.println(uri + "===query方法被调用===");
System.out.println("where参数为:" + where);
return null;
}
// 实现插入的方法,该方法应该新插入的记录的Uri
@Override
public Uri insert(Uri uri, ContentValues values){
System.out.println(uri + "===insert方法被调用===");
System.out.println("values参数为:" + values);
return null;
}
// 实现删除方法,该方法应该返回被删除的记录条数
@Override
public int delete(Uri uri, String where, String[] whereArgs){
System.out.println(uri + "===delete方法被调用===");
System.out.println("where参数为:" + where);
return 0;
}
// 实现删除方法,该方法应该返回被更新的记录条数
@Override
public int update(Uri uri, ContentValues values, String where,
String[] whereArgs){
System.out.println(uri + "===update方法被调用===");
System.out.println("where参数为:"
+ where + ",values参数为:" + values);
return 0;
}
}
MIME 简介
MIME类型就是用来标识当前的Activity所能打开的文件类型。
下面简单列出来系统中自带的几种文件类型和对应的MIME类型:
(前面是文件名,后面是对应的MIME类型字符串)
- {“.bmp”, “image/bmp”}
- {“.c”, “text/plain”}
- {“.class”, “application/octet-stream”}
- {“.conf”, “text/plain”}
- {“.cpp”, “text/plain”}
- {“.doc”, “application/msword”}
那现在看看在android中,MIME类型是用来干什么的呢?
首先,MIME类型主要是Activity的Intent-filter的data域;比如下面这个Activity:
<activity
android:name=".SecondActivity"
android:label="@string/title_activity_second" >
<intent-filter>
<action android:name="harvic.test.qijian"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="image/bmp"/>
</intent-filter>
</activity>
这里指定了data域的MimeType值是”image/bmp”,即在利用隐式Intent匹配时,只有指定MimeType是”image/bmp”时,才能启用这个Activity,也就是说,这个Activity只能打开image/bmp类型的文件!!!!这才是MIME类型匹配的重点;
所以MIME类型在Activity中是用来指定,当前的Activity所支持打开的文件类型!!
ContentResolver 使用
Context 提供了获取 ContentResolver 对象的方法:
getContentResolver();
获取 ContentResolver 对象后,即可调用其方法操作数据:
- insert(Uri uri, ContentValues values) :向 Uri 对应的 ContentProvider 中插入 values 对应的数据。
- delete(Uri uri, String where, String[] selectionArgs) :删除 Uri 对应的 ContentProvider 中 where 条件匹配的数据。
- update(Uri uri, ContentValues values, String where, String[] selectionArgs) :更新 Uri 对应的 ContentProvider 中 where 条件匹配的数据。
- query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) :查询 Uri 对应的 ContentProvider 中 where 条件匹配的数据。
一般来说, ContentProvider 为单实例模式。当多个应用程序通过 ContentResolver 来操作其提供的数据时,所调用的数据操作将会委托同一 ContentProvider 处理。
示例:
public class FirstResolver extends Activity{
ContentResolver contentResolver;
Uri uri = Uri.parse("content://org.crazyit.providers.firstprovider/");
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取系统的ContentResolver对象
contentResolver = getContentResolver();
}
public void query(View source){
// 调用ContentResolver的query()方法。
// 实际返回的是该Uri对应的ContentProvider的query()的返回值
Cursor c = contentResolver.query(uri, null
, "query_where", null, null);
Toast.makeText(this, "远程ContentProvide返回的Cursor为:" + c,
Toast.LENGTH_LONG).show();
}
public void insert(View source){
ContentValues values = new ContentValues();
values.put("name", "fkjava");
// 调用ContentResolver的insert()方法。
// 实际返回的是该Uri对应的ContentProvider的insert()的返回值
Uri newUri = contentResolver.insert(uri, values);
Toast.makeText(this, "远程ContentProvide新插入记录的Uri为:"
+ newUri, Toast.LENGTH_LONG).show();
}
public void update(View source){
ContentValues values = new ContentValues();
values.put("name", "fkjava");
// 调用ContentResolver的update()方法。
// 实际返回的是该Uri对应的ContentProvider的update()的返回值
int count = contentResolver.update(uri, values
, "update_where", null);
Toast.makeText(this, "远程ContentProvide更新记录数为:"
+ count, Toast.LENGTH_LONG).show();
}
public void delete(View source){
// 调用ContentResolver的delete()方法。
// 实际返回的是该Uri对应的ContentProvider的delete()的返回值
int count = contentResolver.delete(uri, "delete_where", null);
Toast.makeText(this, "远程ContentProvide删除记录数为:"
+ count, Toast.LENGTH_LONG).show();
}
}
ContentProvider 与 ContentResolver 的关系
Uri 是 ContentResolver 和 ContentProvider 进行数据交换的标识。
三者之间的关系如下:
注:
CRUD 估计指:Create(包括 Insert)、Query、Update、Delete 等相关数据操作。
系统的 ContentProvider
使用 ContentResolver 操作操作系统的 ContentProvider 数据的步骤:
调用 Activity 的 getContentResolver() 获取 ContentResolver 对象。
按需调用 ContentResolver 的 insert()、delete()、update()、query() 方法操作数据即可。
使用 ContentProvider 管理联系人
Android 系统提供了 Contacts 应用程序来管理联系人。其提供的 ContentProvider 的几个重要 Uri 如下:
- ContactsContract.Contacts.CONTENT_URI :管理联系人的 Uri。
- ContactsContract.CommonDataKinds.Phone.CONTENT_URI :管理联系人的电话的 Uri。
- ContactsContract.CommonDataKinds.Emall.CONTENT_URI :管理联系人的 E-mail 的 Uri。
注:读写系统联系人信息须在 AndroidManifest.xml 配置文件的根元素中添加如下元素——
<!-- 授予读联系人ContentProvider的权限 -->
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<!-- 授予写联系人ContentProvider的权限 -->
<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
示例
public class ContactProviderTest extends Activity{
Button search;
Button add;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 获取系统界面中查找、添加两个按钮
search = (Button) findViewById(R.id.search);
add = (Button) findViewById(R.id.add);
search.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View source){
// 定义两个List来封装系统的联系人信息、指定联系人的电话号码、Email等详情
final ArrayList<String> names = new ArrayList<String>();
final ArrayList<ArrayList<String>> details
= new ArrayList<ArrayList<String>>();
// 使用ContentResolver查找联系人数据
Cursor cursor = getContentResolver().query(
ContactsContract.Contacts.CONTENT_URI, null, null,
null, null);
// 遍历查询结果,获取系统中所有联系人
while (cursor.moveToNext()){
// 获取联系人ID
String contactId = cursor.getString(cursor
.getColumnIndex(ContactsContract.Contacts._ID));
// 获取联系人的名字
String name = cursor.getString(cursor.getColumnIndex(
ContactsContract.Contacts.DISPLAY_NAME));
names.add(name);
// 使用ContentResolver查找联系人的电话号码
Cursor phones = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = " + contactId, null, null);
ArrayList<String> detail = new ArrayList<String>();
// 遍历查询结果,获取该联系人的多个电话号码
while (phones.moveToNext()){
// 获取查询结果中电话号码列中数据。
String phoneNumber = phones.getString(phones
.getColumnIndex(ContactsContract
.CommonDataKinds.Phone.NUMBER));
detail.add("电话号码:" + phoneNumber);
}
phones.close();
// 使用ContentResolver查找联系人的Email地址
Cursor emails = getContentResolver().query(
ContactsContract.CommonDataKinds.Email.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Email.CONTACT_ID
+ " = " + contactId, null, null);
// 遍历查询结果,获取该联系人的多个Email地址
while (emails.moveToNext()){
// 获取查询结果中Email地址列中数据。
String emailAddress = emails.getString(emails
.getColumnIndex(ContactsContract
.CommonDataKinds.Email.DATA));
detail.add("邮件地址:" + emailAddress);
}
emails.close();
details.add(detail);
}
cursor.close();
// 加载result.xml界面布局代表的视图
View resultDialog = getLayoutInflater().inflate(
R.layout.result, null);
// 获取resultDialog中ID为list的ExpandableListView
ExpandableListView list = (ExpandableListView) resultDialog
.findViewById(R.id.list);
// 创建一个ExpandableListAdapter对象
ExpandableListAdapter adapter =
new BaseExpandableListAdapter(){
// 获取指定组位置、指定子列表项处的子列表项数据
@Override
public Object getChild(int groupPosition,
int childPosition){
return details.get(groupPosition).get(
childPosition);
}
@Override
public long getChildId(int groupPosition,
int childPosition){
return childPosition;
}
@Override
public int getChildrenCount(int groupPosition){
return details.get(groupPosition).size();
}
private TextView getTextView(){
AbsListView.LayoutParams lp = new AbsListView
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT
, 64);
TextView textView = new TextView(
ContactProviderTest.this);
textView.setLayoutParams(lp);
textView.setGravity(Gravity.CENTER_VERTICAL
| Gravity.LEFT);
textView.setPadding(36, 0, 0, 0);
textView.setTextSize(20);
return textView;
}
// 该方法决定每个子选项的外观
@Override
public View getChildView(int groupPosition,
int childPosition, boolean isLastChild,
View convertView, ViewGroup parent){
TextView textView = getTextView();
textView.setText(getChild(groupPosition,
childPosition).toString());
return textView;
}
// 获取指定组位置处的组数据
@Override
public Object getGroup(int groupPosition){
return names.get(groupPosition);
}
@Override
public int getGroupCount(){
return names.size();
}
@Override
public long getGroupId(int groupPosition){
return groupPosition;
}
// 该方法决定每个组选项的外观
@Override
public View getGroupView(int groupPosition,
boolean isExpanded, View convertView,
ViewGroup parent){
TextView textView = getTextView();
textView.setText(getGroup(groupPosition)
.toString());
return textView;
}
@Override
public boolean isChildSelectable(int groupPosition,
int childPosition){
return true;
}
@Override
public boolean hasStableIds(){
return true;
}
};
// 为ExpandableListView设置Adapter对象
list.setAdapter(adapter);
// 使用对话框来显示查询结果。
new AlertDialog.Builder(ContactProviderTest.this)
.setView(resultDialog).setPositiveButton("确定", null)
.show();
}
});
// 为add按钮的单击事件绑定监听器
add.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
// 获取程序界面中的3个文本框
String name = ((EditText) findViewById(R.id.name))
.getText().toString();
String phone = ((EditText) findViewById(R.id.phone))
.getText().toString();
String email = ((EditText) findViewById(R.id.email))
.getText().toString();
// 创建一个空的ContentValues
ContentValues values = new ContentValues();
// 向RawContacts.CONTENT_URI执行一个空值插入,
// 目的是获取系统返回的rawContactId
Uri rawContactUri = getContentResolver().insert(
RawContacts.CONTENT_URI, values);
long rawContactId = ContentUris.parseId(rawContactUri);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
// 设置内容类型
values
.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
// 设置联系人名字
values.put(StructuredName.GIVEN_NAME, name);
// 向联系人URI添加联系人名字
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI,
values);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
// 设置联系人的电话号码
values.put(Phone.NUMBER, phone);
// 设置电话类型
values.put(Phone.TYPE, Phone.TYPE_MOBILE);
// 向联系人电话号码URI添加电话号码
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI,
values);
values.clear();
values.put(Data.RAW_CONTACT_ID, rawContactId);
values.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
// 设置联系人的Email地址
values.put(Email.DATA, email);
// 设置该电子邮件的类型
values.put(Email.TYPE, Email.TYPE_WORK);
// 向联系人Email URI添加Email数据
getContentResolver().insert(
android.provider.ContactsContract.Data.CONTENT_URI,
values);
Toast.makeText(ContactProviderTest.this, "联系人数据添加成功",
Toast.LENGTH_LONG).show();
}
});
}
}
使用 ContentProvider 管理多媒体内容
Android 为多媒体提供的 ContentProvider 的 Uri 如下:
- MediaStore.Audio.Media.EXTERNAL_CONTENT_URI :存储在外部存储器(SD卡)上的音频文件内容的 ContentProvider 的 Uri。
- MediaStore.Audio.Media.INTERNAL_CONTENT_URI :存储在手机内部存储器上的音频文件内容的 ContentProvider 的 Uri。
- MediaStore.Image.Media.EXTERNAL_CONTENT_URI :存储在外部存储器(SD卡)上的图片文件内容的 ContentProvider 的 Uri。
- MediaStore.Image.Media.INTERNAL_CONTENT_URI :存储在手机内部存储器上的图片文件内容的 ContentProvider 的 Uri。
- MediaStore.Video.Media.EXTERNAL_CONTENT_URI :存储在外部存储器(SD卡)上的视频文件内容的 ContentProvider 的 Uri。
- MediaStore.Video.Media.INTERNAL_CONTENT_URI :存储在手机内部存储器上的视频文件内容的 ContentProvider 的 Uri。
示例
public class MediaProviderTest extends Activity{
Button add;
Button view;
ListView show;
ArrayList<String> names = new ArrayList<String>();
ArrayList<String> descs = new ArrayList<String>();
ArrayList<String> fileNames = new ArrayList<String>();
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
add = (Button) findViewById(R.id.add);
view = (Button) findViewById(R.id.view);
show = (ListView) findViewById(R.id.show);
// 为add按钮的单击事件绑定监听器
add.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
// 创建ContentValues对象,准备插入数据
ContentValues values = new ContentValues();
values.put(Media.DISPLAY_NAME, "jinta");
values.put(Media.DESCRIPTION, "金塔");
values.put(Media.MIME_TYPE, "image/jpeg");
// 插入数据,返回所插入数据对应的Uri
Uri uri = getContentResolver().insert(
Media.EXTERNAL_CONTENT_URI, values);
// 加载应用程序下的jinta图片
Bitmap bitmap = BitmapFactory.decodeResource(
MediaProviderTest.this.getResources(),
R.drawable.jinta);
OutputStream os = null;
try{
// 获取刚插入的数据的Uri对应的输出流
os = getContentResolver().openOutputStream(uri); //①
// 将bitmap图片保存到Uri对应的数据节点中
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
os.close();
}
catch (IOException e){
e.printStackTrace();
}
}
});
// 为view按钮的单击事件绑定监听器
view.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v){
// 清空names、descs、fileNames集合里原有的数据。
names.clear();
descs.clear();
fileNames.clear();
// 通过ContentResolver查询所有图片信息
Cursor cursor = getContentResolver().query(
Media.EXTERNAL_CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()){
// 获取图片的显示名
String name = cursor.getString(cursor
.getColumnIndex(Media.DISPLAY_NAME));
// 获取图片的详细描述
String desc = cursor.getString(cursor
.getColumnIndex(Media.DESCRIPTION));
// 获取图片的保存位置的数据
byte[] data = cursor.getBlob(cursor
.getColumnIndex(Media.DATA));
// 将图片名添加到names集合中
names.add(name);
// 将图片描述添加到descs集合中
descs.add(desc);
// 将图片保存路径添加到fileNames集合中
fileNames.add(new String(data, 0, data.length - 1));
}
// 创建一个List集合,List集合的元素是Map
List<Map<String, Object>> listItems =
new ArrayList<Map<String, Object>>();
// 将names、descs两个集合对象的数据转换到Map集合中
for (int i = 0; i < names.size(); i++){
Map<String, Object> listItem =
new HashMap<String, Object>();
listItem.put("name", names.get(i));
listItem.put("desc", descs.get(i));
listItems.add(listItem);
}
// 创建一个SimpleAdapter
SimpleAdapter simpleAdapter = new SimpleAdapter(
MediaProviderTest.this, listItems
, R.layout.line,
new String[] { "name", "desc" }
, new int[] {R.id.name, R.id.desc });
// 为show ListView组件设置Adapter
show.setAdapter(simpleAdapter);
}
});
// 为show ListView的列表项单击事件添加监听器
show.setOnItemClickListener(new OnItemClickListener(){
@Override
public void onItemClick(AdapterView<?> parent
, View source, int position, long id){
// 加载view.xml界面布局代表的视图
View viewDialog = getLayoutInflater().inflate(
R.layout.view, null);
// 获取viewDialog中ID为image的组件
ImageView image = (ImageView) viewDialog
.findViewById(R.id.image);
// 设置image显示指定图片
image.setImageBitmap(BitmapFactory.decodeFile(
fileNames.get(position)));
// 使用对话框显示用户单击的图片
new AlertDialog.Builder(MediaProviderTest.this)
.setView(viewDialog).setPositiveButton("确定", null)
.show();
}
});
}
}
ContentObserver 监听数据改变
当 insert、delete、update 等方法导致 ContentProvider 里数据改变时,程序就调用如下代码通知所有注册在该 Uri 上的监听者:
getContext().getContentResolver().notifyChange(uri, null);
为了在应用程序中监听这些数据的改变,需要利用 Android 提供的 ContentObserver 基类。
实现步骤:
定义一个 ContentObserver 的子类,重写其触发方法——
onChange(boolean selfChange);
通过 ContentResolver 提供的注册方法,向指定的 Uri 注册 ContentObserver 监听器:
registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer);
- 参数说明
- uri :该监听器所监听的 ContentProvider 的 Uri。
- notifyForDescendents :是否通知子路径资源的监听者。
- observer :监听器实例。
示例:监听用户发出的短信
public class MonitorSms extends Activity{
private ContentObserver observer;
@Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
observer = new SmsObserver(new Handler());
// 为content://sms的数据改变注册监听器
getContentResolver().registerContentObserver(
Uri.parse("content://sms"), true,
observer );
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消监听
getContentResolver().unregisterContentObserver(observer);
}
// 提供自定义的ContentObserver监听器类
private final class SmsObserver extends ContentObserver{
public SmsObserver(Handler handler){
super(handler);
}
public void onChange(boolean selfChange){
// 查询发送箱中的短信(处于正在发送状态的短信放在发送箱)
Cursor cursor = getContentResolver().query(
Uri.parse("content://sms/outbox")
, null, null, null, null);
// 遍历查询得到的结果集,即可获取用户正在发送的短信
while (cursor.moveToNext()){
StringBuilder sb = new StringBuilder();
// 获取短信的发送地址
sb.append("address=").append(cursor
.getString(cursor.getColumnIndex("address")));
// 获取短信的标题
sb.append(";subject=").append(cursor
.getString(cursor.getColumnIndex("subject")));
// 获取短信的内容
sb.append(";body=").append(cursor
.getString(cursor.getColumnIndex("body")));
// 获取短信的发送时间
sb.append(";time=").append(cursor
.getLong(cursor.getColumnIndex("date")));
System.out.println("Has Sent SMS::" + sb.toString());
}
}
}
}
数据库操作安全(映射):setProjectionMap
在上面的实例中,我们对外提供的数据库列名和内部使用的是一样的,但这样很容易被对方知道我们的数据库结构,那要想自己本地使用一套列名,给外部提供另一套对应的列名,这样别人不就猜不出我们的列名了么,针对这个问题,在SQLiteQueryBuilder类中,为我们提供了内外部列名映射函数,以允许我们在外部和内部列名不同时的提供映射功能。
主要使用的函数是setProjectionMap(HashMap map)
使用方法如下:
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
Map projectionMap = new HashMap<String,String>();
projectionMap.put("out_column_name_1","inside_column_name_1");
projectionMap.put("out_column_name_2","inside_column_name_2");
projectionMap.put("out_column_name_3","inside_column_name_3");
queryBuilder.setTables(DatabaseHelper.TABLE_FIRST_NAME);
queryBuilder.setProjectionMap(projectionMap);