Android数据存储几种方式讲解
作者:落雪小轩韩
一、文件存储
特点:openFileInput()和openFileOutput()读取设备上的文件。
优点:适用于存储大量的数据,可以存储图片、视频、文本等数据。
缺点:如果采用内部存储的方式,存储过量的数据可能会导致内存的不足;如果采用外部sdcard存储的方式,删除或者卸载应用,相关的数据需要手动进行删除,比较麻烦。
默认位置:/data/data/<包>/files/.。
(一)内部存储
// 将数据存储到指定的文件中 // name为文件名,mode为文件的操作模式 FileOutputStream fos = openFileOutput(String name,int mode); // 读取指定文件中的数据 FileInputStream fis = openFileInput(String name);
mode的取值如下:
MODE_PRIVATE:该文件只能被当前程序读写 ;
MODE_APPEND:该文件的内容可以追加;
MODE_WORLD_READABLE:该文件的内容可以被其他程序读;
MODE_WORLD_WRITEABLE:该文件的内容可以被其他程序写
注意:Android系统有一套自己的安全模型,默认情况下任何应用创建的文件都是私有的,其他程序无法访问。
1.写入文件步骤
String fileName = “data.txt”; // 文件名称
String content = “helloworld”; // 保存数据
FileOutputStream fos = openFileOutput(fileName, MODE_PRIVATE);
fos.write(content.getBytes()); //将数据写入文件中
fos.close(); //关闭输出流
2.读取文件步骤
String content = “”;
FileInputStream fis = null;
fis = openFileInput(“data.txt”); //获得文件输入流对象
byte[] buffer = new byte[fis.available()]; // 创建缓冲区,并获取文件长度
fis.read(buffer); // 将文件内容读取到buffer缓冲区
content = new String(buffer);//转换成字符串
fis.close(); //关闭输入流
3.实现存储和读取用户名和密码实例
创建UserInfoIO工具类,实现存储数据方法(saveUserInfo)和读取数据方法(getUserInfo)
public class UserInfoIO { // 数据的写入 public static boolean saveUserInfo(String username, String password, Context context){ // openFileOutput(要操作的文件名,文件访问模式) FileOutputStream fos = null; String msg = null; try { // 在操作文件的时候可能会报异常,需要进行捕获 fos = context.openFileOutput("MyData.txt",Context.MODE_PRIVATE); msg = username + ":" + password; // getBytes()将字符串转换为字节流 fos.write(msg.getBytes()); return true; } catch (IOException e) { e.printStackTrace(); return false; } finally { try { fos.close(); // 流是系统中的稀缺资源,在使用完后要及时关闭 } catch (IOException e) { e.printStackTrace(); } } } // 用户数据的读取 public static Map<String,String> getUserInfo(Context context){ // 获取文件输入流 FileInputStream fis = null; try { fis = context.openFileInput("MyData.txt"); byte[] buffer = new byte[fis.available()]; fis.read(buffer); // 字节转换为字符串 String msg = new String(buffer); String[] userInfo = msg.split(":"); Map<String,String> userMap = new HashMap<>(); userMap.put("username",userInfo[0]); userMap.put("password",userInfo[1]); return userMap; } catch (IOException e) { e.printStackTrace(); return null; } finally { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } }
二、SharedPreferences存储
特点:以XML格式将数据存储到设备。
优点:简单、方便、轻量级、易理解。
缺点:适用于存储少量的数据,并且数据的格式只能是基本的数据类型(int、float、long、boolean)、字符串类型(string),无法进行条件查询等操作。
SharedPreferences是一个轻量级的存储类,特别适合用于保存软件配置参数,其背后是用xml文件存放数据,文件存放在/data/data//shared_prefs目录下。
1.写入文件步骤
SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。
SharedPreferences sp = getSharedPreferences(“data”,MODE_PRIVATE);//获取
SharedPreferences对象
SharedPreferences.Editor editor = sp.edit(); // 获取编辑器对象
editor.putString(“name”, “张三”); // 存入String类型数据
editor.putInt(“age”, 8); // 存入int类型数据
editor.commit(); // 提交数据
2.读取文件步骤
SharedPreferences sp = getSharedPreferences(“data”,MODE_PRIVATE);
String data= sp.getString(“name”,“”);
3.删除文件中数据
editor.remove(“name”); // 根据key删除数据
editor.clear(); // 删除所有数据
4. 实现存储和读取用户名和密码实例
创建UserInfoSharePre工具类,实现存储数据方法(saveUserInfo)和读取数据方法(getUserInfo)
public class UserInfoSharedPre { // 用户数据的存储 public static boolean saveUserInfo(String username, String password, Context context) { // 获取SharedPreferences对象,同时指定文件名称和访问权限 SharedPreferences sp = context.getSharedPreferences("MyData", Context.MODE_PRIVATE); // 获取获取SharedPreferences的编辑器对象 SharedPreferences.Editor edit = sp.edit(); // 通过编辑器进行数据的存储 edit.putString("Uname",username); edit.putString("Pwd",password); edit.commit(); return true; } // 读取用户数据 public static Map<String,String> getUserInfo(Context context) { // 获取SharedPreferences对象,同时指定文件名称和访问权限 SharedPreferences sp = context.getSharedPreferences("MyData", Context.MODE_PRIVATE); String username = sp.getString("Uname", ""); String password = sp.getString("Pwd",""); Map<String,String> userMap = new HashMap<>(); userMap.put("username",username); userMap.put("password",password); return userMap; } }
三、SQLite数据库存储
特点:运算速度快,占用资源少,还支持基本SQL语法。
优点:适合存储结构化数据、轻量级、安全性、隔离性、独立性。
缺点:并发的读写性能不好。
1. 创建数据库步骤
(一)定义数据库帮助类
SQLiteOpenHelper是SQLiteDatabase的一个帮助类,用来管理数据库的创建和版本的更新
class MyDbHelper extends SQLiteOpenHelper { // 构造器的作用:(参数:上下文,数据库文件的名称,结果集工厂,版本号)定义数据库 public MyDbHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } // 数据库初始化时创建的表,用于创建表或视图文件 @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL("create table user(user_id integer primary key autoincrement,userName varchar(10),password varchar(10))"); } // 升级方法(当数据库的版本号增加时调用) @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }
(二)创建数据库和表
// 设置数据库的相关参数,初始化数据库 MyDbHelper myDbHelper = new MyDbHelper(SQLiteActivity.this,"MyDatabase.db",null,1); // 通过帮助类获取到数据库对象 SQLiteDatabase db = myDbHelper.getWritableDatabase();
通过数据库对象可以执行如下方法:
1.插入方法insert
// 创建ContentValues对象用于存储记录的字段值(键值对方式:键对应字段名,值对应字段具体的值) ContentValues contentValues = new ContentValues(); contentValues.put("userName","张三"); contentValues.put("password","123456"); db.insert("user",null,contentValues);
上面等同于
db.execSQL("insert into user(userName,password) values (?,?)",new Object[]{<!--{C}%3C!%2D%2D%20%2D%2D%3E-->"张三","123456"});
执行完之后
db.close();
2.查询方法query
调用query
方法查询数据库中的数据,返回一个行数集合cursor
public Cursor query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
各参数说明:
各参数说明:
table:表名称
colums:列名称数组,返回那些字段,null表示所有字段
selection:查询条件子句,相当于select语句where关键字后面的部分,在条件子句允许使用占位符“?” selectionArgs:对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致, 否则就会有异常。
groupBy:相当于select语句group by关键字后面的部分
having:相当于select语句having关键字后面的部分
orderBy:排序类
limit:分页查询的限制,指定偏移量和获取的记录数
Cursor:返回值,相当于结果集ResultSet
// Cursor: 结果集,内有游标指向结果集中的某一条记录,初始时指向第一条 Cursor cursor = db.query("user", new String[]{"userName,password"}, null, null, null, null, null, null); cursor.moveToFirst(); while (cursor.moveToNext()) { // 移动游标指向下一行数据 showInfo.append("\n" + "用户名" + cursor.getString(0) + ",密码" + cursor.getString(1)); } cursor.close(); db.close();
对于cursor
也提供了一些方法如下:
3.删除方法delete
返回删除的记录条数
public boolean deleteData(String deleteId) { int i = db.delete("user", "id=?", new String[]{deleteId}); return i > 0 ? true : false; }
4.修改方法update
返回更新的记录条数
ContentValues contentValues = new ContentValues(); contentValues.put("userName","李四"); contentValues.put("password","123123"); int i = db.update("user", contentValues, "id=?", new String[]{updateId});
等同于
db.execSQL("update user set userName=?,password=? where id=?",new Object[]{<!--{C}%3C!%2D%2D%20%2D%2D%3E-->username1,password1,id});
四、ContentProvider存储
特点:ContentProvider主要用于不同应用程序之间共享数据,ContentProvider更好的提供了数据共享接口的统一性,使不同应用共享数据更规范和安全。
优点:能够实现所有应用程序共享的一种数据存储方式,可以存储音频,视频,图片和通讯录等。
缺点:不能单独使用,必须与其他存储方式结合使用。
内容提供者(ContentProvider)是Android系统四大组件之一,它是不同应用程序之间进行数据共享的标准API,通过ContentResolver类可以访问ContentProvider中共享的数据。
ContentProvider的工作原理如下:
常见的使用场景:QQ和微信中文件的相互转发;安装完某个app第一次启动的时候获取权限(联系人、拍照、位置等)
ContentProvider 使用基于数据库模型的简单表格来提供需要共享的数据,在该表格中,每一行表示一条记录,而每一列代表特定类型和含义的数据,并且其中每一条数据记录都包含一个名为“_ID”的字段类标识每条数据,可以根据同一个ID查询几个相关表中的信息。知道各个字段对应的数据类型后,可根据Cursor对象提供的相关的方法,如,getInt()、getString()、getLong()等查询字段对应的值。
简单理解:A程序共享数据实际上就是共享一张表,B程序要去获取数据实际上就是要去对表执行查询操作。
问题:B程序如何精确地找到A程序所共享出来的表?
ContentResolver提供一系列增删改查的方法对数据进行操作,并且这些方法以Uri的形式对外提供数据。
Uri为内容提供者中的数据建立了唯一标识符。它主要由三部分组成,scheme、authorities(主机名/域名)和path。
扩展:uri与url功能类似,通过uri可以指向安卓系统中唯一的某个资源。也就是说通过它可以指向共享出来的唯一的某个表文件
如下:
scheme部分,“content://”是一个标准的前缀。
authority(主机名/域名)部分,是在创建内容提供者时指定的authorities属性值,通常采用程序包名的方式命名。
path部分,“/person”代表资源(或者数据),可以动态改变。
UriMatcher类:
用于对ContentProvider中的Uri进行匹配
1、初始化UriMatcher
UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); // 常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)
2、将Uri注册到UriMatcher中
matcher.addURI("cn.itcast.contentprovider", "people", 1); // 添加需要匹配uri,如果匹配就会返回匹配码"1" matcher.addURI("cn.itcast.contentprovider", "person/#", 2); // #表示通配符,任意内容的意思
1.创建数据库与表提供数据
public class MyDBhelper extends SQLiteOpenHelper { public MyDBhelper(Context context) { super(context, "person.db", null, 1); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL("create table user(id integer primary key autoincrement,userName varchar(10),phone varchar(10))"); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }
2.创建内容提供者继承ContentProvider父类
在程序包名处右击选择【New】—>【Other】—>【Content Provider】选项
输入内容提供者的Class Name(类名称)和URI Authorities(唯一标识,通常使用包名)
内容提供者创建完成后,Android Studio会自动在AndroidManifest.xml中对内容提供者进行注册。
<application ......> ...... <provider android:name=".MyContentProvider" android:authorities="cn.com.myapp" android:enabled="true" android:exported="true" > </provider> </application>
属性含义如下:
(1)android:name
:该属性是一个类的全名称
(2)android:authorities
:列出一个或者多个由content provider的提供的URI的authorities,多个authorities由分号隔开(自定义)。
(3)android:enabled
:该属性表明了该content provider是否可以被实例化,默认情况下该属性值是true。
(4)android:exported
:该属性指示了content provider是否可以被其他应用程序使用。
public class MyContentProvider extends ContentProvider { private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); private static final int SUCCESS = 1; private MyDBhelper myDBhelper; // 静态代码块,MyContentProvider对象没有创建出来之前就已经存在 static { // 加载时为uri适配器添加匹配规则 uriMatcher.addURI("cn.com.myapp","user",SUCCESS); // 查询表中的某条记录 uriMatcher.addURI("cn.com.myapp","user/#",2); } public MyContentProvider() { } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { // Implement this to handle requests to delete one or more rows. throw new UnsupportedOperationException("Not yet implemented"); } @Override public String getType(Uri uri) { // TODO: Implement this to handle requests for the MIME type of the data // at the given URI. throw new UnsupportedOperationException("Not yet implemented"); } @Override public Uri insert(Uri uri, ContentValues values) { // TODO: Implement this to handle requests to insert a new row. throw new UnsupportedOperationException("Not yet implemented"); } @Override public boolean onCreate() { // 创建数据库和初始化表的内容 myDBhelper = new MyDBhelper(getContext()); return false; } @Override // 未来会有另外一个app通过uri来调用query方法 public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { // TODO: Implement this to handle query requests from clients. // 匹配传入的uri,如果匹配得上再做相应的查询 int match = uriMatcher.match(uri); if(match == 1) { // 查询user表中的数据并返回 SQLiteDatabase db = myDBhelper.getWritableDatabase(); return db.query("user",projection,selection,selectionArgs,null,null,sortOrder); } else if(match == 2) { // 查询表中的某条记录 } else { return null; } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { // TODO: Implement this to handle requests to update one or more rows. throw new UnsupportedOperationException("Not yet implemented"); } }
3.在第一个app的Activity中给数据库添加数据
public class firstApplicationActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first_application); MyDBhelper myDBhelper = new MyDBhelper(firstApplicationActivity.this); SQLiteDatabase db = myDBhelper.getWritableDatabase(); db.execSQL("insert into user(userName,phone) values ('tom','8888')"); db.execSQL("insert into user(userName,phone) values ('jeny','18888')"); db.execSQL("insert into user(userName,phone) values ('jack','38888')"); } }
4.创建第二个App作为访问者
布局文件,通过点击按钮获取第一个app的数据用TextView展示出来
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".secondApplicationActivity" android:orientation="vertical" > <TextView android:id="@+id/show" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="9"/> <Button android:id="@+id/btn" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:text="获取共享数据" /> </LinearLayout>
java文件
public class secondApplicationActivity extends AppCompatActivity implements View.OnClickListener { private Button btn; private TextView tv_show; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_second_application); initViews(); setListener(); } protected void initViews() { btn = findViewById(R.id.btn); tv_show = findViewById(R.id.show); } protected void setListener() { btn.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btn: // 创建uri,通过uri来指向共享的数据文件 Uri uri = Uri.parse("content://cn.com.myapp/user"); // 获取访问对象(内容解析者) ContentResolver contentResolver = getContentResolver(); Cursor cursor = contentResolver.query(uri, new String[]{"userName", "phone"}, null, null, null); while (cursor.moveToNext()) { tv_show.append("用户名:" + cursor.getString(0) + "----:" + cursor.getString(1) + "\n"); } } } }
最后,两个app都启动,点击第二个app的按钮拿到了第一个app共享的值
五、网络存储
特点:通过网络上提供的存储空间来上传(存储)或下载(获取)我们存储在网络空间中的数据信息
优点:无需考虑内存的影响。
缺点:受网络的状态的影响、需要耗费流量等成本。
到此这篇关于Android数据存储几种方式讲解的文章就介绍到这了,更多相关Android数据存储内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!