参考:Android开发基础之内容提容者ContentProvider

Android四大组件 内容提供者是四大组件之一,还记得Android的四大组件吗?

前面我们学习了三大组件了,对吧!

其中有:

Activity

服务Service

广播接收者

什么是内容提供者?

我们在学习一门新的东西的时候,我们都需要知道,是为什么,有什么用,怎么用!这是最基础的,当你会用以后,如果还有好奇心,可以去究其原理!

这篇文章需要的基础知识有,请看往回跟数据库相关的文章:

https://www.sunofbeach.net/c/1179993764073156608

相关的视频下载:

链接:https://pan.baidu.com/s/1qZ30FSS 密码:m2dg

后面的课程,我们会使用到系统上层应用的源码:

上面这个连接可以下载,后面我们需要分析系统的原码,才可以实现一些功能,大家先下载下来吧!

好,回到我们的内容提供者上面:ContentProvider

内容提供者,我们从字面上认识,就是一个提供内容的东西!

这东西常用吗?用得比较少,我做了四年android开发,只有一个项目用上了,就是蓝牙电话。为什么会用上呢?因为需要拿到联系人的数据!

内容提供者,就是向第三方暴露自己的数据库的!目前来说,只有google的短信/电话是这么做的,其他应用基本上不会这么做!所以呢,使用场景也少了!比如说微信/支付宝,在第一次使用的时候会询问你是否同意让它读取你的联系人/通讯录,就是通过内容提供者来读取的。

但是,这文章学是会详细地写给大家,还是值得一看的文章!

创建数据库

我们在学习之前,都是以Hello world的方式来学习!先是一个入门的例子!

现在,我们要做这样一件事情,在应用A里有数据库,和内容提供者。应用B通过内容提供者操作A的数据库。

数据库的创建:

比如说,我们有一个学生的成绩表,字段有:id、姓名、语文、英语、数学

首先,我们编写一个数据库的帮助类,StudentScoreDBHelper.java,继承自SQLiteOpenHelper

package com.sunofbeaches.providerdemo.db;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; /**
* Created by TrillGates on 18/7/5.
* God bless my code!
*/
public class StudentScoreDBHelper extends SQLiteOpenHelper {
//数据库名
private static final String DB_NAME = "student.db";
//数据库版本,如果数据库相关的不太懂,同学们可以去看数据库相关的视频课程
private static final int DB_VERSION = 1;
//表名,一般是前缀+表名
public static final String TABLE_NAME = "sob_score";
//字段,一般这么设计,我们这里只做演示
public static final String ID = "_id";
public static final String NAME = "name";
public static final String SCORE_CHINESE = "scorechinese";
public static final String SCORE_MATH = "scoremath";
public static final String SCORE_ENGLISH = "scoreenglish";
//数据库创建语句
private static final String CREATE_TABLE_SQL = "create table " + TABLE_NAME +
" (" + ID + " integer primary key autoincrement, " +
NAME + " varchar(32), " +
SCORE_CHINESE + " integer, " +
SCORE_MATH + " integer, " +
SCORE_ENGLISH + " integer" + " )"; private static final String TAG = "StudentScoreDBHelper"; public StudentScoreDBHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
} @Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
Log.d(TAG, "create table...");
//创建数据库
sqLiteDatabase.execSQL(CREATE_TABLE_SQL);
} @Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
//升级数据库,具体的内容同学们去学习数据库相关的课程吧,前面有的
}
}

还在DAO呢!我们暂时提供增删改查的接口,也就是CRUD

