注意:在ContentProvider里面寫對數據庫增刪改查的時候,千萬不能 db.close(); cursor.close(); 等操作,不然其他應用訪問不到數據,也沒有必要寫isOpen();
ContentProviderServer應用-->定義 MySqliteOpenHeper 數據庫幫助操作類(創建數據庫,創建表,初始化數據)
package liudeli.cp.server.cp; import android.content.ContentValues; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class MySqliteOpenHeper extends SQLiteOpenHelper { private static final String DB_NAME = "dogDatabase.db"; private static final int VERSON = 1; /** * 定義單例模式 懶漢式 */ private static MySqliteOpenHeper mySqliteOpenHeper; public static MySqliteOpenHeper getInstance(Context context) { if (null == mySqliteOpenHeper) { synchronized (MySqliteOpenHeper.class) { if (null == mySqliteOpenHeper) { mySqliteOpenHeper = new MySqliteOpenHeper(context, DB_NAME, null,VERSON); } } } return mySqliteOpenHeper; } private MySqliteOpenHeper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table dog(_id integer primary key autoincrement, name text, age integer);"); initDogTableData(db); } private void initDogTableData(SQLiteDatabase db) { ContentValues contentValues = new ContentValues(); contentValues.put("name", "李光四"); contentValues.put("age", 88); db.insert("dog", null, contentValues); contentValues.put("name", "李俊澤"); contentValues.put("age", 22); db.insert("dog", null, contentValues); contentValues.put("name", "李狗"); contentValues.put("age", 23); db.insert("dog", null, contentValues); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
ContentProviderServer應用-->定義 MyContentProvider 對數據庫增刪改查操作
package liudeli.cp.server.cp; import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; import android.util.Log; public class MyContentProvider extends ContentProvider { private final String TAG = MyContentProvider.class.getSimpleName(); /** * 初始化數據庫錯誤的示范: (不僅僅是在ContentProvider不能這樣,在其他的組件也不能這樣) * MySqliteOpenHeper.getInstance(getContext()); 還沒有執行 onCreate 是沒有getContext的,會報錯 * MySqliteOpenHeper.getInstance(this); 還沒有執行 onCreate初始化 是沒有getContext的,會報錯 */ // private MySqliteOpenHeper mySqliteOpenHeper = MySqliteOpenHeper.getInstance(getContext()); private MySqliteOpenHeper mySqliteOpenHeper; /** * 只要在AndroidManifest.xml中配置了provider組件 * 應用打開后,會自動啟動此方法 * @return */ @Override public boolean onCreate() { Log.d(TAG, "onCreate()"); mySqliteOpenHeper = MySqliteOpenHeper.getInstance(getContext()); return false; } /** * 查詢 * @param uri 其他應用傳遞過來的Uri * @param projection 其他應用傳遞過來的查詢列 * @param selection 其他應用傳遞過來的查詢條件 * @param selectionArgs 其他應用傳遞過來的查詢條件參數值 * @param sortOrder 其他應用傳遞過來的排序 * @return */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Log.d(TAG, "查詢到了數據...."); SQLiteDatabase db = mySqliteOpenHeper.getReadableDatabase(); /** * 查詢全部 */ Cursor curosr = db.query("dog", // 表名 projection, // 查詢的列 null, // selection 查詢的條件 xxx=? null, // selectionArgs 查詢條件的值 null, // groupBy 分組 null, // having 分組過濾條件 null); // orderBy 排序 /** * 在內容提供者里面,千萬不能關閉數據庫,關閉游標 */ return curosr; } /** * 增加 * @param uri 其他應用傳遞過來的Uri * @param values 其他應用傳遞過來的ContentValues * @return */ @Override public Uri insert(Uri uri, ContentValues values) { Log.d(TAG, "插入了數據...."); SQLiteDatabase database = mySqliteOpenHeper.getWritableDatabase(); // 參數一:表名 參數二:其他應用傳遞過來的ContentValues database.insert("dog", null, values); /** * 在內容提供者里面,千萬不能關閉數據庫,關閉游標 */ return null; } /** * 修改 * @param uri 其他應用傳遞過來的Uri * @param values 其他應用傳遞過來的ContentValues * @param selection 其他應用傳遞過來的查詢條件 * @param selectionArgs 其他應用傳遞過來的查詢條件參數值 * @return */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { Log.d(TAG, "修改了數據...."); SQLiteDatabase database = mySqliteOpenHeper.getWritableDatabase(); // 參數一:表名 參數二:其他應用傳遞過來的ContentValues 參數三:其他應用傳遞過來的查詢條件 database.update("dog", values, selection, selectionArgs); /** * 在內容提供者里面,千萬不能關閉數據庫,關閉游標 */ return 0; } /** * 刪除 * @param uri 其他應用傳遞過來的Uri * @param selection 其他應用傳遞過來的查詢條件 * @param selectionArgs 其他應用傳遞過來的查詢條件參數值 * @return */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { Log.d(TAG, "刪除了數據...."); SQLiteDatabase database = mySqliteOpenHeper.getWritableDatabase(); // 參數一:表名 參數二:其他應用傳遞過來的查詢條件 參數三:其他應用傳遞過來的查詢條件的值 database.delete("dog", selection, selectionArgs); /** * 在內容提供者里面,千萬不能關閉數據庫,關閉游標 */ return 0; } /** * 得到類型 在后續的博客中會有講解到 * @param uri * @return */ @Override public String getType(Uri uri) { return null; } }
ContentProviderServer應用-->定義 在AndroidManifest.xml 中 對外暴露 MyContentProvider
<!-- ContentProvider是組件需要配置 可以把ContentProvider看作是服務器 authorities 看作是服務器 服務器有訪問的鏈接,authorities(授權) ,是唯一標識 android:enabled="true" 可以被系統實例化 android:exported="true" 允許對外輸出 --> <provider android:authorities="autho.prov.cp.MyContentProvider" android:name=".cp.MyContentProvider" android:enabled="true" android:exported="true" />
然后運行 ContentProviderServer應用:由於在AndroidManifest.xml中配置了MyContentProvider組件,只要運行 ContentProviderServer應用,就會自動初始化onCreate()方法
12-14 09:22:55.187 2013-2013/liudeli.cp.server D/MyContentProvider: onCreate()
ContentProviderClient應用 --> MainActivity中調用 ContentProviderServer應用的內容提供者
package liudeli.cp.client; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.EditText; import android.widget.ListView; import android.widget.SimpleCursorAdapter; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private EditText etID; private ContentResolver contentResolver; private ListView listview; private Uri uri; private Cursor cursor; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); etID = findViewById(R.id.et_id); contentResolver = getContentResolver(); listview = findViewById(R.id.listview); uri = Uri.parse("content://autho.prov.cp.MyContentProvider"); } public void test(View view) { /** * 可以想象客戶端訪問服務器,需要需要用到協議HTTP * 而想訪問ContentProvider,需要ContentResolver */ ContentResolver contentProvider = getContentResolver(); /** * 可以想象訪問服務器,需要這樣拼接訪問地址 http:// * 而想訪問ContentProvider,需要這樣拼接訪問地址 content:// * 必須拿到暴露的授權authorities="autho.prov.cp.MyContentProvider" 進行拼接 */ Uri uri = Uri.parse("content://autho.prov.cp.MyContentProvider"); // 查詢 contentProvider.query(uri, null, null, null, null, null); // 增加 // contentProvider.insert(uri, null); // 修改 // contentProvider.update(uri, null, null, null); // 刪除 // contentProvider.delete(uri, null, null); } /** * 查詢 */ public void query(View view) { cursor = contentResolver.query(uri, new String[]{"_id", "name", "age"}, null, null , null, null); /** * 使用SimpleCursorAdapter 適配器 */ SimpleCursorAdapter simpleCursorAdapter = new SimpleCursorAdapter(MainActivity.this, // 上下文 R.layout.layout_item, // Item布局 cursor, // Cursor 查詢出來的游標 這里面有數據庫里面的數據 new String[]{"_id", "name", "age"}, // 從哪里來,指的是 查詢出數據庫列名 new int[]{R.id.tv_id, R.id.tv_name, R.id.tv_age}, // 到哪里去,指的是,把查詢出來的數據,賦值給Item布局 的控件 SimpleCursorAdapter.NO_SELECTION); // 給ListView設置使用SimpleCursorAdapter適配器 listview.setAdapter(simpleCursorAdapter); // 注意:在數據展示完成后,不要關閉游標, 在Activity結束后在關閉cursor.close(); } /** * 增加 */ public void insert(View view) { if (TextUtils.isEmpty(etID.getText().toString())) { Toast.makeText(MainActivity.this, "請輸入ID", Toast.LENGTH_LONG).show(); return; } ContentValues vs = new ContentValues(); vs.put("name", "劉新龍" + etID.getText().toString()); vs.put("age", 90); contentResolver.insert(uri, vs); // 規范寫法應該是:simpleCursorAdapter.notifyDataSetChanged(); // 我這里為了測試下,就直接這樣掉算了 query(null); } /** * 修改 * * @param view */ public void update(View view) { if (TextUtils.isEmpty(etID.getText().toString())) { Toast.makeText(MainActivity.this, "請輸入ID", Toast.LENGTH_LONG).show(); return; } ContentValues vs = new ContentValues(); vs.put("name", "王二麻子" + etID.getText().toString()); vs.put("age", 78); contentResolver.update(uri, vs, "_id = ?", new String[]{etID.getText().toString()}); // 規范寫法應該是:simpleCursorAdapter.notifyDataSetChanged(); // 我這里為了測試下,就直接這樣掉算了 query(null); } /** * 刪除 * * @param view */ public void delete(View view) { if (TextUtils.isEmpty(etID.getText().toString())) { Toast.makeText(MainActivity.this, "請輸入ID", Toast.LENGTH_LONG).show(); return; } contentResolver.delete(uri, "_id = ?", new String[]{etID.getText().toString()}); // 規范寫法應該是:simpleCursorAdapter.notifyDataSetChanged(); // 我這里為了測試下,就直接這樣掉算了 query(null); } /** * 在Activity結束后在關閉cursor.close(); */ @Override protected void onDestroy() { super.onDestroy(); if (cursor != null) { cursor.close(); } } }
ContentProviderClient應用 --> 布局文件
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <LinearLayout android:id="@+id/ll_buttons" android:layout_width="match_parent" android:layout_height="wrap_content"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="test" android:onClick="test" android:layout_weight="1" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查詢" android:layout_weight="1" android:onClick="query" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="增加" android:layout_weight="1" android:onClick="insert" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="修改" android:layout_weight="1" android:onClick="update" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="刪除" android:layout_weight="1" android:onClick="delete" /> </LinearLayout> <LinearLayout android:id="@+id/ll_et_id" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/ll_buttons" android:layout_marginTop="10dp" android:paddingLeft="10dp" android:paddingRight="10dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="請輸入_id號" android:textSize="20sp" /> <EditText android:id="@+id/et_id" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:background="@android:color/white" android:layout_marginLeft="10dp" /> </LinearLayout> <ListView android:id="@+id/listview" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/ll_et_id" android:layout_marginTop="20dp"></ListView> </RelativeLayout>
ContentProviderClient應用 --> 布局文件 -->ListView--> Item布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="8dp"> <TextView android:id="@+id/tv_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="id" android:textColor="@android:color/black" /> <TextView android:id="@+id/tv_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="name" android:textColor="@android:color/black" android:layout_marginTop="5dp" /> <TextView android:id="@+id/tv_age" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:text="age" android:textColor="@android:color/black" android:layout_marginTop="5dp" /> </LinearLayout>
ContentProviderClient應用 操作 ---> ContentProviderServer應用的數據庫