Android中Shared Preferences、Files、Network、SQLite数据库编程总结及示例

 Android数据存储
前言  Android中提供了4中数据存储方式。但是存储的这些数据都是私有的,要想在其他应用程序中使用这些数据。就需要使用ContentProvider(数据共享)。下面是对4种方式的介绍。
    1. Shared Preferences 存储方式主要是针对系统配置信息的保存,比如给程序界面设置了音效,想在下一次启动前还能够保留上次设置的音效。此方法是用来存
     储"key-value paires"格式的数据。是一个轻量级的键值存储机制,只能存储基本数据类型。
    2. Files 存储其是通过FileInputStream 和FileOutputStream 来对文件的操作。就是把需要保存的东西通过文件的形式记录下来。当需要这写数据时,通过这个文件获取即可。
     但是在Android中文件是私有的,一个应用程序无法读取其他应用程序文件。
    3. SQLite 数据库 可以用来存储打了的数据,并且可以很容易对数据进行增,删,改,查等操作。且支持SQL语句。
    4. NetWork 通过网络来存储和获取数据。即将数据存储到网络上去。
第一部分 Shared Preferences 数据存储
    Shared preferences 主要用来保存引用程序的一些属性设置。
     getPreferences 此方法用来获取Preferences对象
     SharedPreferences.Editor editor = uiState.edit(); 取得编辑对象
     editor.put...() 此方法添加数据
     commit() 保存数据
     Activity.getPreferences 保存私有数据。即不能与其他模块共享数据。
     注意: 用Shared Preferences 保存的数据无法直接在多个程序间共享。
     下例为在推出程序时把音效的状态保存到Preferences中,当再次启动时将读取上次保存的数据。
   
   public class MIDIPlayer{
    public MediaPlayer playerMusic = null;
    private Context  mContext = null;
    public MIDIPlayer(Context context){
     mContext = context;
    }
    public void PlayMusic(){// 播放音乐
     playerMusic = MediaPlayer.create(mContext, R.raw.start);//装载资源中的音乐
     playerMusic.setLooping(true);//设置是否循环
     try{
      playerMusic.prepare();
     }catch (IllegalStateException e){
      e.printStackTrace();
     }catch (IOException e){
      e.printStackTrace();
     }
     playerMusic.start();
    }
    public void FreeMusic(){// 停止并释放音乐
     if (playerMusic != null){
      playerMusic.stop();
      playerMusic.release();
     }
    }
   }  
   public class Activity01 extends Activity{
    private MIDIPlayer mMIDIPlayer = null;
    private boolean  mbMusic  = false;
    private TextView mTextView = null;
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
     mTextView = (TextView) this.findViewById(R.id.TextView01);
     mMIDIPlayer = new MIDIPlayer(this);
     /* 装载数据 */ 
     SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);// 取得活动的preferences对象.  
     mbMusic = settings.getBoolean("bmusic", false);// 取得值.
     if (mbMusic){
      mTextView.setText("当前音乐状态:开");
      mbMusic = true;
      mMIDIPlayer.PlayMusic();
     }else{
      mTextView.setText("当前音乐状态:关");
     }
    }
    public boolean onKeyUp(int keyCode, KeyEvent event){
     switch (keyCode){
      case KeyEvent.KEYCODE_DPAD_UP:
       mTextView.setText("当前音乐状态:开");
       mbMusic = true;
       mMIDIPlayer.PlayMusic();
       break;
      case KeyEvent.KEYCODE_DPAD_DOWN:
       mTextView.setText("当前音乐状态:关");
       mbMusic = false;
       mMIDIPlayer.FreeMusic();
       break;
     }
     return true;
    }
    public boolean onKeyDown(int keyCode, KeyEvent event){
     if (keyCode == KeyEvent.KEYCODE_BACK){
      /* 这里我们在推出应用程序时保存数据 */   
      SharedPreferences uiState = getPreferences(0);// 取得活动的preferences对象.  
      SharedPreferences.Editor editor = uiState.edit(); // 取得编辑对象   
      editor.putBoolean("bmusic", mbMusic);// 添加值   
      editor.commit();// 提交保存
      if ( mbMusic ){
       mMIDIPlayer.FreeMusic();
      }
      this.finish();
      return true;
     }
     return super.onKeyDown(keyCode, event);
    }
   }

