android基礎學習12——內容提供者ContentProvider的使用

內容提供者(ContentProvider)是Android系統四大組件之一,用於保存和檢索數據,是Android系統中不同應用程序之間共享數據的接口。在Android系統中,應用程序之間是相互獨立的,分別運行在自己的進程中,相互之間沒有數據交換。若應用程序之間需要共享數據,就需要用到ContentProvider。ContentProvider是不同應用程序之間進行數據交換的標準API,它以Uri的形式對外提供數據,允許其他應用操作本應用數據。其他應用則使用ContentResolver,並根據ContentProvider提供的Uri操作指定數據。接下來通過一個案例“讀取聯繫人信息”使用內容提供者暴露數據。該案例實現了查詢自己暴露的數據,並將數據捆綁到ListView控件中的功能。

activity.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/ll_root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.lenovo.contentprovider.MainActivity">

    <ListView
        android:id="@+id/lv"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>
</LinearLayout>


在layout文件夾下創建一個list_item.xml文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dip"
    android:gravity="center_vertical"
    android:orientation="horizontal">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="5dip"
        android:src="@drawable/default_avatar"/>
    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="60dip"
        android:layout_marginLeft="20dip"
        android:gravity="center_vertical"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dip"
            android:text="姓名"
            android:textColor="#000000"
            android:textSize="18sp"/>
        <TextView
            android:id="@+id/tv_phone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dip"
            android:layout_marginTop="3dp"
            android:text="電話"
            android:textColor="#88000000"
            android:textSize="16sp" />
    </LinearLayout>
</LinearLayout>


在drawble文件夾下添加一張名爲default_avater的圖片作爲聯繫人的圖片

創建數據庫

package com.example.lenovo.contentprovider;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

/**
 * Created by lenovo on 2017/6/8.
 */

public class PersonSQLiteOpenHelper extends SQLiteOpenHelper {
    private static final String TAG="PersonSQLiteOpenHelper";
    //數據庫的構造方法,用來定義數據庫的名稱、數據庫查詢的結果集、數據庫的版本
    public PersonSQLiteOpenHelper(Context context){
    super(context,"person.db",null,3);
    }
    //數據庫第一次被創建的時候調用的方法
    public void onCreate(SQLiteDatabase db){
        //初始化數據庫的表結構
        db.execSQL("create table person(id integer primary key autoincrement,name varchar(20),number varchar(20))");
    }
    //當數據庫的版本號發生變化的時候(增加的時候)調用
    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion){
        Log.i(TAG,"數據庫的版本變化了......");
    }
}
創建Person類,用於封裝id、name和number屬性

package com.example.lenovo.contentprovider;

/**
 * Created by lenovo on 2017/6/8.
 */


public class Person {
    private int id;
    private String name;
    private String number;
    public Person() {
    }
    public String toString() {
        return "Person [id=" + id + ", name=" + name + ", number=" + number
                + "]";
    }
    public Person(int id, String name, String number) {
        this.id = id;
        this.name = name;
        this.number = number;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}
創建內容提供者

創建PersonDBProvider類繼承ContentProvider,用於實現暴露數據庫程序的功能

package com.example.lenovo.contentprovider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;

/**
 * Created by lenovo on 2017/6/8.
 */


public class PersonDBProvider extends ContentProvider {
    // 定義一個uri的匹配器 用於匹配uri 如果路徑不滿足條件 返回 -1
    private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int INSERT = 1;    //添加數據匹配Uri路徑成功時返回碼
    private static final int DELETE = 2;    //刪除數據匹配Uri路徑成功時返回碼
    private static final int UPDATE = 3;    //更改數據匹配Uri路徑成功時返回碼
    private static final int QUERY = 4;     //查詢數據匹配Uri路徑成功時返回碼
    private static final int QUERYONE = 5; //查詢一條數據匹配Uri路徑成功時返回碼
    //數據庫操作類的對象
    private PersonSQLiteOpenHelper helper;
    static {
        // 添加一組匹配規則.
        matcher.addURI("com.example.lenovo.db.personprovider", "insert", INSERT);
        matcher.addURI("com.example.lenovo.db.personprovider", "delete", DELETE);
        matcher.addURI("com.example.lenovo.db.personprovider", "update", UPDATE);
        matcher.addURI("com.example.lenovo.db.personprovider", "query", QUERY);
        //這裏的“#”號爲通配符凡是符合”query/”皆返回QUERYONE的返回碼
        matcher.addURI("com.example.lenovo.contentprovider.personprovider", "query/#", QUERYONE);
    }
    //當內容提供者被創建的時候 調用 適合 數據的初始化
    public boolean onCreate() {
        helper = new PersonSQLiteOpenHelper(getContext());
        return false;
    }
    //查詢數據操作
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        if (matcher.match(uri) == QUERY) { //匹配查詢的Uri路徑
            //匹配成功 ,返回查詢的結果集
            SQLiteDatabase db = helper.getReadableDatabase();
            //調用數據庫操作的查詢數據的方法
            Cursor cursor = db.query("person", projection, selection,
                    selectionArgs, null, null, sortOrder);
            return cursor;
        } else if (matcher.match(uri) == QUERYONE) {
            //匹配成功,根據id查詢數據
            long id = ContentUris.parseId(uri);
            SQLiteDatabase db = helper.getReadableDatabase();
            Cursor cursor = db.query("person", projection, "id=?",
                    new String[]{id+""}, null, null, sortOrder);
            return cursor;
        } else {
            throw new IllegalArgumentException("路徑不匹配,不能執行查詢操作");
        }
    }
    //獲取當前Uri的數據類型
    public String getType(Uri uri) {
        if (matcher.match(uri) == QUERY) {
            // 返回查詢的結果集
            return "vnd.android.cursor.dir/person";
        } else if (matcher.match(uri) == QUERYONE) {
            return "vnd.android.cursor.item/person";
        }
        return null;
    }
    //添加數據
    public Uri insert(Uri uri, ContentValues values) {
        if (matcher.match(uri) == INSERT) {
            //匹配成功 返回查詢的結果集
            SQLiteDatabase db = helper.getWritableDatabase();
            db.insert("person", null, values);
        } else {
            throw new IllegalArgumentException("路徑不匹配,不能執行插入操作");
        }
        return null;
    }
    //刪除數據
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        if (matcher.match(uri) == DELETE) {
            //匹配成功 返回查詢的結果集
            SQLiteDatabase db = helper.getWritableDatabase();
            db.delete("person", selection, selectionArgs);
        } else {
            throw new IllegalArgumentException("路徑不匹配,不能執行刪除操作");
        }
        return 0;
    }
    //更新數據
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        if (matcher.match(uri) == UPDATE) {
            //匹配成功 返回查詢的結果集
            SQLiteDatabase db = helper.getWritableDatabase();
            db.update("person", values, selection, selectionArgs);
        } else {
            throw new IllegalArgumentException("路徑不匹配,不能執行修改操作");
        }
        return 0;
    }
}
從上述代碼中可以看出,在暴露數據的增、刪、改、查方法之前,首先需要添加一組用於請求數據操作的Uri,然後在相應的增刪改查方法中匹配Uri,匹配成功才能對數據進行操作。