package com.sunofbeaches.providerdemo.db;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase; import com.sunofbeaches.providerdemo.domain.StudentScore; /**
* Created by TrillGates on 18/7/6.
* God bless my code!
*/
public class StudentScoreDAO implements IStudentScoreDao { private final Context mContext;
private final StudentScoreDBHelper mStudentScoreDBHelper; private StudentScoreDAO(Context context) {
this.mContext = context;
mStudentScoreDBHelper = new StudentScoreDBHelper(context);
} private static StudentScoreDAO sInstance = null; /**
* 获取实例对象
*
* @param context 上下文
* @return 返回学生成绩的DAO
*/
public static IStudentScoreDao getInstance(Context context) {
if (sInstance == null) {
synchronized (StudentScoreDAO.class) {
sInstance = new StudentScoreDAO(context);
}
}
return sInstance;
} @Override
public void addStudentScore(String studentName, int chinese, int math, int english) {
SQLiteDatabase writableDatabase = mStudentScoreDBHelper.getWritableDatabase();
try {
//
ContentValues values = new ContentValues();
values.put(StudentScoreDBHelper.NAME, studentName);
values.put(StudentScoreDBHelper.SCORE_CHINESE, chinese);
values.put(StudentScoreDBHelper.SCORE_MATH, math);
values.put(StudentScoreDBHelper.SCORE_ENGLISH, english);
//
writableDatabase.beginTransaction();
writableDatabase.insert(StudentScoreDBHelper.TABLE_NAME, null, values);
writableDatabase.setTransactionSuccessful();
} catch (Exception e) {
e.printStackTrace();
} finally {
writableDatabase.endTransaction();
writableDatabase.close();
}
} @Override
public StudentScore getStudentScoreByName(String name) {
SQLiteDatabase readableDatabase = mStudentScoreDBHelper.getReadableDatabase();
try {
Cursor query = readableDatabase.query(StudentScoreDBHelper.TABLE_NAME,
new String[]{StudentScoreDBHelper.NAME},
null,
new String[]{name},
null,
null,
null);
//
readableDatabase.beginTransaction();
StudentScore studentScore = new StudentScore();
if (query.moveToNext()) {
String studentName = query.getString(query.getColumnIndex(StudentScoreDBHelper.NAME));
int chinese = query.getInt(query.getColumnIndex(StudentScoreDBHelper.SCORE_CHINESE));
int math = query.getInt(query.getColumnIndex(StudentScoreDBHelper.SCORE_MATH));
int english = query.getInt(query.getColumnIndex(StudentScoreDBHelper.SCORE_ENGLISH));
studentScore.name = studentName;
studentScore.scorechinese = chinese;
studentScore.scoreenglish = english;
studentScore.scoremath = math;
}
//
readableDatabase.setTransactionSuccessful();
return studentScore;
} catch (Exception e) {
e.printStackTrace();
} finally {
readableDatabase.endTransaction();
readableDatabase.close();
}
return null;
} @Override
public int deleteStudentByName(String name) {
SQLiteDatabase readableDatabase = mStudentScoreDBHelper.getReadableDatabase();
try {
readableDatabase.beginTransaction();
int delete = readableDatabase.delete(StudentScoreDBHelper.TABLE_NAME,
"where " + StudentScoreDBHelper.NAME + " = ?",
new String[]{name});
readableDatabase.setTransactionSuccessful();
return delete;
} catch (Exception e) {
e.printStackTrace();
} finally {
readableDatabase.endTransaction();
readableDatabase.close();
}
return 0;
} @Override
public int modifyStudentScore(String name, int chinese, int math, int english) {
SQLiteDatabase writableDatabase = mStudentScoreDBHelper.getWritableDatabase();
try {
//
ContentValues values = new ContentValues();
values.put(StudentScoreDBHelper.SCORE_ENGLISH, english);
values.put(StudentScoreDBHelper.SCORE_MATH, math);
values.put(StudentScoreDBHelper.SCORE_CHINESE, chinese);
//
writableDatabase.beginTransaction();
int update = writableDatabase.update(StudentScoreDBHelper.TABLE_NAME, values,
"where " + StudentScoreDBHelper.TABLE_NAME + " = ?", new String[]{name});
writableDatabase.setTransactionSuccessful();
return update;
} catch (Exception e) {
e.printStackTrace();
} finally {
writableDatabase.endTransaction();
writableDatabase.close();
}
return 0;
}
}

