【Android】安卓中的存储

1、存储在App内部

最简单的一种。在尝试过程中发现,手机中很多文件夹都没有权限读写。我们可以将我们需要写的文件存放到App中的files文件夹中,当然我们有权限在整个App中读写文件

可以通过API获取一个file对象,这里的this就是MainActivity类

// 获取当前包下的files路径 /data/data/top.woodwhale.qqlogin/files
File filesDir = this.getFilesDir();

之后就可以通过文件输出流写入文件:

File filesFile = new File(filesDir,"info.txt");
boolean flag = (filesFile.exists() || filesFile.createNewFile());
FileOutputStream fos = new FileOutputStream(file);
fos.write((ac+"***"+pwd).getBytes(StandardCharsets.UTF_8));
fos.close();

写入成功:

当然,我们既然在这个App中都有权限,那么所有目录都可以写:

// 写入到自己有权限写的地方
File file = new File("/data/data/top.woodwhale.qqlogin/info.txt");

2、SD卡外部存储

虽然现在很多的手机都不支持SD卡了,但是仍然有平板使用。

直接放出一个Activity类,其中调用了nvironment.getExternalStorageDirectory();方法类获取一个sd卡file对象,使用Formatter.formatFileSize(this,externalStorageDirectory.getFreeSpace()));Formatter类中的转化,将long类型转化为大小类型,同时调用sd卡file对象的getFreeSpace()方法,获取卡中剩余的空间,之后就是写入externalStorageDirectory.getPath()卡中的路径

public class SdcardActivity extends Activity {
private Button btn;
public static String TAG = "SdcardActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sc_card_rw);
btn = this.findViewById(R.id.bt_sdw);
btn.setOnClickListener(view -> {
File externalStorageDirectory = Environment.getExternalStorageDirectory();
Log.d(TAG, "sd卡路径是:"+externalStorageDirectory.getPath());
Log.d(TAG,"sd卡剩余空间是"+ Formatter.formatFileSize(this,externalStorageDirectory.getFreeSpace()));
File file = new File(externalStorageDirectory,"love.txt");
try {
boolean flag = file.exists() || file.createNewFile();
if (flag) {
FileOutputStream fos = new FileOutputStream(file);
fos.write("woodwhale love sheepbotany".getBytes(StandardCharsets.UTF_8));
fos.close();
}
} catch (Exception e) {
e.printStackTrace();
}
});
}
}

但是,在此之前,我们需要一个SD卡的读写权限,我们在AndrodiManifest.xml中配置下面的ses-permission

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

最终,在我们的sd卡中看到如下结果,证明写入成功:

3、SharedPreferences存储

SharedPreferences是android下的一个类,功能就是记录偏好设置,形成一个xml文件

我们可以用SharedPreferences来存储一些信息。

例如常见的这种:

我们勾选之后,再次打开app仍然处于勾选状态。

那么这种情况如何实现呢?

通过xml生成上面的布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="80dp"> <LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_centerVertical="true"> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未知来源"
android:textColor="@color/teal_200"
android:layout_marginLeft="10dp"
android:textSize="20sp" /> <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="运行安装未知来源的应用"
android:layout_marginLeft="10dp"
android:layout_marginTop="10dp"
android:textSize="18sp"/>
</LinearLayout> <Switch
android:id="@+id/sw_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginTop="30dp"
android:layout_marginRight="10dp" /> </RelativeLayout>

我们把Switch这个选择框在activity类中赋予一个变量,给他加上一个OnCheckedChangeListener,再使用SharedPreferences来进行设置偏好,整体代码如下

package top.woodwhale.qqlogin;

import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.widget.CompoundButton;
import android.widget.Switch;
import androidx.annotation.Nullable; public class PreferenceDemoActivity extends Activity { private Switch sw;
public static String TAG = "PreferenceDemoActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_pre_demo);
sw = (Switch) this.findViewById(R.id.sw_source);
SharedPreferences settingInfo = this.getSharedPreferences("settingInfo", MODE_PRIVATE);
SharedPreferences.Editor edit = settingInfo.edit();
sw.setOnCheckedChangeListener(new MyListener(edit));
boolean state = settingInfo.getBoolean("state", true);
Log.d(TAG,"STATE=="+ state);
sw.setChecked(state);
}
} // 改变状态的监听器
class MyListener implements CompoundButton.OnCheckedChangeListener {
SharedPreferences.Editor editor;
public MyListener(SharedPreferences.Editor editor) {
this.editor = editor;
}
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
Log.d(PreferenceDemoActivity.TAG,"current state : "+ b);
editor.putBoolean("state",b); // 要保存的数据类型
editor.commit(); // 保存数据
}
}

