ContentProvider 使用詳解


和你一起終身學習,這里是程序員 Android

本篇文章主要介紹 Android 開發中的部分知識點,通過閱讀本篇文章,您將收獲以下內容:

一、ContentProvider 概述
二、ContentProvider的注冊
三、自定義ContentProvider 的實現
四、獲取聯系人ContactProvider實現的方法
五、獲取短信內容的實現方法
六、ContentResolver 內容解析者
七、ContentObserver 內容觀察者
八、ContentProvider ContentResolver ContentObserver 三者關系

一、ContentProvider 概述

在了解 ContentProvider 之前,我們首先了解一下ContentProvider 的繼承關系。

ContentProvider 繼承關系如下:

java.lang.Object
    ↳ android.content.ContentProvider

ContentProvider Android 四大組件之一,其本質上是一個標准化的數據管道,它屏蔽了底層的數據管理和服務等細節,以標准化的方式在Android 應用間共享數據。用戶可以靈活實現ContentProvider 所封裝的數據存儲以及增刪改查等,所有的ContentProvider 必須實現一個對外統一的接口(URI)

二、ContentProvider的注冊

ContentProvider 屬於四大組件之一,必須在Androidmainfest.xml 中注冊。

ContentProvider注冊方法如下:

  <provider           
            android:name="com.programandroid.CustomContentProviderMethod"
            android:authorities="ProgramAndroid"
            android:exported="true" />

注意 :
URI 中的元素是android:authorities="ProgramAndroid"

三、自定義ContentProvider 的實現

自定義ContentProvider 需要繼承 ContentProvider ,並實現增刪改查等方法。

1.自定義ContentProvider 類

public class CustomContentProviderMethod extends ContentProvider {

	private SQLiteDatabase db;
	private static final String MAUTHORITIESNAME = "ProgramAndroid";
	private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
	private static final int PERSON = 1;
	private static final int PERSON_NUMBER = 2;
	private static final int PERSON_TEXT = 3;
	private static final String TABLE_NAME = "table_person";
	// 構建URI
	static {
		// content://programandroid/person
		matcher.addURI(MAUTHORITIESNAME, "person", PERSON);
		// # 代表任意數字content://programandroid/person/4
		matcher.addURI(MAUTHORITIESNAME, "person/#", PERSON_NUMBER);
		// * 代表任意文本 content://programandroid/person/filter/ssstring
		matcher.addURI(MAUTHORITIESNAME, "person/filter/*", PERSON_TEXT);
	}

	@Override
	public boolean onCreate() {
		DBHelper helper = new DBHelper(getContext());
		// 創建數據庫
		db = helper.getWritableDatabase();
		return true;

	}

	@Nullable
	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {

		// 過濾URI
		int match = matcher.match(uri);
		switch (match) {
		case PERSON:
			// content://autoname/person

			return db.query(TABLE_NAME, projection, selection, selectionArgs,
					null, null, sortOrder);

		case PERSON_NUMBER:
			break;
		case PERSON_TEXT:
			break;
		default:
			break;
		}
		return null;
	}

	@Nullable
	@Override
	public Uri insert(Uri uri, ContentValues values) {
		// 過濾URI
		int match = matcher.match(uri);
		switch (match) {
		case PERSON:
			// content://autoname/person

			long id = db.insert(TABLE_NAME, null, values);

			// 將原有的uri跟id進行拼接從而獲取新的uri
			return ContentUris.withAppendedId(uri, id);

		case PERSON_NUMBER:
			break;
		case PERSON_TEXT:
			break;
		default:
			break;
		}
		return null;
	}

	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		return 0;
	}

	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {
		return 0;
	}

	@Nullable
	@Override
	public String getType(Uri uri) {
		return null;
	}

}

2. 提供對外提供操作的數據庫方法

public class DBHelper extends SQLiteOpenHelper {
	private static final String DB_NAME = "persons.db";
	private static final int DB_VERSION = 1;
	private static final String TABLE_NAME = "table_person";
	private static final String ID = "_id";
	private static final String NAME = "name";

	public DBHelper(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {

		String sql = "CREATE TABLE " + TABLE_NAME + "(" + ID
				+ " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL" + "," + NAME
				+ " CHAR(10) )";

		db.execSQL(sql);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

	}

}

3.其他APK 訪問此ContentProvider 數據庫的方法

public class MainActivity extends Activity {