测试一下吧,我们添加数据进去:

插入10条记录:

只有几个人及格,太让我失望了!

好啦,到此为止,我们的数据库部分就写完了,接下来我们到重点内容了,内容提供者!

为什么要使用内容提供者呢?

上面我们已经创建了数据库了,我们可以看出,数据库的权限是:

熟悉Linux的同学都知道,Linux的权限是这样分的,(-rw)前面个是用户的权限,(-rw)中间三个是同一个用户组的权限,(—)后面三个是其他用户的权限。

在android里面,每一个应用可以看做是一个应用,那么我们可以认为,其他应用是没办法访问到这个数据库的。有些情况下,需要访问别人的数据库,这个时候就需要我们的内容提供者了!

比如说今日头条需要访问淘宝的内容,根据淘宝的用户访问习惯来给使用今日头条的用户推荐定向广告,比如说我们开发一个蓝牙电话,需要向手机的联系人这个应用拿到手机的所有联系人,etc.

内容提供者,就是给别人暴露我们本应用里的数据库的,至于想暴露那些数据库,怎么样的其他应用才能访问,就看后面的内容吧!

内容提供者

创建内容提者的步骤:

第一步:编写一个类 继承自内容提供者(ContentProvider)这个例子的目的是让大家知道这个内容提供者的工作流程就够了!实际的使用我们后面再详细说明吧!

四大组件都是要继承自XXX的,总结到了吗?为什么呢?因为它要由系统去创建,生命周期由系统去管理呀!

package com.sunofbeaches.providerdemo.provider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable; /**
* Created by TrillGates on 18/7/15.
* God bless my code!
*/
public class StudentScoreProvider extends ContentProvider {
@Override
public boolean onCreate() {
return false;
} @Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] strings, @Nullable String s, @Nullable String[] strings1, @Nullable String s1) {
return null;
} @Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
} @Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
return null;
} @Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
} @Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}

第二步:注册一下呗!

我们四大组件都要注册,这个知道吧!所以我们需要在AndroidManifest.xml里进行注册!

我们可以发现,有两个参数必填的哦!name我们知道,是这填写空上类的全路径名称。

authorities是什么呢?其实就是令牌,口令,暗号!对得上,匹配得了的,才有权限来操作数据库。

一般来说,这个我们填写报名就OK了!如下:

provider最好加多一项:

android:exported=”true”

我们内容提供者的代码怎么写呢?

我们先暂时写一个查询的代码:

package com.sunofbeaches.providerdemo.provider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri; import com.sunofbeaches.providerdemo.db.StudentScoreDBHelper; /**
* Created by TrillGates on 18/7/15.
* God bless my code!
*/
public class StudentScoreProvider extends ContentProvider { StudentScoreDBHelper mStudentScoreDBHelper;
//定义一个Uri匹配器,参数表示不匹配的时候返回什么值,这里返回的是-1,也就是说-1表示不匹配
private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //这个表示匹配
private static final int CODE_MATCH = 1; static {
//添加匹配规则,前面是authority,这个其实就是我们在配置文件里配置的那个认证字符串
//第二个参数是path,一般表示表名
//第三个表示the code that is returned when a URI is matched,也就是说规则匹配则会返回后面那个code
// 否则返回前面我们指定的默认 UriMatcher.NO_MATCH
mUriMatcher.addURI("com.sunofbeaches.providerdemo","sob_score",CODE_MATCH);
} @Override
public boolean onCreate() {
//注意,这里getContext()
//Only available once
// * {@link #onCreate} has been called
//里面的注释是:只有当onCreate方法被调用以后,getContext这个方法才可用。
mStudentScoreDBHelper = new StudentScoreDBHelper(getContext());
return false;
} @Override
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
int match = mUriMatcher.match(uri);
if (match==CODE_MATCH) {
SQLiteDatabase readableDatabase = mStudentScoreDBHelper.getReadableDatabase();
return readableDatabase.query(StudentScoreDBHelper.TABLE_NAME, strings, s, strings1, s1, null, null);
}else{
throw new IllegalArgumentException("Uri not matching.");
}
} @Override
public String getType( Uri uri) {
return null;
} @Override
public Uri insert( Uri uri, ContentValues contentValues) {
return null;
} @Override
public int delete( Uri uri, String s, String[] strings) {
return 0;
} @Override
public int update( Uri uri, ContentValues contentValues, String s, String[] strings) {
return 0;
}
}

