一、使用SQLite实现对数据的操作:
第一步:建立一个要操作的表对应的javabean 第二步:创建一个类,继承SQLiteOpenHelper(完成数据库和表的创建,以及软件升级时,对数据库、表结构进行更新) 第三步:创建一个类,实现对数据的CRUD(Create/Read/Update/Delete)操作(将SQLiteOpenHelper的实例传入其中,从而可以对数据库进行操作) 第四步:编写测试类,对第三步编写的CRUD操作进行测试,包括分页显示数据 第五步:配置清单文件以及布局文件
示例代码: 1、DBHelper类 package com.heima.android.dbhelper;
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper{ private static String db_name = "contacts.db"; private static int version = 1; /** * 在DBHelper初始化 */ public DBHelper(Context context) { //CursorFactory指定在执行查询时获得一个游标实例的工厂类。 设置为null,则使用系统默认的工厂类。 super(context, db_name, null, version); } /** * 真正使用到DBHelper类的时候回调用该方法的时候,注意不是DBhelper初始话的时候就会调用该方法 * 还有要注意的一点就是:DBhelper初始的时候实际上也不会帮我们去创建数据库,在使用到真正使用到 * DBHelper的时候系统会查看db_name数据库是否已经存在,如果不存在则创建,该数据库文件保存的位置 * 为Android系统根目录下/data/data/标识应用的所在包的包名 /databases/我们自己定义的数据库名 */ public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS contacts("+"_id Integer primary key autoincrement," + "name varchar(20)," + "phone varchar(30)"+")"); } /** * 如果软件的版本发生变化,则会调用该方法,在该方法中可以更改数据库及表的结构 * 例如,上面设置的版本号version为1,程序运行一遍之后将version值改为2,当再次 * 运行该程序的时候就会调用该方法,有点困惑的地方就是版本发生变化的时候,执行完 * 该方法后还会去执行以下onCreate方法,不知道为什么 ? */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS contacts"); this.onCreate(db); }
}
二、数据库操作类(CRUD) package com.heima.android.sqlite.service;
import java.util.ArrayList; import java.util.List;
import android.content.Context; import android.database.Cursor;
import com.heima.android.dbhelper.DBHelper; import com.heima.android.sqlite.bean.Contact;
public class ContactService { private DBHelper dbHelper; //定义一个构造器,用于初始化一个DBHelper, public ContactService(Context context){ this.dbHelper = new DBHelper(context); } /* * 新增数据,sql语句使用占位符,与PrepareStatement类似,bindArgs为要填充到占位符中的数据 * 使用dbHelper的getReadableDatabase()方法可以得到一个SQLiteDatabase实例,进而可以操作数据库 */ public void saveContact(Contact contact){ String sql = "insert into contacts(name,phone)values(?,?)"; Object[] bindArgs = {contact.getName(),contact.getPhone()}; dbHelper.getWritableDatabase().execSQL(sql, bindArgs); } /* * 根据id删除数据 */ public void deleteContact(Integer id){ String sql = "delete from contacts where _id=?"; Object[] bindArgs = {id}; dbHelper.getReadableDatabase().execSQL(sql, bindArgs); } /* * 根据数据 */ public void updateContact(Contact contact){ String sql = "update contacts set name=?,phone=? where _id=?"; Object[] bindArgs = {contact.getName(),contact.getPhone(),contact.get_id()}; dbHelper.getWritableDatabase().execSQL(sql, bindArgs); }
/* 根据id查询某个联系人信息 * 最近项目中用到很多查询数据的地方,都用到了Cursor,但是对Cursor的moveToFirst和moveToNext用法却有些疑惑, * 总感觉可以先用moveToFirsrt来判断查询结果是否为空,然后再用moveToNext的while循环来逐条读取数据,我也不知道 * 最初我这种想法从何而来,似乎是这样想的,当用ContentProvider查询得到一个cursor的时候,cursor应该是处于指向 * 第一条记录的位置,因此调不调用moveToFirst都是一样的。可是后来写着写着就觉得矛盾了,如果真的是得到的cursor * 就指向第一条,那么不调用moveToFirst,直接使用while(moveToNext())的循环岂不是把第一条给跳过去了? * 越想越糊涂,还是直接看代码,又写了个例子debug了一下,原来查询得到的cursor是指向第一条记录之前的,因此查询得到 * cursor后第一次调用moveToFirst或moveToNext都可以将cursor移动到第一条记录上。源码中这些moveXXX其实都是通过moveToPosition * 来实现的,而记录position的是一个整型变量mPos。当moveXXXX返回false的时候,mPos会被置为-1,也就是回到了初始状态,指向第一条记录之前。 * 这里记录一下自己犯下的这个错误。
*/ public Contact findContactById(Integer id){ String sql = "select _id,name,phone from contacts where _id=?"; String[] selectionArgs = {id+""};//由于这里要求的是String类型的数据,所以加个空字符串进行处理 Cursor cursor = dbHelper.getReadableDatabase().rawQuery(sql, selectionArgs); if(cursor.moveToFirst()){ return new Contact(cursor.getInt(0), cursor.getString(1), cursor.getString(2)); } return null; } /* * 获取查询出的记录总数 * */ public long getCount(){ String sql = "select count(*) from contacts"; Cursor cursor = dbHelper.getReadableDatabase().rawQuery(sql, null); cursor.moveToNext(); return cursor.getLong(0); } /* * 获取分页数据供SimpleAdapter使用,数据中封装在List中,这种方法不可行,下面有解释 */ /* public List<Contact> getScollDataList(long startIndex,long pageSize){ String sql = "select * from contacts limit ?,?"; String[] selectionArgs = {String.valueOf(startIndex),String.valueOf(pageSize)}; Cursor cursor = dbHelper.getReadableDatabase().rawQuery(sql, selectionArgs); List<Contact> contactList = new ArrayList<Contact>(); //首先判断cursor中是否有数据, if(cursor.moveToFirst()){//要特别要注意的是: 这样在进行分页时,始终会忽略掉第一条记录 while(cursor.moveToNext()){ Contact contact = new Contact(cursor.getInt(0),cursor.getString(1),cursor.getString(2)); contactList.add(contact); } return contactList; } return null; }*/ /* * 获取分页数据供SimpleAdapter使用,数据中封装在List中 */ public List<Contact> getScollDataList(int startIndex,int pageSize){ String sql = "select * from contacts limit ?,?"; String[] selectionArgs = {String.valueOf(startIndex),String.valueOf(pageSize)}; Cursor cursor = dbHelper.getReadableDatabase().rawQuery(sql, selectionArgs); List<Contact> contactList = new ArrayList<Contact>(); while(cursor.moveToNext()){ Contact contact = new Contact(cursor.getInt(0),cursor.getString(1),cursor.getString(2)); contactList.add(contact); } cursor.close(); return contactList; } /* * 获取分页数据供SimpleCursorAdapter使用,数据封装在Cursor中 */ public Cursor getScollDataCursor(long startIndex,long pageSize){ String sql = "select * from contacts limit ?,?"; String[] selectionArgs = {String.valueOf(startIndex),String.valueOf(pageSize)}; Cursor cursor = dbHelper.getReadableDatabase().rawQuery(sql, selectionArgs); return cursor; } }
二、使用SharedPreferences共享数据
一般用来保存软件参数,等小数据 优点:它是一个轻量级的存储类,操作方便, 确定,不宜保存大数据,只能够保存boolean、string、float、long、int类型的原子数据,一般只供本应用使用
(1)使用方法(保存数据):
1、通过上下文对象获得一个SharedPreferences对象。 2、利用得到SharedPreferences对象获得一个Edit对象,使用Edit对象实现对简单数据的保存,注意要记得Edit的commit方法
(2)使用方法(获取数据): 1、通过上下文对象获得一个SharedPreferences对象。 2、使用SharedPreferences对象的get方法获取数据。
需要注意的地方:使用SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下:
示例代码:
package com.heima.android.service;
import java.util.HashMap;
import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor;
public class SharedPreferencesService { private Context context; public SharedPreferencesService() { }
public SharedPreferencesService(Context applicationContext) { this.context = applicationContext; } /** * 使用SharedPreferences保存软件设置参数 * @param name 需要保存的参数名称 * @param age 需要保存的参数名称 */ public void save(String name, String age) { SharedPreferences preferences = this.context.getSharedPreferences("heima",context.MODE_PRIVATE); Editor edit = preferences.edit(); edit.putString("name", name); edit.putInt("age", Integer.parseInt(age)); edit.commit();//执行commit方法后,edit中设置的内容才真正写入文件中 } /** * 从SharedPreferences中获取数据用于在界面回显 * @return 保存的数据 */ public HashMap<String,String> getData(){ HashMap<String, String> map = new HashMap<String, String>(); SharedPreferences preferences = this.context.getSharedPreferences("heima", context.MODE_WORLD_READABLE); String name = preferences.getString("name", ""); int age = preferences.getInt("age", 0); map.put("name", name); map.put("age", age+""); return map; }
}
三、使用内容提供者ContentProvider存取数据 首先明白ContentProvider是什么:在实际的应用中,每个应用都可以创建自己的数据,但是这个数据对其他应用来说是不可见的,当某个应用需要将自己的数据共享给其他应用使用时, 我们就需要使用到内容提供者ContentProvider,使用ContentProvider去访问某个应用程序的数据,让我们不必关心某个应用程序是的数据存储方式是使用数据库还是使用文件方式,还是通过 网络获取数据,这些都不重要,重要的是其他应用程序可以通过ContentProvider这个统一的接口操作某个应用程序的数据。如果一个程序想让其他程序操作自己的数据就必须定义自己的ContentProvider 然后在清单文件中进行注册,其他应用程序就可以根据ContentProvider定义的Uri对某一应用程序的数据进行操作。
android中的电话本等数据就是通过ContentProvider实现数据共享的,系统中有很多已经存在的共享Uri。我们可以使用ContentResolver通过Uri来操作不同表的数据;如Contacts.People.CONTENT_URI
什么是URI?
将其分为A,B,C,D 4个部分: A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://" B:URI
的标识,它定义了是哪个Content
Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。这个标识在 元素的authorities属性中说明:一般是定义该ContentProvider的包.类的名称
;"content://hx.android.text.myprovider" C:路径path,注意这里的路径不一定是表名,这里的路径只是一个匹配符号,例如content://com.heima.providers.userprovider/user,这里的user可以不是一个表名,只是标识,当符合这个标识进入Provider的相应方法时候,就要依据Contentprovider里面的方法里使用的表名进行操作。
D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id
如何定义和使用自己的ContentProvider
第一步:定义自己的ContentProvider,必须继承ContentProvider,根据业务的需要实现相应的方法,要注意的是,我们自己定义的ContentProvider要提供对数据操作的入口(本例中的内容提供者是对数据库进行操作.当然,也可以对文件类型的数据进行操作),所以这里要传入一个SQLiteOpenHelper对象的引用 第二步:要记得在项目清单文件中配置我们自定义的ContentProvider, 第三步:在其他应用中使用ContentResolver对ContentProvider进行访问(实际上ContentResolver对象签名方法在内部最终是根据传入的Uri拿到对应的ContentProvider对象,实现对数据的操作) 两个有用的方法:ContentUris 自定义ContentProvider中用到的类有: (1)ContentProvider(2)SQLiteOpenHelper、
SQLiteDatabase(3)UriMatCher(方法:addURI、match)、ContentUris(方法:
parseId(uri)、withAppendedId(uri)) 使用ContentProvider的应用中需要用到类 ContentResolver、ContentValues
1、自定义ContentProvider: package com.heima.android.dbhelper;
import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper{ private static String db_name = "contacts.db"; private static int version = 1; /** * 在DBHelper初始化 */ public DBHelper(Context context) { //CursorFactory指定在执行查询时获得一个游标实例的工厂类。 设置为null,则使用系统默认的工厂类。 super(context, db_name, null, version); } /** * 真正使用到DBHelper类的时候回调用该方法的时候,注意不是DBhelper初始话的时候就会调用该方法 * 还有要注意的一点就是:DBhelper初始的时候实际上也不会帮我们去创建数据库,在使用到真正使用到 * DBHelper的时候系统会查看db_name数据库是否已经存在,如果 不存在则创建,该数据库文件保存的位置 * 为Android系统根目录下/data/data/标识应用的所在包的包名 /databases/我们自己定义的数据库名 */ public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS contacts("+"_id Integer primary key autoincrement," + "name varchar(20)," + "phone varchar(30)"+")"); } /** * 如果软件的版本发生变化,则会调用该方法,在该方法中可以更改数据库及表的结构 * 例如,上面设置的版本号version为1,程序运行一遍之后将version值改为2,当再次 * 运行该程序的时候就会调用该方法,有点困惑的地方就是版本发生变化的时候,执行完 * 该方法后还会去执行以下onCreate方法,不知道为什么 ? */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS contacts"); this.onCreate(db); }
}
2、在清单文件中进行配置
<!-- 配置自定义的ContentProvider --> <provider android:name=".UserContentProvider" android:authorities="com.heima.providers.userprovider"/> 3、用于测试使用ContentProvider共享数据的另一个应用
package com.contentprovider.test;
import android.content.ContentResolver; import android.content.ContentValues; import android.net.Uri; import android.test.AndroidTestCase; import android.util.Log; /** * 测试使用ContentProvider操作另外一个应用中的数据 * @author Administrator * time 下午09:16:24 */ public class UserContentProviderTest extends AndroidTestCase { private static String TAG = "UserContentProviderTest"; /** *测试添加数据, */ public void testInsert(){ Log.i(TAG, "executing testInsert()1........."); Uri uri = Uri.parse("content://com.heima.providers.userprovider/user"); ContentResolver contentResolver = this.getContext().getContentResolver(); ContentValues contentValues = new ContentValues(); contentValues.put("account", "lganggang131"); contentValues.put("password", "123456"); contentValues.put("nickname", "sky"); //晕的死,竟然把Scheme和要操作的表忘记写了,报了个Unknown URL异常,狂晕 //
contentResolver.insert(Uri.parse("com.heima.providers.userprovider"),
contentValues); contentResolver.insert(Uri.parse("com.heima.providers.userprovider"),
contentValues); Log.i(TAG, "executing testInsert()2........."); contentResolver.insert(uri, contentValues); Log.i(TAG, "executing testInsert()3........."); } } |