	private String uri = "content://ProgramAndroid/person";
	private EditText mEditText;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mEditText = (EditText) findViewById(R.id.ed_name);
	}

	public void QureyData(View view) {
		String name = null;
		Cursor cursor = getContentResolver().query(Uri.parse(uri), null, null, null, null);
		while (cursor.moveToNext()) {
			name = cursor.getString(cursor.getColumnIndex("name"));
		}
		mEditText.setText(name);
	}

	public void InsertData(View view) {
		String editName = mEditText.getText().toString();
		ContentValues values = new ContentValues();
		values.put("name, editName);

		Uri result = getContentResolver().insert(Uri.parse(uri), values);
//             注意 : 此條添加上才ContentObserver可以監聽數據庫改變
		getContentResolver().notifyChange(Uri.parse(uri),null);
		long parseid = ContentUris.parseId(result);
		if (parseid > 0) {
			Toast.makeText(MainActivity.this, "保存成功", Toast.LENGTH_LONG).show();
			mEditText.setText("");
		}

	}

}

注意 :

   //  此條添加上才ContentObserver可以監聽數據庫改變
		getContentResolver().notifyChange(Uri.parse(uri),null);

至此,自定義ContentProvider的使用方法已經實現。

四、獲取聯系人ContactProvider實現的方法

Android 系統自帶一些ContentProvider ,比如 聯系人的ContactProvider
例如: 源碼 packages\providers 下的內容

Android 系統Provider.png

1. 獲取聯系人實現方法

public class ContactListActivity extends Activity {
	private static final String tag = "ContactListActivity";
	private ListView lv_contact_list;
	private List<HashMap<String, String>> mContactList = new ArrayList<HashMap<String, String>>();

	private Handler mHandler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			// 給數據適配器設置數據
			MyAdapter myAdapter = new MyAdapter();

			TextView emptyView = new TextView(getApplicationContext());
			emptyView.setLayoutParams(new LayoutParams(
					LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
			emptyView.setText(getResources().getString(
					R.string.please_add_contanct));
			emptyView.setVisibility(View.GONE);
			emptyView.setTextColor(Color.BLACK);
			emptyView.setTextSize(20);
			emptyView.setGravity(Gravity.CENTER);
			((ViewGroup) lv_contact_list.getParent()).addView(emptyView);
			lv_contact_list.setEmptyView(emptyView);

			lv_contact_list.setAdapter(myAdapter);
		};
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_contact_list);

		initUI();
		initData();
	}

	/**
	 * 從系統數據庫中獲取聯系人數據,權限,讀取聯系人
	 */
	private void initData() {
		new Thread() {
			public void run() {
				// 1,獲取內容解析器(訪問地址(后門))
				ContentResolver contentResolver = getContentResolver();
				// 2,對數據庫指定表進行查詢操作
				Cursor cursor = contentResolver.query(Uri
						.parse("content://com.android.contacts/raw_contacts"),
						new String[] { "contact_id" }, null, null, null);
				// 3,判斷游標中是否有數據,有數據一直度
				while (cursor.moveToNext()) {
					String id = cursor.getString(0);
					Log.i(tag, "id = " + id);// 1,2,3
					// 4,通過此id去關聯data表和mimetype表生成視圖,data1(數據),mimetype(數據類型)
					Cursor indexCursor = contentResolver.query(
							Uri.parse("content://com.android.contacts/data"),
							new String[] { "data1", "mimetype" },
							"raw_contact_id = ?", new String[] { id }, null);
					HashMap<String, String> hashMap = new HashMap<String, String>();
					// 5,游標向下移動獲取數據過程
					while (indexCursor.moveToNext()) {
						String data = indexCursor.getString(0);
						String type = indexCursor.getString(1);

						// Log.i(tag, "data = "+data);
						// Log.i(tag, "type = "+type);

						if (type.equals("vnd.android.cursor.item/phone_v2")) {
							// data就為電話號碼
							hashMap.put("phone", data);
						} else if (type.equals("vnd.android.cursor.item/name")) {
							// data 為聯系人名字
							hashMap.put("name", data);
						}
					}
					indexCursor.close();
					mContactList.add(hashMap);
				}
				cursor.close();
				// 告知主線程集合中的數據以及准備完畢,可以讓主線程去使用此集合,填充數據適配器
				mHandler.sendEmptyMessage(0);
			};
		}.start();
	}

