java攻城狮之路(Android篇)--ListView与ContentProvider

一.ListView
1.三种Adapter构建ListView
ListView添加条目的时候, 可以使用setAdapter(ListAdapter)方法, 常用的ListAdapter有三种
BaseAdapter: 定义一个类继承BaseAdapter, 重写4个抽象方法, ListView的条目是由getView()方法构建出来的
SimpleAdapter: 创建SimpleAdapter对象时, 传入数据(List<Map<String, ?>>), 并指定数据的绑定关系
SimpleCursorAdapter: 创建SimpleCursorAdapter对象时, 传入一个Cursor, 指定数据的绑定关系

练习一:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.shellway.sqlite.MainActivity" 
    android:background="@color/abc_search_url_text_normal">

    <ListView
        android:id="@+id/id_LV"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
         />

</RelativeLayout>
activity_main.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="match_parent"
    android:orientation="horizontal" 
    android:padding="10dp"
    >
    
    <TextView 
        android:id="@+id/idTV"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="1"
        />
    <TextView 
        android:id="@+id/nameTV"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:gravity="center"
        android:text="张三"
        />
    <TextView 
        android:id="@+id/balanceTV"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:gravity="center"
        android:text="10000"
        />

</LinearLayout>
item.xml
package com.shellway.sqlite;

import java.util.List;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;

public class MainActivity extends ActionBarActivity {

    private ListView lv;
    private List<Person> persons;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        PersonDAO dao = new PersonDAO(this);
        persons = dao.findAll();
        //给ListView添加Adapter,按照Adapter中的方法对ListView添加条目
        lv.setAdapter(new myAdapter()); 
        
    }
    //定义Adapter,把每个Person对象生成一个条目,将所有条目装入ListView
    private class myAdapter extends BaseAdapter{

        @Override
        public int getCount() {  //返回ListView中要装入的条目的数量
            return persons.size();
        }

        @Override
        public Object getItem(int position) {//点哪个条目就返回哪个条目的对象
            return persons.get(position);
        }

        @Override
        public long getItemId(int position) {//返回条目的ID
            return position;
        }

        @Override
        //返回指定位置上的View,会被添加到ListView中(即一个Person构建成一个View,然后挂到ListView)
        public View getView(int position, View convertView, ViewGroup parent) {
            Person p = persons.get(position);
            //构建成一个条目(View),第三个参数是要挂到谁身上,这里写null它会自动返回到LListView中
            View item = View.inflate(getApplicationContext(), R.layout.item, null);
            TextView idTV = (TextView) item.findViewById(R.id.idTV);
            TextView nameTV = (TextView) item.findViewById(R.id.nameTV);
            TextView balanceTV = (TextView) item.findViewById(R.id.balanceTV);
            idTV.setText(p.getId()+"");
            nameTV.setText(p.getName());
            balanceTV.setText(p.getBalance()+"");
            return item;
        }
    }
}
MainActivity

SimpleAdapter:

注意:若要修改成SimpleAdapter,不要忘记了修改AndroidManifest.xml中下面加粗位置部分。

        <activity
            android:name=".SimpleAdapterActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
package com.shellway.sqlite;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

public class SimpleAdapterActivity extends ActionBarActivity {

    private ListView lv;
    private List<Person> persons;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        PersonDAO dao = new PersonDAO(this);
        persons = dao.findAll();
        List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
        for (Person p : persons) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id", p.getId());
            map.put("name", p.getName());
            map.put("balance", p.getBalance());
            data.add(map);
        }
        lv.setAdapter(new SimpleAdapter(this, data , R.layout.item, 
                new String[]{"id","name","balance"},
                new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV}));
        /**SimpleAdapter
         * 参数1:上下文环境
         * 参数2:数据,List<Map<String, Object>>每个Person装入一个Map,再将Map装入List
         * 参数3:布局文件的资源id
         * 参数4:Map中的Key,和参数5中的id对应,将指定key的value放入View中指定id对应和组件上
         * 参数5:View中的id
         */
    }
}
SimpleAdapter

SimpleCusorAdapter:

注意:使用SimpleCusorAdapter,在查询结果中要包含有“_id”这一列,这里我把id取别名为_id的方法解决。

    public Cursor queryAllCusor(){
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor c = db.rawQuery("select id,name,balance from people", null);
        Cursor c = db.query("people", new String[]{"id as _id","name","balance"}, null, null, null, null, null, null);
        return c;
    }
package com.shellway.sqlite;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;

public class SimpleCusorAdapterActivity extends ActionBarActivity {

    private ListView lv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        PersonDAO dao = new PersonDAO(this);
        Cursor c = dao.queryAllCusor();
        lv.setAdapter(new SimpleCursorAdapter(this, R.layout.item, c, 
                new String[]{"_id","name","balance"}, 
                new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV}));
        /**SimpleAdapter
         * 参数1:上下文环境
         * 参数2:布局文件的资源id
         * 参数3:包含数据的游标
         * 参数4:游标中的列名
         * 参数5:条目中的组件的ID,游标中的数据就会放在对应的这些组件上
         */
    }
}
SimpleCusorAdapterActivity