相关的细节已经在注释里了!

接下来,我们要写另外一个应用,通过内容提供者的方式来获取到当前应用的数据了。

package com.sunofbeaches.providerdemoteacher;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} public void getScore(View view) {
ContentResolver contentResolver = this.getContentResolver();
Uri uri = Uri.parse("content://com.sunofbeaches.providerdemo/sob_score");
Cursor cursor = contentResolver.query(uri, null, null, null, null);
while (cursor.moveToNext()) {
Log.d(TAG, "id is -- > " + cursor.getInt(0));
Log.d(TAG, "name is -- > " + cursor.getString(1));
Log.d(TAG, "Chinese is -- > " + cursor.getInt(2));
Log.d(TAG, "Math is -- > " + cursor.getInt(3));
Log.d(TAG, "English is -- > " + cursor.getInt(4));
}
cursor.close();
}
}

执行结果怎么样的呢?

OK,到这里的话,我们成功地读取到了另外一个应用的数据库内容了。

我们稍微总结一下:

首先是有一个数据库,但是这个数据库是别人家的呀,但是这个别人家的数据库有一个内容提供者呢!

我们只要知道对应的认证就可以读取到数据了!

所以,接下来我们就要仿今日头条,仿腾讯QQ,微信这些应用获取到手机号码,也就是通讯录的内容。

获取到通讯录的电话号码

有些时候,我们的应用为了推广,所以希望把用户的通讯录手机号码拿到,自动向他推荐好友。

这个时候,我们就可以通过内容提供者来获取到手机通讯录的联系人了。

以上的内容保存在哪个数据库里呢?

我直接帖出来吧:

/data/data/com.android.providers.contacts/databases/contacts2.db

我们把这个数据库导出来以后,用一些工具,或者用AS自带的工具就可以查看数据库了。

其实重要的表有三张:raw_contacts,data,mimetypes.

row_contacts主要是记录联系人的id,data记录数据,各种数据,包括邮箱呀,联系人名称呀,号码之类的,而mimeytypes用于记录data里的数据类型。因为data里同一个id同一个字段会有多条数据,比如说,id=1的联系人,data1字段,可能有联系人名称,联系人号码,邮箱等等,但是一条记录里会有mimetype的id,这样子就可以知道这条记录是联系人名称还是联系人号码了,具体请看下面的文章吧.

代码步骤如下:

1、获取到内容提供者:

通过context来获取到,也就是

ContentResolver contentResolver = getContentResolver();

2、我们需要Uri,但是不知道是什么,对吧!

但是我们知道,android是开源的呢!所以我们可以去看上层应用的源码:

Android上层应用源码下载

下载下来以后,我们去查看一下源码

解压源码以后,找到:

packages/providers/ContactsProvider/src/com/android/providers/contacts

下面的ContactsProvider2.java这个类,你问我怎么知道是这个类的呢?当然是看AndroidManifest.xml这个配置文件啦,你看一下就知道在哪里了:

接着我们去看看代码:

    /** The authority for the contacts provider */
public static final String AUTHORITY = "com.android.contacts";

但是我们的URI,是不是要加上协议呢,也就是前面那部分:content://

在ContactsContract这个类里,我们看到有一个AUTHORITY_URI常量,也就是说,这个可以用,前面部分的:content://com.android.contacts部分就有了!但是我们还要path的内容呀!

前面我们说了,一般来说,path指的是表名,我们的思路是查询到联系人的id,再通过id去查询号码。