	private void initUI() {
		lv_contact_list = (ListView) findViewById(R.id.lv_contact_list);
		lv_contact_list.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				// 1,position點中條目的索引值,集合的索引值
				String phone = mContactList.get(position).get("phone");
				// 2,將此電話號碼傳遞給前一個界面
				Intent intent = new Intent();
				intent.putExtra("phone", phone);
				setResult(0, intent);
				// 3,關閉此界面
				finish();
			}
		});
	}

	class MyAdapter extends BaseAdapter {
		@Override
		public int getCount() {
			return mContactList.size();
		}

		@Override
		public HashMap<String, String> getItem(int position) {
			return mContactList.get(position);
		}

		@Override
		public long getItemId(int position) {
			return position;
		}

		@Override
		public View getView(int position, View convertView, ViewGroup parent) {

			Holder holder;
			if (convertView == null) {
				holder = new Holder();
				// 1,生成當前listview一個條目相應的view對象
				convertView = View.inflate(getApplicationContext(),
						R.layout.list_item_contact, null);
				// 2,找到view中的控件
				holder.tv_name = (TextView) convertView
						.findViewById(R.id.tv_name);
				holder.tv_phone = (TextView) convertView
						.findViewById(R.id.tv_phone);
				convertView.setTag(holder);

			} else {
				holder = (Holder) convertView.getTag();
			}

			// 3,給控件賦值
			holder.tv_name.setText(getItem(position).get("name"));
			holder.tv_phone.setText(getItem(position).get("phone"));

			return convertView;
		}
	}

	class Holder {

		public TextView tv_name;
		public TextView tv_phone;
	}
}

2. ListView 顯示布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
   
    <ListView 
        android:id="@+id/lv_contact_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </ListView>
</LinearLayout>

3. item 布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:orientation="vertical" >
    <TextView 
        android:text="聯系人名稱"
        android:id="@+id/tv_name"
        android:textSize="18sp"
        android:textColor="@color/black"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView 
        android:text="聯系人電話號碼"
        android:id="@+id/tv_phone"
        android:textSize="18sp"
        android:textColor="@color/grey"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

注意:
獲取聯系人需要申請權限

 <!-- 讀取聯系人的權限 -->
    <uses-permission android:name="android.permission.READ_CONTACTS" />

至此,已經可以獲取並顯示聯系人信息。

五、獲取短信內容的實現方法

短信內容數據也是Android 系統提供的,獲取方法如下:

1.短信內容獲取方法

public class MmsListActivity extends Activity {
	private ContentResolver resolver;
	private ListView listView;
	private static final String SMS_URI = "content://sms";
	private Cursor cursor;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mms_list);
		listView = (ListView) findViewById(R.id.lv_mms);
		resolver = getContentResolver();

	}

	public void GetMMSBtn(View view) {

		// 插入數據
		ContentValues values = new ContentValues();
		values.put("address", "136259");
		values.put("body", "測試數據中。。。。。");
		resolver.insert(Uri.parse(SMS_URI), values);
		// 查詢數據方法
		cursor = resolver.query(Uri.parse(SMS_URI), null, null, null, null);
		// 將數據顯示到ListView中
		listView.setAdapter(new MyAdapter(MmsListActivity.this, cursor,
				CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER));

	}

	@Override
	protected void onDestroy() {
		super.onDestroy();
		if (cursor != null) {
			// 關閉cursor
			// cursor.close();
		}
	}

	class MyAdapter extends CursorAdapter {

		public MyAdapter(Context context, Cursor c, int flags) {
			super(context, c, flags);
		}

		// 創建一個視圖,引入listview要展示的子視圖
		@Override
		public View newView(Context context, Cursor cursor, ViewGroup parent) {
			return getLayoutInflater().inflate(R.layout.list_item_mms, null);
		}

		// 綁定數據的方法
		@Override
		public void bindView(View view, Context context, Cursor cursor) {

			TextView tvNumber = (TextView) view.findViewById(R.id.tv_number);
			TextView tvContent = (TextView) view.findViewById(R.id.tv_content);
			TextView tvState = (TextView) view.findViewById(R.id.tv_state);
			TextView tvDate = (TextView) view.findViewById(R.id.tv_date);
			TextView tvId = (TextView) view.findViewById(R.id.tv_id);
			TextView tvRead = (TextView) view.findViewById(R.id.tv_read);

			String number = cursor.getString(cursor.getColumnIndex("address"));
			String body = cursor.getString(cursor.getColumnIndex("body"));
			String date = cursor.getString(cursor.getColumnIndex("date"));
			int read = cursor.getInt(cursor.getColumnIndex("read"));
			int id = cursor.getInt(cursor.getColumnIndex("_id"));
			int type = cursor.getInt(cursor.getColumnIndex("type"));

			if (read == 0) {
				tvRead.setText("短信狀態:未讀");
			} else {
				tvRead.setText("短信狀態:已讀");
			}

			tvNumber.setText("手機號:" + number);
			tvContent.setText("短信內容:" + body);
			tvDate.setText("接收短信時間:" + date);
			tvId.setText("短信Id:" + id);

			if (type == 1) {
				tvState.setText("短信狀態:已接收");

			} else {
				tvState.setText("短信狀態:已發送");
			}
		}
	}

}

