Android四大组件——ContentProvider开发

本文详细介绍了Android中ContentProvider的概念及使用方法,并提供了具体的代码示例。包括如何创建ContentProvider,实现数据共享,以及如何使用ContentProvider访问Android内置数据如通讯录。

一、简单介绍

1、适用场景

1) ContentProvider 为存储和读取数据提供了统一的接口
2) 使用 ContentProvider,应用程序可以实现数据共享

3) android内置的许多数据都是使用ContentProvider形式,供开发者调用的(如视频,音频,图片,通讯录)等

2、相关概念

ContentProvider简介

       当应用继承 ContentProvider 类,并重写该类用于提供数据和存储数据的方法,就可以向其他应用共享其数据。虽然使用其他方法也可以对外共享数据,但数据访问方式会因数据存储的方式而不同,如:采用文件方式对外共享数据,需要进行文件操作读写数据;采用 sharedpreferences 共享数据,需要使用 sharedpreferences API 读写数据。而使用 ContentProvider 共享数据的好处是统一了数据访问方式。

Uri类简介

      Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
      在Content Provider中使用的查询字符串有别于标准的SQL查询。很多诸如 select、add、delete、modify 等操作我们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 代表数据的路径,和一个可选的标识数据的ID。以下是一些示例URI:

content://media/internal/images  这个URI将返回设备上存储的所有图片
content://contacts/people/  这个URI将返回设备上的所有联系人信息
content://contacts/people/45 这个URI返回单个结果(联系人信息中 ID 为 45 的联系人记录)

        尽管这种查询字符串格式很常见,但是它看起来还是有点令人迷惑。为此,Android提供一系列的帮助类(在 android.provider 包下),里面包含了很多以类变量形式给出的查询字符串,这种方式更容易让我们理解一点,因此,如上面 content://contacts/people/45 这个URI就可以写成如下形式:

Uri person = ContentUris.withAppendedId(People.CONTENT_URI,  45);

二、ContentProvider使用实例

1、创建数据库管理类,并创建所需的表和数据

public class MyDatabaseHelper extends SQLiteOpenHelper {
    private static final String  DATABASE_NAME = "Users.db";
    private static final int  DATABASE_VERSION= 1;
    private static final String TABLE_NAME= "User";
 
	public MyDatabaseHelper(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}
 
	@Override
	public void onCreate(SQLiteDatabase db) {
		//创建用于存储数据的表
    	db.execSQL("Create table " + TABLE_NAME + "( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);");
	}
 
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
        onCreate(db);
	}
}

2、创建一个类,定义uri并存储用户名称并显示所有的用户名称(使用 SQLLite数据库存储这些数据)

public class MyUsers {
	public static final String AUTHORITY = "com.cx.datastored.MyContentProvider";
 
    //BaseColumn类中已经包含了 _id字段
	public static final class User implements BaseColumns {
	//定义uri
        public static final Uri CONTENT_URI  = Uri.parse("content://com.cx.datastored.MyContentProvider");
        // 表数据列
        public static final String USER_NAME = "USER_NAME";
    }
}

 3、重写ContentProvider,完成自己的MyContentProvider,这里只做了插入和查询功能

public class MyContentProvider extends ContentProvider {
	private SQLiteDatabase sqlDB;
    private MyDatabaseHelper dbHelper;
    private static final String TABLE_NAME= "User";
 
	@Override
	public int delete(Uri arg0, String arg1, String[] arg2) {
		//根据Uri删除arg1指定的条件所匹配的全部记录
		return 0;
	}
 
	@Override
	public String getType(Uri arg0) {
		//返回当前Uri的MIME类型,如果该URI对应的数据可能包含多条记录,那么MIME类型字符串就是以vnd.android.dir开头
		//如果该URI对应的数据只有一条,该MIME类型字符串就是以vnd..android.cursor.item开头
		return null;
	}
 
	@Override
	public Uri insert(Uri arg0, ContentValues arg1) {
		//根据Uri插入arg1对应的数据
		sqlDB = dbHelper.getWritableDatabase();
        long rowId = sqlDB.insert(TABLE_NAME, "", arg1);
        if (rowId > 0) {
            Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
            getContext().getContentResolver().notifyChange(rowUri, null);
            return rowUri;
        }
        throw new SQLException("Failed to insert row into " + arg0);
	}
 