运行结果:

2.监听ListView的点击
调用ListView.setOnItemClickListener(OnItemClickListener)方法注册一个监听器
在监听器的onItemClick()方法中使用 parent.getItemAtPosition(position) 方法可以获取指定条目上的数据
BaseAdapter: 返回的就是自定义的getItem()方法中返回的数据
SimpleAdapter: 返回的是一个Map, 就是创建SimpleAdapter时List中的一个Map
SimpleCursorAdapter: 返回的是一个Cursor, 这个Cursor就是创建时传入的Cursor, 但是已经通过moveToPosition()方法指定到点击的索引了

练习2:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.shellway.sqlite.SimpleAdapterActivity" 
    android:background="@color/abc_search_url_text_normal">
    
<LinearLayout 
    android:id="@+id/ll"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" 
    >
    
    <TextView 
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="序号"
        />
    <TextView 
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:gravity="center"
        android:text="姓名"
        />
    <TextView 
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:gravity="center"
        android:text="余额"
        />
</LinearLayout>
    
    <ListView
        android:id="@+id/id_LV"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_below="@id/ll"
    />

</RelativeLayout>
activity_main.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="match_parent"
    android:orientation="horizontal" 
    android:padding="10dp"
    >
    
    <TextView 
        android:id="@+id/idTV"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:text="1"
        />
    <TextView 
        android:id="@+id/nameTV"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:gravity="center"
        android:text="张三"
        />
    <TextView 
        android:id="@+id/balanceTV"
        android:textSize="20sp"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:gravity="center"
        android:text="10000"
        />

</LinearLayout>
item.xml
package com.shellway.sqlite;

import java.util.List;

import android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class BaseAdapterActivity extends ActionBarActivity {

    private ListView lv;
    private List<Person> persons;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        PersonDAO dao = new PersonDAO(this);
        persons = dao.findAll();
        //给ListView添加Adapter,按照Adapter中的方法对ListView添加条目
        lv.setAdapter(new myAdapter()); 
        //给ListView添加条目点击监听器
        lv.setOnItemClickListener(new myOnClickListener());
        
    }
    
    private class myOnClickListener implements OnItemClickListener{
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            //获取点击的条目上的数据,其内部实际上是调用:myAdapter.getItem()
              Person p = (Person) parent.getItemAtPosition(position);
              Toast.makeText(getApplicationContext(), p.getBalance()+"", Toast.LENGTH_SHORT).show();
        }
    }
    
    //定义Adapter,把每个Person对象生成一个条目,将所有条目装入ListView
    private class myAdapter extends BaseAdapter{

        @Override
        public int getCount() {  //返回ListView中要装入的条目的数量
            return persons.size();
        }

        @Override
        public Object getItem(int position) {//点哪个条目就返回哪个条目的对象
            return persons.get(position);
        }

        @Override
        public long getItemId(int position) {//返回条目的ID
            return position;
        }

        @Override
        //返回指定位置上的View,会被添加到ListView中(即一个Person构建成一个View,然后挂到ListView)
        public View getView(int position, View convertView, ViewGroup parent) {
            Person p = persons.get(position);
            //构建成一个条目(View),第三个参数是要挂到谁身上,这里写null它会自动返回到LListView中
            View item = View.inflate(getApplicationContext(), R.layout.item, null);
            TextView idTV = (TextView) item.findViewById(R.id.idTV);
            TextView nameTV = (TextView) item.findViewById(R.id.nameTV);
            TextView balanceTV = (TextView) item.findViewById(R.id.balanceTV);
            idTV.setText(p.getId()+"");
            nameTV.setText(p.getName());
            balanceTV.setText(p.getBalance()+"");
            return item;
        }
    }
}
BaseAdapterActivity添加ListView中条目点击监听事件
package com.shellway.sqlite;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class SimpleAdapterActivity extends ActionBarActivity {

    private ListView lv;
    private List<Person> persons;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        PersonDAO dao = new PersonDAO(this);
        persons = dao.findAll();
        List<Map<String, Object>> data = new ArrayList<Map<String,Object>>();
        for (Person p : persons) {
            Map<String, Object> map = new HashMap<String, Object>();
            map.put("id", p.getId());
            map.put("name", p.getName());
            map.put("balance", p.getBalance());
            data.add(map);
        }
        lv.setAdapter(new SimpleAdapter(this, data , R.layout.item, 
                new String[]{"id","name","balance"},
                new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV}));
        /**SimpleAdapter
         * 参数1:上下文环境
         * 参数2:数据,List<Map<String, Object>>每个Person装入一个Map,再将Map装入List
         * 参数3:布局文件的资源id
         * 参数4:Map中的Key,和参数5中的id对应,将指定key的value放入View中指定id对应和组件上
         * 参数5:View中的id
         */
        lv.setOnItemClickListener(new myOnClickListener());
    }
    private class myOnClickListener implements OnItemClickListener{
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            //SimpleAdapter返回的是一个map
              Map<String,Object> map =  (Map<String, Object>) parent.getItemAtPosition(position);
              Toast.makeText(getApplicationContext(), map.get("name").toString(), Toast.LENGTH_SHORT).show();
        }
    }
}
SimpleAdapterActivity添加ListView中条目点击监听事件
package com.shellway.sqlite;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.ActionBarActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