其中,editor的功能是保存数据

其次,为了每次打开App都可以看到我们的配置,通过读取偏好配置文件,设置switch框的勾选

这样就可以同步偏好设置的勾选啦!

最后我们可以在手机内部看到我们写入的偏好设置xml文件了,这样也属于存储在App内部

4、使用SQLite数据库存储

Android设备自带SQLite数据库,如果掌握过mysql,那么SQLite非常容易上手,且不说提供了非常简便的API,就算是自己写也比JDBC简单!

首先我们不适用提供的API来实现一次增删改查!

4.1 自己完成一个BaseDao类

BaseDao类本来是用来连接数据库等基础的,具体的增删改查应该在service层实现,但为了这里测试,我们将crud的方法写入到BaseDao类中封装起来。具体架构如下:

首先是Constants类,是常量类,其中有我们的数据库名、版本号、表名

public class Constants {
public static final String DATABASE_NAME = "woodwhale.db";
public static final int VERSION_CODE = 1;
public static final String TABLE_NAME = "user";
}

其次是DatabaseHelper类,继承SQLiteOpenHelper类,用来开启数据库,其中的onCreate方法是数据库创建时的回调,onUpgrade方法时升级数据时的回调,我们在Constans类中写了一个版本号,爸爸那边每次升级可以加入新的功能,可以写在onUpgrade方法中,通过switch实现。不过需要注意,升级只能让版本号上升,不能降级,否则会报错

package top.woodwhale.qqlogin.SQLite.utils;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; public class DatabaseHelper extends SQLiteOpenHelper {
public static String TAG = "DatabaseHelper";
/**
* @param context 上下文
*/
public DatabaseHelper( Context context) {
super(context, Constants.DATABASE_NAME, null, Constants.VERSION_CODE);
} @Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
// 创建时的回调
Log.d(TAG, "创建数据库");
String sql = "create table " + Constants.TABLE_NAME + " (id integer,name varchar,age integer)";
sqLiteDatabase.execSQL(sql);
} @Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
// 升级数据库的回调
Log.d(TAG, "升级数据库!");
String sql = null;
switch (i) {
case 1:
sql = "alter table "+ Constants.TABLE_NAME + " add phone integer";
sqLiteDatabase.execSQL(sql);
break;
case 2:
sql = "alter table "+ Constants.TABLE_NAME + " add address varchar";
sqLiteDatabase.execSQL(sql);
break;
}
}
}

最后就是我们封装好的数据库BaseDao类,通过语句实现了增删改查

package top.woodwhale.qqlogin.SQLite.dao;

import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log; import top.woodwhale.qqlogin.SQLite.utils.Constants;
import top.woodwhale.qqlogin.SQLite.utils.DatabaseHelper; // BaseDao类
public class BaseDao { private final DatabaseHelper dbh;
private SQLiteDatabase db;
public static String TAG = "BaseDao"; public BaseDao(Context context) {
dbh = new DatabaseHelper(context);
} // 增
public void add(int id, String name, int age) {
db = dbh.getWritableDatabase();
String sql = "insert into " + Constants.TABLE_NAME + "(id,name,age) values(?,?,?)";
Object[] params = new Object[]{id,name,age};
db.execSQL(sql,params);
db.close();
} // 删
public void free(int id) {
db = dbh.getWritableDatabase();
String sql = "delete from " + Constants.TABLE_NAME + " where id=?";
Object[] params = new Object[]{id};
db.execSQL(sql,params);
db.close();
} // 改
public void edit(int id, int age) {
db = dbh.getWritableDatabase();
String sql = "update " + Constants.TABLE_NAME +" set age = ? where id = ?";
Object[] params = new Object[]{age,id};
db.execSQL(sql,params);
db.close();
} // 查
@SuppressLint("Range")
public void show(int id) {
db = dbh.getReadableDatabase();
String sql = "select * from " + Constants.TABLE_NAME +" where id = ?";
String[] params = new String[]{String.valueOf(id)};
Cursor cursor = db.rawQuery(sql, params);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex("name"));
Log.d(TAG,"name == "+name);
int age = cursor.getInt(cursor.getColumnIndex("age"));
Log.d(TAG,"age == "+age);
}
cursor.close();
db.close();
}
}

接着我们在AndroidTest包下进行测试