那么我们的URI就有了:AUTHORITY_URI+”/raw_contacts”

代码如下:

package com.sunofbeaches.providerdemoteacher;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View; import static android.provider.ContactsContract.AUTHORITY_URI; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} public void getScore(View view) {
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(AUTHORITY_URI+"/raw_contacts"),//uri
new String[]{"contact_id"},//要查的内容
null,//条件
null,//条件参数
null,//排序
null);
while (cursor.moveToNext()) {
Log.d(TAG, "contact_id -- > " + cursor.getInt(0));
}
}
}

运行结果:

contact_id -- > 1
contact_id -- > 2
contact_id -- > 3
contact_id -- > 4

有了id,以后,我们再去查询对应的号码之类的数据

package com.sunofbeaches.providerdemoteacher;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View; import java.util.ArrayList;
import java.util.List; import static android.provider.ContactsContract.AUTHORITY_URI; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} public void getScore(View view) {
List<Integer> contactIds = new ArrayList<>();
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(AUTHORITY_URI + "/raw_contacts"),//uri
new String[]{"contact_id"},//要查的内容
null,//条件
null,//条件参数
null,//排序
null);
while (cursor.moveToNext()) {
Log.d(TAG, "contact_id -- > " + cursor.getInt(0));
contactIds.add(cursor.getInt(0));
}
cursor.close(); for (Integer contactId : contactIds) {
Cursor dataCursor = contentResolver.query(Uri.parse(AUTHORITY_URI + "/data"),
new String[]{"mimetype", "data1", "raw_contact_id"},
"raw_contact_id=?",
new String[]{contactId + ""},
null);
while (dataCursor.moveToNext()) {
Log.d(TAG, "contact info is -- > " +
"mimetype -- > " + dataCursor.getString(dataCursor.getColumnIndex("mimetype")) +
"\ndata1 -- > " + dataCursor.getString(dataCursor.getColumnIndex("data1")) +
"\nraw_contact_id -- > " + dataCursor.getString(dataCursor.getColumnIndex("raw_contact_id")));
}
Log.d(TAG, "\n|-------------------------------|");
dataCursor.close();
} }
}

运行结果如下:

09-16 01:13:22.814 6309-6309/com.sunofbeaches.providerdemoteacher D/MainActivity: contact_id -- > 1
contact_id -- > 2
contact_id -- > 3
contact_id -- > 4
09-16 01:13:22.818 6309-6309/com.sunofbeaches.providerdemoteacher D/MainActivity: contact info is -- > mimetype -- > vnd.android.cursor.item/phone_v2
data1 -- > 1 234-567-8901
raw_contact_id -- > 1
contact info is -- > mimetype -- > vnd.android.cursor.item/name
data1 -- > Zhangsan
raw_contact_id -- > 1
|-------------------------------|
09-16 01:13:22.823 6309-6309/com.sunofbeaches.providerdemoteacher D/MainActivity: contact info is -- > mimetype -- > vnd.android.cursor.item/phone_v2
data1 -- > 1180-974-6573
raw_contact_id -- > 2
contact info is -- > mimetype -- > vnd.android.cursor.item/name
data1 -- > Lisi
raw_contact_id -- > 2
|-------------------------------|
09-16 01:13:22.826 6309-6309/com.sunofbeaches.providerdemoteacher D/MainActivity: contact info is -- > mimetype -- > vnd.android.cursor.item/phone_v2
data1 -- > 668-9
raw_contact_id -- > 3
contact info is -- > mimetype -- > vnd.android.cursor.item/name
data1 -- > HuangDachui
raw_contact_id -- > 3
|-------------------------------|
09-16 01:13:22.829 6309-6309/com.sunofbeaches.providerdemoteacher D/MainActivity: contact info is -- > mimetype -- > vnd.android.cursor.item/phone_v2
data1 -- > 1 353-232-3332
raw_contact_id -- > 4
contact info is -- > mimetype -- > vnd.android.cursor.item/name
data1 -- > Chenzao
raw_contact_id -- > 4
|-------------------------------|