public class SimpleCusorAdapterActivity extends ActionBarActivity {

    private ListView lv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        PersonDAO dao = new PersonDAO(this);
        Cursor c = dao.queryAllCusor();
        lv.setAdapter(new SimpleCursorAdapter(this, R.layout.item, c, 
                new String[]{"_id","name","balance"}, 
                new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV}));
        /**SimpleAdapter
         * 参数1:上下文环境
         * 参数2:布局文件的资源id
         * 参数3:包含数据的游标
         * 参数4:游标中的列名
         * 参数5:条目中的组件的ID,游标中的数据就会放在对应的这些组件上
         */
        lv.setOnItemClickListener(new myOnClickListener());
    }
    
    private class myOnClickListener implements OnItemClickListener{
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            //SimpleCursorAdapter返回的是一个Cursor
              Cursor c = (Cursor) parent.getItemAtPosition(position);
              Toast.makeText(getApplicationContext(), c.getString(0), Toast.LENGTH_SHORT).show();
        }
    }
}
SimpleCusorAdapterActivity添加ListView中条目点击监听事件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shellway.sqlite"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />
        <instrumentation
        android:targetPackage="com.shellway.sqlite"
        android:name="android.test.InstrumentationTestRunner" />
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
     <uses-library android:name="android.test.runner" />
        <activity
            android:name=".BaseAdapterActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
AndroidManifest.xml

辅助类:

package com.shellway.sqlite;

public class Person {
    
    private Integer id;
    private String name;
    private Integer balance;
    public Person() {
        super();
    }
    public Person(String name, Integer balance) {
        super();
        this.name = name;
        this.balance = balance;
    }
    public Person(Integer id, String name, Integer balance) {
        super();
        this.id = id;
        this.name = name;
        this.balance = balance;
    }
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getBalance() {
        return balance;
    }
    public void setBalance(Integer balance) {
        this.balance = balance;
    }
    @Override
    public String toString() {
        return "person [id=" + id + ", name=" + name + ", balance=" + balance
                + "]";
    }
}
Person.java
package com.shellway.sqlite;

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

public class DBSQLiteHelper extends SQLiteOpenHelper {
    public DBSQLiteHelper(Context context){
        super(context,"data.db" , null, 4);
        /**
         * 由于弗雷没有无参的构造函数,必须显式调用有参的构造函数
         * 参数1:上下文环境,用来确定数据库文件存储的目录
         * 参数2:数据库文件的名字
         * 参数3:生成游标的工厂,填null就是使用默认的
         * 参数4:数据库的版本,从1开始
         */
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
      System.out.println("onCreate");
      db.execSQL("CREATE TABLE people(id INTEGER PRIMARY KEY AUTOINCREMENT,name VACHAR(20))");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
     System.out.println("onUpgrade");
     db.execSQL("ALTER TABLE people ADD balance INTEGER");
    }
}
DBSQLiteHelper.java
package com.shellway.sqlite;

import java.util.ArrayList;
import java.util.List;

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

public class PersonDAO {
    private DBSQLiteHelper helper;
    
    public PersonDAO(Context context) {
        helper = new DBSQLiteHelper(context);
    }
    
