我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android中的存储详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android中的存储详解

1、存储在App内部

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

image-20220115094918127

可以通过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();

写入成功:

image-20220115095219121

当然,我们既然在这个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卡中看到如下结果,证明写入成功:

image-20220115095832549

3、SharedPreferences存储

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

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

例如常见的这种:

image-20220115102422273

我们勾选之后,再次打开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的功能是保存数据

image-20220115102821376

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

image-20220115102916926

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

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

image-20220115103013462

image-20220115103022504

4、使用SQLite数据库存储

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

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

4.1 自己完成一个BaseDao类

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

image-20220115142740925

首先是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";
    
    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包下进行测试

image-20220115143231582

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;


@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);
    }

}

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

image-20220115143337724

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

image-20220115143411403

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中的信息都回滚了,没有改变。

image-20220115200101144

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

image-20220115200213656

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

总结

到此这篇关于Android中的存储详解的文章就介绍到这了,更多相关Android存储内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android中的存储详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

详解Android文件存储

摘要 其实安卓文件的操作和java在pc环境下的操作并无二致,之所以需要单独讲解是因为安卓系统提供了不同于pc的访问文件系统根路径的api,同时对一个应用的私有文件做了统一的管理。根据我的经验,初学者在这部分感到很容易混淆内部存储和外部存
2022-06-06

Android SharedPreferences存储用法详解

先看Demo运行效果SharedPreferences详解SharedPreferences是Android平台上一个轻量级的存储类,用来保存应用的一些常用配置,比如Activity状态,Activity暂停时,将此activity的状态保
2022-06-06

Android本地存储SharedPreferences详解

Android本地存储SharedPreferences详解存储位置SharedPreferences数据保存在: /data /data/ /shared_prefs 文件夹下,以XML格式保存,根元素为:
2023-05-31

详解Android的网络数据存储

一、网络保存数据介绍 可以使用网络来保存数据,在需要的时候从网络上获取数据,进而显示在App中。 用网络保存数据的方法有很多种,对于不同的网络数据采用不同的上传与获取方法。 本文利用LeanCloud来进行网络数据的存储。 LeanClou
2022-06-06

详解Android四种存储方式

在Android程序开发中我们经常遇到四种数据存储方式,每种存储方式都各有不同;以下我分别列举了Android开发中的不同存储方式的特点一,PreferencesPreferences是一个较轻量级的存储数据的方法,具体使用方法:在A中保存
2022-06-06

Android学习之SharedPerference存储详解

SharedPerference不同同于文件存储,它是使用键值的方式来存储数据,对于保存的每一条数据都会给一个键值,这样在读取数据时直接通过键值取出相应数据。amdroid提供了三个方法来获取实例:1.Context类中的getShareP
2023-05-30

详解mysql中的存储引擎

mysql存储引擎概述 什么是存储引擎? MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种技术都使用不同的存储机制、索引技巧、锁定水平并且最终提供广泛的不同的功能和能力。通过选择不同的技术,你能够获得额外的速度
2022-05-24

MySQL中存储过程的详细详解

目录概述优点缺点mysql存储过程的定义存储过程的基本语句格式存储过程的使用定义一个存储过程定义一个有参数的存储过程定义一个流程控制语句 IF ELSE定义一个条件控制语句 CASE定义一个循环语句 WHILE定义一个循环语句 REPEAT
2022-06-26

Android中的数据储存之文件存储

当我们在使用各种程序时,其实际上是在和各种数据打交道,当我们聊QQ,刷微博,看新闻,其实都是在和里面的数据交互例如在聊天时发出的消息,以及在登录时输入的账号密码,其实都是瞬时数据,那什么是瞬时数据呢?就是指储存在内存中,有可能因为程序关闭或
2023-06-04

详解Android数据存储之Android 6.0运行时权限下文件存储的思考

前言: 在我们做App开发的过程中基本上都会用到文件存储,所以文件存储对于我们来说是相当熟悉了,不过自从Android 6.0发布之后,基于运行时权限机制访问外置sdcard是需要动态申请权限,所以以往直接sdcard根目录上直接新建了一个
2022-06-06

Android 文件数据存储实例详解

Android之文件数据存储 一、文件保存数据介绍 Activity提供了openFileOutput()方法可以用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是一样的。文件可用来存放大量数据,如文本、图书、音频等
2022-06-06

Android开发之数据的存储方式详解

在Android中,数据的存储分为两种方式: 1、直接以文件的形式存储在目录中 2、以json格式存储在数据库中 将数据以文件的存储又分为两种方式: 1、生成.txt文件 2、生成xml文件 那么今天就来详细的说一下以文件的形式存储,由于没
2022-06-06

详解C++中存储类的使用

存储类定义 C++ 程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。auto、register、static、extern和mutable是C++程序中常用的存储类,本文主要介绍了它们的使用方法,需要的可以参考一下
2022-12-28

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录