这样子,我们就可以获取到了联系人的数据了。

接着我们整理一下,就可以得到联系人名称和号码了,如果以后你还需要邮箱地址的话,获取方式也是一样的。

package com.sunofbeaches.providerdemoteacher;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View; import java.util.ArrayList;
import java.util.List; import static android.provider.ContactsContract.AUTHORITY_URI; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} class ContactInfo {
public int id;
public String name;
public String phoneNum; @Override
public String toString() {
return "ContactInfo{" +
"id=" + id +
", name='" + name + '\'' +
", phoneNum='" + phoneNum + '\'' +
'}';
}
} public void getScore(View view) {
List<ContactInfo> contactInfos = new ArrayList<>();
List<Integer> contactIds = new ArrayList<>();
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(AUTHORITY_URI + "/raw_contacts"),//uri
new String[]{"contact_id"},//要查的内容
null,//条件
null,//条件参数
null,//排序
null);
while (cursor.moveToNext()) {
//Log.d(TAG, "contact_id -- > " + cursor.getInt(0));
contactIds.add(cursor.getInt(0));
}
cursor.close(); for (Integer contactId : contactIds) {
ContactInfo contactInfo = new ContactInfo();
contactInfo.id = contactId;
Cursor dataCursor = contentResolver.query(Uri.parse(AUTHORITY_URI + "/data"),
new String[]{"mimetype", "data1", "raw_contact_id"},
"raw_contact_id=?",
new String[]{contactId + ""},
null);
while (dataCursor.moveToNext()) {
// Log.d(TAG, "contact info is -- > " +
// "mimetype -- > " + dataCursor.getString(dataCursor.getColumnIndex("mimetype")) +
// "\ndata1 -- > " + dataCursor.getString(dataCursor.getColumnIndex("data1")) +
// "\nraw_contact_id -- > " + dataCursor.getString(dataCursor.getColumnIndex("raw_contact_id"))); String mimetype = dataCursor.getString(dataCursor.getColumnIndex("mimetype"));
if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
String phoneNum = dataCursor.getString(dataCursor.getColumnIndex("data1"));
contactInfo.phoneNum = phoneNum;
} if ("vnd.android.cursor.item/name".equals(mimetype)) {
String name = dataCursor.getString(dataCursor.getColumnIndex("data1"));
contactInfo.name = name;
} }
//Log.d(TAG, "\n|-------------------------------|");
dataCursor.close();
contactInfos.add(contactInfo);
} //查询完成:输出结果
for (ContactInfo contactInfo : contactInfos) {
Log.d(TAG, "contactInfo -- > " + contactInfo);
} }
}

输出结果:

contactInfo -- > ContactInfo{id=1, name='Zhangsan', phoneNum='1 234-567-8901'}
contactInfo -- > ContactInfo{id=2, name='Lisi', phoneNum='1180-974-6573'}
contactInfo -- > ContactInfo{id=3, name='HuangDachui', phoneNum='668-9'}
contactInfo -- > ContactInfo{id=4, name='Chenzao', phoneNum='1 353-232-3332'}

到此,我们就拿到了手机联系人的地址了。不过要注意的是,这需要权限呢:

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

读取联系人的权限,有了这些知识,是不是可以去做手机联系人备份了呢?

我们获取到了联系人,接下来,是不是要操作短信呢?

短信相关的内容提供者!

跟前面一样,我们首先要知道,短信是存在哪个数据库里的,存在什么表里的。这样子我们都知道怎么样去操作它。

/data/data/com.android.providers.telephony/databases/mmssms.db

在这个路径下就可以找到信息相关的内容了

但是我们没有短信内容,就创建一些吧

我们把数据库导出来

数据库是怎么样的呢?

address,目的发送的号码,比如说我要发送给

data,也就是时间

