在android里保存数据的三种形式(Saving Data)

Android Activity数据保存机制与最佳实践 在Android开发中,数据持久化是确保应用稳定性的关键技术。Activity作为核心组件,其生命周期管理涉及onSaveInstanceState、ViewModel等多种数据保存机制。这些技术通过Bundle序列化、Parcelable接口等方式,解决了屏幕旋转、内存回收等场景下的数据丢失问题。合理运用这些机制不仅能提升用户体验(如表单数据保留),还能优化性能(如RecyclerView状态恢复)。现代Android架构更推荐结合ViewModel、LiveData和SavedStateHandle,构 阅读详情


在android里保存数据的三种形式(Saving Data)

  (2012-12-06 17:06:42)
标签: 

android

 

it

分类: android开发

Saving Data

Most Android apps need to save data, even if only to save information about the app state during onPause() so the user's progress is not lost. Most non-trivial apps also need to save user settings, and some apps must manage large amounts of information in files and databases.

This class introduces you to the principal data storage options in Android, including:
1. Saving key-value pairs of simple data types in a shared preferences file
2. Saving arbitrary files in Android's file system
3. Using databases managed by SQLite

 

                                                            Lessons
1. Saving Key-Value Sets  存贮小型键值对数据集
Learn to use a shared preferences file for storing small amounts of information in key-value pairs.
2. Saving Files                      存储文件
Learn to save a basic file, such as to store long sequences of data that are generally read in order.
3. Saving Data in SQL Databases   在数据库存储
Learn to use a SQLite database to read and write structured data.

 

                                          1. 保存键值对(Saving Key-Value Sets)

如果你要存储存储小型的键值对(key-value)数据集的话,可以使用SharedPreferences API。SharedPreferences对象是一个包含键值对的文件,提供了读和写的方法。每一个SharedPreferences文件都被框架(framework)管理,并且既可以是私有的,也可以使公有的。以下内容主要是:使用SharedPreferences APIs 来存储和检索简单的数值。

注意:SharedPreferences APIs 只用来读和写键值对,不要把它和 Preference APIs混淆了, Preference APIs是给app的设置(settings)建立用户界面的,这个app的设置结果是存储在 SharedPreferences 里的。 

Get a Handle to a SharedPreferences

新建一个shared preference文件,或者访问一个已存在的文件,用以下方法:getSharedPreferences() — 如果涉及到多个存储文件,这些文件以名字区分,名字用作函数第一个参数,这个方法可以在app里任意的地方(any Context)调用。

getPreferences() — 如果这个activity只用到一个存储文件,那么在这个activity里调用这个方法就可以,而且不需要指定名字参数,因为检索的存储文件是属于这个activity的默认文件。

 

以下代码在一个Fragment里执行,第一个指定name的参数从资源文件夹(R.string.preference_file_key) 里找,第二个参数指定文件打开方式:Context.MODE_PRIVATE,所以这个文件只有这个app可以访问。

Context context = getActivity();
      SharedPreferences sharedPref = context.getSharedPreferences(
        getString(R.string.preference_file_key), Context.MODE_PRIVATE);

 

当命名shared preference 时,必须使用一个unique名字。比如 "com.example.myapp.PREFERENCE_FILE_KEY"

或者,如果你的activity只需要一个 shared preference 文件,你可以使用getPreferences()方法。这样的话,就不用name参数了。但要指定文件打开方式。

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

注意:如果创建文件的时候,文件打开方式是 MODE_WORLD_READABLE或者MODE_WORLD_WRITEABLE,那么其他app都可以访问里面的数据了。

 

Write to Shared Preferences

要写入数据,

1.先在SharedPreferences通过调用edit()方法创建一个SharedPreferences.Editor 。

2.通过putInt()和putString()来把键值对写入,

3.然后调用commit()方法来保存。

For example:

//新建一个SharedPreferences文件,设定为private模式
SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

//新建一个editor对象,通过SharedPreferences对象的edit()方法。
SharedPreferences.Editor editor = sharedPref.edit();

//调用editor的putInt()方法,参数就是键值对。
editor.putInt(getString(R.string.saved_high_score), newHighScore);

//调用editor的commit()方法,保存。
editor.commit(); 

 

Read from Shared Preferences

调用SharedPreferences对象的getInt()方法和getString()方法,读取SharedPreferences文件。参数可以写default,也可以写key。返回值都是value。

//创建一个SharedPreferences对象

SharedPreferences sharedPref = getActivity().getPreferences(Context.MODE_PRIVATE);

//参数我default情况
long default = getResources().getInteger(R.string.saved_high_score_default));

//参数为(key,default)情况
long highScore = sharedPref.getInt(getString(R.string.saved_high_score), default); 

 

2.保存文件(saving files)

android系统里对文件进行读写,主要使用了File APIs。

File对象主要用来读写大的数据量。比如图像文件、通过网络交换的文件。

以下内容基于读者已经了解Linux文件系统以及java.io里的标准文件输入输出API。

 

Choose Internal or External Storage

android设备都有两个文件存储区域:1.内部的(internal) 2.外部的(external)。 这种叫法的历史渊源是:早起android设备都有一个内置存储器,还有一个外置存储器(internal storage),外置存储器一般都是microSD卡(external storage)。

 内部存储Internal storage的特点: 

1.一直可用

2.是默认程序的默认文件访问位置,其他app访问不了。

3.卸载app时,系统会删除这个app所有文件

Internal storage 可以确保用户和其他app无法访问你的文件。很好的私密性。

 

外部存储External storage的特点:

1.并非一直可用。可以插在卡槽,但是卸载掉,这时候系统就看不到了,或者用户把sd卡当U盘使。

2.文件存储不安全。sd卡拿出来,里面的信息就泄露了。

3.卸载app时,只有你在路径getExternalFilesDir()保存文件的时候,系统才可以删除这个app的文件。

  如果不在这个路径的话,那SD卡上就会有残留文件了。

如果文件没有访问限制,或者你想让文件为其他app所用,或者希望通过电脑查看,外部存储不失为一种好方法。

 

TIPS:可以设置manifest文件里的android:installLocation属性来让app装在外部存储。

 

Obtain Permissions for External Storage

要向外部存储写文件,必须在manifest文件里声明WRITE_EXTERNAL_STORAGE权限。 

<manifest ...>
    <uses-permission

           //写入外部存储文件的权限

           android:name="android.permission.WRITE_EXTERNAL_STORAGE"

    />
    ...
</manifest>

 

注意:目前,所有的app都可以读取外部存储上的文件,不需要特殊的权限。但是以后这个肯定要改的,读取外部存储的文件也是需要权限的。所以也要在manifest文件里声明READ_EXTERNAL_STORAGE权限。

<manifest ...>
    <uses-permission

           //读取外部存储文件的权限

           android:name="android.permission.READ_EXTERNAL_STORAGE"

    />
    ...
</manifest>

 

往内部存储写文件是不需要权限的,从内部存储读文件也是不需要权限的。

所以内外存储文件还有权限上的区别:外部的要权限,内部的不需要。

 

Save a File on Internal Storage

保存一个文件到内部存储的时候,需要获得一个存储路径,方法有二:

1.getFilesDir()--------返回文件File路径

2.getCacheDir()--------返回一个缓存文件路径

临时缓存文件里存放着temporary cache files,确保不用这些文件的时候删除,并有size上的控制,系统在低存储空间的时候,会直接删除这个临时文件夹内容。

 

在存储路径上创建文件时候,使用File()构造器,参数为路径,和文件名称。

//使用File()构造器,参数1:文件路径  参数2:文件名称

File file = new File(context.getFilesDir(), filename);

 

或者,可以调用openFileOutput()方法来获取FileOutputStream,来向内部存储路径写文件。

String filename = "myfile";
String string = "Hello world!";
FileOutputStream outputStream;

try {

 //调用方法创建流,参数1:文件名参数2:文件类型为私有
putStream = openFileOutput(filename, Context.MODE_PRIVATE);

 //调用流的write方法
outputStream.write(string.getBytes());
//关闭流

outputStream.close();
}

catch (Exception e) {
  e.printStackTrace();
}

 

如果要创建一个缓存文件,就要用createTempFile()方法。

以下代码从url提取文件name,然后用那个name在缓存文件夹创建一个文件。

public File getTempFile(Context context, String url) {
    File file;
    try {
        String fileName = Uri.parse(url).getLastPathSegment();
        file = File.createTempFile(fileName, null, context.getCacheDir());
    catch (IOException e) {
        // Error while creating file
    }
    return file;
}

 

注意:app的内部存储路径是由你的app的包的名称指定的,在android文件系统的特定位置。技术上看,如果你把文件mode设置为可读,其他app是可以读取你的app的数据的。但是其他app要想读取,必须要知道你的包的名称和文件的名称。除非你明确设定file mode为readable或者writable,否则其他app肯定没法读和写你的internal file。 所以,只要你把文件mode设置为Context.MODE_PRIVATE,其他app是无论如何也没法访问到这个文件的。

 

Save a File on External Storage

 因为外部存储有可能不可用,所以用之前要查询一下外部存储的状态,使用之前肯定要查一下外部存储的可用空间。调用getExternalStorageState()方法,如果返回的状态是MEDIA_MOUNTED的话,外部存储就是可用的,包括读和写。

//检查外部存储是否可以读和写

public boolean isExternalStorageWritable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state)) {
        return true;
    }
    return false;
}

//查看外部存储是否至少可读

public boolean isExternalStorageReadable() {
    String state = Environment.getExternalStorageState();
    if (Environment.MEDIA_MOUNTED.equals(state) ||
        Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
        return true;
    }
    return false;
}

 

有两个文件夹来存放文件:

1.公共文件夹(Public files)

  其他用户和app都可以访问,app卸载以后,文件仍然存在,其他app和用户可以继续使用。

  比如:app拍的照片

2.私有文件夹(Private files)

  文件私有,其他用户和app无权访问。app卸载以后,文件即被删除。尽管文件存储在外部存储上,其他用户

  和app是可以访问的,但是实际上,这些文件是不会向app以外的其他用户提供数据的。、

  比如:app下载的内容和临时文件

 

存储public文件到外部存储:调用getExternalStoragePublicDirectory()方法。这个方法有个参数,来指定你要存储文件的类型,这样,其他公共文件夹就可以将其包括进去。比如DIRECTORY_MUSIC和 DIRECTORY_PICTURES。

public File getAlbumStorageDir(String albumName) {
    // 获取用户公共图片文件夹路径

    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

 

存储public文件到外部存储:调用getExternalStoragePublicDirectory()方法。这个方法有个参数,来指定你要存储文件的类型,这样,其他公共文件夹就可以将其包括进去。比如DIRECTORY_MUSIC和 DIRECTORY_PICTURES。

public File getAlbumStorageDir(String albumName) {
// 获取用户公共图片文件夹路径 

// 参数1:Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)

// 参数2:albumName

    File file = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_PICTURES), albumName);
    if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

 

存储private文件到外部存储:调用getExternalFilesDir()方法,也要传入一个指定文件类型的参数。以下是创建一个私人相册的文件的代码:

public File getAlbumStorageDir(Context context, String albumName) {
// 获取应用的私人相册文件路径

//参数1:context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)

//参数2:albumName

File file = new File(context.getExternalFilesDir(Environment.DIRECTORY_PICTURES), albumName);
   

if (!file.mkdirs()) {
        Log.e(LOG_TAG, "Directory not created");
    }
    return file;
}

也可以调用 getExternalFilesDir()方法,传如null参数,这样就会返回外部存储的根目录。

记住:

1. getExternalFilesDir()方法创建的文件路径,会在用户卸载这个app的时候被删除。

2.   如果想让你的文件路径在app被卸载以后仍然存在,那么getExternalStoragePublicDirectory()  是一个更好的选择。

3.无论是建立可以共享的文件路径,还是私有文件路径,使用API 常量来指定文件目录类型是很重要的。比如,DIRECTORY_PICTURES,这可以确保系统正确的识别目录里的数据。

 

Query Free Space

如果你事先知道要存储的数据量的大小,你就可以先查询一下可用空间有多少, 这样就可以避免IOException,可用的方法是:getFreeSpace()和getTotalSpace()。

getFreeSpace()---返回剩余空间

getTotalSpace()---返回总空间。

 

但是返回的可用空间大小,并不说明,你就可以存入这么多的数据。如果空间比你要存入的数据大几M,或者空间被占用率小于90%,都还可以,反之,就未必能存入。

 

并不需要每次存文件之前都来这么个判断,加个异常捕捉就OK。而且,你也未必知道你要存入的数据有多大,你想判断,也没法判断的,所以能catch这个IOException就行。

 

Delete a File

删除文件最简单的方法是调用文件本身的方法:delete()

                                myFile.delete();

如果文件存储在内部存储介质上,可以利用上下文的方法:deleteFile()

                       myContext.deleteFile(fileName);

 

注意:当用户卸载app时候,系统会删除以下文件:

     1.所有存储在内部介质上的数据

     2.使用getExternalFilesDir()创建路径的外部介质上的数据

但是,所有缓存文件夹是需要手动删除的

 

 

     3.Saving Data in SQL Databases

重复的和结构化的数据最好存储到数据库,比如联系人信息。

本节主要讲SQLite数据库,包android.database.sqlite包含了使用SQLite数据库的所有API

 

Define a Schema and Contract

SQL数据库主要概念之一就是Schema----一个关于如何组织数据库的定义

Schema反映在你用SQL语句定义数据库的时候。

合同类(抽象类)是一个容器,里面装着定义URI、表、列的常量。

合同类允许在同一个包内不同的类里使用相同的变量。

组织合同类的最好的方法是:把这些用来定义的常量在最上层父类里设置为整个数据库的全局常量。然后把每一个表创建为一个内部类,用这个内部类“包裹”出每一个表,列举出每个表所有的列。

实现BaseColumns接口以后,内部类就可以继承一个主键字段_ID,这也是很多android里的类所需要的,比如游标适配器(cursor adaptor)。这虽然不是必须,但是可以使数据库和android的框架协调工作。

BaseColumns接口有两个常量:1.总行数_count   2.每行的独特的_ID

以下是一个为单表定义表名和列名的代码片段:

//定义一个合同类的子类,实现BaseColumns接口,父类是FeedReaderContract

//这个inner类就代表了数据库很多表的其中之一

public static abstract class FeedEntry implements BaseColumns {

//定义表名
    public static final String TABLE_NAME = "entry";

//定义列名
    public static final String COLUMN_NAME_ENTRY_ID = "entryid";
    public static final String COLUMN_NAME_TITLE = "title";
    public static final String COLUMN_NAME_SUBTITLE = "subtitle";
    ...
}

其中,合同类会定义出所有的表名和所有的列名,分属不同的表,然后用内部类来实现区分。

 

为了避免有人实例化合同类FeedReaderContract(抽象类),定义了一个空构造器:

private FeedReaderContract() {}

 

Create a Database Using a SQL Helper

定义好数据库大体的样子以后,你需要实现创建和维护数据库和表的方法,以下是创建表和删除表的典型的语句:

private static final String TEXT_TYPE = " TEXT";
private static final String COMMA_SEP = ",";


private static final String SQL_CREATE_ENTRIES =
    "CREATE TABLE " + FeedReaderContract.FeedEntry.TABLE_NAME + " (" +
    FeedReaderContract.FeedEntry._ID + " INTEGER PRIMARY KEY," +
    FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + TEXT_TYPE + COMMA_SEP +
    FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE + TEXT_TYPE + COMMA_SEP +
    ... // Any other options for the CREATE command
    " )";

private static final String SQL_DELETE_ENTRIES =
    "DROP TABLE IF EXISTS " + TABLE_NAME_ENTRIES;

 

android系统里,数据库存储在private空间里,很安全,其他app无法访问。

 

SQLiteOpenHelper类里有很多有用的API,当你使用这个类获取数据库的引用的时候,只有当你需要的时候,系统才有可能进行耗时较长的操作,比如创建和更新数据库。你只要调用getWritableDatabase() 和 getReadableDatabase()方法就可以。

 

注意,因为有些操作可能会long-running,所以务必在后台进程调用getWritableDatabase() 和 getReadableDatabase()方法。

 

要使用SQLiteOpenHelper类(抽象类),就需要创建一个子类,并且覆写 onCreate(), onUpgrade() 和 onOpen()这几个回调方法,同时,也最好实现 onDowngrade()方法。

 

以下是使用以上方法的代码示例:

//创建SQLiteOpenHelper的子类

public class FeedReaderDbHelper extends SQLiteOpenHelper {

// 如果要改变数据库模式,则要升级数据库版本.
    public static final int DATABASE_VERSION = 1;
    public static final String DATABASE_NAME = "FeedReader.db";

//子类构造器调用父类SQLiteOpenHelper的构造器

    public FeedReaderDbHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

//覆写方法
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE_ENTRIES);
    }
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // This database is only a cache for online data, so its upgrade policy is
        // to simply to discard the data and start over

//删除数据并重新建库
        db.execSQL(SQL_DELETE_ENTRIES);
        onCreate(db);
    }
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        onUpgrade(db, oldVersion, newVersion);
    }
}

 

要使用数据库,就必须要实例化SQLiteOpenHelper类的子类:

FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext());

 

Put Information into a Database

把 ContentValues对象当参数传给insert()方法,就可以把数据存入数据库。代码如下:

//调用SQLiteOpenHelper的子类的对象的方法

                 SQLiteDatabase db = mDbHelper.getWritableDatabase();

//创建值的键值对, 列名是key

//新建ContentValues类的对象values
                        ContentValues values = new ContentValues();

//调用values的put()方法,这个values装了一个row
           values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID, id);
           values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);
           values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_CONTENT, content);

//插入新的一行,返回这行的主键

//insert()方法参数:

参数1:表名table_name

参数2:指定是否可以可空,nullable可插入空数据,null不可插入空数据

参数3:插入的键值对,也就是一个row

long newRowId;
newRowId = db.insert(
         FeedReaderContract.FeedEntry.TABLE_NAME,
         FeedReaderContract.FeedEntry.COLUMN_NAME_NULLABLE,
         values

         );

 

 Read Information from a Database

从数据库读取信息使用 query()方法,查询返回值在一个Cursor对象里。

//获取可读数据库,赋给SQLiteDatabase对象

SQLiteDatabase db = mDbHelper.getReadableDatabase();

// 定义一个projection

String[] projection = {
    FeedReaderContract.FeedEntry._ID,
    FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE,
    FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED,
    ...
    };

//查询结果在cursor里的排序方式

String sortOrder =
    FeedReaderContract.FeedEntry.COLUMN_NAME_UPDATED + " DESC";

//查询结果赋给Cursor对象

Cursor c = db.query(
    FeedReaderContract.FeedEntry.TABLE_NAME,  //表名

    projection,                              // The columns to return
    selection,                                // The columns for the WHERE clause
    selectionArgs,                         // The values for the WHERE clause
    null,                                       // don't group the rows
    null,                                       // don't filter by row groups
    sortOrder                                 // 排序方式

    );

 

查看cursor里一个row的方法:在读数值之前,必须要调用move方法,首先调用 moveToFirst()方法,这样游标就到了第一个位置,取值就用get系方法,比如getString()和getLong() ,但是get系方法要传入index作参数,这个参数往往不知道,可以通过getColumnIndex() 和 getColumnIndexOrThrow()方法获取,故只需要输入列名就可以。

 

//指针置首

cursor.moveToFirst();

//根据给定的列名,取值
long itemId = cursor.getLong(
    cursor.getColumnIndexOrThrow(FeedReaderContract.FeedEntry._ID)
);

 

 

Delete Information from a Database

要是想删除一个表里的行,就要提供筛选标准来确定要删除的行。

数据库API提供了一个机制来创建筛选标准,来防止SQL注入攻击。

// 定义查询的WHERE部分
String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";

// Specify arguments in placeholder order.
String[] selelectionArgs = { String.valueOf(rowId) };
// 组装SQL语句

//delete()方法中

参数1:表名

参数2:WHERE语句

参数3:要查的字段
db.delete(table_name, selection, selectionArgs);

 

Update a Database 

要修改数据库值的子集时,使用 update()方法。 

语法其实是把insert()里的values和delete()里的where子句结合了起来。

SQLiteDatabase db = mDbHelper.getReadableDatabase();

//列的新值

ContentValues values = new ContentValues();
values.put(FeedReaderContract.FeedEntry.COLUMN_NAME_TITLE, title);

//根据ID,确定需要update的列

String selection = FeedReaderContract.FeedEntry.COLUMN_NAME_ENTRY_ID + " LIKE ?";
String[] selelectionArgs = { String.valueOf(rowId) };

//执行update

int count = db.update(
    FeedReaderDbHelper.FeedEntry.TABLE_NAME,
    values,
    selection,
    selectionArgs); 

转载自http://blog.sina.com.cn/luckybevin 

Godot游戏开发数据持久化实战:从文件系统到存档管理 数据持久化是游戏开发中的基础技术,指将程序运行时的状态数据存储到非易失性介质中,确保应用关闭后数据不丢失。其核心原理是通过序列化将内存中的复杂数据结构转换为可存储的字节流或文本格式,再通过文件I/O操作写入磁盘。这项技术对提升用户体验和产品完整性至关重要,广泛应用于游戏存档、用户设置、应用配置等场景。在Godot引擎中,开发者常面临路径选择、序列化方案和错误处理等挑战。本文聚焦Godot 4.x,深入解析如何利用JSON序列化和ConfigFile等工具,构建健壮的存档系统,并分享user://路径的正确使 阅读详情

相关推荐

香橙派5 plus 和 max 装机后的一些技巧,rk3588

5) 然后就能看到 HDMI In 的输入画面了(下图中 HDMI In 显示的是 opi5 开发板 HDMI 输出的画面,此时正在播放一个视频)。脚本默认会将 HDMI In 输入的音频播放到开发板的 HDMI_TX1 、HDMI_TX2 和 ES8388(代表喇叭或者 耳机)中去。2) 首先使用下图所示的 HDMI 转 HDMI 线将其它设备的 HDMI 输出接到开发板的 HDMI In 接口上。l. 然后使用键盘的方向键定位到下图所示的位置,再使用。c. 设置绿灯常亮的命令如下。

babytiger的专栏 4919

android 存储数据的3种方式