    public long insert(Person p){
         SQLiteDatabase db = helper.getWritableDatabase();//获取数据库链接(可写的)
        //db.execSQL("INSERT INTO people(name,balance) VALUES(?,?)", new Object[]{p.getName(),p.getBalance()} );
         ContentValues values = new ContentValues();
         values.put("name", p.getName());
         values.put("balance", p.getBalance());
         /**
          * 这里的第二个参数 可以随便填表里的任意一个字段名,是为了防止插入的字段名为空时会出错,
          * 当你确定插入的值不会出错时候可以null
          * 返回值表示最新插入的记录的ID
          */
         long rows = db.insert("people", null, values);
         db.close();
         return rows;
    }
    public void delete(Integer id){
        SQLiteDatabase db = helper.getWritableDatabase();
        //db.execSQL("DELETE FROM people WHERE id = ?", new Object[]{id});
        db.delete("people", "id=?", new String[]{id+""});
        db.close();
    }
    public void update(Person p){
        SQLiteDatabase db = helper.getWritableDatabase();
        //db.execSQL("update people set name=?,balance=? where id=? ", new Object[]{p.getName(),p.getBalance(),p.getId()});
        ContentValues values = new ContentValues();
        values.put("name", p.getName());
        values.put("balance", p.getBalance());
        db.update("people", values, "id=?", new String[]{p.getId()+""});
        db.close();
    }
    //根据id查询某条记录
    public Person query(Integer id){
        /**
         * 查询时候应该优先使用getReadableDatabase()而不是getWritableDatabase(),
         * 其实getReadableDatabase是先获取getWritableDatabase,若获取失败则采用getReadableDatabase
         */
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor c = db.rawQuery("select name,balance from people where id=?",new String[]{id+""});
        Cursor c = db.query("people", new String[]{"id","name","balance"}, "id=?", new String[]{id+""}, null, null, null, null);
        Person p = null ;
        if (c.moveToNext()) {
            String name = c.getString(c.getColumnIndex("name"));
            int balance = c.getInt(2);//若直接用下标方式,则注意该字段的索引,游标的索引是从0开始的
            p = new Person(id,name,balance);
        }
        c.close();
        db.close();
        return p;
    }
    //查询全部记录
    public List<Person> findAll(){
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor c = db.rawQuery("select id,name,balance from people", null);
        Cursor c = db.query("people", null, null, null, null, null, null, null);
        List<Person>  persons = new ArrayList<Person>();
        while (c.moveToNext()) {
            Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
            persons.add(p);
        }
        c.close();
        db.close();
        return persons;
    }
    public Cursor queryAllCusor(){
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor c = db.rawQuery("select id,name,balance from people", null);
        Cursor c = db.query("people", new String[]{"id as _id","name","balance"}, null, null, null, null, null, null);
        return c;
    }
    //查询记录总条数
    public int queryCount(){
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor c = db.rawQuery("select count(*) from people", null);
        Cursor c = db.query("people", new String[]{"count(*)"}, null, null, null, null, null);
        c.moveToNext();
        int i = c.getInt(0);
        c.close();
        db.close();
        return i;
    }
    //分页查询
    public List<Person> queryPage(int pageNum,int capacity){
        String offset = (pageNum-1) * capacity +"";  //偏移量,即是第几页的页数
        String len = capacity + "";                  //一页中显示的个数
        SQLiteDatabase db = helper.getReadableDatabase();
        //Cursor c = db.rawQuery("select id,name,balance from people limit ?,?", new String[]{offset,len});
        Cursor c = db.query("people", new String[]{"id","name","balance"}, null, null, null, null, null, offset+","+len);
        List<Person>  persons = new ArrayList<Person>();
        while (c.moveToNext()) {
            Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
            persons.add(p);
        }
        c.close();
        db.close();
        return persons;
    }
}
PersonDAO.java
package com.shellway.sqlite;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.database.sqlite.SQLiteDatabase;
import android.provider.SyncStateContract.Helpers;
import android.test.AndroidTestCase;

public class TestSQLite extends AndroidTestCase {
     public void test1(){
         DBSQLiteHelper helper = new DBSQLiteHelper(getContext());
         SQLiteDatabase sql = helper.getWritableDatabase();
         /**
          * 获取可写的数据库连接
          * 数据库文件不存在时,会创建数据库文件,并且执行onCreate()方法
          * 数据库文件存在,且版本没有改变时,不执行任何方法
          * 数据库文件存在,版本提升,执行onUpdate方法
          */
     }
     public void testInsert(){
         PersonDAO personDAO = new PersonDAO(getContext());
         long rows = personDAO.insert(new Person("KKK",20000));
         System.out.println(rows);
     }
     public void testDelete(){
         PersonDAO personDAO = new PersonDAO(getContext());
         personDAO.delete(104);
     }
     public void testUpdate(){
         PersonDAO personDAO = new PersonDAO(getContext());
         personDAO.update(new Person(1,"www",30000));
     }
     public void testQuery(){
         PersonDAO personDAO = new PersonDAO(getContext());
         System.out.println(personDAO.query(5));
     }
     public void testFindAll(){
         PersonDAO personDAO = new PersonDAO(getContext());
         List<Person> persons = personDAO.findAll();
         for (Person p : persons) {
            System.out.println(p);
        }
     }
     public void testQueryCount(){
         PersonDAO personDAO = new PersonDAO(getContext());
         int count = personDAO.queryCount();
         System.out.println(count);
     }
     public void testQueryPage(){
         PersonDAO personDAO = new PersonDAO(getContext());
         List<Person> persons = personDAO.queryPage(3, 20);
         for (Person p : persons) {
            System.out.println(p);
        }
     }
     
}
TestSQLite.java测试类

运行结果:

二.内容提供者(ContentProvider)
1.什么是ContentProvider
ContentProvider可以用来把程序中的数据对外进行共享, 提供增删改查的方法
ContentProvider中可以注册观察者, 监听数据的变化
* 2.怎么创建?

步骤1:在清单文件AndroidManifest.xml中注册

步骤2:定义类继承ContentProvider

