Android进阶:步骤2:ContentProvider初体验

ContentProvider


1.ContentProvider简介

Android四大组件之一,为存储和获取数据提供统一的接口。

可以在不同的应用程序之间共享数据。

ContentProviders 管理访问结构化的数据集。它们可以封装这些数据,并且为定义安全的数据提供机制。

Contentproviders 是标准的接口,它能将一个线程中的数据 与其他线程中的运行的代码进行连接

。也就是说 Content providers 支持跨应用间的访问。

对于ContentProvier而言,无论 数据的来源是什么,它都认为是种表,然后把数据组织成表格。

Android进阶:步骤2:ContentProvider初体验_第1张图片

2.ContentProvider使用场景

当你想要在 Content provider[内容提供者] 中访问数据,你要在你的应用程序的 Context 中使用 ContentResolver[内容解析者] 对象与 provider 进行连接作为客户端[通过 Context 中 getContentResolver()方法] 。这个 ContentResolver 会与 provider 对象进行连接,provider 它是一个继承 Content provider 的类的实例。 provide 这个对象会接受从客户端请求的数据,执行请求动作,返回结果。

如果你不打算将自己的数据分享给其他应用程序,就不必要开发自己的 内容提供者 [提供者], 然而你想要在你的应用程序中提供自定义的搜索建议你需要开发内容提供 者,如果你想要复制数据或者文件到其它应用程序,你就需要拥有自己的 provider 。

Android 自己提供了一个 content providers 来管理数据,如果 音频,视频,或 者个人信息等。你可以在 android.provider 包中查看,这些提供者可以给任何应用 程序所访问。

3. 如何创建 ContentProvider

  • ContentProvider配置
  1.  自定义类继承于ContentProvider,实现要求的方法
  2.  在自动在AndroidManifest.xml配置文件中通过provider标签配置
  3. 通过android:name属性指定待配置的类
  4. 通过android:authorities属性授权 :指定当前内容提供者的uri标识(访问标识),必须唯一。

1.在应用A(contentprovider)中添加ContentProvider的步骤:

点击要添加的包右键new——>Others——>Content Provider

修改成:android:authorities="com.demo.contentprovide"了

Android进阶:步骤2:ContentProvider初体验_第2张图片

在配置文件会自动生成配置标签(四大组件都会在配置标签注册)

       

2.那怎么在应用B(contentresolver)中访问A中的数据呢?

在应用B中 利用ContentResolver内容解析类的对象 resolver来操作

//拿到内容解析对象
resolver=getContentResolver();

内容解析类中的对象可以调用 以下几个和数据库SQLite的操作类似的增删查改方法(只不过是对其他应用的数据进行操作)

只有传入你想访问的应用(该应用必要定义有ContentProvider)的Uri就可以对你要访问的应用进行调用该应用相应的操作

Uri(uniform Resource Identifier 统一资源定位符):指定你要访问的应用的uri

(ContentResolver的操作影响contentprovider的相应方法)

通过以下方法得到uri

Uri.parse("content://authorities[/path]")

resolver.query(Uri uri,..省略);
resolver.insert(Uri uri,..省略);
resolver.delete(Uri uri,..省略);
resolver.update(Uri uri,..省略);

3.ContentProvider提供的方法

  1. - 创建:boolean onCreate() //在ContentProvider创建时调用
  2. - 获取数据类型:String getType(Uri uri)
  3. - 查询:Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder)
  4. - 添加: Uri insert(Uri uri, ContentValues values)
  5. - 更新: int update(Uri uri, ContentValues values, String selection, String[] selectionArgs)
  6. - 删除: int delete(Uri uri, String selection, String[] selectionArgs)

A. 什么时候决定创建一个内容提供者呢?

1). 你需要提供完整的数据和文件给其他的应用程序.
2). 你想要允许用户从你的应用程序复制完整的数据到其他的应用程序

3). 你想要使用搜索框架来提供自定义的搜索建议。

B. 创建内容提供者需要掌握的知识

Android进阶:步骤2:ContentProvider初体验_第3张图片

C. 查看 API 文档中 ContentProvider 类的说明,可以发现这是一个抽象类

Content providers 是 Android 应用程序中主要的组成部分之一,为应用程序 提供对外的内容,它封装了数据然后提供给那些通过简单 ContentResolver 接口的应用程序,这些数据是可以跨应用访问的。

对与解析者来说,它会通过 ContentResolver 会发出一个请求,这个请求是否通过是通过去检查由系统给予的 URI 的授权,授权是由 contentprovider 注册的在 AndroidMainifest.xml 中去注册这种授权。UriMatcher 类可以 帮助解析 URI.

D. 在这里为什么要注册 URI 授权呢?


原因是一个应用程序中可以有多个 contentprovider, 但是每一个 contentprovider 的授权都是唯一的,所以内容解析者可以通过这种 URL 的授权来找到自己想要的 content provider. 这里的 URI 的授权也可以理解为 content provider 对外的一个访问的路径。