創建數據庫邏輯操作類

package com.example.lenovo.contentprovider;

import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;

/**
 * Created by lenovo on 2017/6/8.
 */

public class PersonDao2 {
    private PersonSQLiteOpenHelper helper;
    //在構造方法裏面完成helper的初始化
    public PersonDao2(Context context){
        helper=new PersonSQLiteOpenHelper(context);
    }
    //添加一條記錄到數據庫
    public long add(String name,String number,int money){
        SQLiteDatabase db=helper.getWritableDatabase();
        ContentValues values=new ContentValues();
        values.put("name",name);
        values.put("number",number);
        long id=db.insert("person",null,values);
        db.close();
        return id;
    }
}

MainActivity

package com.example.lenovo.contentprovider;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    private ListView lv;
    private List<Person>  persons ;
    //創建一個Handler對象用於線程間通信
    private Handler handler = new Handler(){
        public void handleMessage(android.os.Message msg) {
            switch (msg.what) {
                case 100://接收到數據查詢完畢的消息
                    //UI線程適配ListView
                    lv.setAdapter(new MyAdapter());
                    break;
            }
        };
    };
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.lv);
        //由於添加數據、查詢數據是比較耗時的,因此需要在子線程中做這兩個操作
        new Thread(){
            public void run() {
                //添加數據
                AddData();
                //獲取persons集合
                getPersons();
                //如果查詢到數據 則向UI線程發送消息
                if(persons.size() > 0){
                    handler.sendEmptyMessage(100);
                }
            };
        }.start();
    }
    //往person表中插入10條數據
    public void AddData(){
        PersonDao2 dao = new PersonDao2(this);
        long number = 885900000l;
        Random random = new Random();
        for(int i=0;i<10;i++){
            dao.add("wangwu"+i, Long.toString(number+i), random.nextInt(5000));
        }
    }
    //利用ContentResolver對象查詢本應用程序使用ContentProvider暴露的數據
    private void getPersons() {
        //首先要獲取查詢的uri
        String url = "content://com.example.lenovo.db.personprovider/query";
        Uri uri = Uri.parse(url);
        //獲取ContentResolver對象 這個對象的使用後面會詳細講解
        ContentResolver contentResolver = getContentResolver();
        //利用ContentResolver對象查詢數據得到一個Cursor對象
        Cursor cursor = contentResolver.query(uri, null, null, null, null);
        persons = new ArrayList<Person>();
        //如果cursor爲空立即結束該方法
        if(cursor == null){
            return;
        }
        while(cursor.moveToNext()){
            int id = cursor.getInt(cursor.getColumnIndex("id"));
            String name = cursor.getString(cursor.getColumnIndex("name"));
            String number = cursor.getString(cursor.getColumnIndex("number"));
            Person p = new Person(id, name, number);
            persons.add(p);
        }
        cursor.close();
    }
    //適配器
    private class MyAdapter extends BaseAdapter{
        private static final String TAG = "MyAdapter";
        // 控制listview裏面總共有多個條目
        public int getCount() {
            return persons.size(); //條目個數 == 集合的size
        }
        public Object getItem(int position) {
            return persons.get(position);
        }
        public long getItemId(int position) {
            return 0;
        }
        public View getView(int position, View convertView, ViewGroup parent) {
            //得到某個位置對應的person對象
            Person person = persons.get(position);
            View view = View.inflate(MainActivity.this, R.layout.list_item, null);
            //一定要在view對象裏面尋找孩子的id
            //姓名
            TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
            tv_name.setText("姓名:"+person.getName());
            //電話
            TextView tv_phone = (TextView) view.findViewById(R.id.tv_phone);
            tv_phone.setText("電話:"+person.getNumber());
            return view;
        }
    }
}
上述代碼使用ContentResolver查詢本程序使用ContentProvider暴露的數據。

需要注意的是,當執行耗時操作時,應創建一個子線程將耗時操作放在子線程中,然後使用handler實現子線程與UI線程的通信。
在清單文件中註冊PersonDBProvider

        <provider
            android:authorities="com.example.lenovo.db.personprovider"
            android:name="com.example.lenovo.contentprovider.PersonDBProvider">
        </provider>


運行程序能看到如圖所示







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章