package com.shellway.sqlite.provider;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public class SQLiteProvider extends ContentProvider {

    @Override
    public boolean onCreate() {
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        System.out.println("query");
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        System.out.println("insert");
        return null;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        System.out.println("delete");
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        System.out.println("update");
        return 0;
    }
    
    @Override
    public String getType(Uri uri) {
        return null;
    }
}
定义类SQLiteProvider继承ContentProvider

步骤3:另创建一个工程访问内容提供者

package com.shellway.other;

import android.support.v7.app.ActionBarActivity;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //获取解析器对象
        ContentResolver resolver = getContentResolver();
        //访问内容提供者
        ContentValues values = new ContentValues();
        Uri uri = Uri.parse("content://com.shellway.sqlite.provider");
        resolver.insert(uri, values);
        resolver.delete(uri, null, null);
        resolver.update(uri, values, null, null);
        resolver.query(uri, null, null, null, null);
    }
}
MainActivity


3.在手机上注册
将应用安装到手机上即可, 不用运行程序
* 4.怎么访问
获取解析器ContentResolver, 指定Uri
通过ContentResolver.insert(), delete(), update(), query()方法访问Uri关联的ContentProvider
5.Uri的处理
使用UriMatcher可以检查传入的Uri是否和指定的匹配
如果Uri带了id, 可以使用ContentUris获取id, 插入方法可以使用ContentUris给Uri加上id

练习:

package com.shellway.sqlite.provider;

import com.shellway.sqlite.dao.DBSQLiteHelper;

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;

public class SQLiteProvider extends ContentProvider {
     private UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
     private DBSQLiteHelper helper;
     private static final int PERSON = 1;
     private static final int PERSON_ID = 2;
    @Override
    public boolean onCreate() {
        helper = new DBSQLiteHelper(getContext());
        //设置一个Uri,如果匹配到person,则返回PERSON
        matcher.addURI("com.shellway.sqlite.provider", "person", PERSON);
        matcher.addURI("com.shellway.sqlite.provider", "person/#", PERSON_ID);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = helper.getReadableDatabase();
        switch (matcher.match(uri)) {
        case PERSON_ID:
             long id = ContentUris.parseId(uri);
             selection = selection==null ? "id=" + id : selection + " AND id =" + id ;
        case PERSON:
            return db.query("people", projection, selection, selectionArgs, null, null, sortOrder);
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case PERSON:
            long id = db.insert("people", "id", values);
            return ContentUris.withAppendedId(uri, id);
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }
    

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case PERSON_ID:
             long id = ContentUris.parseId(uri);
             selection = selection==null ? "id=" + id : selection + " AND id =" + id ;
        case PERSON:
            return db.delete("people", selection, selectionArgs);
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        SQLiteDatabase db = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case PERSON_ID:
             long id = ContentUris.parseId(uri);
             selection = selection==null ? "id=" + id : selection + " AND id =" + id ;
        case PERSON:
            return db.update("people", values, selection, selectionArgs);
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }
    
    @Override
    public String getType(Uri uri) {
        return null;
    }
}
02.SQLlite工程中的内容提供者SQLiteProvider
package com.shellway.other;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;

public class ProviderTest extends AndroidTestCase {
   public void test1(){
        //获取解析器对象
        ContentResolver resolver = getContext().getContentResolver();
        //访问内容提供者
        ContentValues values = new ContentValues();
        Uri uri = Uri.parse("content://com.shellway.sqlite.provider");
        
        resolver.insert(uri, values);
        resolver.delete(uri, null, null);
        resolver.update(uri, values, null, null);
        resolver.query(uri, null, null, null, null);
   }
   
