編輯:關於Android編程
ContentProvider 在android中的作用是對外共享數據,也就是說你可以通過ContentProvider把應用中的數據共享給其他應用訪問,其他應用可以通過ContentProvider 對你應用中的數據進行添刪改查。關於數據共享,以前我們學習過文件操作模式,知道通過指定文件的操作模式為Context.MODE_WORLD_READABLE 或Context.MODE_WORLD_WRITEABLE同樣也可以對外共享數據。那麼,這裡為何要使用ContentProvider 對外共享數據呢?是這樣的,如果采用文件操作模式對外共享數據,數據的訪問方式會因數據存儲的方式而不同,導致數據的訪問方式無法統一,如:采用xml文件對外共享數據,需要進行xml解析才能讀取數據;采用sharedpreferences共享數據,需要使用sharedpreferences API讀取數據。
使用ContentProvider對外共享數據的好處是統一了數據的訪問方式。
ContentProvider的原理是按照一定規則暴露自己的接口給其它應用來訪問自己應用的數據(其實就是自定義增刪改查接口並暴露出去,讓別的應用訪問自己的數據)。
ContentResolver就是按照一定規則訪問內容提供者的數據(其實就是調用內容提供者自定義的接口來操作它的數據)。
采用了authorities(主機名/域名)對它進行唯一標識,你可以把 ContentProvider看作是一個網站(想想,網站也是提供數據者),authorities 就是他的域名:
例如:要得到所有person記錄的Uri為content://cn.itcast.provider.personprovider/person,那麼返回的MIME類型字符串應該為:“vnd.android.cursor.dir/person”。如果要操作的數據屬於非集合類型數據,那麼MIME類型字符串應該以vnd.android.cursor.item/開頭,例如:得到id為10的person記錄,Uri為content://cn.itcast.provider.personprovider/person/10,那麼返回的MIME類型字符串應該為:“vnd.android.cursor.item/person”。
掌握它們的使用,會便於我們的開發工作。
UriMatcher類用於匹配Uri,它的用法如下:
首先第一步把你需要匹配Uri路徑全部給注冊上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼
UriMatcher sMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://cn.itcast.provider.personprovider/person路徑,返回匹配碼為1
sMatcher.addURI(“cn.itcast.provider.personprovider”, “person”, 1);//添加需要匹配uri,如果匹配就會返回匹配碼
//如果match()方法匹配content://cn.itcast.provider.personprovider/person/230路徑,返回匹配碼為2
sMatcher.addURI(“cn.itcast.provider.personprovider”, “person/#”, 2);//#號為通配符
switch (sMatcher.match(Uri.parse("content://cn.itcast.provider.personprovider/person/10"))) {
case 1
break;
case 2
break;
default://不匹配
break;
}
注冊完需要匹配的Uri後,就可以使用sMatcher.match(uri)方法對輸入的Uri進行匹配,如果匹配就返回匹配碼,匹配碼是調用addURI()方法傳入的第三個參數,
假設匹配content://cn.itcast.provider.personprovider/person路徑,返回的匹配碼為1
package com.itheima.transaction;
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;
public class AccountProvider extends ContentProvider {
private static final int QUEYSUCESS = 0; // ctrl + shift + X(變大寫) 變小寫 + y
private static final int INSERTSUCESS = 1;
private static final int UPDATESUCESS = 2;
private static final int DELSUCESS = 3;
//1 想使用內容提供者 必須定義 匹配規則 code:定義的匹配規則 如果 匹配不上 有一個返回碼 -1
static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
private MyOpenHelper helper;
//2 我要添加匹配規則
static{
//開始添加匹配規則
/**
* authority 主機名 通過主機名來訪問我暴露的數據
* path 你也可以隨意 寫 com.itheima.contentprovider/query
* code 匹配碼
*/
matcher.addURI("com.itheima.contentprovider", "query", QUEYSUCESS);
//添加插入匹配規則
matcher.addURI("com.itheima.contentprovider", "insert", INSERTSUCESS);
//添加更新匹配規則
matcher.addURI("com.itheima.contentprovider", "update", UPDATESUCESS);
//添加刪除匹配規則
matcher.addURI("com.itheima.contentprovider", "delete", DELSUCESS);
}
@Override
public boolean onCreate() {
helper = new MyOpenHelper(getContext());
return false;
}
//Uri 范圍比較大 不但可以指定 tel: 可以定義很多語法
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//傳遞過來的uri 是否和我們定義的匹配規則 匹配
int match = matcher.match(uri);
if (match == QUEYSUCESS ) {
//說明匹配成功
SQLiteDatabase db = helper.getReadableDatabase(); //獲取數據庫對象
Cursor cursor = db.query("info", projection, selection, selectionArgs, null, null, sortOrder);
//注意 這個地方 不要關閉 cursor 和 db
//大吼一聲 數據庫發生了改變
getContext().getContentResolver().notifyChange(uri, null);
return cursor;
}else{
//匹配失敗
throw new IllegalArgumentException("路徑匹配失敗");
}
}
@Override
public String getType(Uri uri) {
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
int match = matcher.match(uri);
if (match == INSERTSUCESS) {
//說明匹配成功
SQLiteDatabase db = helper.getReadableDatabase();
long insert = db.insert("info", null, values);
//執行上面這句話 說明我的數據庫內容 發生了變化 首先 要發送一條通知 說明 我發生改變
if (insert>0) {
//數據庫發生變化 發送一個通知
getContext().getContentResolver().notifyChange(uri, null);
}
Uri uri2 = Uri.parse("com.itheima.contentprovider/"+insert);
return uri2;
}else{
//匹配失敗
throw new IllegalArgumentException("路徑匹配失敗");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int match = matcher.match(uri);
if (match == DELSUCESS) {
//匹配成功
SQLiteDatabase db = helper.getReadableDatabase();
int delete = db.delete("info", selection, selectionArgs);
if (delete>0) {
//大吼一聲 數據庫發生了改變
getContext().getContentResolver().notifyChange(uri, null);
}
return delete;
}else {
//匹配失敗
throw new IllegalArgumentException("路徑匹配失敗");
}
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int match = matcher.match(uri);
if (match == UPDATESUCESS) {
//匹配成功
SQLiteDatabase db = helper.getReadableDatabase();
int update = db.update("info", values, selection, selectionArgs);
if (update>0) {
//大吼一聲 數據庫發生了改變
getContext().getContentResolver().notifyChange(uri, null);
}
return update;
}else {
//匹配失敗
throw new IllegalArgumentException("路徑匹配失敗");
}
}
}
可以使用Activity提供的getContentResolver()方法。 ContentResolver 類提供了與ContentProvider類相同簽名的四個方法:
public Uri insert(Uri uri, ContentValues values)
該方法用於往ContentProvider添加數據。
public int delete(Uri uri, String selection, String[] selectionArgs)
該方法用於從ContentProvider刪除數據。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
該方法用於更新ContentProvider中的數據。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
該方法用於從ContentProvider中獲取數據。
這些方法的第一個參數為Uri,代表要操作的ContentProvider和對其中的什麼數據進行操作,假設給定的是: Uri.parse(“content://cn.itcast.providers.personprovider/person/10”),那麼將會對主機名為cn.itcast.providers.personprovider的ContentProvider進行操作,操作的數據為person表中id為10的記錄。
使用ContentResolver對ContentProvider中的數據進行添加、刪除、修改和查詢操作:
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://cn.itcast.provider.personprovider/person");
//添加一條記錄
ContentValues values = new ContentValues();
values.put("name", "itcast");
values.put("age", 25);
resolver.insert(uri, values);
//獲取person表中所有記錄
Cursor cursor = resolver.query(uri, null, null, null, "personid desc");
while(cursor.moveToNext()){
Log.i("ContentTest", "personid="+ cursor.getInt(0)+ ",name="+ cursor.getString(1));
}
//把id為1的記錄的name字段值更改新為liming
ContentValues updateValues = new ContentValues();
updateValues.put("name", "liming");
resolver.update(updateIdUri, updateValues, null, null);
getContentResolver().notifyChange(uri, null)來通知注冊在此URI上的訪問者,例子如下:
public class PersonContentProvider extends ContentProvider {
public Uri insert(Uri uri, ContentValues values) {
db.insert("person", "personid", values);
getContext().getContentResolver().notifyChange(uri, null);
}
}
如果ContentProvider的訪問者需要得到數據變化通知,必須使用ContentObserver對數據(數據采用uri描述)進行監聽,當監聽到數據變化通知時,
系統就會調用ContentObserver的onChange()方法:
getContentResolver().registerContentObserver(Uri.parse("content://cn.itcast.providers.personprovider/person"),
true,//true表示只要發出通知的Uri以方法第一個參數開頭都能被監聽到,否側監聽uri必須與發出通知的uri完全匹配才能被監聽到
new PersonObserver(new Handler()));
public class PersonObserver extends ContentObserver{
public PersonObserver(Handler handler) {
super(handler);
}
public void onChange(boolean selfChange) {
//此處可以進行相應的業務處理
}
}
package com.itheima.backupsms;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.app.Activity;
import android.database.Cursor;
import android.util.Xml;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 點擊按鈕備份短信
*
* @param v
*/
public void backup(View v) {
try {
// 1 初始化xml 序列化器
XmlSerializer serializer = Xml.newSerializer();
// 2初始化xml序列化器的 參數
File file = new File(Environment.getExternalStorageDirectory()
.getPath(), "smsbackup.xml");
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8");
// 3 開始寫 xml 頭
serializer.startDocument("utf-8", true);
// 開始寫 xml 根節點 smss
serializer.startTag(null, "smss");
// 1 把我們關系的短信數據庫裡的內容 給 獲取出來 我們要做的操作 就是通過內容解析者 把數據給查詢出來
Uri uri = Uri.parse("content://sms");
Cursor cursor = getContentResolver().query(uri,
new String[] { "address", "date", "body" }, null, null,
null);
while (cursor.moveToNext()) {
// 寫sms 節點
serializer.startTag(null, "sms");
String address = cursor.getString(0);
String date = cursor.getString(1);
String body = cursor.getString(2);
//開始寫 address 節點
serializer.startTag(null, "address");
serializer.text(address);
serializer.endTag(null, "address");
//開始寫 date 節點
serializer.startTag(null, "date");
serializer.text(date);
serializer.endTag(null, "date");
//開始寫 body 節點
serializer.startTag(null, "body");
serializer.text(body);
serializer.endTag(null, "body");
serializer.endTag(null, "sms");
}
// 開始寫 address date body
serializer.endTag(null, "smss");
// 文檔結束
serializer.endDocument();
} catch (Exception e) {
e.printStackTrace();
}
}
}
//1 先拿到 內容解析器
Uri uri = Uri.parse("content://sms");
ContentValues values = new ContentValues();
values.put("address", "110");
values.put("date", System.currentTimeMillis());
values.put("body", "您的事犯了 請您馬上來一趟");
getContentResolver().insert(uri, values);
package com.itheima.getcontactinfo.utils;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import com.itheima.getcontactinfo.domain.ContactInfo;
public class ContactUtils {
public static List getContactInfos(Context context) {
List contactLists = new ArrayList();
// 1 首先 我要查詢raw_contacts表 獲取到 contact_id列
// (1)我如何查詢 ?用 內容解析者 path
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(uri, new String[] { "contact_id" },
null, null, null);
while (cursor.moveToNext()) {
// 獲取到contact_id 的值
String contact_id = cursor.getString(0);
System.out.println("contact_id--" + contact_id);
// 有個小細節注意一下 我要判斷 contact_id 是否為空
if (contact_id != null) {
ContactInfo info = new ContactInfo();
info.setId(contact_id);
// 2去data表 根據 contact_id 去獲取 mimetyple_id列 data1 列
Cursor dataCursor = resolver.query(datauri, new String[] {
"mimetype", "data1" }, "raw_contact_id=?",
new String[] { contact_id }, null);
while (dataCursor.moveToNext()) {
String mimetype = dataCursor.getString(0); // 獲取到mimeytype
String data1 = dataCursor.getString(1);
// 3 然後根據 mimetype類型 來區分數據類型
if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
info.setEmail(data1);
} else if ("vnd.android.cursor.item/name".equals(mimetype)) {
info.setName(data1);
} else if ("vnd.android.cursor.item/phone_v2"
.equals(mimetype)) {
System.out.println("data---電話號碼-" + data1);
info.setPhone(data1);
}
}
dataCursor.close();
contactLists.add(info); // 把info信息存到集合中
}
}
cursor.close();
return contactLists;
}
}
package com.itheima.getcontactinfo.domain;
public class ContactInfo {
private String id;
private String name;
private String phone;
private String email;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "ContactInfo [id=" + id + ", name=" + name + ", phone=" + phone
+ ", email=" + email + "]";
}
}
package com.itheima.insert.contact;
import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
private EditText et_name;
private EditText et_phone;
private EditText et_email;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1找到我們關心的控件
et_name = (EditText) findViewById(R.id.et_name);
et_phone = (EditText) findViewById(R.id.et_phone);
et_email = (EditText) findViewById(R.id.et_email);
}
/**
* 保存聯系人的信息
* @param v
*/
public void click(View v){
//1 要先拿到 edittext的值
String name = et_name.getText().toString().trim();
String phone = et_phone.getText().toString().trim();
String email = et_email.getText().toString().trim();
//2 往 raw_contacts裡插入一條數據 拿到內容解析者
Uri uri = Uri.parse("content://com.android.contacts/raw_contacts");
Uri datauri = Uri.parse("content://com.android.contacts/data");
ContentValues values = new ContentValues();
// 插入數據之前 我要先查詢 一下 raw_contacts 表一共有多少行的數據
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
int count = cursor.getCount(); //獲取到一共有多少行
int contact_id = count + 1;
values.put("contact_id", contact_id);
getContentResolver().insert(uri, values);
// 3 往data 表插入數據
ContentValues nameValues = new ContentValues();
nameValues.put("data1", name);
nameValues.put("mimetype", "vnd.android.cursor.item/name");
nameValues.put("raw_contact_id", contact_id);
getContentResolver().insert(datauri, nameValues);
ContentValues phoneValues = new ContentValues();
phoneValues.put("data1", phone);
phoneValues.put("mimetype", "vnd.android.cursor.item/phone_v2");
phoneValues.put("raw_contact_id", contact_id);
getContentResolver().insert(datauri, phoneValues);
ContentValues emailValues = new ContentValues();
emailValues.put("data1", email);
emailValues.put("mimetype", "vnd.android.cursor.item/email_v2");
emailValues.put("raw_contact_id", contact_id);
getContentResolver().insert(datauri, emailValues);
}
}
package com.itheima.smslistener;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.app.Activity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.view.Menu;
public class MainActivity extends Activity {
private Uri uri;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//1注冊一個內容觀察者
uri = Uri.parse("content://sms");
getContentResolver().registerContentObserver(uri, true, new MyObserver(new Handler()));
}
private class MyObserver extends ContentObserver{
public MyObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
//當短信的數據庫發生了變化 我就去取出所有短信的內容
Cursor cursor = getContentResolver().query(uri, new String[]{"address","body","date"}, null, null, null);
while(cursor.moveToNext()){
String address = cursor.getString(0);
String body = cursor.getString(1);
String date = cursor.getString(2);
System.out.println("address---"+address+"--body:"+body);
}
super.onChange(selfChange);
}
}
}
Android 從StackTraceElement反觀Log庫
一、概述大家編寫項目的時候,肯定會或多或少的使用Log,尤其是發現bug的時候,會連續在多個類中打印Log信息,當問題解決了,然後又像狗一樣一行一行的去刪除剛才隨便添加的
ArthurHub/Android-Image-Cropper 相冊裁剪框架學習
以前寫過2篇關於相冊選取、裁剪的demo,今天我們來學習下github上一款開源的相冊裁剪開源庫開源庫地址 https://github.com/ArthurHub/An
Android輸入法架構學習總結
安卓輸入法框架(Input Method Framework)IMF一.輸入法框架簡介自Android平台1.5版本以後,Google開放了 Android平台輸入法框架
Android開發之圖形圖像與動畫(一)Paint和Canvas類學習
Paint類 *Paint類代表畫筆,用來描述圖形的顏色和風格,如線寬,顏色,透明度和填充效果等信息。 *使用Paint類時,需要先創建該類的對象,可以通過該類的構造函數