E. 注意定义好一个内容提供者之后需要在 AndroidMainifest.xml 中


注册
android:authorities 授权属性,表示外部应用程序访问当前内容提供者的标示符,它是自定义的,一般我们是以 "包名 + 类名" 的形式来定义的。有些人可以理解为 URI 路径

F.查看 UriMatcher 类的概要描述


这是一个在 content provider 中帮助匹配 URIs 的实用类。

G.查看 content provider中public void addURI (String authority, String path, int code)方法

这个方法是用来表示在 content provider 里面添加外部对其的匹配的规则,当 URI 被匹配的时候,就会返回 code 码, URI 节点可以精确的匹配字符串, "*" 号匹配任何的字符串 "#" 号只能匹配数字。[比如删除一条记录中 ID 往往是数字]

参数说明


authority : 授权, 就是 AndroidMainifest.xml 中的授权

path : 匹配路径(通常是一个表名)[* 可以作为匹配任意字符的通配符, # 可以作为匹配数字的通配符]。

【注意这里如果是单条记录,需要添加 /# 标示符】

H.查 看 content provider 中 的 public abstract String getType (Uri uri) 方法

根据给定的 URI 来实现处理 MIME 类型的请求, 对于单条记录返回的 MIME 类型是以 vnd.android.cursor.item 开始的, 对于多条记录返回的 MIME 类型是以 vnd.android.cursor.dir/ 开始的. 这个方法可以在多线程环 境下被调用。 详细参考 Processes and Threads.

I.插入操作

查看 content provider 中 public abstract Uri insert (Uri uri, ContentValues values) 方法

实现这个方法来处理插入一个新行的请求, 在插入后可以友好的调用 notifyChange() 方法。

参数说明:

uri : 这种格式 "content:// URI" 的插入请求[后续会认真的讲解这一部分内 容]。 此处不为空
values: 添加到数据库的 ContentValues 类型集合。博客前面章节讲过次内 容,详情可以去参考。此处不为空

返回 新插入选项的 URI,可以给其他用户去使用。

J. 删除操作

查看 content provider 的 public abstract int delete (Uri uri, String selection, String[] selectionArgs) 方法

实现这个方法主要是用来处理删除一行或者多行的请求操作,实现这个删除操 作需要 有 selection 语句,你可以在删除之后调用 notifyDelete() 方法来 做友好的提示

实现这个方法还需要在 URI 的末尾解析出行的 ID,如果一个指定的行被删 除之后,例如,客户端在创建 SQL 语句的时候会通过 content://contacts/people/22 的这个 URI,来解析出末尾的 ID 号,确定删 除 ID 为 22 的这个记录。

参数说明:

URI : 完整的 URI 路径,包括行 ID

selection : 可选项,在删除行的时候适用

返回 :影响数据库的行数

3.案例1

应用B通过ContentProvider访问应用A的数据

Android进阶:步骤2:ContentProvider初体验_第4张图片

1.添加操作

应用A(contentprovider)中

首先新建一个ContentProvider(新建步骤在上面),叫MyContentProvider.java

这里要创建数据库,并实现增删查改操作(数据来源应用B)


import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;

/**
 * 内容提供者类
 */
public class MyContentProvider extends ContentProvider {
    private SQLiteDatabase db;
    public MyContentProvider() {
    }

    //ContentProvider创建时调用
    @Override
    public boolean onCreate() {
        //创建数据库 stu.db默认在内存的私有目录 data/data/包名/
        SQLiteOpenHelper helper=new SQLiteOpenHelper(getContext(),"stu.db",null,1) {
            @Override
            public void onCreate(SQLiteDatabase db) {
                //创建数据库的表
                String sql="create table info_tb(_id integer primary key autoincrement," +
                        "name varchar(20)," +
                        "age integer," +
                        "gender varchar(4))";
                db.execSQL(sql);
            }
            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            }
        };
        //拿到读写数据库对象,如果没有数据库则创建,有则打开
       db=helper.getReadableDatabase();
        //返回true 后面的方法才能被调用
        return true;
    }



    @Override
    public String getType(Uri uri) {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        //想数据库中插入从contentResolver传来的数据,返回id
        Long id=db.insert("info_tb",null,values);
        //通过ContentU
        Uri ReturnuUri=ContentUris.withAppendedId(uri,id);
        //将id追加到uri后面
        Log.e("Tag","执行了contentprovider的insert操作");
        //返回给contentResolver
        return ReturnuUri;
    }


    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

应用B(contentresolver)中

主要是新建ContentResolver对象

通过对象调用insert方法,进而调用应用A的insert方法,将数据插入数据库

                ContentResolver contentResolver=getContentResolver();
                Uri uri=Uri.parse("content://com.demo.contentprovider");
                ContentValues values=new ContentValues();
                contentResolver.insert(uri,values);

主要的是以上四个步骤,还有一个ContentUris类的方法

public class MainActivity extends AppCompatActivity {

    private ContentResolver contentResolver;
    private EditText name;
    private EditText age;
    private EditText number;
    private RadioGroup gendergp;
    private String genderStr = "男";
    private ListView list;
    private RadioButton gender_male;

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

    }

    private void initView() {
        name = findViewById(R.id.name_edt);
        age = findViewById(R.id.age_edt);
        number = findViewById(R.id.number);
        gendergp = findViewById(R.id.gender_gp);
        gender_male = findViewById(R.id.male);
        list = findViewById(R.id.info_list);
        //单选的监听
        gendergp.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                if (checkedId == R.id.male) {
                    genderStr = "男";
                } else {
                    genderStr = "女";

                }
            }
        });

    }

    public void operate(View v) {
        //获取编辑框的内容
        String nameStr = name.getText().toString();
        String ageStr = age.getText().toString();
        String numStr = number.getText().toString();
        switch (v.getId()) {
            case R.id.insert_btn:
                //Uri.parse("content//authorities[/path]")
                Uri uri=Uri.parse("content://com.demo.contentprovider");
                ContentValues values=new ContentValues();
                values.put("name",nameStr);
                values.put("age",ageStr);
                values.put("gender",genderStr);
                //insert的方法会返回ContentProvider中insert方法执行返回的uri
                Uri ReturnUri=contentResolver.insert(uri,values);
                //parseId拿到ContentProvider拼接到uri后面的id
                Long id= ContentUris.parseId(ReturnUri);
                Toast.makeText(this, "添加成功,新学生学号为:"+id, Toast.LENGTH_SHORT).show();
                break;
            case R.id.select_btn:

                Toast.makeText(this, "查询成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.delect_btn:

                Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
                break;
            case R.id.update_btn:
                //更新

                Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show();
                break;
        }
        //每次执行完一次操作都要清空一下编辑框
        name.setText("");
        age.setText("");
        gender_male.setChecked(true);
        number.setText("");
    }

   
}