   public void testQuery(){
       ContentResolver resolver = getContext().getContentResolver();
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person");
       Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC");
       while (c.moveToNext()) {
          Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
          System.out.println(p);
       }
   }
   public void testQuery2(){
       ContentResolver resolver = getContext().getContentResolver();
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/10");
       Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC");
       while (c.moveToNext()) {
          Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
          System.out.println(p);
       }
   }
   public void testInsert(){
       ContentResolver resolver = getContext().getContentResolver();
       ContentValues values = new ContentValues();
       values.put("name", "Insert");
       values.put("balance", "54321");
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person");
       Uri count = resolver.insert(uri, values);
          System.out.println(count);
   }
   public void testUpdate(){
       ContentResolver resolver = getContext().getContentResolver();
       ContentValues values = new ContentValues();
       values.put("name", "Update");
       values.put("balance", "12345");
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/106");
       int i = resolver.update(uri, values, null, null);
          System.out.println(i);
   }
   public void testDelete(){
       ContentResolver resolver = getContext().getContentResolver();
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/107");
       int i = resolver.delete(uri, null, null);
          System.out.println(i);
   }
}
03.Other测试工程中的测试内容提供者的类ProviderTest

 细节补充:

    @Override
    public String getType(Uri uri) {
        switch (matcher.match(uri)) {
        case PERSON_ID:
             //一般返回的类型是:minetype? image/jpg  html/text css/text
             return "vnd.android.cursor.item/person"; //这里表示返回的是单条的person数据
        case PERSON:
            return "vnd.android.cursor.dir/person";//这里表示返回的是多条的person数据
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }
SQLiteProvider中的getType
   public void testType(){
       ContentResolver resolver = getContext().getContentResolver();
       String s1 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person/106"));
       String s2 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person"));
       System.out.println(s1);
       System.out.println(s2);
   }
ProviderTest中的测试getType方法:testType

注意1:ProviderTest类中的ContentResolver resolver = getContext().getContentResolver();这一行不能放到类的成员变量里边,因为:.class ->.dex ->.app ->安装 ->开启进程(开启主线程)->创建ProviderTest对象 ->setContext ->测试方法 ->getConTest。否则会出现空指针异常,因为还没有setContext就getContext.

注意2:SQLiteProvider中的onCreate() 方法是在第一次启动时执行,然后会长期驻留在后台,除非是被杀死,否则不会再执行。

6.注册观察者
在应用程序中可以对ContentProvider注册一个观察者(ContentObserver)
定义类继承ContentObserver, 重写onChange()方法
使用ContentResolver.registerContentObserver(Uri, boolean, ContentObServer)方法可以注册, 传入指定Uri, 是否监听子级路径, 和一个观察者对象
在收到数据改变通知之后, 会自动执行onChange()方法
7.通知观察者
注册观察者之后, 需要在ContentProvider中进行通知, 观察者才能收到, 使用ContentResolver.notifyChange()方法可以通知数据的改变

练习:

package com.shellway.sqlite.ui;

import java.util.List;

import com.shellway.sqlite.R;
import com.shellway.sqlite.dao.PersonDAO;
import com.shellway.sqlite.domain.Person;

import android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat;
import android.support.v7.app.ActionBarActivity;
import android.content.ContentProvider;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class BaseAdapterActivity extends ActionBarActivity {

    private ListView lv;
    private List<Person> persons;
    private PersonDAO dao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.id_LV); //获取ListView
        dao = new PersonDAO(this);
        persons = dao.findAll();
        //给ListView添加Adapter,按照Adapter中的方法对ListView添加条目
        lv.setAdapter(new myAdapter()); 
        //给ListView添加条目点击监听器
        lv.setOnItemClickListener(new myOnClickListener());
        //注册一个内容观察者
        Uri uri = Uri.parse("content://com.shellway.sqlite.provider");
        //第二个参数表示监听上面uri子路径下所有的变化,若改为false则只监听uri本身的变化
        getContentResolver().registerContentObserver(uri , true, new MyContentObserver());
    }
    private class MyContentObserver extends ContentObserver{
        public MyContentObserver() {
            super(new Handler());//Handler()是一个处理器,目前没有用到
        }
        @Override
        public void onChange(boolean selfChange) {
            persons = dao.findAll();
            lv.setAdapter(new myAdapter()); 
        }
    }
    
    private class myOnClickListener implements OnItemClickListener{
        @Override
        public void onItemClick(AdapterView<?> parent, View view, int position,
                long id) {
            //获取点击的条目上的数据,其内部实际上是调用:myAdapter.getItem()
              Person p = (Person) parent.getItemAtPosition(position);
              Toast.makeText(getApplicationContext(), p.getBalance()+"", Toast.LENGTH_SHORT).show();
        }
    }
    
    //定义Adapter,把每个Person对象生成一个条目,将所有条目装入ListView
    private class myAdapter extends BaseAdapter{

        @Override
        public int getCount() {  //返回ListView中要装入的条目的数量
            return persons.size();
        }

        @Override
        public Object getItem(int position) {//点哪个条目就返回哪个条目的对象
            return persons.get(position);
        }

        @Override
        public long getItemId(int position) {//返回条目的ID
            return position;
        }

        @Override
        //返回指定位置上的View,会被添加到ListView中(即一个Person构建成一个View,然后挂到ListView)
        public View getView(int position, View convertView, ViewGroup parent) {
            Person p = persons.get(position);
            //构建成一个条目(View),第三个参数是要挂到谁身上,这里写null它会自动返回到LListView中
            View item = View.inflate(getApplicationContext(), R.layout.item, null);
            TextView idTV = (TextView) item.findViewById(R.id.idTV);
            TextView nameTV = (TextView) item.findViewById(R.id.nameTV);
            TextView balanceTV = (TextView) item.findViewById(R.id.balanceTV);
            idTV.setText(p.getId()+"");
            nameTV.setText(p.getName());
            balanceTV.setText(p.getBalance()+"");
            return item;
        }
    }
}
02.SQLite工程中在BaseAdapterActivity里注册观察者观察一个Uri
package com.shellway.sqlite.provider;

import com.shellway.sqlite.dao.DBSQLiteHelper;

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;