package top.woodwhale.qqlogin;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log; import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test;
import org.junit.runner.RunWith; import static org.junit.Assert.*; import top.woodwhale.qqlogin.SQLite.dao.BaseDao;
import top.woodwhale.qqlogin.SQLite.utils.DatabaseHelper; /**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest { public static final String TAG = "ExampleInstrumentedTest";
public static final Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();;
public static final BaseDao dao = new BaseDao(appContext);; @Test
public void useAppContext() {
// Context of the app under test.
assertEquals("top.woodwhale.qqlogin", appContext.getPackageName());
} @Test
public void testCreate() {
DatabaseHelper dbh = new DatabaseHelper(appContext);
SQLiteDatabase writableDatabase = dbh.getWritableDatabase();
Log.d(TAG, writableDatabase.getPath());
} @Test
public void testAdd() {
dao.add(1,"woodwhale",19);
dao.add(2,"sheepbotany",21);
}
@Test
public void testFree() {
dao.free(1);
}
@Test
public void testEdit() {
dao.edit(1,3);
}
@Test
public void testShow() {
dao.show(1);
} }

增删改查都成功,成功就如图所示:

由于只有查询有log回显,在logcat中之后show方法出现了log

4.2 使用Google写的API处理

那么使用Google写好的增删改查api可以避免我们sql语句的格式问题和语法错误

经过测试,如下代码没有问题(在BaseDao类中)

// 使用API的添加
public void addByAPI(int id, String name, int age) {
ContentValues contentValues = new ContentValues();
contentValues.put("id",id);
contentValues.put("name",name);
contentValues.put("age",age);
db = dbh.getWritableDatabase();
db.insert(Constants.TABLE_NAME,null,contentValues);
db.close();
}
// 删除
public void freeByAPI(int id) {
db = dbh.getWritableDatabase();
db.delete(Constants.TABLE_NAME,"id = ?",new String[]{String.valueOf(id)});
db.close();
Log.d(TAG,"API删除成功!");
}
// 修改
public void editByAPI(int id, String name, Integer age) {
ContentValues contentValues = new ContentValues();
if (name != null) {
contentValues.put("name",name);
}
if (age != null) {
contentValues.put("age",age);
}
db = dbh.getWritableDatabase();
db.update(Constants.TABLE_NAME,contentValues,"id = ?", new String[]{String.valueOf(id)});
db.close();
}
// 查询
public void showByAPI(int id) {
db = dbh.getReadableDatabase();
Cursor cursor = db.query(false, Constants.TABLE_NAME, new String[]{"id", "name", "age"}, "id = ?", new String[]{String.valueOf(id)}, "id", null, null, null);
while (cursor.moveToNext()) {
int ID = cursor.getInt(0);
Log.d(TAG,"ID == "+ID);
String name = cursor.getString(1);
Log.d(TAG,"name == "+name);
int age = cursor.getInt(2);
Log.d(TAG,"age == "+age);
}
cursor.close();
db.close(); }

4.3 事务使用

使用db.beginTransaction(); db.setTransactionSuccessful(); db.endTransaction();三个方法来进行事务的处理。

简单的一个测试类

// 测试一个数据库事物
@Test
public void testTransaction() {
DatabaseHelper dbh = new DatabaseHelper(appContext);
SQLiteDatabase db = dbh.getWritableDatabase();
db.beginTransaction();
Log.d(TAG,"事物开启!");
try {
db.execSQL("update " + Constants.TABLE_NAME +" set age = 114 where id = 1");
int i = 10 / 0;
db.execSQL("update " + Constants.TABLE_NAME +" set age = 114 where id = 2");
db.setTransactionSuccessful();
Log.d(TAG,"事物成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction();
db.close();
Log.d(TAG,"事物关闭!");
}
dao.showByAPI(1);
dao.showByAPI(2);
}

看看logcat,首先是进入了 事物开启,然后程序进入了try中,因为除以了一个0所以报错,捕获异常了之后就是进入到finally中关闭了事务,可以发现我们sql中的信息都回滚了,没有改变。

我们把int i = 10 / 0;删了试一试,可以看到成功执行事物。

值得注意的是,事物开启之后,仅有当前的db对象可以执行sql语句,使用Dao类中的方法是无法进行增删改查的,因为对这些得到的db对象上了锁!

【Android】安卓中的存储的更多相关文章

  1. 安卓中的数据存储方式以及ContentProvider的简单介绍

    1.介绍android的数据存储方式 File存储 sharedPrefrence存储方式 conmtentprovider sqlitedatabase 网络存储   2.请介绍下ContentPr ...

  2. Android中数据存储(一)

    国庆没有给国家添堵,没有勾搭妹子,乖乖的写着自己的博客..... 本文将为大家介绍Android中数据存储的五种方式,数据存储可是非常重要的知识哦. 一,文件存储数据 ①在ROM存储数据 关于在ROM ...

  3. android中数据存储

    android中数据存储     Android 中存储数据的方式有五种:SQLite数据库.文件存储.内容提供者.网络.SharedPreferences(Key----value)五种存储方式. ...

  4. android开发中的5种存储数据方式

    数据存储在开发中是使用最频繁的,根据不同的情况选择不同的存储数据方式对于提高开发效率很有帮助.下面笔者在主要介绍Android平台中实现数据存储的5种方式. 1.使用SharedPreferences ...

  5. Android开发(24)---安卓中实现多线程下载(带进度条和百分比)

    当我们学完java中多线程的下载后,可以将它移植到我们的安卓中来,下面是具体实现源码: DownActivity.java package com.example.downloads; import ...

  6. Android编程中的5种数据存储方式

    Android编程中的5种数据存储方式 作者:牛奶.不加糖 字体:[增加 减小] 类型:转载 时间:2015-12-03我要评论 这篇文章主要介绍了Android编程中的5种数据存储方式,结合实例形式 ...

  7. Android开发中使用七牛云存储进行图片上传下载

    Android开发中的图片存储本来就是比较耗时耗地的事情,而使用第三方的七牛云,便可以很好的解决这些后顾之忧,最近我也是在学习七牛的SDK,将使用过程在这记录下来,方便以后使用. 先说一下七牛云的存储 ...

  8. Android中数据存储(四)——ContentProvider存储数据

    目录(?)[+]   当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方 ...

  9. Android中数据存储(三)——SQLite数据库存储数据

    当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方式: 1. Share ...

随机推荐

  1. PowerDotNet平台化软件架构设计与实现系列(07):数据同步平台

    上文介绍定时任务调度平台的时候提到,定时任务调度平台的类模式一般用于处理耗时较长的任务.但是根据经验,有些耗时较长的任务,可以通过简化业务逻辑.分页.批量多次处理,改造为耗时较小的适合使用RESTfu ...

  2. Django常用的QuerySet操作

    在这里我根据是否支持链式调用分类进行介绍 1. 支持链式调用的接口 all 使用频率比较高,相当于SELECT * FROM table 语句,用于查询所有数据. filter 使用频率比较高,根据条 ...

  3. 洛谷八月月赛 II T1 题解

    Content 在大小为 \(n\) 的数字三角形中,第 \(i\) 行包含有 \(i\) 个数字,数字从上到下,从左到右依次排列为 \(1,2,3,\dots\). 设第 \(i\) 行第 \(j\ ...

  4. CF755C PolandBall and Forest 题解

    Content 给定无向图的 \(n\) 个点的父亲节点,求无向图的联通块个数. 数据范围:\(1\leqslant n\leqslant 10^4\). Solution 并查集模板题. 我们将在当 ...

  5. LuoguP4263 [Code+#3]投票统计 题解

    Content 有 \(t\) 组询问,每组询问给定一个长度为 \(n\) 的数列,请将出现次数最多的数按照从小到大的顺序输出,或者这些数在数列中出现的次数都相等. 数据范围:\(t\) 未知,\(n ...

  6. AcWing1264. 动态求连续区间和 (线段树做法)

    1.题目 给定 n 个数组成的一个数列,规定有两种操作,一是修改某个元素,二是求子数列 [a,b] 的连续和. 输入格式 第一行包含两个整数 n 和 m,分别表示数的个数和操作次数. 第二行包含 n ...

  7. 1164 - Horrible Queries

    1164 - Horrible Queries    PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 64 M ...

  8. uniapp h5中解决跨域问题

    "h5": { "title": "互联云玺", "template": "", "rou ...

  9. [数学]高数部分-Part IV 一元函数积分学

    Part IV 一元函数积分学 回到总目录 Part IV 一元函数积分学 不定积分定义 定积分定义 不定积分与定积分的几何意义 牛顿-莱布尼兹公式 / N-L 公式 基本积分公式 点火公式(华里士公 ...

  10. Java 16 中新增的 Stream 接口的一些思考

    这里先提一个题外话,如果想看 JDK 不同版本之间有何差异,增加或者删除了哪些 API,可以通过下面这个链接查看: https://javaalmanac.io/jdk/17/apidiff/11/ ...