read表示的是是否已经读过,1表示已经读了,0表示未读

body表示内容

当然啦,还有其他的字段,比如说表示状态的,这个大家可以自己去看看啦!

接下来,我们就要写代码了,模拟给自己的手机发送一条短信。

首先,我们知道uri吧,怎么办?看代码呗

packages/providers/TelephonyProvider

先看清单文件

这一个Provider就是短信内容提供者的类了,同学们应该也看到了,需要读写的权限,需要在我们的清单文件里声明短信的读写权限

我们尝试一下读取短信的内容:


public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
} public void sendMsm(View view) { //获取到内容提供者解析器
ContentResolver contentResolver = this.getContentResolver();
Uri uri = Uri.parse("content://sms/");
Cursor query = contentResolver.query(uri, null, null, null, null);
while (query.moveToNext()) {
String body = query.getString(query.getColumnIndex("body"));
Log.d(TAG, "body -- > " + body);
}
}
}

运行结果:

09-17 14:39:43.601 3334-3334/? D/MainActivity: body -- > Your phone has been lost
09-17 14:39:43.601 3334-3334/? D/MainActivity: body -- > 这是一条来自模拟器的短信

OK,到这里就可以获取到了 短信的内容了。

这样是不是有一个应用场景,监听到有短信来的广播,然后去读取短信,自动获取到验证码,自动填充到你的应用里呢。

总结一下吧

我们当我们需要暴露数据给第三方使用时,就需要知道前面的内容提供者怎么写了。

如果是读取别人的内容提供者内容,就需要后面的知识了。

学习内容提供者的话,顺便也要把数据库的知识巩固一下。

内容提供者的使用场景比较少,能列举出来就那么几个了。我只有在蓝牙电话上使用过内容提供者,当然啦,在我们的平时开发中,如果做社交软件的话,也需要获取到用户的联系人列表

然后保存到后台去,向用户推荐对应的好友用户。

最新文章

  1. java web学习总结(二) -------------------TOMCAT使用帮助(一)
  2. 关于举办 2015年 Autodesk 助力云应用项目开发活动通知
  3. Winform开发主界面菜单的动态树形列表展示
  4. 简单可用好实现的 HA 高可用设计
  5. HDU 1087 Super Jumping! Jumping! Jumping
  6. JavaWeb项目开发案例精粹-第3章在线考试系统-006实体层
  7. 地形图比例尺、等高距和DEM分辨率关系
  8. leetcode@ [87] Scramble String (Dynamic Programming)
  9. 分西瓜(DFS)
  10. siverlight 后台动态设置图片路径的总结
  11. JUnit----单元测试
  12. sql 中三大范式详解
  13. dede织梦 arclist标签完美支持currentstyle属性
  14. [Swift]LeetCode796. 旋转字符串 | Rotate String
  15. 第一册:lesson 101。
  16. csrfguard3.1 部署笔记
  17. GET和POST中文乱码的解决方法
  18. PHP-不同Str 拼接方法性能对比
  19. [翻译] USING GIT IN XCODE [1] 在XCODE中使用GIT[1]
  20. Java普通编程和Web网络编程准备工作

热门文章

  1. 分享自己亲测过的Visualstudio 2019中开发Typescript时,设置自动编译生成js,无需手工运行命令生成的方法。
  2. cmd窗口中java命令报错。错误:找不到或无法加载主类 java的jdk安装过程中踩过的坑
  3. [常用工具] Python视频解码库DeFFcode使用指北
  4. [能源化工] TE田纳西-伊斯曼过程数据集
  5. 让 Win8.1 微软拼音新体验模式支持 Metro 应用
  6. angular 引入服务报错global is not defined----angular11引入service报错Can&#39;t resolve &#39;@core/net/aa/aa.service&#39; in &#39;D:\pro&#39;
  7. 【一句话】Java8后abstract class和interface的区别
  8. IDEA创建新的模块springboot
  9. Listen 1音乐播放器
  10. 远程控制 todesk