public class SQLiteProvider extends ContentProvider {
     private UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
     private DBSQLiteHelper helper;
     private static final int PERSON = 1;
     private static final int PERSON_ID = 2;
    @Override
    public boolean onCreate() {
        helper = new DBSQLiteHelper(getContext());
        //设置一个Uri,如果匹配到person,则返回PERSON
        matcher.addURI("com.shellway.sqlite.provider", "person", PERSON);
        matcher.addURI("com.shellway.sqlite.provider", "person/#", PERSON_ID);
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = helper.getReadableDatabase();
        switch (matcher.match(uri)) {
        case PERSON_ID:
             long id = ContentUris.parseId(uri);
             selection = selection==null ? "id=" + id : selection + " AND id =" + id ;
        case PERSON:
            return db.query("people", projection, selection, selectionArgs, null, null, sortOrder);
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase db = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case PERSON:
            long id = db.insert("people", "id", values);
            getContext().getContentResolver().notifyChange(uri, null);
            return ContentUris.withAppendedId(uri, id);
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }
    

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case PERSON_ID:
             long id = ContentUris.parseId(uri);
             selection = selection==null ? "id=" + id : selection + " AND id =" + id ;
        case PERSON:
            int count = db.delete("people", selection, selectionArgs);
            getContext().getContentResolver().notifyChange(uri, null);
            return count;
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        SQLiteDatabase db = helper.getWritableDatabase();
        switch (matcher.match(uri)) {
        case PERSON_ID:
             long id = ContentUris.parseId(uri);
             selection = selection==null ? "id=" + id : selection + " AND id =" + id ;
        case PERSON:
            int count = db.update("people", values, selection, selectionArgs);
            getContext().getContentResolver().notifyChange(uri, null);
            return count;
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }
    
    @Override
    public String getType(Uri uri) {
        switch (matcher.match(uri)) {
        case PERSON_ID:
             //一般返回的类型是:minetype? image/jpg  html/text css/text
             return "vnd.android.cursor.item/person"; //这里表示返回的是单条的person数据
        case PERSON:
            return "vnd.android.cursor.dir/person";//这里表示返回的是多条的person数据
        default:
            throw new RuntimeException("Uri不能试看识别。。。 ");
        }
    }
}
02.SQLite工程中在SQLiteProvider内容提供者里编写内容变更通知语句
package com.shellway.other;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;

public class ProviderTest extends AndroidTestCase {
   public void test1(){
        //获取解析器对象
        ContentResolver resolver = getContext().getContentResolver();
        //访问内容提供者
        ContentValues values = new ContentValues();
        Uri uri = Uri.parse("content://com.shellway.sqlite.provider");
        
        resolver.insert(uri, values);
        resolver.delete(uri, null, null);
        resolver.update(uri, values, null, null);
        resolver.query(uri, null, null, null, null);
   }
   
   public void testQuery(){
       ContentResolver resolver = getContext().getContentResolver();
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person");
       Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC");
       while (c.moveToNext()) {
          Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
          System.out.println(p);
       }
   }
   public void testQuery2(){
       ContentResolver resolver = getContext().getContentResolver();
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/10");
       Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC");
       while (c.moveToNext()) {
          Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2));
          System.out.println(p);
       }
   }
   public void testInsert(){
       ContentResolver resolver = getContext().getContentResolver();
       ContentValues values = new ContentValues();
       values.put("name", "Test5");
       values.put("balance", "12345");
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person");
       Uri count = resolver.insert(uri, values);
   }
   public void testUpdate(){
       ContentResolver resolver = getContext().getContentResolver();
       ContentValues values = new ContentValues();
       values.put("name", "shellway");
       values.put("balance", "10000");
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/116");
       int i = resolver.update(uri, values, null, null);
   }
   public void testDelete(){
       ContentResolver resolver = getContext().getContentResolver();
       Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/122");
       int i = resolver.delete(uri, null, null);
   }
   public void testType(){
       ContentResolver resolver = getContext().getContentResolver();
       String s1 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person/106"));
       String s2 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person"));
       System.out.println(s1);
       System.out.println(s2);
   }
}
在A应用(03.Other)中使用内容提供者,使得数据发生改变
package com.shellway.contentobserver;

import android.support.v7.app.ActionBarActivity;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Uri uri = Uri.parse("content://com.shellway.sqlite.provider");
        getContentResolver().registerContentObserver(uri , true, new MyContentOberver());
    }
    private class MyContentOberver extends ContentObserver{
        public MyContentOberver() {
            super(new Handler());
        }
        @Override
        public void onChange(boolean selfChange) {
            Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person");
            Cursor c = getContentResolver().query(uri, null, null, null, "id desc limit 1");
            while (c.moveToNext()) {
                Toast.makeText(getApplicationContext(), c.getString(1), Toast.LENGTH_SHORT).show();
            }
        }
    }
}
在B应用(04.ContentObserver)中也注册一个观察者观察相同的Uri,若该Uri上的数据发生改变,我会收到通知然后执行我的onChange方法

