Android笔记系列--ContentProvider

源码地址:
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 :该监听器所监听的 ContentProviderUri    - 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);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星点点-

请我喝杯咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值