自定义内容提供者
步骤:
定义一个类继承ContentProvider
public class MyProvider extends ContentProvider {...}
注册,要添加主机名
<!-- authorities 它就相当于是一个口令,外部应用如果想操作这个内容提供者, 为他干活,必须要与内容提供者的口令一致才可以。 --> <provider android:name="com.liuwen.providerdemo.MyProvider" android:authorities="com.liuwen.providerdemo.BANK"> </provider>
定义一个Uri匹配器
// 定义一个uri的匹配器,指定里面如果传递过来的uri,一旦没有匹配,将返回的int值 -1 static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
预设一些匹配规则
static { // 一开始就给这个匹配器预设一些匹配规则,如果传递过来的uri // 匹配了authority 和 path ,那么将返回后面的匹配码 //数据库里面有可能有多张表,为了规范匹配规则,path一般都写表名,根据表的名字来判定 //当前访问的是哪个表的数据 ,如果有多张表,并且还想把这些所有表都暴露出来,那么这个匹配规则 //就应该写多条语句了。并且注意返回的code不能一样。 matcher.addURI("com.liuwen.providerdemo.BANK", "account", 200); // matcher.addURI("com.liuwen.providerdemo.BANK", "stu", 201); }
编写CRUD方法
public Uri insert(Uri arg0, ContentValues arg1) { //只有authority 和 path 都写对了matcher.match(arg0)才会返回200 int code = matcher.match(arg0); if(code==200){ MyDb mydb = new MyDb(getContext()); SQLiteDatabase database = mydb.getWritableDatabase(); database.insert("account", "name=?,money=?", arg1); System.out.println("insert方法被调用了..."); } return null; }
在其他应用操作内容提供者
public void insert(View v) { // 其他应用通过内容提供者 content provider 暴露数据 // 本应用想操作内容提供者---内容解析者 // 1.通过上下文得到内容解析者 ContentResolver resolver = getContentResolver(); // 2. 要访问内容提供者,必须要指定口令 // 由于内容提供者设置了口令,既有主机名也有path路径,所以这里为了能够匹配成功 // 必须加上主机名还有path路径名 Uri uri = Uri.parse("content://com.liuwen.providerdemo.BANK/account"); //定义要插入的数据 ContentValues values = new ContentValues(); values.put("name", "zhangsan"); values.put("money", 5000); // 3.调用内容提供者中的添加方法 resolver.insert(uri, values); }
学习内容提供者的目的
- 一般在开发当中都不会把自己的数据暴露出来,也就是编写自定义的内容提供者的几率非常之小。
- 去访问系统应用暴露出来的数据,如:备份来联系人和备份短信
用短信内容提供者去查询短信内容
查询短信
public static List<Sms> querySms(Context context) {
// 1.得到一个内容解析者对象
ContentResolver resolver = context.getContentResolver();
// 2.指定要访问内容提供者,必须要口令,查看短信应用的内容提供者的源码获得
/*
* <provider android:name="SmsProvider" android:authorities="sms"
* android:multiprocess="true"
* android:readPermission="android.permission.READ_SMS"
* android:writePermission="android.permission.WRITE_SMS" />
*/
Uri uri = Uri.parse("content://sms");
//查询的字段.这个我们可以去把短信内容提供者的数据库下载下来查看自己所需要的字段
//这些查询语句和使用SqliteOpenHelper所使用的crud操作一样
String []fields = {"address","type","date","date_sent","body"};
Cursor query = resolver.query(uri, fields, null, null, null);
List<Sms> list = new ArrayList<Sms>();
while(query.moveToNext()){
Sms sms = new Sms();
String address = query.getString(query.getColumnIndex("address"));
String type = query.getString(query.getColumnIndex("type"));
String date = query.getString(query.getColumnIndex("date"));
String date_sent = query.getString(query.getColumnIndex("date_sent"));
String body = query.getString(query.getColumnIndex("body"));
sms.setAddress(address);
sms.setType(type);
sms.setDate(date);
sms.setDate_sent(date_sent);
sms.setBody(body);
list.add(sms);
}
return list;
}
向短信内容提供者中添加数据
/**
*
* @param context 上下文对象
* @param sms 要添加的短信对象
* @return 返回true:说明添加成功,返回false说明添加失败
*/
public static boolean insertSms(Context context,List<Sms> smss){
//得到内容解析者对象
ContentResolver resolver = context.getContentResolver();
Uri uri = Uri.parse("content://sms");
ContentValues values = new ContentValues();
if(smss!=null&&smss.size()>0){
for (Sms sms2 : smss) {
values.put("address",sms2.getAddress() );
values.put("date",sms2.getDate() );
values.put("date_sent",sms2.getDate_sent() );
values.put("body",sms2.getBody() );
values.put("type",sms2.getType() );
//添加数据
resolver.insert(uri, values);
}
//所有数据插入成功
return true;
}else{
//失败
return false;
}
}
操作联系人内容提供者数据
查询所有的联系人
/**
*
* @param context
* 上下文
* @return 返回的是一个List<Contacts>对象,包含所有联系人的信息
*/
public static List<Contacts> queryContacts(Context context) {
List<Contacts> list = new ArrayList<Contacts>();
ContentResolver resolver = context.getContentResolver();
/**
//联系人信息要通过多张表来查询,所以复杂一点,可以把联系人内容提供者的
//数据库contacts导出来,研究一下三张表的关系raw_contacts, data, mimetypes
//联系人内容提供者的注册的一小段
* <provider android:name="ContactsProvider2"
* android:authorities="contacts;com.android.contacts"
* android:label="@string/provider_label" android:multiprocess="false"
* android:readPermission="android.permission.READ_CONTACTS"
* android:writePermission="android.permission.WRITE_CONTACTS">
*/
// 找到系统定义的内容提供者定义的路径
// 查询raw_contacts表的时候用这个
Uri raw_contact_uri = Uri
.parse("content://com.android.contacts/raw_contacts");
// 查询data表的时候用这个
Uri data_uri = Uri.parse("content://com.android.contacts/data");
// 查询到了raw_contact表中的contact_id列
Cursor query_contact = resolver.query(raw_contact_uri,
new String[] { "_id" }, null, null, null);
while (query_contact.moveToNext()) {
// id查询到了
String id = query_contact.getString(query_contact
.getColumnIndex("_id"));
System.out.println("id是多少"+id);
// 这个id要特别的注意,一定要做一下判空操作,不然会查询出null数据,或者直接报错
// 因为android联系人里面的数据库设计的比较特殊,你删除数据的时候,并没有直接把数据删除掉,只是把id给置空了
// 我们判断一下id==null时,我们就结束本次循环
if (id == null) {
continue;
}
// 进到这里说明有数据了
Contacts contact = new Contacts();
//这个id也给存起来
contact.setContactId(id);
// 再通过id去查询数据和数据类型
Cursor query_data = resolver.query(data_uri, new String[] {"data1", "mimetype" }, "raw_contact_id=?",new String[] { id }, null);
while (query_data.moveToNext()) {
String data = query_data.getString(query_data
.getColumnIndex("data1"));
String mimetype = query_data.getString(query_data
.getColumnIndex("mimetype"));
if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
contact.setEmail(data);
} else if ("vnd.android.cursor.item/nickname".equals(mimetype)) {
contact.setNickname(data);
} else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {
contact.setNumber(data);
} else if ("vnd.android.cursor.item/name".equals(mimetype)) {
contact.setName(data);
}
}
// 关闭资源
query_data.close();
if (contact != null) {
// 到这里为止,说明循环完一个人的信息了
list.add(contact);
}
}
// 关闭资源
query_contact.close();
return list;
}
往联系人中添加数据
/**
*
* @param context
* 上下文
* @param cont
* 联系人信息对象
* @return 返回 true说明添加成功,返回false说明添加失败
*/
public static boolean insertContacts(Context context, Contacts cont) {
ContentResolver resolver = context.getContentResolver();
// 查询raw_contacts表的时候用这个
Uri raw_contact_uri = Uri
.parse("content://com.android.contacts/raw_contacts");
// 查询data表的时候用这个
Uri data_uri = Uri.parse("content://com.android.contacts/data");
// 先查询raw_contacts表的id,看看现在有几条记录,然后在这个记录的基础之上添加一条记录
// 然后再去data表通过这个id添加记录
// 查询raw_contacts表的id,并且倒序显示
Cursor query = resolver.query(raw_contact_uri, new String[] { "_id" },
null, null, "_id desc");
int id = 0;
if (query.moveToFirst()) {
// 等下要添加的ID,
id = query.getInt(query.getColumnIndex("_id")) + 1;
} else {
// 进这里说明数据库第一次添加
id = 1;
}
// 有数据我才去添加,传进来的数据是空的话,那就直接返回false
if (cont != null) {
// 要添加的id准备好了,现在可以在raw_contacts表中插入id了
ContentValues id_values = new ContentValues();
id_values.put("_id", id);
resolver.insert(raw_contact_uri, id_values);
// id插入完成,接下来可以通过这个插入的id去data表中添加数据了
// data1,mimetype ,raw_contact_id
// 添加姓名
ContentValues data_name = new ContentValues();
data_name.put("data1", cont.getName());
data_name.put("raw_contact_id", id);
data_name.put("mimetype", "vnd.android.cursor.item/name");
resolver.insert(data_uri, data_name);
// 添加邮箱
ContentValues data_email = new ContentValues();
data_email.put("data1", cont.getEmail());
data_email.put("raw_contact_id", id);
data_email.put("mimetype", "vnd.android.cursor.item/email_v2");
resolver.insert(data_uri, data_email);
// 添加昵称
ContentValues data_nickname = new ContentValues();
data_nickname.put("data1", cont.getNickname());
data_nickname.put("raw_contact_id", id);
data_nickname.put("mimetype", "vnd.android.cursor.item/nickname");
resolver.insert(data_uri, data_nickname);
// 添加号码
ContentValues data_number = new ContentValues();
data_number.put("data1", cont.getNumber());
data_number.put("raw_contact_id", id);
data_number.put("mimetype", "vnd.android.cursor.item/phone_v2");
resolver.insert(data_uri, data_number);
// 能走到这里说明四个都添加成功了
return true;
} else {
return false;
}
}
界面提醒
- Toast
- Dialog-对话框
- notification – 通知
通知是运行在另外一个进程中 , systemui
状态栏通知:
//低版本使用的状态栏
public void status(View view) {
// 1.得到通知管理器
NotificationManager nm = (NotificationManager) getSystemService(this.NOTIFICATION_SERVICE);
// 2.构建通知
Notification notification = new Notification(
android.R.drawable.stat_notify_chat, "这是提示信息",
System.currentTimeMillis());
// 3.设置通知点击事件
Intent intent = new Intent(this, MainActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 100,
intent, 0);
notification.setLatestEventInfo(this, "通知的标题", "通知的内容", contentIntent);
notification.flags = Notification.FLAG_AUTO_CANCEL; // 点击通知后自动消失
// 4.发送通知
nm.notify(100, notification);
}
//高版本(16)的状态栏通知,这个还是自定义的状态栏
public void hightstatus(View view) {
// 1.得到通知管理器
NotificationManager nm = (NotificationManager) getSystemService(this.NOTIFICATION_SERVICE);
// 2.构建通知
Notification notification = new Notification();
notification.icon = android.R.drawable.stat_notify_missed_call; // 图标
notification.tickerText = "有未接电话哦";
RemoteViews contentView = new RemoteViews(getPackageName(), R.layout.content);
notification.contentView = contentView; // 设置通知显示的布局
// 设置通知点击的事件
Intent intent = new Intent(this,SecondActivity.class);
PendingIntent contentIntent = PendingIntent.getActivity(this, 100, intent, 0);
notification.contentIntent = contentIntent;
// 设置点击通知后自动消失
notification.flags = Notification.FLAG_AUTO_CANCEL;
// 发出通知
nm.notify(100, notification);
}
内容观察者
在定义内容提供者的地方注册一个内容观察者的应用
//注册了内容观察者的应用 //参数一:就是数据变化时,给的uri,观察者使用该uri,观察该应用的数据变化 //参数二:如果为null,那么是所有的,如果为指定的ContentObserver,那只有该观察者可以接收到。 getContext().getContentResolver().notifyChange( Uri.parse("content://xxx.ooo.xxx"), null);
定义一个内容观察者应用
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //要符合在内容提供者中注册的uri一样 Uri uri = Uri.parse("content://xxx.ooo.xxx"); //注册一个内容观察者 getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler())); } private class MyObserver extends ContentObserver{ public MyObserver(Handler handler) { super(handler); } //实现onchange方法 @Override public void onChange(boolean selfChange) { System.out.println("数据发生了变化,被我抓住了"); super.onChange(selfChange); } } }