第二部分 Files数据存储    
   public class MIDIPlayer{
    public MediaPlayer playerMusic = null;
    private Context  mContext = null;
    public MIDIPlayer(Context context){
     mContext = context;
    }
    public void PlayMusic(){//播放音乐
     playerMusic = MediaPlayer.create(mContext, R.raw.start);//装载资源中的音乐
     playerMusic.setLooping(true);//设置是否循环
     try{
      playerMusic.prepare();
     }catch (IllegalStateException e){
      e.printStackTrace();
     }catch (IOException e){
      e.printStackTrace();
     }
      playerMusic.start();
    }
    public void FreeMusic(){//停止并释放音乐
     if (playerMusic != null){
      playerMusic.stop();
      playerMusic.release();
     }
    }
   }
   
   public class Activity01 extends Activity{ 
    private MIDIPlayer mMIDIPlayer = null;
    private boolean  mbMusic  = false;
    private TextView mTextView = null;
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
     mTextView = (TextView) this.findViewById(R.id.TextView01);
     mMIDIPlayer = new MIDIPlayer(this);

     load();//通过load方法来获取文件数据
     if (mbMusic){
      mTextView.setText("当前音乐状态:开");
      mbMusic = true;
      mMIDIPlayer.PlayMusic();
     }else{
      mTextView.setText("当前音乐状态:关");
     }
    }
    public boolean onKeyUp(int keyCode, KeyEvent event){
     switch (keyCode){
      case KeyEvent.KEYCODE_DPAD_UP:
       mTextView.setText("当前音乐状态:开");
       mbMusic = true;
       mMIDIPlayer.PlayMusic();
       break;
      case KeyEvent.KEYCODE_DPAD_DOWN:
       mTextView.setText("当前音乐状态:关");
       mbMusic = false;
       mMIDIPlayer.FreeMusic();
       break;
     }
     return true;
    }
    public boolean onKeyDown(int keyCode, KeyEvent event){
     if (keyCode == KeyEvent.KEYCODE_BACK){
      save();//通过save方法在退出应用程序时保存数据,当再次启动时通过load方法来读取这些数据   
      if ( mbMusic ){
       mMIDIPlayer.FreeMusic();
      }
      this.finish();
      return true;
     }
     return super.onKeyDown(keyCode, event);
    }
    void load(){//装载、读取数据
     Properties properties = new Properties();//构建Properties对象
      try{
       FileInputStream stream = this.openFileInput("music.cfg");//利用输入流创建文件 "music.cfg"为文件名
       properties.load(stream);//读取文件内容
      }catch (FileNotFoundException e){
       return;
      }catch (IOException e){
       return;
      }
     mbMusic = Boolean.valueOf(properties.get("bmusic").toString());//取得数据
    }
    boolean save(){//保存数据
     Properties properties = new Properties();
     properties.put("bmusic", String.valueOf(mbMusic));//将数据打包成Properties
     try{
      FileOutputStream stream = this.openFileOutput("music.cfg", Context.MODE_WORLD_WRITEABLE);//用文件来存储数据可以通过openFileOutput方法打开一个文件并存入数据
                             如果这个文件不存在就自动创建这个文件,可以deleteFile方法可以
                             删除指定文件
      properties.store(stream, "");//将打包好的数据写入文件中
     }catch (FileNotFoundException e){
      return false;
     }catch (IOException e){
      return false;
     }
      return true;
    }
   }
   如果在开发程序前,需要通过加载一个文件内容来初始化程序,就可以在编译程序之前,在res/raw/tempFile中建立一个static文件,就可以在程序中
   通过Resources.openRawResource(R.raw.文件名)方法返回一个InputStream对象,直接读取文件内容。
   
第三部分  Network 数据存储
    网络数据存储必须要联网。下例是在应用程序退出程序时,将数据发送到电子邮件中备份,所以需要在模拟器中配置电子邮件账户,
    配置电子邮件账户步骤:启动模拟器,打开“菜单”,选择“电子邮件”项。在启动之后,填写好邮件地址和密码,选中“默认情况下从账户发送电子邮件” 程序将自动
    配置电子邮件相关信息,最后点击完成。
   public class Activity01 extends Activity{
    private int  miCount = 0;
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main); 
     miCount=1000;
    }
    public boolean onKeyDown(int keyCode, KeyEvent event){
     if (keyCode == KeyEvent.KEYCODE_BACK){
      //退出应用程序时保存数据
      Uri uri = Uri.parse("发送邮件的地址 
      Intent it = new Intent(Intent.ACTION_SENDTO, uri); //创建Intent 
      it .putExtra(android.content.Intent.EXTRA_SUBJECT, "数据备份");//通过putExtra方法设置邮件的主题,内容,附件等
      it .putExtra(android.content.Intent.EXTRA_TEXT, "本次计数:"+miCount);//设置邮件的内容
      startActivity(it);//Android中是通过startActivity方法来调用要发送的邮件数据的Inten.   
      this.finish();
      return true;
     }
     return super.onKeyDown(keyCode, event);
    }
   }