2.查询操作:

Android进阶:步骤2:ContentProvider初体验_第5张图片

应用A中MyContentProvider.java

的query查询方法中

特别注意:使用带条件查询时,需要再传入的selection后面加上“=?”;

否则报:Cannot bind argument at index 1 because the index is out of range. The statement has 0 param 错误

String selection1=selection+"=?"
 @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        // TODO: Implement this to handle query requests from clients.
       Cursor c= db.query("info_tb",
                projection,//指定在哪些列中查询,填null是查询所有
                selection,//查询条件
                selectionArgs,//查询条件值数组
                null,//指定分组的列名
                null,//去除不符合条件的列条件
                null,//排序
                sortOrder);
        return c;
    }

应用B中MainActivity.java

的查询点击事件中

                //通过ContentProvider返回的Cursor结果集放到SimpleCursorAdapter上显示  
Cursor c=contentResolver.query(uri,null,null,null,null);
                //参数1:上下文 参数2:资源布局 参数3:cursor 参数4:属性名 参数5:显示控件id的数组 参数6:flag
                SimpleCursorAdapter cursorAdapter = new SimpleCursorAdapter(
                        this,
                        R.layout.item,
                        c,
                        new String[]{"_id", "name", "age", "gender"},
                        new int[]{R.id.info_id, R.id.info_name, R.id.info_age, R.id.info_gender},
                        CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER
                );
                list.setAdapter(cursorAdapter);
                Toast.makeText(this, "查询成功", Toast.LENGTH_SHORT).show();
                break;

 3.删除操作

应用A中MyContentProvider.java

的delete查询方法中

  @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // Implement this to handle requests to delete one or more rows.
        //返回的是int类似删除操作影响了多少行
        int result= db.delete("info_tb",selection,selectionArgs);
        return result;
    }

应用B中MainActivity.java

的删除点击事件中

                //参数1:uri 统一资源定位符,ContentProvider的标识
                //参数2:查询条件
                //参数3:查询条件值数组
                //返回值是影响了多少行
                int result=contentResolver.delete(uri,"_id=?",new String[]{numStr});
                if(result>0)
                Toast.makeText(this, "删除成功", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(this, "删除失败", Toast.LENGTH_SHORT).show();

4.更新操作

应用A中MyContentProvider.java

的update更新方法中

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        // TODO: Implement this to handle requests to update one or more rows.
        // update info_tb set name=?,age=?,gender=? where _id=?
       int result= db.update("info_tb",values,selection,selectionArgs);
       return  result;
    }

应用B中MainActivity.java

的更新点击事件中

       //更新
                ContentValues values1 = new ContentValues();
                values1.put("name", nameStr);
                values1.put("age", ageStr);
                values1.put("gender", genderStr);
                int result2 = contentResolver.update(uri, values1, "where _id=", new String[]{numStr});
                if (result2 > 0)
                    Toast.makeText(this, "修改成功", Toast.LENGTH_SHORT).show();
                else
                    Toast.makeText(this, "修改失败", Toast.LENGTH_SHORT).show();

                break;

Android进阶:步骤2:ContentProvider初体验_第6张图片

你可能感兴趣的:(2-android进阶)