目录
Android系统提供了四种存储数据方式:SharedPreference
,SQLite
,Content Provider
,File I/O
安卓系统中,数据基本都是私有的,存放于/data/data程序包下,若要在应用之间共享数据,可以使用Content Provider
组件。
SQLite
是安卓系统提供的轻量级数据库,支持基本的SQL语法
SharedPreference
是一个格式化数据文件,采用XML的标记方法,常用于设置参数的存储
文件I/O是对各操作系统模块最基本常见的操作
12.1、SharedPreference
SharedPreferences是Android平台上一个轻量级的存储类,用来保存应用的一些常用配置.
比如Activity状态,Activity暂停时,将此activity的状态保存到SharedPereferences中;当Activity重载,系统回调方法onSaveInstanceState时,再从SharedPreferences中将值取出。
其中的原理是通过Android系统生成一个xml文件保到:/data/data/包名/shared_prefs目录下,类似键值对的方式来存储数据。
Sharedpreferences提供了常规的数据类型保存接口比如:int、long、boolean、String、Float、Set和Map这些数据类型。
public abstract SharedPreferences getSharedPreferences (String name,
int mode)
参数 | |
---|---|
name | String : 所需的首选项文件 |
mode | int : 操作模式. Value is either 0 or a combination of MODE_PRIVATE , MODE_WORLD_READABLE , MODE_WORLD_WRITEABLE , and MODE_MULTI_PROCESS |
参数有两个
第一个表示Share文件的名称,不同的名称对应这不同的Share文件,其中的内容也是不同.
第二个参数表示操作模式,操作模式有四种:MODE_WORLD_READABLE,MODE_WORLD_WRITEABLE,MODE_PRIVATE、MODE_MULTI_PROCESS.
MODE_PRIVATE //默认操作模式(0),只有当前的应用程序才可以对当前这个SharedPreferences文件进行读写。
MODE_MULTI_PROCESS //用于多个进程共同操作一个SharedPreferences文件。
MODE_WORLD_WRITEABLE //数据能被其他应用程序读和写.
MODE_WORLD_READABLE //数据能被其他应用程序读,但不能写.
SharedPreferences sp=this.getSharedPreferences("myth",MODE_PRIVATE);//1.
if(sp!=null){
String stmp;
Toast.makeText(this,stmp=sp.getString("testmsg","no value"),Toast.LENGTH_SHORT).show();
if(stmp.equals("no value")){
SharedPreferences.Editor editor=sp.edit();
editor.putString("testmsg","SharedPreferences!!");//2.3.
editor.commit();
}
}
else {
sp.getString("testmsg","");
}
步骤:
-
获取Editor对象,这个对象用于写入
-
Editor对象有几个方法需要注入:clear(),commit(),putXXX(),
clear()为清空Share文件中的内容,commit()为提交,
editor在put值以后,需要调用commit方法才能被真正写入到Share文件中.
-
当要取回数据时,先获取对应的Share
-
根据key取出对应的值,其中第二个参数为默认值,即当从Share中取不到时,返回这个值,这里会返回一个空串。
Android会在文件资源的/data/data目录下,创建一个本工程名的目录,我们所编写的“myth”文件就在此
<map>
<string name="testmsg">SharedPreferences!!</string>
</map>
12.2、File I/O
Android自带的文件输入输出流:
public FileOutputStream openFileOutput(String name, int mode) throws FileNotFoundException {
throw new RuntimeException("Stub!");
}
public FileInputStream openFileInput(String name) throws FileNotFoundException {
throw new RuntimeException("Stub!");
}
变量定义:
EditText file_name,file_content;
FileOutputStream fos;
FileInputStream fis;
Button bt_copy;
String fname;
int flag=1;
file_name=(EditText) findViewById(R.id.exer_filename);
file_content=(EditText) findViewById(R.id.exer_filecontent);
bt_copy=(Button)findViewById(R.id.exer_copyfile);
bt_copy.setEnabled(false);
对于文件存储:
case R.id.exer_savefile:{
try {
fname=file_name.getText()==null?"":file_name.getText().toString();//判断取值
if(!fname.equals("")) {
fos = openFileOutput(fname + ".txt", MODE_PRIVATE);
}
else fos=openFileOutput("defaultname.txt",MODE_PRIVATE);
//如果文件名文本框为空,则自动创建一个defaultname.txt文件
String content=file_content.getText().toString();
fos.write(content.getBytes());//字节流输入
if(fos!=null)//设置赋值按钮可用
{
bt_copy.setEnabled(true);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fos!=null) {
fos.flush();
fos.close();
}
}catch (IOException e){
e.printStackTrace();
}
}
file_name.setText("");//清空内容
file_content.setText("");//清空内容
break;
}
对于文件复制:
case R.id.exer_copyfile:{
try {
fis=openFileInput(fname+".txt");
int n=0;
StringBuffer stringBuffer=new StringBuffer();//用于存放读取的字节流
do{
n=fis.read();
if(n!=-1)
stringBuffer.append((char)n);
}while (n!=-1);
fos = openFileOutput(fname + "("+flag+")"+".txt", MODE_PRIVATE);//flag用于多次复制
fos.write(stringBuffer.toString().getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fis!=null)
fis.close();
if(fos!=null)
{
fos.flush();
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
flag++;
break;
}
12.3、sqlite3
1、在Android Shell中操作SQLite
通过命令提示符定位到adb.exe所在路径:
使用shell命令即可运行,执行sqlite3
即可进入sqlite数据库
注:运行adb时只能有一个设备连接,否则会出现错误
注意:在连接自己手机时,需要root权限才能查看data数据
2、APP使用SQLite
准备数据库有关的基本信息
private static final String DB_NAME = "people.db";
private static final int DB_VERSION = 1;
private static final String TB_NAME1 = "peopleinfo";
public static final String KEY_ID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
public static final String KEY_HEIGHT = "height";
创建数据库对象
public class DBHelper extends SQLiteOpenHelper {
Context context;//供Toast使用
public DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
this.context=context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
//oncreate,一般进行数据表的创建;
//该函数的形参,为帮助器成功连接数据库后连接到的数据库对象的引用
//onupgrade,一般销毁或更新数据表
}
继承SQLiteOpenHelper
类,调用其构造方法岂可创建一个数据库对象
参数: | |
---|---|
context | Context : 依附的安卓组件,用于定位数据库的路径,可为空 |
name | String : 数据库文件名 |
factory | SQLiteDatabase.CursorFactory : 用于创建游标对象,默认为null |
version | int : 数据库标号(从1开始)如果数据库较旧,则使用onUpgrade(sqlitedatdatabase, int, int) 来升级数据库;如果数据库较新,则使用onDowngrade(SQLiteDatabase, int, int) 来降级数据库 |
布局文件:
<?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="vertical" >
<Button android:id="@+id/exer_createdb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="exer创建/连接数据库" />
<TextView android:id="@+id/exer_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="数据库内容为:"/>
<TextView android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="操作数据库:" />
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="姓名:"/>
<EditText
android:id="@+id/exer_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="年龄: "/>
<EditText
android:id="@+id/exer_age"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="身高: "/>
<EditText
android:id="@+id/exer_height"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"/>
</LinearLayout>
<Button android:id="@+id/exer_insert"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="插入记录"/>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="当ID为: "/>
<EditText
android:id="@+id/exer_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="number"/>
<Button android:id="@+id/exer_query"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查询记录"/>
<Button android:id="@+id/exer_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="删除记录"/>
</LinearLayout>
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/exer_result"
android:text="操作结果为:"/>
<!-- space -->
<Space android:layout_width="match_parent"
android:layout_height="16dip"/>
<!-- hori bar -->
<View android:layout_width="match_parent"
android:layout_height="2dip"
android:background="#9b9b9b"/>
<Button
android:id="@+id/exer_queryall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="查询所有"/>
<TextView
android:id="@+id/exer_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="info:"/>
</LinearLayout>
连接数据库
case R.id.exer_createdb:{
if(helper!=null){
Log.i("mytag","helper not null");
try {
//调用构造函数来建立数据库对象
//回调oncreate函数
db=helper.getWritableDatabase();
}catch (Exception e){
Toast.makeText(DBTest.this,"数据库创建失败",Toast.LENGTH_SHORT).show();
}
}
else{
Log.i("mytag","helper is null");
}
break;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
if (sqLiteDatabase != null) {
//执行数据库语句创建数据表
sqLiteDatabase.execSQL("create table " + TB_NAME1 + "(" +
KEY_ID + " integer primary key autoincrement, " +
KEY_NAME + " text not null, " +
KEY_AGE + " integer, " +
KEY_HEIGHT + " double" +
");");
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
if(db != null) {
db.execSQL("drop table if exists " + TB_NAME1);
onCreate(db);
}
}
我们所有的CRUD操作都放在Helper类中编写
查询所有数据
public String queryall(SQLiteDatabase sqLiteDatabase) {
Cursor cursor = null;
String str = "";
if (sqLiteDatabase != null)
cursor = sqLiteDatabase.query(TB_NAME1, null, null, null, null, null, null);
if (cursor != null) {
cursor.moveToFirst();
do{
str += "id:" + cursor.getInt(0) +
"name:" + cursor.getString(1) +
"age" + cursor.getInt(2) +
"height" + cursor.getDouble(3) + "\n";
}while (cursor.moveToNext());
}
else {
Toast.makeText(this.context,"查询失败",Toast.LENGTH_SHORT).show();
}
if(str.equals("")||str.hashCode()==0)
return "null";
else
return str;
}
//构造方法
public Cursor query (String table,
String[] columns,
String selection,
String[] selectionArgs,
String groupBy,
String having,
String orderBy)
Parameters | |
---|---|
table | String : 要编译查询的表名 |
columns | String : 要返回的列的列表。传递null将返回所有列。 |
selection | String :声明要返回哪些行的过滤器,格式化为SQL WHERE子句(不包括WHERE本身)。传递null将返回给定表的所有行。 |
selectionArgs | String : 您可以在selection 中包含'?',它们将被来自selectionArgs 的值替换,以便它们出现在选择中。这些值将被绑定为string |
groupBy | String : 一个过滤器,声明如何将格式化为SQL group BY子句的行分组(不包括group BY本身)。传递null将导致行不分组。 |
having | String : 如果使用行分组,则过滤器声明将哪些行组包含在游标中,格式化为SQL HAVING子句(不包括HAVING本身)。传递null将导致包含所有行组,当不使用行分组时需要此参数。 |
orderBy | String : 如何对格式化为SQL order BY子句的行进行排序(不包括order BY本身)。传递null将使用默认的排序顺序,这可能是无序的。 |
Cursor游标:
Query函数会返回一个游标
Returns | |
---|---|
Cursor | Cursor : 它位于第一个条目之前。注意游标不是同步的, |
通过id查询
String arg[]={id};
cursor=sqLiteDatabase.query(TB_NAME1,null,KEY_ID+"=?",arg,null,null,null);
//通过selection指明的where来进行判断
插入一条记录
public long insert(String name,String age,String height,SQLiteDatabase sqLiteDatabase){
Log.i("mytag","insert");
long row=0;
ContentValues contentValues=new ContentValues();
contentValues.put(KEY_HEIGHT,height);
contentValues.put(KEY_AGE,age);
contentValues.put(KEY_NAME,name);
if(sqLiteDatabase!=null)
{
row=sqLiteDatabase.insert(TB_NAME1,KEY_NAME,contentValues);
Log.i("mytag","row:"+row);
}
return row;
}
//构造方法
public long insert (String table,
String nullColumnHack,
ContentValues values)
Parameters | |
---|---|
table | String : 要插入的表名 |
nullColumnHack | String : 可选的;可能是null。SQL不允许在没有命名至少一个列名的情况下插入一个完全空的行。如果提供的值为空,则没有已知的列名,不能插入空行。如果不设置为空,nullColumnHack参数提供可空列名的名称,以便在值为空的情况下显式插入null。 |
values | ContentValues :此映射包含该行的初始列值。键应该是列名,值应该是列值 |
删除一条数据
public int deletebyid(String id,SQLiteDatabase sqLiteDatabase){
int row=0;
String arg[]={id};
if(sqLiteDatabase!=null)
row=sqLiteDatabase.delete(TB_NAME1,KEY_ID+"=?",arg);
return row;
}
//构造函数
public int delete (String table,
String whereClause,
String[] whereArgs)
Parameters | |
---|---|
table | String :要操作的表名 |
whereClause | String :在删除时应用可选的WHERE子句。传递null将删除所有行。 |
whereArgs | String :可以在where子句中包含'?',该子句将被来自whereArgs的值替换。这些值将被绑定为string。 |
12.4、ContentProvider
ContentProvider(内容提供者)是Android中的四大组件之一。
主要用于对外共享数据,也就是通过ContentProvider把应用中的数据共享给其他应用访问,其他应用可以通过ContentProvider对指定应用中的数据进行操作。
ContentProvider分为系统的和自定义的,系统的也就是例如联系人,图片等数据。
Android提供了一些主要数据类型的ContentProvider
,比如音频、视频、图片和私人通讯录等。可在android.provider
包下面找到一些Android提供的ContentProvider
。通过获得这些ContentProvider
可以查询它们包含的数据,当然前提是已获得适当的读取权限。
步骤:
1.自定义类继承ContentProvider
,并重写6个函数
public class cp extends ContentProvider {
DBHelper helper;//数据库
SQLiteDatabase database;
/*oncreate:
获取数据库指针
将数据库连接封装到helper中的自定义类opendb
主程序只需要调用方法即可拿到db对象
*/
@Override
public boolean onCreate() {
helper=new DBHelper(TESTCP.this.getContext());
database=helper.opendb();
return true;
}
//oncreate()函数在创建cp对象后回调,一般在oncreate中建立cp与数据源的连接
//需要根据数据源的初始化情况,手动返回bool
@Nullable
@Override
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
database=helper.opendb();
Cursor cursor=null;
if(uri.toString().equals(uri_str+multi_path1))
cursor=helper.Queryall(database);
else if(uri.toString().equals(uri_str+Single_path1))
cursor=helper.QueryByID(database,selectionArgs);
return cursor;
}
/*getType:
定义数据源的MIME类型
格式:type/subtype
在安卓中自定义mime类型 vnd.android.cursor.dir/ vnd.android.cursor.item/
以上两种cp的mime类型使用了安卓规定的主类型,子类型可以自定义
.dir/ 对应数据集(表或多行记录)
.item/ 对应单个数据(单条记录)
安卓中,自定义cp的uri时,要注意其对应的mime类型
content://com.mo.cp/testcp/#
"content://com.mo.cp/" 定位到数据源
"testcp"定位到数据表
"/#" 定位到表中某条记录
uri协议如果配合content使用则协议必须使用content
*/
String uri_str="content://com.mo.cp",multi_path1="/testcp",Single_path1="/testcp/#";
@Nullable
@Override
public String getType(@NonNull Uri uri) {
if(uri.toString().equals(uri_str+multi_path1))
return "vnd.android.cursor.dir/mytestcp";
else if(uri.toString().equals(uri_str+Single_path1))
return "vnd.android.cursor.item/mytestcp";
else
return null;
}
//getType()函数主要用来根据uri来返回对应的mime类型
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
long res=-1;
if(uri.toString().equals(uri_str+multi_path1))
if(helper!=null)
res=helper.insertdb(database,contentValues);
if (res!=-1) {
helper.closedb(database);
return Uri.parse(uri_str + multi_path1 + "/" + res);
}
else {
helper.closedb(database);
return null;
}
//插入成功后,要手动返回新的uri值
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
int res=0;
if (uri.toString().equals(uri_str+Single_path1)){
res=helper.delete(database,s,strings);
}
return res;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
}
2.编写DBHelper函数建立与数据库的连接
public class DBHelper extends SQLiteOpenHelper {
Context context;
private static final String DB_NAME = "people.db";
private static final int DB_VERSION = 1;
private static final String TB_NAME1 = "userinfo";
public static final String KEY_ID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
public static final String KEY_HEIGHT = "height";
public DBHelper(@Nullable Context context) {
super(context, DB_NAME, null,DB_VERSION);
this.context=context;
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL("create table " + TB_NAME1 + "(" +
KEY_ID + " integer primary key autoincrement, " +
KEY_NAME + " text not null, " +
KEY_AGE + " integer, " +
KEY_HEIGHT + " double" +
");");
}
public SQLiteDatabase opendb(){
SQLiteDatabase db=null;
try {
db=this.getWritableDatabase();
}catch (Exception e){
Log.i("mytag","db open failed...");
}
return db;
}
public void closedb(SQLiteDatabase database){
if(database!=null)
database.close();
}
public long insertdb(SQLiteDatabase database, ContentValues contentValues){
long res=-1;
if(database!=null)
res=database.insert(TB_NAME1,null,contentValues);
return res;
}
//查询所有用户信息
public Cursor Queryall(SQLiteDatabase database){
Cursor cursor=null;
if(database!=null)
cursor=database.query(TB_NAME1,null,null,null,null,null,null);
return cursor;
}
//通过ID号查询信息
public Cursor QueryByID(SQLiteDatabase database,String[] selectionArgs){
Cursor cursor=null;
if(database!=null)
cursor=database.query(TB_NAME1,null,KEY_ID+"=?",selectionArgs,null,null,null);
return cursor;
}
//删除一条记录
public int delete(SQLiteDatabase database,String whereClause,String[] whereArgs){
int res=0;
if(database!=null)
res=database.delete(TB_NAME1,whereClause,whereArgs);
return res;
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
}
3.在清单文件中对provider对象进行注册
<!--要与自定义的mime类型一致-->
<provider
android:authorities="com.mo.cp"
android:name=".TESTCP"
android:exported="true">
</provider>
4.新建Model项目通过ContentResolver
对象来实现CRUD
public class Amain extends Activity {
ContentResolver contentResolver;
EditText name,age,height;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ly_amain);
name=(EditText)findViewById(R.id.exer_id);
age=(EditText)findViewById(R.id.exer_age);
height=(EditText)findViewById(R.id.exer_height);
contentResolver=getContentResolver();
Listener Listener=new Listener();
findViewById(R.id.exer_insert).setOnClickListener(Listener);
findViewById(R.id.exer_queryall).setOnClickListener(Listener);
findViewById(R.id.exer_query).setOnClickListener(Listener);
findViewById(R.id.exer_delete).setOnClickListener(Listener);
}
class Listener implements View.OnClickListener{
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.exer_insert:{
Uri res=null;
ContentValues contentValues=new ContentValues();
contentValues.put(profile.KEY_NAME,name.getText().toString());
contentValues.put(profile.KEY_AGE,age.getText().toString());
contentValues.put(profile.KEY_HEIGHT,height.getText().toString());
if(contentResolver!=null)
res=contentResolver.insert(Uri.parse(profile.uri_str+profile.multi_path1),contentValues);
if(res==null)
Toast.makeText(Amain.this,"failed",Toast.LENGTH_SHORT).show();
else
Toast.makeText(Amain.this,res.toString(),Toast.LENGTH_LONG).show();
break;
}
case R.id.exer_queryall:{
Cursor cursor=null;
String str="";
if(contentResolver!=null)
cursor= contentResolver.query(Uri.parse(profile.uri_str+profile.multi_path1),null,null,null,null);
while (cursor.moveToNext()){
str += "id:" + cursor.getInt(0) +
"name:" + cursor.getString(1) +
"age" + cursor.getInt(2) +
"height" + cursor.getDouble(3) + "\n";
TextView tv=findViewById(R.id.exer_info);
tv.setText(str);
}
break;
}
case R.id.exer_query:{
String id=((EditText) findViewById(R.id.exer_id)).getText().toString();
Cursor cursor=null;
String str="";
String[] arg={id};
if(contentResolver!=null){
cursor=contentResolver.query(Uri.parse(profile.uri_str+ profile.Single_path1),null,null,arg,null);
}
cursor.moveToNext();
str += "id:" + cursor.getInt(0) +
"name:" + cursor.getString(1) +
"age" + cursor.getInt(2) +
"height" + cursor.getDouble(3) + "\n";
TextView tv = findViewById(R.id.exer_result);
tv.setText(str);
break;
}
case R.id.exer_delete:{
String id=((EditText) findViewById(R.id.exer_id)).getText().toString();
int res=0;
if(contentResolver!=null){
res=contentResolver.delete(Uri.parse(profile.uri_str+profile.Single_path1),profile.KEY_ID+"=?",new String[]{id});
}
if(res>0)
Toast.makeText(Amain.this,"删除成功",Toast.LENGTH_SHORT).show();
else
Toast.makeText(Amain.this,"删除失败",Toast.LENGTH_SHORT).show();
break;
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
}
}