第四部分 SQLite数据库,专门用于处理数据量较大的数据。SQLite他是一款轻型数据库。其特点:
    1.轻量级
     SQLite他是进程内的数据库引擎,因此不存在数据库的客户端和服务器。使用时只需带上它的一个动态库,就可用使用其全部功能,且动态库尺寸也很小。
    2.独立性
     SQLite数据库不依赖第三方软件,使用他不需要安装。
    3.隔离性
     SQLite数据库中所有的信息(如 表,视图,触发器)都包含在一个文件内,方便管理和维护。
    4.跨平台
     SQLite在很多系统上都可运行。
    5.多语言接口
     SQLite数据库支持多语言编程接口,如:C/C++,JAVA等。
    6.安全性
     SQLite数据库通过数据库级上的独占性和共享锁来实现独立事务处理。就是说多个进程在同一时间可以从同一数据库读取数据,但只有一个可以写入。在某个进程或线程
     向数据库执行写入之前,必须获得独占锁定。在发出独占锁定后,其他的读或写操作将不会再发生。
   SQLite数据库的基本操作包括:创建数据库,打开数据库,创建表,向表中添加数据,从表中删除数据,修改表中的数据,关闭数据库,删除指定表,删除数据库,和查询表中的
    某条数据。下面有一例子。
    
   public class Activity01 extends Activity{
    private static int   miCount   = 0;
    private SQLiteDatabase  mSQLiteDatabase = null;//数据库对象
    private final static String DATABASE_NAME = "Examples_06_05.db";//数据库名
    private final static String TABLE_NAME  = "table1";//表名

    /* 表中的字段 */
    private final static String TABLE_ID  = "_id";
    private final static String TABLE_NUM  = "num";
    private final static String TABLE_DATA  = "data";

    /* 创建表的sql语句 */
    private final static String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + TABLE_ID + " INTEGER PRIMARY KEY," + TABLE_NUM + " INTERGER,"+ TABLE_DATA + " TEXT)";

    LinearLayout    m_LinearLayout = null;//线性布局
    ListView     m_ListView  = null;//用列表ListView视图-显示数据库中的数据

    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     m_LinearLayout = new LinearLayout(this);
     /* 设置布局LinearLayout的属性 */
     m_LinearLayout.setOrientation(LinearLayout.VERTICAL);
     m_LinearLayout.setBackgroundColor(android.graphics.Color.BLACK);
     /* 创建ListView对象 */
     m_ListView = new ListView(this);
     LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
     m_ListView.setBackgroundColor(Color.BLACK);
     /* 添加m_ListView到m_LinearLayout布局 */
     m_LinearLayout.addView(m_ListView, param);
     
     /* 设置显示m_LinearLayout布局 */
     setContentView(m_LinearLayout);

     // 创建或打开已经存在的数据库。在Android中创建和打开一个数据库都可以用openOrCreateDatebase方法来完成因它会去自动检测是否存在这个数据库,如果存
     在则打开,如果不存在则创建一个数据库;创建成功则返回一个SQLiteDatabase对象
     mSQLiteDatabase = this.openOrCreateDatabase(DATABASE_NAME, MODE_PRIVATE, null);
     try{
      mSQLiteDatabase.execSQL(CREATE_TABLE);//在数据库mSQLiteDatabase中创建一个表.利用SQL语句创建数据中的一个表。一个数据库中可以包含多个表,我们的
                每一条数据保存在一个指定的表中,可以通过execSQL方法来完成。
      }catch (Exception e){
       UpdataAdapter();
      }
     }
     public boolean onKeyUp(int keyCode, KeyEvent event){
      switch (keyCode){
       case KeyEvent.KEYCODE_DPAD_LEFT:
        AddData();
        break;
       case KeyEvent.KEYCODE_DPAD_RIGHT:
        DeleteData();
        break;
       case KeyEvent.KEYCODE_1:
        UpData();
        break;
       case KeyEvent.KEYCODE_2:
        DeleteTable();
        break;
       case KeyEvent.KEYCODE_3:
        DeleteDataBase();
        break;
      }
      return true;
     }
     public void DeleteDataBase(){//删除数据库可直接调用deleteDataBase方法
      this.deleteDatabase(DATABASE_NAME);
      this.finish();
     }
     public void DeleteTable(){//删除指定表此处使用了execSQLite方法
      mSQLiteDatabase.execSQL("DROP TABLE " + TABLE_NAME);
      this.finish();
     }
     public void UpData(){//修改表中数据
      ContentValues cv = new ContentValues();
      cv.put(TABLE_NUM, miCount);
      cv.put(TABLE_DATA, "修改后的数据" + miCount);
      mSQLiteDatabase.update(TABLE_NAME, cv, TABLE_NUM + "=" + Integer.toString(miCount - 1), null);//使用update方法来更新一天数据。此处修改了TABLE_NUM值为
                              miCount-1的数据。
      UpdataAdapter();
     }
     public void AddData(){//向表中添加一条数据.使用insert方法添加数据,但使用此方法时要把数据打包到ContentValues中。ContentValues其实就是一个Map,Key值是字段
          名称,Value值是字段的值。通过ContentValues的put方法可以把数据放到ContentValues对象中去。
      ContentValues cv = new ContentValues();
      cv.put(TABLE_NUM, miCount);
      cv.put(TABLE_DATA, "测试数据库数据" + miCount);
      mSQLiteDatabase.insert(TABLE_NAME, null, cv);//使用insert方法插入数据。也可使用execSQL方法插入代码如:
               String INSERT_DATA = "INSERT INTO  table1(_id,num,data) values(1,1,‘通过SQL语句插入’)";
               mSQLiteDatabase.insert(INSETR_DATA);
      miCount++;
      UpdataAdapter();
     }
     public void DeleteData(){//从表中删除指定的一条数据
      mSQLiteDatabase.execSQL("DELETE FROM " + TABLE_NAME + " WHERE _id=" + Integer.toString(miCount));//此处通过execSQLite方法来删除字段为miCount+1的数据。也可使用delete方法删除如:
                       mSQLiteDatabase.delete("Examples_06_05.db","WHERE _id"+0,null);此处删除了字段等于1的数据
      miCount--;
      if (miCount < 0){
       miCount = 0;
      }
      UpdataAdapter();
     }
     public void UpdataAdapter(){//更新视图显示
      // 获取数据库Phones的Cursor,Cursor对象指向的是每一条数据
      Cursor cur = mSQLiteDatabase.query(TABLE_NAME, new String[] { TABLE_ID, TABLE_NUM, TABLE_DATA }, null, null, null, null, null);

      miCount = cur.getCount();
      if (cur != null && cur.getCount() >= 0){      
       ListAdapter adapter = new SimpleCursorAdapter(this,// ListAdapter是ListView和后台数据的桥梁
       // 定义List中每一行的显示模板    
       android.R.layout.simple_list_item_2,// 表示每一行包含两个数据项   
       cur,// 数据库的Cursor对象    
       new String[] { TABLE_NUM, TABLE_DATA },// 从数据库的TABLE_NUM和TABLE_DATA两列中取数据    
       new int[] { android.R.id.text1, android.R.id.text2 });// 与NAME和NUMBER对应的Views

       m_ListView.setAdapter(adapter);//将adapter添加到m_ListView中
      }
     }
     public boolean onKeyDown(int keyCode, KeyEvent event){
      if (keyCode == KeyEvent.KEYCODE_BACK){
       mSQLiteDatabase.close();//使用SQLite数据库后要及时关闭,否则可能会抛出SQLiteException异常
       this.finish();
       return true;
      }
      return super.onKeyDown(keyCode, event);
     }
    }
    上例中没有涉及到查询表中数据
     在Android中查询数据是通过Cursor类来实现的,当我们使用SQLiteDatabase.query()方法时。得到的是一个Cursor对象,Cursor指向的是每一条数据。
      Cursor类常用方法:
      move     以当前位置为参考,将Cursor移动到指定位置,成功返回true,失败返回false
      moveToPosition  将Cursor移动到指定位置,成功true,失败返回false
      moveToNext   将Cursor向前移动一个位置,成功true,失败返回false
      moveToLast   将Cursor向后移动一个位置,成功true,失败返回false
      moveToFirst   将Cursor移动到第一行,成功true,失败返回false
      isBeforeFirst  返回Cursor是否指向第一项数据之前
      isAfterLast   返回Cursor是否指向最后一项数据
      isClosed   返回Cursor是否关闭
      isFirst    返回Cursor是否指向第一项数据
      isLast    返回Cursor是否指向最后一项数据
      isNull    返回指定位置的值是否为空
      getCount   返回总的数据项数
      getInt    返回当前行中指定索引的数据
      
     下面为使用Cursor来查询数据库中的数据的代码。
     Cursor cur = mSQLiteDatabase.rawQuery("SELECT*FORM table",null);
     if (cur !=null){
      if(cur.moveToFirst()){
       do{
        int numColumn = cur.getColumnIndex("num");
        int num = cur.getInt(numColumn);
       }while(cur.moveToNext());
      }
     }
   SQLiteOpenHelper应用
    为了能更好的管理和维护数据库,我们可以封装一个继承自SQLiteOpenHelper类的数据库操作类。  
   public class MyDataBaseAdapter{ 
    private static final String TAG    = "MyDataBaseAdapter";// 用于打印log 
    public static final String KEY_ID   = "_id";// 表中一条数据的名称             
    public static final String KEY_NUM   = "num";// 表中一条数据的内容             
    public static final String KEY_DATA  = "data";// 表中一条数据的id 
    private static final String DB_NAME   = "Examples_06_06.db";// 数据库名称为data 
    private static final String DB_TABLE  = "table1";// 数据库表名 
    private static final int DB_VERSION  = 1;// 数据库版本 
    private Context    mContext  = null;// 本地Context对象 
    //创建一个表的SQL语句
    private static final String DB_CREATE  = "CREATE TABLE " + DB_TABLE + " (" + KEY_ID + " INTEGER PRIMARY KEY," + KEY_NUM + " INTERGER,"+ KEY_DATA + " TEXT)"; 
    private SQLiteDatabase  mSQLiteDatabase = null;// 执行open()打开数据库时,保存返回的数据库对象 
    private DatabaseHelper  mDatabaseHelper = null;// 由SQLiteOpenHelper继承过来
    
    public MyDataBaseAdapter(Context context){//构造函数-取得Context
     mContext = context;
    } 
    public void open() throws SQLException{// 打开数据库,返回数据库对象
     mDatabaseHelper = new DatabaseHelper(mContext);
     mSQLiteDatabase = mDatabaseHelper.getWritableDatabase();
    } 
    public void close(){// 关闭数据库
     mDatabaseHelper.close();
    }
    public long insertData(int num, String data){//插入一条数据
     ContentValues initialValues = new ContentValues();
     initialValues.put(KEY_NUM, num);
     initialValues.put(KEY_DATA, data);
     return mSQLiteDatabase.insert(DB_TABLE, KEY_ID, initialValues);
    }
    public boolean deleteData(long rowId){//插入一条数据
     return mSQLiteDatabase.delete(DB_TABLE, KEY_ID + "=" + rowId, null) > 0;
    }
    public Cursor fetchAllData(){//通过Cursor查询所有数据
     return mSQLiteDatabase.query(DB_TABLE, new String[] { KEY_ID, KEY_NUM, KEY_DATA }, null, null, null, null, null);
    }
    public Cursor fetchData(long rowId) throws SQLException{//查询指定数据
     Cursor mCursor = mSQLiteDatabase.query(true, DB_TABLE, new String[] { KEY_ID, KEY_NUM, KEY_DATA }, KEY_ID + "=" + rowId, null, null, null, null, null);
     if (mCursor != null){
      mCursor.moveToFirst();
     }
     return mCursor;
    }
    public boolean updateData(long rowId, int num, String data){//更新一条数据
     ContentValues args = new ContentValues();
     args.put(KEY_NUM, num);
     args.put(KEY_DATA, data);
     return mSQLiteDatabase.update(DB_TABLE, args, KEY_ID + "=" + rowId, null) > 0;
    }
    /*
    *此处封装了一个继承自SQLiteOpenHelper类的数据库操作类。SQLiteOpenHelper的构造方法中分别需要传入Context,数据库名称,CursorFactory(一般为null,否则为默认数据库
    *,数据库的版本号(不能为负数),在SQLiteOpenHelper中当数据库第一次被创建时首先执行的是onCreate方法。
    /
    private static class DatabaseHelper extends SQLiteOpenHelper{
     DatabaseHelper(Context context){//构造函数在此处并没有真正创建一个数据库,而是当调
               用getWritableDatabase()或 getReadableDatabase()方法时,则创建一个数据库,并且返回一个SQLiteDatabase对象
      super(context, DB_NAME, null, DB_VERSION);   
     }
     public void onCreate(SQLiteDatabase db){//创建一个表   
      db.execSQL(DB_CREATE);// 数据库没有表时创建一个,此处利用SQL语句在数据库对象 db中创建了一个表
     }
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){//升级数据库
      db.execSQL("DROP TABLE IF EXISTS notes");
      onCreate(db);
     }
    }
   }
   如果使用上面封装的MyDataBaseAdapter类如下:
   public class Activity01 extends Activity{
    private static int   miCount   = 0;
    LinearLayout    m_LinearLayout = null;
    ListView     m_ListView  = null;//ListView显示数据库中的数据
 
    MyDataBaseAdapter m_MyDataBaseAdapter;
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     m_LinearLayout = new LinearLayout(this);//创建LinearLayout布局对象
     m_LinearLayout.setOrientation(LinearLayout.VERTICAL);//设置布局LinearLayout的属性
     m_LinearLayout.setBackgroundColor(android.graphics.Color.BLACK);

     m_ListView = new ListView(this);//创建ListView对象
     LinearLayout.LayoutParams param = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
     m_ListView.setBackgroundColor(Color.BLACK);

     m_LinearLayout.addView(m_ListView, param);//添加m_ListView到m_LinearLayout布局
     setContentView(m_LinearLayout);//设置显示m_LinearLayout布局
     
     m_MyDataBaseAdapter = new MyDataBaseAdapter(this);//构造MyDataBaseAdapter对象
     m_MyDataBaseAdapter.open();//取得数据库对象
  
     UpdataAdapter();
    } 
    public boolean onKeyUp(int keyCode, KeyEvent event){
     switch (keyCode){
      case KeyEvent.KEYCODE_DPAD_LEFT:
       AddData();
       break;
      case KeyEvent.KEYCODE_DPAD_RIGHT:
       DeleteData();
       break;
      case KeyEvent.KEYCODE_1:
       UpData();
       break;
     }
     return true;
    }
    public void UpData(){//更新一条数据 
     m_MyDataBaseAdapter.updateData(miCount - 1, miCount, "修改后的数据" + miCount);
     UpdataAdapter();
    }
    public void AddData(){//向表中添加一条数据
     m_MyDataBaseAdapter.insertData(miCount, "测试数据库数据" + miCount);
     miCount++;
     UpdataAdapter();
    }
    public void DeleteData(){//从表中删除指定的一条数据
     m_MyDataBaseAdapter.deleteData(miCount);//删除数据
     miCount--;
     if (miCount < 0){
      miCount = 0;
     }
     UpdataAdapter();
    }
    public boolean onKeyDown(int keyCode, KeyEvent event){//按键事件处理
     if (keyCode == KeyEvent.KEYCODE_BACK){
      m_MyDataBaseAdapter.close();//退出时,不要忘记关闭
      this.finish();
      return true;
     }
     return super.onKeyDown(keyCode, event);
    }
    public void UpdataAdapter(){//更新视图显示
     // 获取数据库Phones的Cursor
     Cursor cur = m_MyDataBaseAdapter.fetchAllData();
     miCount = cur.getCount();
     if (cur != null && cur.getCount() >= 0){    
      ListAdapter adapter = new SimpleCursorAdapter(this,// ListAdapter是ListView和后台数据的桥梁
       // 定义List中每一行的显示模板    
       android.R.layout.simple_list_item_2,// 表示每一行包含两个数据项   
       cur,// 数据库的Cursor对象   
       new String[] {MyDataBaseAdapter.KEY_NUM, MyDataBaseAdapter.KEY_DATA },// 从数据库的TABLE_NUM和TABLE_DATA两列中取数据    
       new int[] { android.R.id.text1, android.R.id.text2 });// 与NAME和NUMBER对应的Views
       m_ListView.setAdapter(adapter);//将adapter添加到m_ListView中
     }
    }
   }

第五部分 ContentProvider
    ContentProvider是所有应用程序之间数据存储和检索的一个桥梁,其作用为使得各个应用程序之间实现数据共享。
    1、ContentResolver
     所有的Content Provider都会实现一些共同的接口,包括数据的查询、添加、更改和删除。应用可用通过一个唯一的ContentResolver接口来使用具体的某个Content Provider.
     可以通过getContentResolver()方法来得到ContentResolver对象,然后就可以用ContentResolver提供的方法来操作你需要的Content Provider了。
      ContentResolver cr = getContentResolver();
     对于每一种类型的ContentProvider只有一个实例(如音频、视频、图像、个人联系信息等常用Content Providers),但是这个实例可以与不同的程序或进程中的多个ContentResolver对象进行通信。
     进程之间的互动是由ContentResolver和ContentProvider类处理的。
     Content Provider把它的数据作为数据库模型上的单一数据表提供出来,在数据表中,每一行是一条记录,每一列代表某一特定类型的值。
    2、URI
     每个Content Providers都会对外提供一个公共的URI(包装成Uri对象)。如果应用程序有数据需要共享时,就需要使用Content Provider为这些数据定义一个URI,然后其他
     应用程序就可以通过Content Providers 传入这个URI来对数据进程操作。
     URI由三部分组成:"content://"、数据路径、标识ID(可选)。如下是系统的一些URI:
      content://media/internal/images    这个URI将返回设备上存储的所有图片
      content://contacts/people/5    联系人信息中ID为5的联系人记录
      content://contacts/people/  这个URI将返回设备上的所有联系人记录
      上面第二个URI可改写为:
       Uri person = ContentUris.withAppendedId(People.CONTENT_URI,5);//所有与其Content Provider互动的过程中都可以使用这个URI常量
     每个ContentResolver都把URI作为其第一个参数,它决定了ContentResolver将与哪个Provider对话。
    3、数据查询
     可以通过ContentResolver.query()方法或者Activity.managedQuery()方法得当Cursor对象.
      不同的是managedQuery包含了Cursor生命周期,当Activity被暂停后,如果要使用Cursor就必须通过Activity.startManagingCursor()方法来重新启动。但两者都需要传入
      一个URI来确定当前要操作的数据。
      Cursor cur = managedQuery(people ,null,null,null);//查询联系人信息中ID为5的联系人记录
      要得到更详细内容可以
       String[] projection = new String[] {People._ID, People._COUNT, People.NAME, People.NUMBER};
       Uri contacts = People.CONTENT_URI;
       Cursor managedCursor = managedQuery(contacts ,projection , null, null, People.NAME+"ASC");//第二个参数为返回指定列的数据,第三个返回所有行的数据
                              第四个参数可选参数,第五个参数是按名字的升序排序
       一条查询返回的是一组数据的集合,列名,缺省排序,数据类型对每个Content Provider是特定的。但是每个Provider都有一个_id列,它为每条记录保存一个数字ID
       值。每个Provider都可以把返回的记录数量作为_count列报告出来,这个值对所有的行来说是相同的。
      读取查询的数据可通过Cursor对象的方法来操作,其对象用于在结果集中前向或后向列举数据,Cursor对象只能用来读取数据。其增加、修改、删除数据必须使用ContentResolver对象
       private void getColumnData(Cursor cur){
        if(cur.moveToFirst()){
         String name;
         String phoneNumber;
         int nameColumn = cur.getColumnIndex(People.NAME);
         int phoneColumn = cur.getColumnIndex(People.NUMBER);
         String imagePath;
         do{
          name = cur.getString(nameColumn);
          phoneNumber = cur.getString(phoneColumn);
         }while(cur.moveToNext());
        }
       }
       如果是通过ID来查询某一特定记录的,那么这个结果只有一个值,否则,它将包含多个值(如果没有匹配的值,将返回空)。我们可以读取记录的特定字段,但是必须知道
       字段的类型,这是因为Cursor对象读每种数据类型都有不同的方法,例如:getString()、getInt()、getFloat()等方法。当然对于大多数类型,如果调用读字符串的函数,
       Cursor对象将会返回一个代表这个数据的字符串。
       Cursor可以通过列的索引得到列名,或者通过列名得到列索引。
    4、修改数据
     可通过ContentReolver.update()方法来修改数据。
      private void updateRecord(int id,int num){//创建一个updateRecord方法来修改数据
       Uri uri = ContentUris.withAppendedId(People.CONTENT_URI,id);
       ContentValues values = new ContentValues();
       values.put(People.NUMBER,num);
       getContentResolver().update(uri,values,null,null);
      }
    5、添加数据
     通过调用ContentResolver.insert()方法来增加一条记录,该方法接受一个要增加的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI(包括记录号)。
     代码:
      ContentValues values = new ContentValues();
      values.put(People.NAME,"Abraham Lincoln");
      values.put(People.STARRED,1);
      Uri uri = getContentResolver().insert(People.CONTENT_URI,values);
      下面为刚刚添加的记录加入新数据
      Uri phoneUri = null;
      Uri emailUri = null;
      PhoneUri = Uri.withAppendedPath(uri,People.Phones.CONTENT_DIRECTORY);
      values.clear();
      values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
      values.put(People.Phones.NUMBER, "1243442121");
      getContentResolver().insert(phoneUri ,values);
      emailUri = Uri.withAppendedPath(uri, People.ContactMethods.CONTENT_DIRECTORY);
      values.clear();
      values.put(People.Phones.KIND, People.Phones.KIND_EMALL);
      values.put(People.Phones.DATA, "

test@example.com");
      values.put(People.ContentMethods.TYPE, People.ContactMethods.TYPE_HOME);
      getContentResolver().insert(emailUri ,values);
     如果一个查询返回如图像和声音的二进制数据,那么在数据表中可以被直接存储或者数据的表入口是指定一个Content的字符串:URI,通过这个URI可以得到数据。
     通常小数量的数据如0K-50K常常在表中直接存储,通过Cursor.getBlob()函数读取,这个函数返回的是一个二进制数组。
     如果数据表入口是一个Content:URI,就不要直接打开和读取文件(一个原因是权限问题),而是通过调用ContentResolver.openInputStream()函数来得到一个可以用来读取数据
     的InputStream对象 代码如下:
      ContentValues values = new ContentValues(3);
      values.put(Media.DISPLAY_NAME, "road_trip_1");
      values.put(Media.DESCRIPTION, "Day 1, trip to Los Angeles");
      values.put(Media.MIME_TYPE, "image/jpeg");
      Uri uri = getContentResolver().insert(Media.EXTERNAL_CONTENT_URI, values);
      try{
       OutputStream outStream = getContentResolver().openOutputStream(uri);
       sourceBitmap.compress(Bitmap.CompressFormat.JPEG, 50, outStream);
       outStream.close();
      }catch(Exception e){
      }
    6、删除数据
     通过ContentResolver.delete()方法;
      Uri uri = People.CONTENT_URI;
      getContentResolver().delete(uri,null,null);
      getContentResolver().delete(uri ,"NAME="+"'xxx'",null);//指定Where条件语句来删除
    7、创建Content Provider
      上面是使用Content Provider 下面我们将自己创建一个Content Provider
      在创建的Content Provider 类中定义一个公共的、静态的常量来代表这个URI,该地址是唯一的.代码如下:
       public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transporationprovider");
      必须定义你要返回给客户端的数据列名。就像其他数据可一样,但要为其定义一个叫_id的列,它用来表示每条记录的唯一性。模式使用
       "INTEGER PRIMARY KEY AUTOINCREMENT"自动更新。
      如果你要处理的数据类型是一种比较新的类型,就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。
      MIME类型有两种形式:一种是指定的单个记录,另一个是多条记录
       单个记录的MIME类型:
        vnd.android.cursor.item/vnd.yourcompanyname.contenttype
        比如一个请求列车信息的URI,如:content://com.example.transportationprovider/trains/122,可能会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型
       多个记录的MIME类型
        vnd.android.cursor.dir/vnd.yourcompanyname.contenttype
        比如一个请求所有列车信息的URI,如:content://com.example.transportationprovider/trains,可能会返回vnd.android.cursor.dir/vnd/example.rail这样一个MIME类型
      在AndroidManifest.xml中使用<provider>标签来设置Content Provider.
       如果我们创建的Content Provider类名为MyContentProvider 这在AndroidManifest中需要添加如下代码:
        <provider
         android:name = "MyContentProvider"
         android:authorities = "com.wissen.MyContentProvider"
         />
       还有通过setReadPermission()和setWriterPermission()来设置其权限。当然也可在在上面代码中加入android:readPermission或者android:writePermission来控制权限
      最后,要在其他应用程序中使用这个ContentProvider,这就需要将MyContentProvider加入项目中,也可以定义了静态字段的文件打包成jar文件,加入到要使用的工程中。通过
      import导入。因为ContentProvider可能被不同的进程和线程调用,所以这些方法必须是线程安全的。
      
   例:下例实现了ContentProvider的创建及使用  
   public class NotePad{//此类创建了一些字段数据,
    public static final String AUTHORITY = "com.google.provider.NotePad";//ContentProvider的uri的数据路径
    private NotePad(){
    } 
    public static final class Notes implements BaseColumns{// 定义基本字段,字段类继承自BaseColumns类
     private Notes(){
     }
     public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/notes");//ContentProvider的uri  
     public static final String CONTENT_TYPE  = "vnd.android.cursor.dir/vnd.google.note";// 新的MIME类型-多个  
     public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.google.note";// 新的MIME类型-单个

     public static final String DEFAULT_SORT_ORDER = "modified DESC";

     //字段
     public static final String TITLE    = "title";
     public static final String NOTE    = "note";
     public static final String CREATEDDATE   = "created";
     public static final String MODIFIEDDATE  = "modified";
    }
   }

   public class NotePadProvider extends ContentProvider{//创建自己的ContentProvider类NotePadProvider,包括了查询、添加、删除、更新等操作以及打开与创建数据库
    private static final String    TAG     = "NotePadProvider";
    private static final String    DATABASE_NAME  = "note_pad.db";// 数据库名
    private static final int    DATABASE_VERSION = 2;    
    private static final String    NOTES_TABLE_NAME = "notes";// 表名
    private static HashMap<String, String> sNotesProjectionMap;
    private static final int    NOTES    = 1;
    private static final int    NOTE_ID    = 2;
    private static final UriMatcher   sUriMatcher;
    private DatabaseHelper mOpenHelper;
    //创建表SQL语句
    private static final String    CREATE_TABLE="CREATE TABLE "
                 + NOTES_TABLE_NAME
                 + " (" + Notes._ID
                 + " INTEGER PRIMARY KEY,"
                 + Notes.TITLE
                 + " TEXT,"
                 + Notes.NOTE
                 + " TEXT,"
                 + Notes.CREATEDDATE
                 + " INTEGER,"
                 + Notes.MODIFIEDDATE
                 + " INTEGER" + ");";
 
    static{
     sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     sUriMatcher.addURI(NotePad.AUTHORITY, "notes", NOTES);
     sUriMatcher.addURI(NotePad.AUTHORITY, "notes/#", NOTE_ID);

     sNotesProjectionMap = new HashMap<String, String>();
     sNotesProjectionMap.put(Notes._ID, Notes._ID);
     sNotesProjectionMap.put(Notes.TITLE, Notes.TITLE);
     sNotesProjectionMap.put(Notes.NOTE, Notes.NOTE);
     sNotesProjectionMap.put(Notes.CREATEDDATE, Notes.CREATEDDATE);
     sNotesProjectionMap.put(Notes.MODIFIEDDATE, Notes.MODIFIEDDATE);
    }
    private static class DatabaseHelper extends SQLiteOpenHelper{  
     DatabaseHelper(Context context){//构造函数-创建数据库
      super(context, DATABASE_NAME, null, DATABASE_VERSION);
     }
     public void onCreate(SQLiteDatabase db){//创建表
      db.execSQL(CREATE_TABLE);
     }
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){//更新数据库
      db.execSQL("DROP TABLE IF EXISTS notes");
      onCreate(db);
     }
    }
    public boolean onCreate(){
     mOpenHelper = new DatabaseHelper(getContext());
     return true;
    }
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder){//查询操作
     SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
     switch (sUriMatcher.match(uri)){
      case NOTES:
       qb.setTables(NOTES_TABLE_NAME);
       qb.setProjectionMap(sNotesProjectionMap);
       break;
      case NOTE_ID:
       qb.setTables(NOTES_TABLE_NAME);
       qb.setProjectionMap(sNotesProjectionMap);
       qb.appendWhere(Notes._ID + "=" + uri.getPathSegments().get(1));
       break;
      default:
       throw new IllegalArgumentException("Unknown URI " + uri);
     }
     String orderBy;
     if (TextUtils.isEmpty(sortOrder)){
      orderBy = NotePad.Notes.DEFAULT_SORT_ORDER;
     }else{
      orderBy = sortOrder;
     }
     SQLiteDatabase db = mOpenHelper.getReadableDatabase();
     Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
     c.setNotificationUri(getContext().getContentResolver(), uri);
     return c;
    } 
    public String getType(Uri uri){// 如果有自定义类型,必须实现该方法
     switch (sUriMatcher.match(uri)){
      case NOTES:
       return Notes.CONTENT_TYPE;
      case NOTE_ID:
       return Notes.CONTENT_ITEM_TYPE;
      default:
       throw new IllegalArgumentException("Unknown URI " + uri);
     }
    }
    public Uri insert(Uri uri, ContentValues initialValues){//插入数据库
     if (sUriMatcher.match(uri) != NOTES){
      throw new IllegalArgumentException("Unknown URI " + uri);
     }
     ContentValues values;
     if (initialValues != null){
      values = new ContentValues(initialValues);
     }else{
      values = new ContentValues();
     }
      Long now = Long.valueOf(System.currentTimeMillis());
     if (values.containsKey(NotePad.Notes.CREATEDDATE) == false){
      values.put(NotePad.Notes.CREATEDDATE, now);
     }
     if (values.containsKey(NotePad.Notes.MODIFIEDDATE) == false){
      values.put(NotePad.Notes.MODIFIEDDATE, now);
     }
     if (values.containsKey(NotePad.Notes.TITLE) == false){
      Resources r = Resources.getSystem();
      values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled));
     }
     if (values.containsKey(NotePad.Notes.NOTE) == false){
      values.put(NotePad.Notes.NOTE, "");
     }
     SQLiteDatabase db = mOpenHelper.getWritableDatabase();
     long rowId = db.insert(NOTES_TABLE_NAME, Notes.NOTE, values);
     if (rowId > 0){
      Uri noteUri = ContentUris.withAppendedId(NotePad.Notes.CONTENT_URI, rowId);
      getContext().getContentResolver().notifyChange(noteUri, null);
      return noteUri;
     }
      throw new SQLException("Failed to insert row into " + uri);
    }
    public int delete(Uri uri, String where, String[] whereArgs){//删除数据
     SQLiteDatabase db = mOpenHelper.getWritableDatabase();
     int count;
     switch (sUriMatcher.match(uri)){
      case NOTES:
       count = db.delete(NOTES_TABLE_NAME, where, whereArgs);
       break;
      case NOTE_ID:
       String noteId = uri.getPathSegments().get(1);
       count = db.delete(NOTES_TABLE_NAME, Notes._ID + "=" + noteId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
       break;
      default:
       throw new IllegalArgumentException("Unknown URI " + uri);
     }
     getContext().getContentResolver().notifyChange(uri, null);
     return count;
    }
    public int update(Uri uri, ContentValues values, String where, String[] whereArgs){//更新数据
     SQLiteDatabase db = mOpenHelper.getWritableDatabase();
     int count;
     switch (sUriMatcher.match(uri)){
      case NOTES:
       count = db.update(NOTES_TABLE_NAME, values, where, whereArgs);
       break;
      case NOTE_ID:
       String noteId = uri.getPathSegments().get(1);
       count = db.update(NOTES_TABLE_NAME, values, Notes._ID + "=" + noteId + (!TextUtils.isEmpty(where) ? " AND (" + where + ')' : ""), whereArgs);
       break;
       default:
      throw new IllegalArgumentException("Unknown URI " + uri);
     }
     getContext().getContentResolver().notifyChange(uri, null);
     return count;
    }
   }

   public class Activity01 extends Activity{//要使用自己创建出来的NotePadProvider类,首先向其中插入两条数据,然后用Toast来显示数据库中的所以数据
    public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main); 
     /* 插入数据 */
     ContentValues values = new ContentValues();
     values.put(NotePad.Notes.TITLE, "title1");
     values.put(NotePad.Notes.NOTE, "NOTENOTE1");
     getContentResolver().insert(NotePad.Notes.CONTENT_URI, values);

     values.clear();
     values.put(NotePad.Notes.TITLE, "title2");
     values.put(NotePad.Notes.NOTE, "NOTENOTE2");
     getContentResolver().insert(NotePad.Notes.CONTENT_URI, values);
  
     /* 显示 */
     displayNote();
    } 
    private void displayNote(){
     String columns[] = new String[] { NotePad.Notes._ID,
             NotePad.Notes.TITLE,
             NotePad.Notes.NOTE,
             NotePad.Notes.CREATEDDATE,
             NotePad.Notes.MODIFIEDDATE };
     Uri myUri = NotePad.Notes.CONTENT_URI;
     Cursor cur = managedQuery(myUri, columns, null, null, null);
      if (cur.moveToFirst()){
        String id = null;
        String title = null;
       do{
        id = cur.getString(cur.getColumnIndex(NotePad.Notes._ID));
        title = cur.getString(cur.getColumnIndex(NotePad.Notes.TITLE));
        Toast toast=Toast.makeText(this, "TITLE:"+id + "NOTE:" + title, Toast.LENGTH_LONG);
        toast.setGravity(Gravity.TOP|Gravity.CENTER, 0, 40);
        toast.show();
       }
       while (cur.moveToNext());
      }
    }

   }
   
   AndroidManifest.xml
   <?xml version="1.0" encoding="utf-8"?>
    <manifest
     xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.yarin.android.Examples_06_07"
      android:versionCode="1"
      android:versionName="1.0">
      <application
       android:icon="@drawable/icon"
       android:label="@string/app_name">
       <provider //声明我们所使用的ContentProvider
        android:name="NotePadProvider"
        android:authorities="com.google.provider.NotePad"/>
        <activity
         android:name=".Activity01"
         android:label="@string/app_name">
         <intent-filter>
          <action android:name="android.intent.action.MAIN"/>
          <category android:name="android.intent.category.LAUNCHER"/>
         </intent-filter>
         <intent-filter>
          <data android:mimeType="vnd.android.cursor.dir/vnd.google.note"/>
         </intent-filter>
         <intent-filter>
          <data android:mimeType="vnd.android.cursor.item/vnd.google.note"/>
         </intent-filter>
        </activity>
      </application>
      <uses-sdk android:minSdkVersion="5"/>
    </manifest>


   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值