结果:一旦在A应用中通过内容提供者改变数据,则注册在该Uri上的观察者都会收到数据发生改变的通知,因为事先在内容提供者里都已经放了一个“通知者”,这里的通知者即是:getContext().getContentResolver().notifyChange(uri, null);所以,SQLite工程和B应用中的观察者都会接收到通知,从而执行自己的onChange()方法。


四.监听短信
1.获取源码
安装GIT工具:
在网站上下载com.android.providers.telephony源码:https://github.com/android
通过清单文件可以查找到Uri
2.监听改变
对指定Uri添加ContentOberver
在onChange方法中查询最新的记录, 收发短信时都会收到修改通知, 这样就能获取刚刚收发的短信了

package com.shellway.smsobserver;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.support.v7.app.ActionBarActivity;
import android.content.ContentResolver;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;

public class MainActivity extends ActionBarActivity {

    private Uri uri;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //注册短信观察者
        uri = Uri.parse("content://sms");
        getContentResolver().registerContentObserver(uri , true, new MyObserver());
    }
    private class MyObserver extends ContentObserver{

        private long flag;
        public MyObserver() {
            super(new Handler());
        }
        @Override
        public void onChange(boolean selfChange) {
            ContentResolver resolver = getContentResolver();
            //查询最新的一条短信信息,因为收发短信,信息都会先往数据库存然后才显示在手机屏幕
            Cursor c = resolver.query(uri, null, null, null, "_id desc limit 1");
            while(c.moveToNext()){
                String addr = c.getString(c.getColumnIndex("address"));
                String body = c.getString(c.getColumnIndex("body"));
                int type = c.getInt(c.getColumnIndex("type"));
                Long date = c.getLong(c.getColumnIndex("date"));
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
                Date d = new Date(date);
                String dd = sdf.format(d);
                if(flag!=date){ //因为回短信时会打印出三条重复的信息,这里利用时间来控制打印结果只为一条信息
                System.out.println(dd +" " + (type == 1 ? "收":"发") + addr +" "+ body);
                flag = date;
                }
            }
        }
    }
}
监听短信信息变化:MainActivity.java
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shellway.smsobserver"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />
    <uses-permission  android:name="android.permission.READ_SMS"/>
    <uses-permission  android:name="android.permission.WRITE_SMS"/>

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
AndroidManifest.xml

 结果:

五.读写联系人
1.获取原码
和监听短信相同, 获取com.android.providers.contacts源码
2.读取联系人
先读raw_contacts表中的id, 在根据id查找data表
3.写出联系人
先向raw_contacts表中写出一个id(自动生成)
在向data表写出3条对应数据, raw_contact_id一列使用刚刚插入的id

 练习:(读联系人)

1.新建一个工程:

2.分析:

3.编写程序:

package com.shellway.contacts;

import android.content.ContentResolver;
import android.database.Cursor;
import android.net.Uri;
import android.test.AndroidTestCase;

public class ContactsTest extends AndroidTestCase {
    
    public void testContexts(){
     Uri idUri = Uri.parse("content://com.android.contacts/raw_contacts");
     Uri dataUri = Uri.parse("content://com.android.contacts/data");
     ContentResolver resolver = getContext().getContentResolver();
     Cursor c = resolver.query(idUri, new String[]{"_id"}, null, null, null);
    /* String[] arr = c.getColumnNames();//打印raw_contacts表中的所有列名
     for (String s : arr) {
        System.out.println(s);
      }*/
     while(c.moveToNext()){
         int idRaw = c.getInt(0); 
         Cursor datac = resolver.query(dataUri, new String[]{"mimetype","data1","data2","data3"}
                               , "raw_contact_id=?"
                               , new String[]{idRaw+""}
                               , null);
         while(datac.moveToNext()){
             if(datac.getString(0).equals("vnd.android.cursor.item/name")){
                 System.out.println("姓名: "+ datac.getString(datac.getColumnIndex("data1")));
             }else if(datac.getString(0).equals("vnd.android.cursor.item/phone_v2")){
                 System.out.println("电话: "+ datac.getString(datac.getColumnIndex("data1")));
             }else if(datac.getString(0).equals("vnd.android.cursor.item/email_v2")){
                 System.out.println("邮箱: "+ datac.getString(datac.getColumnIndex("data1")));
             }
         }
     }
  /*   Cursor datac = resolver.query(dataUri, null, null, null, null);//打印data表中的所有列名
     String[] s = datac.getColumnNames();
     for (String st : s) {
        System.out.println(st);
    }*/
     
   } 
}
ContactsTest.java
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.shellway.contacts"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="21" />
        <instrumentation
        android:targetPackage="com.shellway.contacts"
        android:name="android.test.InstrumentationTestRunner" />
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
     <uses-library android:name="android.test.runner" />
    </application>

</manifest>
AndroidManifest.xml

 结果:

原文地址:https://www.cnblogs.com/shellway/p/4120088.html