2.ListView 布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    
  <ListView
        android:id="@+id/lv_mms"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
</LinearLayout>

3.item 布局如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv_number"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ddd" />

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"

        android:text="ddd" />

    <TextView
        android:id="@+id/tv_state"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ddd" />
    <TextView
        android:id="@+id/tv_date"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ddd" />
    <TextView
        android:id="@+id/tv_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ddd" />
    <TextView
        android:id="@+id/tv_read"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="ddd" />
</LinearLayout>

六、ContentResolver 內容解析者

ContentResolver 主要是通過URI調用getContentResolver()獲取ContentProvider 提供的數據接口,進而進行增刪改查等操作。
ContentResolver 使用舉例如下:

// 查詢
	Cursor cursor = getContentResolver().query(Uri.parse(uri), null, null, null, null);
// 插入數據到指定 URI 中
 getContentResolver().insert(Uri.parse(uri), ContentValues);

七、ContentObserver 內容觀察者

ContentObserver 內容觀察者通過指定URI 監聽ContentProvider數據是否改變。

下面介紹自定義 ContentObserver內容觀察者

1.注冊ContentObserver 內容觀察者

	/**
	 * 監聽ContentProvider數據庫變化
	 */
	private void ContentObserverDatabase() {
		// [1]注冊內容觀察者
		Uri uri = Uri.parse("content://ProgramAndroid/person");
		// false 觀察的uri 必須是一個確切的uri 如果是true
		getContentResolver().registerContentObserver(uri, true,
				new CustomContentObserver(new Handler()));
	}

2.繼承 ContentObserver 實現 onChange方法

public class CustomContentObserver extends ContentObserver {

	/**
	 * @param handler
	 */
	public CustomContentObserver(Handler handler) {
		super(handler);
		// TODO Auto-generated constructor stub
	}

	// 當我們觀察的uri發生改變的時候調用
	@Override
	public void onChange(boolean selfChange) {
		System.out.println(" 數據庫被操作了 ");

		super.onChange(selfChange);
	}

}

至此自定義內容觀察者已經實現完成

3.調用ContentObserver 監聽短信數據改變

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //[1]注冊一個內容觀察者
        Uri uri = Uri.parse("content://sms/");
        getContentResolver().registerContentObserver(uri, true, new MyContentObserver(new Handler()));

    }

    private class MyContentObserver extends ContentObserver{

        public MyContentObserver(Handler handler) {
            super(handler);
        }
        //當觀察的內容發生改變的時候調用
        @Override
        public void onChange(boolean selfChange) {
            System.out.println(" 短信的數據庫發生了改變");
            super.onChange(selfChange);
        }

    }

八、ContentProvider ContentResolver ContentObserver 三者關系

  • 三者關系圖如下

關系圖.png

至此,本篇已結束。轉載網絡的文章,小編覺得很優秀,歡迎點擊閱讀原文,支持原創作者,如有侵權,懇請聯系小編刪除,歡迎您的建議與指正。同時期待您的關注,感謝您的閱讀,謝謝!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM