安卓数据存储总结(永久存储和临时存储)

0.目录

安卓开发数据存储主要分为两种形式,一种是永久存储,一种是临时存储。

永久存储

  • 本地数据库Room
  • 网络
  • SharedPreferences
  • 文件存储数据
  • SQLite数据库
  • ContentProvider

临时存储

  • ViewModel
  • Bundle
  • Intent
  • Application

1.永久存储

1.1本地数据库Room

应用场景:一般用来存储结构化数据;因为存储在本地,从本地读取速度会慢于从内存读取;好处是如果数据需要按照某个字段排序,则可以直接利用数据库语句而不用自己重写comparator。

用法:建立数据库(单例),建立实体类,编写接口(Dao),调用。

数据库建立:

//entities注解中填写本数据库的实体类,并在下面返回dao
@Database(entities = {Keyword.class}, version = 1, exportSchema = false)
public abstract class MyDatabase extends RoomDatabase {
    private static final String DB_NAME = "my_database.db";//数据库名称
    private static MyDatabase instance;

    //单例
    public static synchronized MyDatabase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(
                    context.getApplicationContext(),
                    MyDatabase.class,
                    DB_NAME
            ).build();
        }
        return instance;
    }

    public abstract KeywordDao getKeywordDao();
}

编写实体类

@Entity(tableName = "keyword")
public class Keyword {
    @PrimaryKey
    @NonNull
    @ColumnInfo(name = "_id")
    public String _id;

    @ColumnInfo(name = "type")
    public int type;

    @ColumnInfo(name = "sortStr")
    public String sortStr;

    @ColumnInfo(name = "name")
    public String name;

    @NonNull
    public String get_id() {
        return _id;
    }
//此处省略其他属性的getter和setter

编写Dao接口

@Dao
public interface KeywordDao {
    //冲突处理
    @Insert(onConflict = REPLACE)
    void insertKeywords(List<Keyword> keywordList);

    @Query("select * from keyword")
    DataSource.Factory<Integer, Keyword> getKeywordList();

    @Query("select * from keyword where type = 0 order by sortStr")
    DataSource.Factory<Integer, Keyword> getDiseaseList();

    @Query("select * from keyword where type = 1 order by sortStr")
    DataSource.Factory<Integer, Keyword> getMedicineList();
}

调用

 private void insertKeywords(List<Keyword> keywords) {
        Log.d(TAG, "insertKeywords: ");
     //异步操作
        AsyncTask.execute(() -> MyDatabase.getInstance(this).getKeywordDao().insertKeywords(keywords));
    }

1.2 网络

没做过,我不会,但我知道有。

学会了再补。

1.3 sharedpreferences

应用场景:不想保存在本地数据库,数据量比较少,数据结构比较简单(例如基本数据类型),可以保存在sharedpreferences,本质是在本地建立一个xml文件以map的形式存储键值对。

使用:建立自定义sharedpreferences类,获取shp的editor进行保存即可。

要注意获取到editor进行编辑之后一定要及时apply()或commit()对editor修改的内容进行保存,这两种方法的区别在于:

apply()异步进行,commit()同步进行,一般使用apply()比较好,可以参照之前的笔记

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;

public class UserInfoShp {
    private static final String USER_NAME = "USER_NAME";
    private static final String USER_PASSWORD = "USER_PASSWORD";
    private static final String USER_TOKEN = "USER_TOKEN";
    private static final String PREFERENCES_FILE = "PREFERENCES_FILE_USER";

    public static String getUserName(Context context) {
        return getUserPreference(context).getString(USER_NAME, "");
    }

    @SuppressLint("CommitPrefEdits")
    public static void setUserName(Context context, String userName) {
        getUserPreference(context).edit().putString(USER_NAME, userName).apply();
    }

    public static String getUserPassword(Context context) {
        return getUserPreference(context).getString(USER_PASSWORD, "");
    }

    @SuppressLint("CommitPrefEdits")
    public static void setUserPassword(Context context, String userPassword) {
        getUserPreference(context).edit().putString(USER_PASSWORD, userPassword).apply();
    }

    public static String getUserToken(Context context) {
        return getUserPreference(context).getString(USER_TOKEN, "");
    }

    @SuppressLint("CommitPrefEdits")
    public static void setUserToken(Context context, String userToken) {
        getUserPreference(context).edit().putString(USER_TOKEN, userToken).apply();
    }
    /**
    *重点在这里,调用context的getSharedPreferences方法即可获取shp,第一个参数是shp对应的xml文件名,第二个参数是模式。
    */
    private static SharedPreferences getUserPreference(Context context) {
        return context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);
    }
}

对于方法context.getSharedPreferences(PREFERENCES_FILE, Context.MODE_PRIVATE);

getSharedPreferences()适用于需要建立多个shp文件的情况,这时可以在第一个参数为xml文件自由命名;如果只需要一个shp文件,且文件生命周期只存在于该活动,则可以调用该活动(activity)的getPreferences()方法。

关于shp所能保存的数据类型

  • 安卓官方提供的可以保存的数据类型包括以下几种

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-piAQv0HG-1636447253392)(F:\Notes\Typora\Pictures\image-20211109162957512.png)]

但有些情况下我们需要保存其他数据类型,例如ArrayList、Map等,安卓并没有提供相应方法,但我们也可以借助putString来实现相同的效果

  • 保存arraylist类型

    原理:保存数据:利用Gson工具将arraylist转换为json字符串,利用putString保存在shp;

    ​ 取出数据:利用Gson工具将保存在shp的json字符串解析为所需实体类。

        /**
         * 保存List
         */
        public static <T> void setDataList(String tag, List<T> datalist, Context context) {
            if (null == datalist || datalist.size() <= 0)
                return;
            Gson gson = new Gson();
            //转换成json数据,再保存
            String strJson = gson.toJson(datalist);
            SharedPreferences.Editor editor = getKeywordPreference(context).edit();
            editor.putString(tag, strJson);
            editor.apply();
    
        }
    
        /**
         * 获取List
         */
        public static <T> List<T> getDataList(Class<T> clazz, String tag, Context context) {
            List<T> datalist = new ArrayList<>();
            String strJson = getKeywordPreference(context).getString(tag, null);
            if (null == strJson) {
                return datalist;
            }
            Gson gson = new Gson();
            Type listType = com.google.gson.internal.$Gson$Types.newParameterizedTypeWithOwner(null, ArrayList.class, clazz);
    //        datalist = gson.fromJson(strJson, new TypeToken<List<T>>() {}.getType());
            //上面这行获取type的方法不提倡,因为在使用返回数据时会报"LinkedTreeMap can't be cast into 你想要的数据类型"错误,因为它的本质还是强制类型转换,解析出来的不是我们想要的数据类型而是LinkedTreeMap
            datalist = gson.fromJson(strJson, listType);
            return datalist;
        }
    

1.4 文件存储

待补充

1.5 SQLite数据库

待补充

1.6 ContentProvider

待补充

2.临时存储

2.1 viewmodel

Android-Developer ViewModel概览

应用场景:一般和LiveData结合使用,监听数据状态,响应数据的动态变化;生命周期伴随activity或fragment(一般为activity,方便activity的各个fragment间数据传递)。

使用:创建viewmodel类,在activity中创建对应实例即可。

创建viewmodel类,需要继承AndroidViewModel

public class LoginViewModel extends AndroidViewModel {

    protected MutableLiveData<String> userNameLD = new MutableLiveData<>();
    protected MutableLiveData<String> userPasswordLD = new MutableLiveData<>();

    public LoginViewModel(@NonNull Application application) {
        super(application);
    }

    public void login() {
        //此处省略登录具体实现代码,知道用到了viewmodel里的属性就可以了
    }

    //一般不写属性的set方法,因为可以直接使用get方法获取到对象之后调用setValue直接对其赋值
    public MutableLiveData<String> getUserNameLD() {
        return userNameLD;
    }

    public MutableLiveData<String> getUserPasswordLD() {
        return userPasswordLD;
    }
}

创建实例对象

//activity onCreate
loginViewModel= new ViewModelProvider(requireActivity()).get(LoginViewModel.class);

生命周期介绍如图,官网介绍如下

ViewModel 对象存在的时间范围是获取 ViewModel 时传递给 ViewModelProviderLifecycleViewModel 将一直留在内存中,直到限定其存在时间范围的 Lifecycle 永久消失:对于 activity,是在 activity 完成时;而对于 fragment,是在 fragment 分离时。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZU5lcEaI-1636447253394)(F:\Notes\Typora\Pictures\viewmodel-lifecycle.png)]

2.2 bundle

应用场景:安卓开发者官网介绍

如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失。例如,应用可能会在它的某个 Activity 中包含用户列表。为配置更改重新创建 Activity 后,新 Activity 必须重新提取用户列表。对于简单的数据,Activity 可以使用 onSaveInstanceState() 方法从 onCreate() 中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据,如用户列表或位图。

例如当某个activity隐藏,又被销毁,而我们重新返回到此activity时若不做任何操作数据就会丢失,此时可以通过onSavedInstanceState()回调

代码如下

@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
    super.onSaveInstanceState(outState, outPersistentState);
    String tempData = "Something you just typed!";
    outState.putString("tempData", tempData);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_dialog);

    if (savedInstanceState != null) {
        String tempData = savedInstanceState.getString("tempData");
        Log.d("SavedData", tempData);
    }
}

2.3 intent

Android-Developer Intent和Intent过滤器

Intent 是一个消息传递对象,您可以用来从其他应用组件请求操作。尽管 Intent 可以通过多种方式促进组件之间的通信,但其基本用例主要包括以下三个:

  • 启动 Activity

Activity 表示应用中的一个屏幕。通过将 Intent 传递给 startActivity(),您可以启动新的 Activity 实例。Intent 用于描述要启动的 Activity,并携带任何必要的数据。

如果您希望在 Activity 完成后收到结果,请调用 startActivityForResult()。在 Activity 的 onActivityResult() 回调中,您的 Activity 将结果作为单独的 Intent 对象接收。如需了解详细信息,请参阅 Activity 指南。

  • 启动服务

Service 是一个不使用用户界面而在后台执行操作的组件。使用 Android 5.0(API 级别 21)及更高版本,您可以启动包含 JobScheduler 的服务。如需了解有关 JobScheduler 的详细信息,请参阅其 API-reference documentation

对于 Android 5.0(API 级别 21)之前的版本,您可以使用 Service 类的方法来启动服务。通过将 Intent 传递给 startService(),您可以启动服务执行一次性操作(例如,下载文件)。Intent 用于描述要启动的服务,并携带任何必要的数据。

如果服务旨在使用客户端-服务器接口,则通过将 Intent 传递给 bindService(),您可以从其他组件绑定到此服务。如需了解详细信息,请参阅服务指南。

  • 传递广播

广播是任何应用均可接收的消息。系统将针对系统事件(例如:系统启动或设备开始充电时)传递各种广播。通过将 Intent 传递给 sendBroadcast()sendOrderedBroadcast(),您可以将广播传递给其他应用。

本页的其余部分将说明 Intent 的工作方式及如何使用 Intent。对于相关信息,请参阅与其他应用交互共享内容

Intent 类型

Intent 分为两种类型:

  • 显式 Intent:通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理 Intent 的应用。通常,您会在自己的应用中使用显式 Intent 来启动组件,这是因为您知道要启动的 Activity 或服务的类名。例如,您可能会启动您应用内的新 Activity 以响应用户操作,或者启动服务以在后台下载文件。
  • 隐式 Intent :不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理。例如,如需在地图上向用户显示位置,则可以使用隐式 Intent,请求另一具有此功能的应用在地图上显示指定的位置。

可以为activity间传递数据,例如:当A活动想要启动B活动时,可以调用B活动的startActivity静态方法,并传入所需参数。

 public static void BActivity(Context context, int type) {
        Intent intent = new Intent(context, BActivity.class);
        intent.putExtra("type", type);
        context.startActivity(intent);
    }

2.4 application

应用场景:Android系统在运行每一个程序应用的时候,都会创建一个Application对象,用于存储与整个应用相关的公共变量。 一个Android应用只会生成一个Application对象,在不同的Activity中获取的Application对象是一样的,所以Application对象是一个单例**(SingleTon)**。 Application对象非常适合用于存储一些与整个应用相关数据,例如应用版本,应用登录账户,数据缓存等。 利用Application对象存储公共数据或数据传递 ,免去activity的频繁切换所需的凌乱数据传递。

使用:待补充。

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android数据存储是一种将数据存储在设备上的技术,以便应用程序可以使用它们,无论它们是否在运行时。这使得应用程序可以使用一些简单且安全的存储方法,而无需直接与设备存储硬件进行交互。Android提供了几种不同的数据存储选项,包括: 1. **Shared Preferences**:这是最基本的数据存储方式,它允许应用程序存储和检索键值对数据。这对于小型数据存储需求非常有用,因为它们是本地存储并且对其他应用程序不可见。 2. **Files**:应用程序可以使用文件系统来存储和检索数据。这可以包括文本文件、图像、音频和视频文件等。文件系统存储数据可以在应用程序关闭后保持持久性。 3. **SQLite**:SQLite是一个轻量级的关系型数据库,它可以在设备上作为嵌入式数据库使用。SQLite提供了对数据的结构化查询支持,因此更适合存储大量数据。 4. **Content Providers**:Content Providers允许应用程序之间共享数据。它们使得跨应用程序访问和共享数据成为可能,这对于构建大型应用程序的跨应用程序功能非常有用。 5. **外部存储(如SD卡)**:Android允许应用程序访问设备的外部存储(如SD卡)。这可以用于存储大文件或大型数据集,但需要注意的是,访问外部存储可能需要额外的权限。 在选择适当的存储选项时,需要考虑数据的类型、大小、持久性和安全性。例如,如果数据只需要在应用程序运行时可用,Shared Preferences可能是一个合适的选择。然而,如果需要存储大量结构化数据或大型文件,那么SQLite或外部存储可能更适合。 此外,对于敏感数据(如用户密码或个人信息),建议使用加密来保护这些数据。在Android中,可以使用AES或其他加密算法来实现这一点。 最后,记住在处理存储数据时遵循最佳实践,例如使用适当的错误处理和日志记录,以确保数据的安全性和完整性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值