Android菜鸟饭团#Udacity Android#第十课 活动笔记
- 什么是ContentProvider(允许你利用underlying data source安全并且高效的通过应用程序的界限分享你的数据,和这里一样使用SQlite或者使用文件,或者使用其他的方法,以便其他应用可以访问它,而不需要真正理解你是如何存储它的)
数据库在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。
不能将数据库设为WORLD_READABLE,每个数据库都只能创建它的包访问,
这意味着只有由创建数据库的进程可访问它。如果需要在进程间传递数据,
则可以使用AIDL/Binder或创建一个ContentProvider,但是不能跨越进程/包边界直接来使用数据库。
一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。
也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。
外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,
重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,
当然,中间也会涉及一些权限的问题。下边列举一些较常见的接口,这些接口如下所示。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。- 什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的 ContentResolver实例。
ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor。
· insert(Uri url, ContentValues values):将一组数据插入到Uri 指定的地方。
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据。
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。 - ContentProvider和ContentResolver中用到的Uri
在ContentProvider和 ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。
我们看下面的例子。
· content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
· content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
· 第一部分是方案:"content://" 这部分永远不变
· 第二部分是授权:"contacts"
· 第二部分是路径:"people/","people/1"(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
· Contacts.People.CONTENT_URI (联系人的URI)。 - Uri代表了要操作的数据,Uri主要包含了两部分信息:1.需要操作的ContentProvider ,2.对ContentProvider中的什么数据进行操作,一个Uri由以下几部分组成:
1.scheme:ContentProvider(内容提供者)的scheme已经由Android所规定为:content://。
2.主机名(或Authority):用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。
3.路径(path):可以用来表示我们要操作的数据,路径的构建应根据业务而定,如下:
• 要操作contact表中id为10的记录,可以构建这样的路径:/contact/10
• 要操作contact表中id为10的记录的name字段, contact/10/name
• 要操作contact表中的所有记录,可以构建这样的路径:/contact
要操作的数据不一定来自数据库,也可以是文件等他存储方式,如下:
要操作xml文件中contact节点下的name节点,可以构建这样的路径:/contact/name
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
因为Uri代表了要操作的数据,所以我们很经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri的工具类,分别为UriMatcher 和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher:用于匹配Uri,它的用法如下:
1.首先把你需要匹配Uri路径全部给注册上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就会返回匹配码
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
ContentUris:用于获取Uri路径后面的ID部分,它有两个比较实用的方法:
• withAppendedId(uri, id)用于为路径加上ID部分
• parseId(uri)方法用于从路径中获取ID部分
ContentResolver:当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成,要获取ContentResolver 对象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,来操作数据。
在我们的实例MyProvider中是如下定义的: public static final String CONTENT_AUTHORITY = "com.example.asus.sunshine.app"; public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);// public static final String PATH_WEATHER = "weather"; public static final String PATH_LOCATION = "location"; public static final String CONTENT_TYPE = ContentResolver.CURSOR_DIR_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_LOCATION; public static final String CONTENT_ITEM_TYPE = ContentResolver.CURSOR_ITEM_BASE_TYPE + "/" + CONTENT_AUTHORITY + "/" + PATH_LOCATION; matcher.addURI(auyhority, WeatherContract.PATH_WEATHER, WEATHER); matcher.addURI(auyhority, WeatherContract.PATH_WEATHER + "/*", WEATHER_WITH_LOCATION); matcher.addURI(auyhority, WeatherContract.PATH_WEATHER +"/*/#", WEATHER_WITH_LOCATION_); matcher.addURI(auyhority, WeatherContract.PATH_LOCATION, LOCATION); 【Loaders】 用于避免直接从UI获取数据 【直接从UI获取数据的弊端】 【Loaders有这些特点】 1.他们对每一个activity和fragment都有效 2.他们提供异步加载数据 3.他们监听他们的数据源并且当数据内容发生改变的时候传递新的结果 4.在一个配置改变时,他们自动的重新连接到最后loader的光标.因此,他们不需要去重新查询他们的数据。 【Starting a Loader(启动一个loader)】 【Restarting a Loader(重新启动一个loader)】 当你使用 initLoader() 方法时,如上所述,如果有一个已经存在的使用指定id的loader,它就会使用它.如果没有,则会创建它.但是有时你会想要放弃掉你旧的数据并且重新开始。 【CursorLoader】 是AsyncTaskLoader的实现类(专门用于处理对ContentProvider的请求) 通过返回Cursor对象来实现数据读取。 使用来帮助我们高效的帮助我们高效的从互联网同步数据,再加上Android框架里的一些辅助功能,他还简化了与用户界面交流数据库的内容,我们通过扩展一个类实现一个新的类 weatherprovider, 用这个来读写我们刚创建的数据库 URI:每一个URI能包含这个content格式,
这是URI的第一部分,在冒号的前面,他指明这个URI将使用的协议,content表示这个URI指向一个内容provider, 中间这一段,是一个唯一的字符串用来定位你的内容provider,他应该总是这个程序的包的名字
位置,通常指向程序里的数据库的一个表 最后是可选的Query ,他既可以是URI路径的一部分,他也可以是传统URI加问号的形式
根据目前情况,一个URI是数据的主要部分,他被传递进那些intent, 2.contract是一个协定,描述了在我们数据模型和视图之间信息如何储存。 3.继承SQLiteopenHelper类,创建数据库,并帮助我们处理数据库版本控制。 TextView的对象就等同于TextView的xml语句(每一个xml控件在java代码中都有与之对应的对象,修改对象的方法实现xml中属性的改变) 为控件绑定监听器: 调用布局文件: setContentView(R.layout.布局文件名) ProgressBar:进度条 p.setProgress(20); for( i=1;i<=100;i++){ p.setProgress(i); //创建另外一个线程,防止阻塞 new Thread(){ public void run() { SystemClock.sleep(3);//睡眠3秒 Toast.makeText(MainActivity.this, ""+i, Toast.LENGTH_SHORT).show(); }; }.start(); } 常用属性: android:max="100":最大进度 android:progress="10":当前进度 SeekBar(拖动条): 其他属性和ProgressBar差不多,只不过多了一个setOnSeekBarChangeListener监听器。同时SeekBar可以手动拖动 以下为扩充和补充知识不写入上传笔记 wrap_content :(长或宽)包含内容 match_parent :(长或宽)与所在布局相同 ListView:列表布局 GridView:表格布局 android:orientation=“vertical”;表示方向属性(在垂直方向上排成一条线) android:orientation=“horizontal”;(在水平方向排成一条线 ) ContentProvider(内容提供者)是Android中的四大组件之一。主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中的数据进行操作。ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。 以下这段是Google Doc中对ContentProvider的大致概述。
内容提供者将一些特定的应用程序数据供给其它应用程序使用。数据可以存储于文件系统、SQLite数据库或其它方式。内容提供者继承于ContentProvider 基类,为其它应用程序取用和存储它管理的数据实现了一套标准方法。然而,应用程序并不直接调用这些方法,而是使用一个 ContentResolver 对象,调用它的方法作为替代。ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。 1.ContentProvider
Android提供了一些主要数据类型的ContentProvider,比如音频、视频、图片和私人通讯录等。可在android.provider包下面找到一些Android提供的ContentProvider。通过获得这些ContentProvider可以查询它们包含的数据,当然前提是已获得适当的读取权限。
主要方法:
public boolean onCreate() 在创建ContentProvider时调用
public Cursor query(Uri, String[], String, String[], String) 用于查询指定Uri的ContentProvider,返回一个Cursor
public Uri insert(Uri, ContentValues) 用于添加数据到指定Uri的ContentProvider中
public int update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的数据
public int delete(Uri, String, String[]) 用于从指定Uri的ContentProvider中删除数据
public String getType(Uri) 用于返回指定的Uri中的数据的MIME类型
*如果操作的数据属于集合类型,那么MIME类型字符串应该以vnd.android.cursor.dir/开头。
例如:要得到所有person记录的Uri为content://contacts/person,那么返回的MIME类型字符串为"vnd.android.cursor.dir/person"。
*如果要操作的数据属于非集合类型数据,那么MIME类型字符串应该以vnd.android.cursor.item/开头。
例如:要得到id为10的person记录的Uri为content://contacts/person/10,那么返回的MIME类型字符串应为"vnd.android.cursor.item/person"。 2.ContentResolver
当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver类来完成,要获取ContentResolver对象,可以使用Context提供的getContentResolver()方法。 - ContentResolver cr = getContentResolver();
ContentResolver提供的方法和ContentProvider提供的方法对应的有以下几个方法。
public Uri insert(Uri uri, ContentValues values) 用于添加数据到指定Uri的ContentProvider中。
public int delete(Uri uri, String selection, String[] selectionArgs) 用于从指定Uri的ContentProvider中删除数据。
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) 用于更新指定Uri的ContentProvider中的数据。
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) 用于查询指定Uri的ContentProvider。
3.Uri
Uri指定了将要操作的ContentProvider,其实可以把一个Uri看作是一个网址,我们把Uri分为三部分。
第一部分是"content://"。可以看作是网址中的"http://"。
第二部分是主机名或authority,用于唯一标识这个ContentProvider,外部应用需要根据这个标识来找到它。可以看作是网址中的主机名,比如"blog.csdn.net"。
第三部分是路径名,用来表示将要操作的数据。可以看作网址中细分的内容路径。 注意:这里的联系人操作有点乱,关键是我还不是很熟,SDK1.6和SDK2.1的联系人操作很有很大不同,希望哪位大侠指点一下。 - /**
- * MainActivity
- *
- * @author zuolongsnail
- */
- public class MainActivity extends Activity {
- private EditText nameET;
- private EditText numberET;
- private Button insertBtn;
- private Button deleteBtn;
- private Button queryBtn;
- private ListView contentView;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- nameET = (EditText) findViewById(R.id.name);
- numberET = (EditText) findViewById(R.id.number);
- insertBtn = (Button) findViewById(R.id.insert);
- deleteBtn = (Button) findViewById(R.id.delete);
- queryBtn = (Button) findViewById(R.id.query);
- // 用于显示数据
- contentView = (ListView) findViewById(R.id.content);
- insertBtn.setOnClickListener(new OperateOnClickListener());
- deleteBtn.setOnClickListener(new OperateOnClickListener());
- queryBtn.setOnClickListener(new OperateOnClickListener());
- }
-
- class OperateOnClickListener implements OnClickListener {
- @Override
- public void onClick(View v) {
- String name = nameET.getText().toString();
- String number = numberET.getText().toString();
- Person p = new Person(name, number);
- switch (v.getId()) {
- // 插入数据
- case R.id.insert:
- insert(p);
- view();
- break;
- // 删除数据
- case R.id.delete:
- delete(name);
- view();
- break;
- // 查询数据
- case R.id.query:
- view();
- break;
- }
- }
- }
-
- // 显示数据
- private void view() {
- Cursor c = query("");
- ListAdapter listAdapter = new SimpleCursorAdapter(this, R.layout.list,
- c, new String[] { People._ID, People.NAME, People.NUMBER },
- new int[] { R.id.id, R.id.name, R.id.number });
- contentView.setAdapter(listAdapter);
- }
-
- // 插入联系人
- private void insert(Person p) {
- // 获得ContentResolver对象
- ContentResolver cr = getContentResolver();
- ContentValues values = new ContentValues();
- values.put(People.NAME, p.name);
- // 表示是否把联系人添加到收藏(加星),1表示加入,0表示不加入,这行代码注释默认是不加入。
- values.put(Contacts.People.STARRED, 1);
- Uri uri = Contacts.People.createPersonInMyContactsGroup(cr, values);
- // 获得联系人People表的Uri
- Uri url = Uri.withAppendedPath(uri,
- Contacts.People.Phones.CONTENT_DIRECTORY);
- values.clear();
- values.put(Contacts.Phones.TYPE, Contacts.Phones.NUMBER);
- values.put(Contacts.Phones.NUMBER, p.number);
- // 插入操作
- cr.insert(url, values);
- }
-
- // 插入联系人
- private void delete(String name) {
- // 获得ContentResolver对象
- ContentResolver cr = getContentResolver();
- Uri url = Contacts.People.CONTENT_URI;
- // 设置删除条件
- String where = People.NAME + "=?";
- String[] selectionArgs = { name };
- cr.delete(url, where, selectionArgs);
- }
-
- // 查询联系人
- private Cursor query(String name) {
- // 获得ContentResolver对象
- ContentResolver cr = getContentResolver();
- Uri uri = Contacts.People.CONTENT_URI;
- // 查询对象
- String[] projection = { People._ID, People.NAME, People.NUMBER };
- // 设置查询条件,这里我把selection和selectionArgs参数都设为null,表示查询全部数据
- String selection = null;
- String[] selectionArgs = null;
- if (!"".equals(name)) {
- selection = People.NAME + "=?";
- selectionArgs = new String[] { name };
- }
- // 设置排序条件
- String sortOrder = Contacts.People._ID;
- Cursor c = cr.query(uri, projection, selection, selectionArgs,
- sortOrder);
- // if (c.moveToFirst()) {
- // for (int i = 0; i < c.getCount(); i++) {
- // c.moveToPosition(i);
- // String name = c.getString(c.getColumnIndexOrThrow(People.NAME));
- // String number = c.getString(c
- // .getColumnIndexOrThrow(People.NUMBER));
- // }
- // }
- return c;
- }
- }
【书记员:刘鑫】 每一个技术小白都有一个成为大神的梦想,现在Android菜鸟饭团就给你这个成就梦想的机会。我们提供最新的Android技术教学,只要你又耐心和毅力就一定会在这里有所收获。 Android菜鸟饭团由南阳GDG组织发起,秉承着开放、分享、创新的原则,希望通过GDG社区的力量能够给更多的想要学习Android开发技术的小白们创造一个学习,交流,分享的环境。同往常的GDG活动一样,我们依然是任性的一个子都不要,并且还在周六的分享中提供盒饭和不定期的惊喜小礼物呦~所以快来加入我们吧,为你的大神梦想迈出第一步。
|