Android 内容提供器(Content Provider)
一、简介
Content Provider主要提供数据对外共享,通过内容提供器(Content Provider)可以将应用中的数据提供给其它应用共享(存储与读取)。也可以通过内容提供器(Content Provider)对其它应用的数据进行存储与读取。
简单的说就是在Android里内置了一个包含数据的数据库,通常是SQLite形式的数据库,由Android OS内置的功能进行驱动。
通过Android内容提供器(Content Provider),可以访问可共享的数据结构,称为数据库(Database)。基本的操作步骤如下:
- 获取打开数据库的权限。
- 查询数据。
- 访问数据。
在访问数据库时,可对数据进行添、删、改、查操作,这些操作的权限及级别,都存储于工程的清单(AndroidManifest.xml)文件中。
二、数据库与数据库管理系统
内容提供器通过数据库管理系统(DBMS)为Android应用程序提供数据结构的。DBMS可以管理数据库,并为用户提供创建数据库的途径,为读写操作提供数据的。
在Android中,使用SQLite数据库,其数据库管理系统为RDBMS,SQLite是关系型数据库。在数据库中,表中存储的二维数据称为记录(Record)。
三、Android 内置的内容提供器
- 联系人数据库内容提供器
- Android MediaStore内容提供器
四、定义内容提供器
在使用内容提供器前,必须在Android应用程序中进行注册。这个操作可以通过AndroidManifest.xml文件的<provider>标签指定。<provider>标签的定义是在应用启动时,需要访问哪些内容提供器。例如:一个用于Images内容提供器的<provider>标签:
1 <provider android:name="MediaStore.Images.ImageColumns" />
所有的内容提供器都针对一个数据库,为开发者提供一个可进行公用访问的唯一引用,或者说一个地址,这个地址称为URI。在Android中,数据库表中指向数据的位置常量通常是CONTENT_URI。
数据库中包含多个表,会为每一个表提供一个唯一的URI。例如:
1 android.provider.Contacts.Phones.CONTENT_URI
- 第一个词android代表Android OS。
- 第二个词provider代表组件类型。
- 第三个词Contents代表数据库。
- 第四个词Phones代表数据库中的一个表。
- 第五个词CONTENT_URI代表表中指向数据位置的常量。
看似很复杂,其实理解起来就一句话:在Android OS中某个组件类型中的数据库的一个表,数据在表中的位置。
五、定义安全权限
在AndroidManifest.xml文件定义读取数据库的安全权限,定义读取联系人安全权限示例如下:
1 <uses-permission android:name="android.permission.READ_CONTACTS" />
六、自定义内容提供器示例
定义一个ContentProvider需要以下必个步骤:
- 创建一个类,继承于ContentProvider基类;
- 定义一个CONTENT_URI静态常量,以public static final修饰,初始值最好以类名全名。比如:
1 public static final String CONTENT_URI = "naray.myproviderdemo.userprovider"
此变量用于指定数据库在外访问的唯一地址。
- 定义数据库表的数据列,如果,使用的是android的内部数据,必须定义一个为_id的列为主键;
- 创建数据存储系统,Content Provider可以使用Sqlite数据库,或者Android的文件系统存储。也可以是其它的存储方式。
- 覆盖实现基类Content Provider类的抽象方法。
- 在AndroidManifest.xml清单文件中,添加provider标签,指定Content Provider。
下面自定义一个Content Provider,存储数据为姓名与电话:
UserInfo类,用于声名数据库表的数据结构与声名指定此数据库的唯一引用:
1 public class UserInfo implements BaseColumns 2 { 3 public static final String AUTHORITY = "naray.myproviderdemo.UserProvider"; 4 5 public static final class User 6 { 7 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY); 8 public static final String NAME = "NAME"; 9 public static final String PHONE = "PHONE"; 10 } 11 }
PS:CONTENT_URI是为使用者提供一个公用的数据库的唯一引用,一般以类名为初始值。
UserProvider使用Sqlite存储数据(DataBaseHelper):
1 public class DataBaseHelper extends SQLiteOpenHelper 2 { 3 // local member var 4 private String mDBName; 5 private String mTableName; 6 private SQLiteDatabase.CursorFactory mFactory; 7 private int mVersion; 8 9 10 // construceor 11 public DataBaseHelper(Context context, String name, String tableName, 12 SQLiteDatabase.CursorFactory factory, int version) 13 { 14 super(context, name, factory, version); 15 mDBName = name; 16 mTableName = tableName; 17 mFactory = factory; 18 mVersion = version; 19 } 20 21 // inherit baseclass member method 22 @Override 23 public void onCreate(SQLiteDatabase db) 24 { 25 String sql = "create table " + mTableName + " ( _id INTEGER PRIMARY KEY AUTOINCREMENT, " + 26 "NAME TEXT, PHONE TEXT);"; 27 db.execSQL(sql); 28 } 29 30 @Override 31 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) 32 { 33 34 } 35 }
UserProvider类,是继承于Content Provider类,定义一个内容提供器:
1 public class UserProvider extends ContentProvider 2 { 3 // local member var 4 private DataBaseHelper mDBHelper; 5 private SQLiteDatabase mSqlDB; 6 private static final String sDataBaseName = "user.db"; 7 private static final String sTableName = "user"; 8 private static final int sVersion = 1; 9 10 // inherit baseclass member method 11 @Override 12 public boolean onCreate() 13 { 14 mDBHelper = new DataBaseHelper(getContext(), sDataBaseName, sTableName, null, sVersion); 15 return false; 16 } 17 18 @Nullable 19 @Override 20 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 21 { 22 SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder(); 23 SQLiteDatabase db = mDBHelper.getReadableDatabase(); 24 sqLiteQueryBuilder.setTables(sTableName); 25 Cursor cursor = db.query(sTableName, projection, selection, selectionArgs, null, null, sortOrder); 26 cursor.setNotificationUri(getContext().getContentResolver(), uri); 27 28 return cursor; 29 } 30 31 @Nullable 32 @Override 33 public String getType(Uri uri) 34 { 35 return null; 36 } 37 38 @Nullable 39 @Override 40 public Uri insert(Uri uri, ContentValues values) 41 { 42 mSqlDB = mDBHelper.getWritableDatabase(); 43 long rowId = mSqlDB.insert(sTableName, null, values); 44 if (0 < rowId) 45 { 46 Uri rowUri = ContentUris.appendId(UserInfo.User.CONTENT_URI.buildUpon(), rowId).build(); 47 getContext().getContentResolver().notifyChange(rowUri, null); 48 return rowUri; 49 } 50 return null; 51 } 52 53 @Override 54 public int delete(Uri uri, String selection, String[] selectionArgs) 55 { 56 return 0; 57 } 58 59 @Override 60 public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 61 { 62 return 0; 63 } 64 }
下面是显示UI元素的界面,MainActivity:
1 public class MainActivity extends FragmentActivity 2 { 3 @Override 4 public void onCreate(Bundle savedInstanceStata) 5 { 6 super.onCreate(savedInstanceStata); 7 setContentView(R.layout.activity_fragment); 8 9 // fragment manager 10 FragmentManager fm = getSupportFragmentManager(); 11 Fragment fragment = fm.findFragmentById(R.id.fragmentContainter); 12 if (null == fragment) 13 { 14 fragment = new MainFragment(); 15 fm.beginTransaction().add(R.id.fragmentContainter, fragment).commit(); 16 } 17 } 18 }
MainActivity委托MainFragment构建界面(MainFragment):
1 public class MainFragment extends Fragment implements View.OnClickListener 2 { 3 // local member var 4 private EditText mNameEditText; 5 private EditText mTelEditText; 6 private final ContentValues mContentVals = new ContentValues(); 7 8 // constructor 9 public MainFragment() 10 { 11 } 12 13 // inherit baseclass member method 14 @Override 15 public View onCreateView(LayoutInflater inflater, ViewGroup containter, Bundle savedInstanceState) 16 { 17 View view = inflater.inflate(R.layout.fragment_main, null); 18 19 // 存储UI元素 20 mNameEditText = (EditText) view.findViewById(R.id.name); 21 mTelEditText = (EditText) view.findViewById(R.id.tel); 22 23 // 添加事件代理的UI元素 24 Button insert = (Button) view.findViewById(R.id.insert); 25 insert.setOnClickListener(this); 26 27 Button query = (Button) view.findViewById(R.id.query); 28 query.setOnClickListener(this); 29 30 return view; 31 } 32 33 // inherit View.OnClickListener interface member method 34 @Override 35 public void onClick(View view) 36 { 37 switch (view.getId()) 38 { 39 case R.id.insert: 40 { 41 mContentVals.put(UserInfo.User.NAME, mNameEditText.getText().toString()); 42 mContentVals.put(UserInfo.User.PHONE, mTelEditText.getText().toString()); 43 Uri resUri = getActivity().getContentResolver().insert(UserInfo.User.CONTENT_URI, mContentVals); 44 if (null != resUri) 45 { 46 Toast.makeText(getActivity(), "添加数据成功", Toast.LENGTH_SHORT).show(); 47 } 48 else 49 { 50 Toast.makeText(getActivity(), "添加数据失败", Toast.LENGTH_SHORT).show(); 51 } 52 break; 53 } 54 case R.id.query: 55 { 56 ArrayList<String> data = new ArrayList<>(); 57 Cursor cursor = getActivity().getContentResolver().query(UserInfo.User.CONTENT_URI, null, null, null, null); 58 if (null != cursor) 59 { 60 if (cursor.moveToFirst()) 61 { 62 do 63 { 64 String name = cursor.getString(cursor.getColumnIndex(UserInfo.User.NAME)); 65 data.add(name); 66 } while (cursor.moveToNext()); 67 } 68 cursor.close(); 69 } 70 // 获取Fragment管理系统对象 71 FragmentManager fm = getFragmentManager(); 72 // 创建一个fragment对象 73 UserListFragment userListFragment = UserListFragment.newInstance(data); 74 // 通过Fragment管理器,将新创建的fragment对象添加到指定的布局容器中 75 fm.beginTransaction().add(R.id.user_list, userListFragment).commit(); 76 Toast.makeText(getActivity(), "查询数据", Toast.LENGTH_SHORT).show(); 77 break; 78 } 79 default: 80 Toast.makeText(getActivity(), "Without event in element.", Toast.LENGTH_SHORT).show(); 81 } 82 } 83 }
显示数据数据的列表数据界面(UserListFragment):
1 public class UserListFragment extends ListFragment 2 { 3 public static final String sExtra_Users_Key = "users"; 4 5 // member method 6 public static UserListFragment newInstance(ArrayList<String> data) 7 { 8 Bundle args = new Bundle(); 9 args.putStringArrayList(sExtra_Users_Key, data); 10 UserListFragment userListFragment = new UserListFragment(); 11 userListFragment.setArguments(args); 12 13 return userListFragment; 14 } 15 16 // inherit baseclass member method 17 @Override 18 public void onCreate(Bundle savedInstanceState) 19 { 20 super.onCreate(savedInstanceState); 21 ArrayList<String> data = getArguments().getStringArrayList(sExtra_Users_Key); 22 if (null != data) 23 { 24 ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), 25 android.R.layout.simple_list_item_1, data); 26 setListAdapter(adapter); 27 } 28 } 29 }
MianFramgent界面容器(activity_fragment):
1 <FrameLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 android:id="@+id/fragmentContainter" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent"> 6 7 </FrameLayout>
主界面显示UI样式(fragment_main):
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout 3 xmlns:android="http://schemas.android.com/apk/res/android" 4 android:orientation="vertical" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent"> 7 8 <LinearLayout 9 android:layout_width="match_parent" 10 android:layout_height="wrap_content"> 11 12 <TextView 13 android:layout_width="wrap_content" 14 android:layout_height="wrap_content" 15 android:text="@string/main_name" 16 android:textColor="#000" 17 android:textSize="14sp"/> 18 19 <EditText 20 android:id="@+id/name" 21 android:layout_width="match_parent" 22 android:layout_height="wrap_content" 23 android:textSize="14sp" 24 android:textColor="#000"/> 25 26 </LinearLayout> 27 28 <LinearLayout 29 android:layout_width="match_parent" 30 android:layout_height="wrap_content"> 31 32 <TextView 33 android:layout_width="wrap_content" 34 android:layout_height="wrap_content" 35 android:text="@string/main_tel" 36 android:textColor="#000" 37 android:textSize="14sp"/> 38 39 <EditText 40 android:id="@+id/tel" 41 android:layout_width="match_parent" 42 android:layout_height="wrap_content" 43 android:textSize="14sp" 44 android:textColor="#000"/> 45 46 </LinearLayout> 47 48 <LinearLayout 49 android:layout_width="match_parent" 50 android:layout_height="wrap_content" 51 android:orientation="horizontal" 52 android:gravity="center"> 53 54 <Button 55 android:id="@+id/insert" 56 android:layout_width="wrap_content" 57 android:layout_height="wrap_content" 58 android:text="@string/main_insert" 59 android:textColor="#000"/> 60 61 <Button 62 android:id="@+id/query" 63 android:layout_width="wrap_content" 64 android:layout_height="wrap_content" 65 android:text="@string/main_query" 66 android:textColor="#000"/> 67 68 </LinearLayout> 69 70 <FrameLayout 71 android:id="@+id/user_list" 72 android:layout_width="match_parent" 73 android:layout_height="wrap_content"> 74 75 </FrameLayout> 76 77 </LinearLayout>
在主界面布局XML文件中,有这样一段代码:
1 <FrameLayout 2 android:id="@+id/user_list" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content"> 5 6 </FrameLayout>
使用FrameLayout在界面内部再嵌入一个fragment,用于显示列表数据的UI元素,动态载入UserListFragment:
1 // 获取Fragment管理系统对象 2 FragmentManager fm = getFragmentManager(); 3 // 创建一个fragment对象 4 UserListFragment userListFragment = UserListFragment.newInstance(data); 5 // 通过Fragment管理器,将新创建的fragment对象添加到指定的布局容器中 6 fm.beginTransaction().add(R.id.user_list, userListFragment).commit();