android开发中存储数据的三种方式    一、 使用文件存储数据 使用文件存储数据是把数据保存成一个文件存在手机或者android 设备当中,需要的时候程序打开文件读取里面的资料。 写入代码如下: public void saveData(String data){ FileOutPutStream out = null; BufferedWtriter writer = null

muMUXiong_的博客 654

LE5010低功耗蓝牙开发实战:从环境搭建到量产避坑指南

低功耗蓝牙(BLE)技术作为物联网设备实现无线连接的核心方案,其关键在于在有限的功耗预算内实现稳定通信。其工作原理基于事件驱动的协议栈,通过广播、连接参数协商及GATT服务交互完成数据交换。这项技术的核心价值在于为电池供电的嵌入式设备提供了长续航的无线连接能力,广泛应用于智能穿戴、传感器网络、远程控制等场景。本文聚焦于凌思微LE5010芯片,深入剖析其ARM Cortex-M0+内核在低功耗设计上的优势,并结合实际项目经验,详细解读如何通过优化GPIO配置、合理管理连接间隔与MTU,以及采用事件驱动架构来有

congjukun0600的博客 391

SaveData-android储存数据

SaveData-android储存数据

小虎的博客 1179

Ubuntu下用Certbot为Nginx配置Let’s Encrypt免费SSL证书

HTTPS已成为现代Web服务的强制性基础设施,其核心是SSL/TLS协议实现的加密通信与身份认证。原理上,它依赖公钥基础设施(PKI)和可信证书颁发机构(CA)签发的数字证书来建立安全信道。技术价值在于保障数据机密性、完整性及服务器身份真实性,规避混合内容拦截、API调用失败、浏览器降权等生产级风险。典型应用场景包括Nginx反向代理、云服务器(如阿里云/腾讯云Ubuntu实例)、Vue/React前端项目及SaaS系统交付。本文聚焦于Let’s Encrypt这一全球主流免费CA,结合Certbot自动

weixin_30613727的博客 530

【信息科学与工程学】【数据中心】第三十五篇 云计算数据中心的学科知识04

编号学科(课程)核心知识点在云计算/云存储/云网络/云安全/云MaaS中的作用代表教材/资料/论文 + 数学方程式列表工业界应用D1421​云原生数据库:TiDB​HTAP(混合事务/分析处理)、分布式SQL、水平扩展、强一致(Raft)、自动故障恢复、与MySQL兼容、TiFlash列式引擎提供弹性扩展的分布式数据库;支撑高并发在线交易与实时分析教材:《TiDB in Action》PingCAP(2020) 论文:《TiDB: A Raft-based HTAP Database》(2017)

weixin_49199313的博客 399

Android获取应用程序路径getCacheDir()和getFilesDir()

Activity提供了getCacheDir()和getFilesDir()两个方法: getCacheDir()方法用于获取/data/data/cache目录 getFilesDir()方法用于获取/data/data/files目录   如果想将文件写入SD卡内: 获取SDCard的状态: Environment.getExtemalStorageState() Env

WangMark的专栏 3185

手动测试台架搭建,让你的车载测试更轻松

如果你也在经常进行车载测试,就知道有时候手头的设备并不能完全满足需求。 在我之前的测试中,发现了一些问题,但是却找不到根本原因。于是,我开始思考如何打造一个更为完美的手动测试台架,让测试变得更加轻松。 经过不断地实践和调试,我终于成功搭建出了一个能够满足自己需求的测试台架。现在,我想和大家分享这个搭建的经验,希望能够帮助到在车载测试领域工作的小伙伴们,让你们的测试更加准确、高效!

weixin_67553250的博客 1万+

Android关于保存数据(Saving data)

1.SharedPreferences(key-value)   SharedPreferences保存的数据主要是类似于配置信息格式的数据,因此保存的数据主要是简单类型的键值对(key-value),它保存的是一个XML文件。   (1)创建sharedPreferences的两种方法:getSharedPreferences(String name, int mode)----如果你需要通...

weixin_30337251的博客 174

silvaco(TCAD)击穿特性仿真,的不收敛问题

在silvaco仿真学习过程中,最近这段时间一直有一个很大的问题困扰着我,那就是我在仿真击穿特型曲线的过程中,在电压加到后期比较大的情况下,往往会出现仿真不收敛的情况,正常情况下器件的击穿应该在一个外加偏压上,电流出现迅速的上升,这才是击穿的定义,但是我仿真的过程中程序跑到一个较大的电压时,器件出现击穿,我观察电压电流曲线,在击穿电压(程序报告的)电流并没有出现迅速的变化,类似于下图这种形式。(2)解决方法,将初始电压调整到合适的值,这个值可能是更大的也可能是更小的,要注意多多尝试。

zm19992533002908的博客 4944

grpc安装与简单demo测试

grpc安装: 由于工作需要,最近在学google的rpc开源框架;由于本人菜鸟一枚,而且是从Windows开发转到Linux;由于之前版本控制使用的是svn,没有使用过git,所以从GitHub上下载代码是通过浏览器下载zip格式的压缩文件,折腾了好几天了,发现了各种坑,各种缺少库,各种不知道怎么把这个库给装机器上。对于一个不熟悉Linux的人来说,这个体验简直糟糕透了。没办法了又在机器上装了...

old__captain的博客 4833

Saving Data --- Saving Key-Value Sets(一)

Most Android apps need to save data, even if only to save information about the app state during onPause() so the user’s progress is not lost. Most non-trivial apps also need to save user settings, and

wblyuyang的专栏 831

2006NOIP普及组真题 4. 数列

从上述推理中,我们发现要输出的幂次和中的第 N 项(也就是幂次和中的序号N),对应的二进制位数和幂次和对应的二进制位数相同。当 k=3 时,k的幂次为1, 3, 9, 27, 81…,即第0位,第2位均为1,转成 k 进制(k=3)时。,即第2位,第3位均为1,转成 k 进制(k=3)时。转成 k 进制(k=3)时的幂次和为。,即第0位,第1位,第2位均为1,(即第0位,第1位,第2位均为1)N=12时,12的二进制是。(即第0位,第2位均为1)(即第2位,第3位均为1)结论:这道题就变成了。

weixin_40252159的博客 1514

Saving Data

很多Android app都需要存储数据,即使是在onPause()函数中保存app state.在Android 中存数据主要分为下面三类: 1:以key-value 配对的方式存储数据的preferences file 2:以文件形式直接存储. 3: 以SQlite方式存储. 我们先来看第一种: 在sharePreferences中数据是以key-value 配对的形式存储的

jason的笔记 522

HFSS仿真中如何提取最大E/H场强与频率关系曲线

本文详细讲解了在HFSS仿真中提取最大E/H场强与频率关系曲线的完整流程。通过使用场计算器(Field Calculator)和创建场报告,工程师可以准确获得器件在整个频带内的电场或磁场强度峰值,并进一步推导出关键的功率容量曲线,为高功率射频器件(如滤波器、耦合器)的设计提供至关重要的安全评估依据。

ss78901的博客 1098

基于 Simulink 的 PFC + LLC 级联系统联合仿真与优化实战教程

摘要:本教程详细介绍了2kW级AC-DC变换器建模方法,采用"BoostPFC+LLC谐振变换器"组合架构。重点解析了双级系统的核心控制策略:前级BoostPFC实现功率因数校正(PF>0.95),后级LLC通过变频调制(PFM)实现高效能量传输。教程包含四大关键步骤:PFC建模与控制、LLC参数计算与实现、母线电容优化设计以及系统联合调试,特别针对级联系统常见的母线电容震荡问题提供了解决方案。同时给出了仿真波形分析指南和避坑建议,包括代数环处理、仿真步长设置等实用技巧,为新能源车

MHD0815的博客 118

【愚公系列】《移动端AI应用开发》019-Android端应用开发(数据存储与本地缓存)

摘要 本文聚焦于Android应用开发中的数据存储与本地缓存技术,重点探讨了Room数据库与SQLite的对比及应用场景,并介绍了文件存储与SharedPreferences的轻量级数据持久化方案。 核心内容 Room与SQLite数据库 Room作为SQLite的抽象层,通过注解和DAO简化数据库操作,提供类型安全与编译时检查,适合大多数应用场景。 SQLite更底层,适合需要精细控制SQL或高性能需求的场景,但需手动处理事务与SQL语句。 示例展示了Room的实体类定义、DAO接口及数据库操作流程。 文

愚公智库 2万+

封装POD与PinMap文件总结学习-20250516

芯片封装外形图(POD)和PinMap是芯片设计和制造中的关键文档。POD详细描述了芯片的物理尺寸、引脚布局和封装类型,而PinMap则提供了芯片封装上各个引脚的位置、名称和功能的详细信息。这些文档在芯片设计、制造和应用过程中起到“地图”的作用,帮助设计人员理解芯片的物理和电气特性。在PCB设计中,POD用于确定socket的footprint和pogo的选型,而PinMap则用于器件的合理布局和布线。这些信息对于确保芯片的正确安装和功能至关重要。

歌颂苦难的行为是无耻的; 2365

智能红绿灯预警App开发:从数据融合到车路协同的实战指南

实时交通数据处理与智能算法是智慧交通系统的核心技术基础。其原理在于通过多源数据融合(如GPS轨迹、传感器信息与云端交通流数据),构建动态预测模型,从而实现对路口通行状态的精准感知与预判。这项技术的核心价值在于提升单个车辆的通行效率与驾驶体验,并通过速度引导优化整体车流,减少拥堵与能耗。典型的应用场景包括通勤导航、物流车队管理及网约车服务,旨在实现从被动“看灯”到主动“算灯”的范式转变。本文以开发一款智能红绿灯预警App为例,深入探讨了如何整合高德地图等众包数据与V2X车联网信息,设计核心预警算法,并解决定位

weixin_34326558的博客 350

高质量文献综述原来这么写!借助Gemini 3 Pro+四步法,手把手教你写出有逻辑深度的综述

总结与展望先总结现状,说明前人已经做到什么地步了,再指出缺口,前人没解决什么问题、方法有什么局限等等,最后再在这个缺口的基础上,提出你的未来研究方向。当你有个复杂的研究题目,但没办法拆解出关键词,就可以让Gemini 3 Pro来辅助帮你拆解,并帮你写专业的检索式,若是找英文文献,还能帮你将中文关键词翻译成地道的专业术语。做文献笔记,可以建立文献笔记矩阵,可以用Excel建个表格,横向是文献,纵向是关键要素,包括作者/年份、研究问题/假设、理论框架/模型、研究方法/数据来源、核心结论、局限性/Gap。

智能AI让学术科研更容易 991

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

SaveGameFree 安全防作弊实战:保护存档不被篡改的完整方案

SaveGameFree 是一款免费、简单但功能强大的 Unity 存档系统,它内置了存档加密与防篡改能力,可以帮助开发者保护游戏存档不被玩家随意修改。本文将以 Unity 存档防作弊为主题,为你完整演示如何用 SaveGameFree 开启加密、设置安全密码、自定义加密器,并给出从入门到进阶的完整存档安全方案,让单机游戏也能有效抵御修改器与存档篡改。 [![SaveGameFree 存档加密防

gitblog_00756的博客 285

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

NXP Layerscape平台内核崩溃转储(kdump)配置与实战分析指南

在嵌入式Linux系统开发中,内核崩溃是开发者面临的棘手挑战,尤其是在无法复现现场时,传统的调试手段往往失效。内核崩溃转储(kdump)技术应运而生,它基于kexec快速重启机制,能在系统崩溃瞬间引导一个轻量级捕获内核,将主内核的完整内存状态保存为vmcore文件。这项技术的核心价值在于为事后分析提供了精确的“事故现场”快照,是定位空指针解引用、内存损坏等深层次问题的关键。在ARMv8架构的嵌入式平台,特别是集成了DPAA/DPAA2等复杂硬件加速引擎的NXP Layerscape系列处理器上,配置稳定可靠

a631278993的博客 561

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

3个步骤彻底解放Windows:AtlasOS如何让你的电脑重获新生?

你是否厌倦了Windows系统越来越慢、后台程序偷偷占用资源、隐私设置复杂难懂的现状?AtlasOS正是为这些问题量身定制的开源Windows优化方案,它通过系统级调整让Windows 11运行更流畅、更私密、更易用。这个项目采用透明化的配置方式,让普通用户也能轻松实现专业级的系统优化效果。 ## 🎯 你的Windows到底被什么拖慢了? ### 系统资源的"隐形消耗者" 想象一下,你的电

gitblog_00870的博客 412

低功耗广域网LPWAN技术全解析:从原理、选型到项目落地

物联网终端往往部署在偏远、无电网覆盖的现场,传感器节点需要以极低功耗完成远距离数据回传,传统Wi-Fi、蓝牙和蜂窝网络难以兼顾覆盖、成本与续航。低功耗广域网(LPWAN)正是为解决这一矛盾而生,通过极窄带宽、扩频调制与高链路预算设计,以牺牲速率为代价换取数公里级覆盖和数年电池寿命。LoRa、NB-IoT等主流LPWAN技术各具特色:LoRa支持自建私有网络,灵活可控;NB-IoT依托运营商基站,广域免维护。在智慧农业、智能表计、工业物联网等场景中,合理选择无线通信技术直接决定项目的可用性与长期成本。本文从L

weixin_30235225的博客 422
上一篇: 通用Adapter,完成对LisetView的优化抽取
下一篇: Dialog
lizz821
博客等级 码龄12年 11粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值