	@Override
	public boolean onCreate() {
		// 在ContentProvider创建后被调用
		 dbHelper = new MyDatabaseHelper(getContext());
	     return (dbHelper == null) ? false : true;
	}
 
	@Override
	public Cursor query(Uri arg0, String[] arg1, String arg2, String[] arg3,
			String arg4) {
		//根据Uri查询出arg2指定的条件所匹配的全部记录,并且可以指定查询那些列,以什么方式(arg4)排序
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        qb.setTables(TABLE_NAME);
        Cursor c = qb.query(db, arg1, arg2, null, null, null, arg4);
        c.setNotificationUri(getContext().getContentResolver(), arg0);
        return c;
	}
 
	@Override
	public int update(Uri arg0, ContentValues arg1, String arg2, String[] arg3) {
		//根据Uri修改arg2指定的条件所匹配的全部记录
		return 0;
	}
}

4、 在AndroidMenifest.xml中使用<provider>标签来设置ContentProvider

<provider
      android:exported="true"
      android:name="com.cx.datastored.MyContentProvider"
      android:authorities="com.cx.datastored.MyContentProvider" >
</provider>

         注:android:exported="true"如果不设置,在本应用中访问数据时正常,但在其他应用中访问时就会出现异常。

5、在本应用中测试

public class MainActivity extends Activity {
 
	@SuppressLint("SdCardPath")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		insertRecord("user");
        displayRecords();
	}
	
	private void insertRecord(String userName) {
		ContentValues values = new ContentValues();
        values.put(MyUsers.User.USER_NAME, userName);
        getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
    }
 
	private void displayRecords() {
    	ContentResolver cr = getContentResolver();  
        String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
        Uri myUri = MyUsers.User.CONTENT_URI;
        Cursor cur = cr.query(myUri, columns, null, null, null);  
        if (cur.moveToFirst()) {
            String id = null;
            String userName = null;
            do {
                id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
                userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
                Toast.makeText(this, id + " " + userName, Toast.LENGTH_LONG).show();
           } while (cur.moveToNext());
       }
    }
}

 6、在其他应用中测试,这里需要新建一个工程。

1)新建工程中创建一个与上面2相同的类

public class MyUsers {
	public static final String AUTHORITY = "com.cx.datastored.MyContentProvider";
 
    //BaseColumn类中已经包含了 _id字段
	public static final class User implements BaseColumns {
	//定义uri
        public static final Uri CONTENT_URI  = Uri.parse("content://com.cx.datastored.MyContentProvider");
        // 表数据列
        public static final String USER_NAME = "USER_NAME";
    }
}

2)测试代码也与5相同,只是他们是不同的应用

public class MainActivity extends Activity {
 
	@SuppressLint("SdCardPath")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		insertRecord("user");
        displayRecords();
	}
	
	private void insertRecord(String userName) {
		ContentValues values = new ContentValues();
        values.put(MyUsers.User.USER_NAME, userName);
        getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
    }
 
	private void displayRecords() {
    	ContentResolver cr = getContentResolver();  
        String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
        Uri myUri = MyUsers.User.CONTENT_URI;
        Cursor cur = cr.query(myUri, columns, null, null, null);  
        if (cur.moveToFirst()) {
            String id = null;
            String userName = null;
            do {
                id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
                userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
                Toast.makeText(this, id + " " + userName, Toast.LENGTH_LONG).show();
           } while (cur.moveToNext());
       }
    }
}

源码下载

测试应用源码

        注意:只有在先运行了上面的应用后,再运行测试应用源码才会看到效果。

        由于ContentProvider的主要用途是不同应用间的数据共享,所以在开发时很少会用到。例如,一个公司的一系列产品中的的某些数据需要相互使用。

三、获取通讯录实例

1、实现代码

public class MainActivity extends Activity {
 
