Chia sẻ Database giữa các application, sử dụng Content Provider
1. Giới thiệu về Content Provider Trong mô hình bảo mật Android, một ứng dụng không thể trực tiếp truy cập (đọc / ghi) dữ liệu của các ứng dụng khác. Mỗi ứng dụng có dữ liệu riêng và bộ nhớ riêng của mình. Content Provider chính là cách tốt nhất để chia sẻ dữ liệu giữa các ứng dụng. Ứng dụng ...
1. Giới thiệu về Content Provider
Trong mô hình bảo mật Android, một ứng dụng không thể trực tiếp truy cập (đọc / ghi) dữ liệu của các ứng dụng khác. Mỗi ứng dụng có dữ liệu riêng và bộ nhớ riêng của mình.
Content Provider chính là cách tốt nhất để chia sẻ dữ liệu giữa các ứng dụng. Ứng dụng phải tự đăng ký như là một nhà cung cấp dữ liệu. Các ứng dụng khác có thể yêu cầu Android để đọc / ghi dữ liệu đó thông qua một API được định nghĩa trước. Các Content Provider API tuân thủ theo CRUD.
Một số ví dụ về Content Provider: Contacs: cho phép lấy thông tin từ danh bạ để sử dụng ở ứng dụng khác, Media Store: cho phép các ứng dụng khác truy cập, lưu trữ các file media.
- Cách hoạt động của Content Provider
Content Provider sử dụng các phương thức insert(), query(), update(), delete() để truy cập vào database của ứng dụng.
Một URI riêng được bắt đầu bằng content:// sẽ được gán cho mỗi Content Provider và nó sẽ được sử dụng trên các ứng dụng.
Các lớp ContentProvider là thành phần chính của một Content Provider. Để tạo ra một Content Provider, chúng ta cần
- Tạo sub class cho ContentProvider.
- Định nghĩa URI, có dạng: content://authority/path/id
- Implement tất cả các phương pháp chưa được thực hiện: insert(), update(), query(), delete(), getType().
- Khai báo trong AndroidManifest.xml
2. Ví dụ về sử dụng Content Provider chia sẻ dữ liệu giữa các ứng dụng
Ở đây chúng ta sẽ tạo ra 2 ứng dụng, ứng dụng thứ nhất sẽ tạo ra 1 content provider và chia sẻ database của nó, ứng dụng thứ 2 sẽ truy cập vào để lấy dữ liệu.
1. Ứng dụng 1: MyProvider
Tạo ra Content Provider và chia sẻ dữ liệu. Sử dụng database có tên là "mydb", có 1 bảng là "names", bảng có 2 trường là "id" và "name"
URI của content provider này sẽ là: content://com.example.contentproviderexample.MyProvider/cte
Các thành phần của ứng dụng 1:
Layout xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_awidth="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_awidth="fill_parent" android:layout_height="wrap_content" android:text="Name" /> <EditText android:id="@+id/txtName" android:layout_awidth="fill_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/btnAdd" android:layout_awidth="fill_parent" android:layout_height="wrap_content" android:onClick="onClickAddName" android:text="Add Name" /> </LinearLayout>
Content provider class:
MyProvider.java
package com.example.contentproviderexample; import java.util.HashMap; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.UriMatcher; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteQueryBuilder; import android.net.Uri; public class MyProvider extends ContentProvider { static final String PROVIDER_NAME = "com.example.contentproviderexample.MyProvider"; static final String URL = "content://" + PROVIDER_NAME + "/cte"; static final Uri CONTENT_URI = Uri.parse(URL); static final String id = "id"; static final String name = "name"; static final int uriCode = 1; static final UriMatcher uriMatcher; private static HashMap<String, String> values; static { uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); uriMatcher.addURI(PROVIDER_NAME, "cte", uriCode); uriMatcher.addURI(PROVIDER_NAME, "cte/*", uriCode); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)) { case uriCode: count = db.delete(TABLE_NAME, selection, selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } @Override public String getType(Uri uri) { switch (uriMatcher.match(uri)) { case uriCode: return "vnd.android.cursor.dir/cte"; default: throw new IllegalArgumentException("Unsupported URI: " + uri); } } @Override public Uri insert(Uri uri, ContentValues values) { long rowID = db.insert(TABLE_NAME, "", values); if (rowID > 0) { Uri _uri = ContentUris.withAppendedId(CONTENT_URI, rowID); getContext().getContentResolver().notifyChange(_uri, null); return _uri; } throw new SQLException("Failed to add a record into " + uri); } @Override public boolean onCreate() { Context context = getContext(); DatabaseHelper dbHelper = new DatabaseHelper(context); db = dbHelper.getWritableDatabase(); if (db != null) { return true; } return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); qb.setTables(TABLE_NAME); switch (uriMatcher.match(uri)) { case uriCode: qb.setProjectionMap(values); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } if (sortOrder == null || sortOrder == "") { sortOrder = name; } Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder); c.setNotificationUri(getContext().getContentResolver(), uri); return c; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; switch (uriMatcher.match(uri)) { case uriCode: count = db.update(TABLE_NAME, values, selection, selectionArgs); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } getContext().getContentResolver().notifyChange(uri, null); return count; } private SQLiteDatabase db; static final String DATABASE_NAME = "mydb"; static final String TABLE_NAME = "names"; static final int DATABASE_VERSION = 1; static final String CREATE_DB_TABLE = " CREATE TABLE " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, " + " name TEXT NOT NULL);"; private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_DB_TABLE); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME); onCreate(db); } } }
Activity calss:
MainActivity.java
package com.example.contentproviderexample; import android.app.Activity; import android.content.ContentValues; import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.Toast; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public void onClickAddName(View view) { ContentValues values = new ContentValues(); values.put(MyProvider.name, ((EditText) findViewById(R.id.txtName)) .getText().toString()); Uri uri = getContentResolver().insert(MyProvider.CONTENT_URI, values); Toast.makeText(getBaseContext(), "New record inserted", Toast.LENGTH_LONG) .show(); } }
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.contentproviderexample" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.example.contentproviderexample.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <provider android:name=".MyProvider" android:authorities="com.example.contentproviderexample.MyProvider" android:exported="true" android:multiprocess="true" > </provider> </application> </manifest>
Output screenshots:
Ở đây có một EditText để nhập name và Button Add để lưu name vào Database, thực hiện nhập 1 số bản ghi ở đây và chúng sẽ được lấy ra từ ứng dụng thứ 2
2. Ứng dụng 2: MyUser
Sử dụng URI để truy cập và lấy dữ liệu từ ứng dụng 1 và hiển thị:
Layout xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_awidth="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/btnRetrieve" android:layout_awidth="fill_parent" android:layout_height="wrap_content" android:onClick="onClickDisplayNames" android:text="Display names" /> <TextView android:id="@+id/res" android:layout_awidth="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="20dp" android:clickable="false" android:ems="10" > </TextView> </LinearLayout>
Activity class:
MainActivity.java
Chúng ta sẽ sử dụng CusrsorLoader để lấy dữ liệu:
package com.example.contentprovideruser; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.view.Menu; import android.view.View; import android.widget.TextView; public class MainActivity extends FragmentActivity implements LoaderManager.LoaderCallbacks<Cursor> { TextView resultView=null; CursorLoader cursorLoader; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); resultView= (TextView) findViewById(R.id.res); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.activity_main, menu); return true; } public void onClickDisplayNames(View view) { getSupportLoaderManager().initLoader(1, null, this); } @Override public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) { cursorLoader= new CursorLoader(this, Uri.parse("content://com.example.contentproviderexample.MyProvider/cte"), null, null, null, null); return cursorLoader; } @Override public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) { cursor.moveToFirst(); StringBuilder res=new StringBuilder(); while (!cursor.isAfterLast()) { res.append(" "+cursor.getString(cursor.getColumnIndex("id"))+ "-"+ cursor.getString(cursor.getColumnIndex("name"))); cursor.moveToNext(); } resultView.setText(res); } @Override public void onLoaderReset(Loader<Cursor> arg0) { // TODO Auto-generated method stub } @Override public void onDestroy() { super.onDestroy(); } }
**Output: ** Click vào button Display names, trên màn hình sẽ hiển thị danh sách các bản ghi đã được insert vào database ở ứng dụng 1.
**Nguồn tham khảo: ** http://www.compiletimeerror.com/2013/12/content-provider-in-android.html#.VixFzLzGInR