	@SuppressLint("SdCardPath")
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		ContentResolver cr = getContentResolver();
		//向联系人中插入一条数据
		ContentValues values = new ContentValues();
		Uri uri = cr.insert(RawContacts.CONTENT_URI, values);
		//解析uri
		Long raw_contacts_id = ContentUris.parseId(uri);
		values.clear();
		//插入人名,指定联系人和插入行
		values.put(StructuredName.RAW_CONTACT_ID, raw_contacts_id);
		//追加插入联系人信息
		values.put(StructuredName.DISPLAY_NAME, "张三");
		values.put(StructuredName.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
		uri = cr.insert(Data.CONTENT_URI, values);
		
		//插入电话信息
		values.clear();
		values.put(Phone.RAW_CONTACT_ID, raw_contacts_id);
		values.put(Phone.NUMBER, "123434556456");
		values.put(Phone.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
		uri = cr.insert(Data.CONTENT_URI, values);
		
		
		//查询通讯录数据
		Cursor c = cr.query(ContactsContract.Contacts.CONTENT_URI, new String[]{Contacts._ID, Contacts.DISPLAY_NAME}, null, null, null);
		if(c != null){
			while (c.moveToNext()) {
				//如果不知道对应字段名称可以写成Contacts._ID和Contacts.DISPLAY_NAME
				int id = c.getInt(c.getColumnIndex("_id"));
				Log.e(">>>>>>>>>>>>", "_id" + id);
				Log.e(">>>>>>>>>>>>", "name" + c.getString(c.getColumnIndex("display_name")));
				
				//查询联系人电话号码,必须使用ID获取。通过ID查询号码和类型
				Cursor c1 = cr.query(Phone.CONTENT_URI, new String[]{Phone.NUMBER, Phone.TYPE}, Phone.CONTACT_ID + "=" + id, null, null);
				if(c1 != null){
					while (c1.moveToNext()) {
						int type = c1.getInt(c1.getColumnIndex(Phone.TYPE));
						if(type == Phone.TYPE_HOME){
							Log.e(">>>>>>>>>>>>", "家庭电话:" + c1.getString(c1.getColumnIndex(Phone.NUMBER)));
						}else if (type == Phone.TYPE_MOBILE){
							Log.e(">>>>>>>>>>>>", "手机:" + c1.getString(c1.getColumnIndex(Phone.NUMBER)));
						}
					}
					c1.close();
				}
				
				//根据联系人id查询出联系人的邮箱地址
				Cursor c2 = cr.query(ContactsContract.CommonDataKinds.Email.CONTENT_URI, new String[]{Email.DATA, Email.TYPE}, Email.CONTACT_ID + "=" + id, null, null);
				if(c2 != null){
					while (c2.moveToNext()) {
						int type = c2.getInt(c2.getColumnIndex(Email.TYPE));
						if(type == Email.TYPE_WORK){
							Log.e(">>>>>>>>>>>>", "工作邮箱:" + c2.getString(c2.getColumnIndex(Email.DATA)));
						}
					}
					c2.close();
				}
			}
		}
		c.close();
	}
}

 2、添加权限

<!-- 查询联系人 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- 添加联系人 -->
<uses-permission android:name="android.permission.WRITE_CONTACTS" />

下载源码s

四、FW中常见使用

        对于 Framework 开发来说,经常需要向 APP 暴露一些系统的状态,例如电源、蓝牙、wifi状态等。这时可以可以通过 get 方法以及回调函数获取,但这样如果是第三方应用需要通过提供jar包,调取里面的方法,jar包更新和调用都不是太方便。所以我们可以使用 ContentProvider 进行数据存储和读取。

1、存储用户id

Settings.System.putInt(getContentResolver(), "user_name_id", 1);

 2、读取用户id

int userId = Settings.System.getInt(getContentResolver(), "user_name_id", -1);

 3、实时监听

//监听内容变化
getContentResolver().registerContentObserver(Settings.System.getUriFor("user_name_id"), true,
        new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        //监听到内容有变化
        int userId = Settings.System.getInt(getContentResolver(), "user_name_id", -1);
    }
});

更多内容>>

参考文章:Android之ContentProvider总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

c小旭

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值