Android -- (12) ,数据的存储

解决Android应用发布模式下的加密存储问题 摘要:本文针对Android应用在发布模式下的加密存储崩溃问题,特别是NoSuchFieldError异常,提供了详细的解决方案。关键步骤包括创建ProGuard配置文件、添加保留规则确保加密类不被混淆、配置选择性备份及调整Android 12+的数据提取规则。通过正确配置ProGuard和适配不同Android版本特性,能有效解决加密存储功能在发布模式下的兼容性问题,保障应用稳定运行和用户数据安全。(150字) 阅读详情

2013/1/31


一,数据的存储

数据的存储是很重要的,由于某些数据要在未来reuse。所以有必要将数据保存起来。
安卓里面有三种基本保存数据的办法:
(1)shared preferences机制,用来保存一小撮数据。
(2)传统的File System
(3)通过sqlite的支持用DB来存储数据

1.1,存储和载入用户的配置

一般来说,以上三种方法都可以用来存储用户配置,不过后两者明显有点大题小做了。我们可以简单的用SharedPreferences对象来存储一对对的name/value的值,这些值会自动的存到一个xml文件中去。

1.1.1 存储用户配置

首先我们要创建一个xml文件,里面是你想要存储的配置的类型等等等:
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory android:title="Category 1">
        <CheckBoxPreference
            android:title="CheckBox"
            android:defaultValue="false"
            android:summary="True Or False"
            android:key="checkboxPref"
            />
    </PreferenceCategory>
    <PreferenceCategory android:title="Category 2">
        <EditTextPreference
            android:summary="ENter a text"
            android:defaultValue="[enter a string here]"
            android:title="Edit text"
            android:key="editTextPref"
            />
       	<RingtonePreference
       	    android:summary="select a ringtone"
       	    android:title="RingTones"
       	    android:key="ringtonepref"
       	    />
       	<PreferenceScreen
       	    android:title="Second Prefercence Screen"
       	    android:summary="Click here to go to second PreScreen"
       	    android:key="Second Pref Sceen"
       	    >
       	<EditTextPreference
       	    android:summary="enter a string"
       	    android:title="Edit text sp"
       	    android:key="secondEditTextPref"
       	    />
       	</PreferenceScreen>
    </PreferenceCategory>
</PreferenceScreen>

通过上面的xml文件,可以看出:
(1)有两个preference categories来为配置分组
(2)两个checkbox--checkboxpref和secondEdittextpref
(3)一个选择ringtone的选项,ringtonepref
(4)另外一个preferenceScreen包含另外一个配置eidtTExt

注意上面每个控件都有一个key的属性,这个属性是用来保存或取出对应的配置信息的关键字。会自动的存储用户选择的配置。

为了让os去展示出配置的页面,我们新建了一个活动,继承于PreferenceActivity:

public class AppPreferenceActivity extends PreferenceActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//---load the preferences from an XML file---
addPreferencesFromResource(R.xml.myapppreferences);
}
}

最后用之前讲过的intent方法来开始新活动:

Intent i = new Intent(“net.learn2develop.AppPreferenceActivity”);
startActivity(i);

注意也要在androidmainfest文件中加入这个活动的相关信息(之前的文章有讲)。这样就可以打开配置页面了。


OK,那么当你打开并且更改了一个配置之后,修改后的信息会自动保存在/data/data/net.learn2develop.UsingPreferences/shared_prefs里面


1.1.2取出用户的配置


--存储了之后读取用户的配置。

假设我们在主活动里面添加几个按钮来进入配置页面和保存读取存储的信息:

 <Button
        android:id="@+id/btnPreferences"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:onClick="onClickLoad"
        android:text="Load Preferences Screen" />

<Button
android:id="@+id/btnDisplayValues"
android:text="Display Preferences Values"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="onClickDisplay"/>
<EditText
android:id="@+id/txtString"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<Button
android:id="@+id/btnModifyValues"
android:text="Modify Preferences Values"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="onClickModify"/>

第一个button用来载入配置页面,第二个button用来展示存储的信息,然后是一个edittext,用来输入信息,最后button是用来存储信息,给第二个button展示用。

这几个button对应的方法如下:

 public void onClickLoad(View view) {
    	Intent i = new Intent("net.learn2develop.AppPreferenceActivity");
    	startActivity(i);
    	}
    public void onClickDisplay(View view) {
    	SharedPreferences appPrefs =
    	getSharedPreferences("net.learn2develop.UsingPreferences_preferences",
    	MODE_PRIVATE);
    	DisplayText(appPrefs.getString("editTextPref", ""));
    	}
    public void onClickModify(View view) {
    	SharedPreferences appPrefs =
    	getSharedPreferences("net.learn2develop.UsingPreferences_preferences",
    	MODE_PRIVATE);
    	SharedPreferences.Editor prefsEditor = appPrefs.edit();
    	prefsEditor.putString("editTextPref",
    	((EditText) findViewById(R.id.txtString)).getText().toString());
    	prefsEditor.commit();
    	}
    private void DisplayText(String str) {
    	Toast.makeText(getBaseContext(), str, Toast.LENGTH_LONG).show();
    	}

这几个方法分别用来载入配置页面(上一个小节中设置的配置页面),展示存储的配置信息,确定保存输入的配置信息。

在onClcikDIsplay方法中,我们用getSharedPreferences方法来获取之前存储的配置(这里是“net.learn2develop.UsingPreferences_preferences,”,用<PackageName>_preferences来指定),这里的MODE_private指定这个配置文件只能由创建他的活动打开。然后在用SharedPreferences对象的getString方法,传入想要获得的对象的key,再获得对应的对象。

在onCLickModify方法中,首先还是要获得一个SharedPreferences对象,然后获得它的SharedPreferences.Editor对象,用这个对象来存储对应的数据,该类的putString方法用的就是一对name/value的方法来存储值。

1.1.3自定义存储文件名字


--在上一个例子中,存储配置的文件是net.learn2develop.UsingPreferences_preferences,有点长而且不好记住。我们可以在配置选择的活动中(也就是上面的AppPreferenceActivity类中)的onCreate方法中添加如下代码:

	PreferenceManager prefMgr = getPreferenceManager();
    prefMgr.setSharedPreferencesName("YourName");

这样就把存储的xml文件改名为了YourName了。

在读取的时候只要把YourName当做参数提供到相应的方法中即可,如下:
public void onClickDisplay(View view) {
    	SharedPreferences appPrefs =
    	getSharedPreferences("YourName",
    	MODE_PRIVATE);
    	DisplayText(appPrefs.getString("editTextPref", ""));
    	}
    public void onClickModify(View view) {
    	SharedPreferences appPrefs =
    	getSharedPreferences("YourName",
    	MODE_PRIVATE);
    	SharedPreferences.Editor prefsEditor = appPrefs.edit();
    	prefsEditor.putString("editTextPref",
    	((EditText) findViewById(R.id.txtString)).getText().toString());
    	prefsEditor.commit();
    	}

就可以往自己定义的配置xml文件中读取配置了。


1.2 将数据存储到文件上面


--在上一种用SharedPreferences 对象来存储,我们可以用name/value这种数据对的形式来存储数据,下面介绍传统的文件系统存储。

1.2.1存到内部储存器中(internal Storage)

要把内容写入文件中,要用FileOutputStream类,这个类打开一个新文件,将输入的内容写入其中:
FileOutputStream fOut =
openFileOutput(“textfile.txt”,
MODE_WORLD_READABLE);
这样就把文件写入到了一个叫做textfile.txt的文件中了。这里除了MODE_WORLD_READABLe之外,还可以选择:MODE_PRIVATE(只能有创建文件的应用访问),MODE_APPEND(把内容追加到已有的文件中去),MODE_WORLD_WRITEABLE(所有的应用都可以访问)。

把文件创建好了之后,就要用OutputStreamWriter类把字符流转换为比特流:

OutputStreamWriter osw = new
OutputStreamWriter(fOut);

将这个OutputStreamWriter类跟一个FileOutputStream绑定起来之后,用他的write(String str)方法将字符串写入文件中去:

//---write the string to the file---
osw.write(str);
osw.flush();
osw.close();

为了确保字符都写入到文件中去了,可以用flush方法。最后用close方法来关闭文件。

对应的,读取文件要用FileInputStream类:

FileInputStream fIn =
openFileInput(“textfile.txt”);
其中openFileInput方法提供的是文件的名字(由于是内部存储空间,不用提供绝对路径)。
再用InputStreamReader跟对应的FileInputStream绑定起来:

InputStreamReader isr = new
InputStreamReader(fIn);

因为并不知道输入的字符串的长度,定义最大长度为100.可以用循环来读取字符串内容:

char[] inputBuffer = new char[READ_BLOCK_SIZE];
String s = “”;
int charRead;
while ((charRead = isr.read(inputBuffer))>0)
{
//---convert the chars to a String---
String readString =
String.copyValueOf(inputBuffer, 0,
charRead);
s += readString;
inputBuffer = new char[READ_BLOCK_SIZE];
}

其中read()方法读取一个字符,如果到末尾了就返回-1,
当存储了文件之后,可以到ddms去看看文件的内容。绝对路径是/data/data/net.learn2develop.Files/files/textFile.txt


1.2.2存储到sd卡上

--外部存储,大多数就是SDcard啦。

File sdCard = Environment.getExternalStorageDirectory();
File directory = new File (sdCard.getAbsolutePath() +
“/MyFiles”);
directory.mkdirs();
File file = new File(directory, “textfile.txt”);
FileOutputStream fOut = new FileOutputStream(file);
OutputStreamWriter osw = new
OutputStreamWriter(fOut);
//---write the string to the file---
osw.write(str);
osw.flush();
osw.close();

这里用import android.os.Environment;的Environment类中的getExternalStorageDirectory方法来获取外部存储的路径,这里是/mnt/sdcard,然后再在这个目录下面创建目录(MyFile)和文件(textFile.txt).然后就是用FileOutputStream类来写入文件。

读取方式也类似:
try
    	{
    		File sdCard=Environment.getExternalStorageDirectory();
    		File directory=new File(sdCard.getAbsolutePath()+"/MyFile");
    		File file=new File(directory,"textFile.txt");
    		FileInputStream fin=new FileInputStream(file);
    		InputStreamReader isr=new InputStreamReader(fin);
    		char[] inputBuffer = new char[READ_BLOCK_SIZE];
    		String s = "";
    		int charRead;
    		while((charRead=isr.read(inputBuffer))>0)
    		{
    			String readString =String.copyValueOf(inputBuffer,0,charRead);
    			s+=readString;
    			inputBuffer = new char[READ_BLOCK_SIZE];
    		}
    		textBox.setText(s);
    		Toast.makeText(getBaseContext(), "FIle load succ", Toast.LENGTH_SHORT).show();
    		
    	}catch (IOException ioe) {
    		ioe.printStackTrace();
    	}

也是用Environment类来获取外部路径的信息。再用InputStreamReader读入文件内容。

注意:要读取外部文件时候要再AndroidManifest中添加:<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE” />

1.2.3使用静态的文件。


--除了可以在程序运行的时候动态的添加文件之外,还可以在设计阶段往Package里面添加文件。 -- 例如添加某些帮助文件。

例如:在Package里面的/res中添加文件夹raw,然后再往下面添加textfile.txt.就可以在程序里面将该文件的内容读取了:(当然要敲对应的代码)
InputStream is = this.getResources().openRawResource(R.raw.textfile);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String str = null;
try {
while ((str = br.readLine()) != null) {
Toast.makeText(getBaseContext(),
str, Toast.LENGTH_SHORT).show();
}
is.close();
br.close();
} catch (IOException e) {
e.printStackTrace();
}

这里用getResources()(Activity类的)返回一个Resources的对象,然后用openRawResource方法打开raw目录下面的文件。


1.3创建和使用数据库

--存储大量相关的数据,还是数据库好。

--Android使用的是SQLite数据库,应用创建的数据库只能由它自己访问,别的应用都不能访问。

1.3.1创建DBAdapter helper类


--贴出代码如下:
package com.example.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class DBAdapter {
	static final String KEY_ROWID="_id";
	static final String KEY_NAME="name";
	static final String KEY_EMAIL="email";
	static final String TAG="DBAdapter";
	static final String DATABASE_NAME = "MyDB";
	static final String DATABASE_TABLE = "contacts";
	static final int DATABASE_VERSION = 1;
	static final String DATABASE_CREATE =
			"create table contacts (_id integer primary key autoincrement, "
			+ "name text not null, email text not null);";
	final Context context;
	DatabaseHelper DBHelper;
	SQLiteDatabase db;
	public DBAdapter(Context ctx)
	{
		this.context=ctx;
		DBHelper=new DatabaseHelper(context);
		
	}
	private static class DatabaseHelper extends SQLiteOpenHelper
	{
		DatabaseHelper(Context context)
		{
			super(context,DATABASE_NAME,null,DATABASE_VERSION);
		}
		public void onCreate(SQLiteDatabase db)
		{
			try
			{
				db.execSQL(DATABASE_CREATE);
				
			}catch(SQLException e)
			{
				e.printStackTrace();
			}
		}
		public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
		{
			Log.w(TAG, "Upgrading database from "+oldVersion+" to "+newVersion+" which will destory all data.");
			db.execSQL("DROP TABLE IF EXISTS contacts");
			onCreate(db);
		}
		
	}
	public DBAdapter open() throws SQLException
	{
		db = DBHelper.getWritableDatabase();
		return this;
	}
	public void close()
	{
		DBHelper.close();
	}
	public long insertContact(String name,String email)
	{
		ContentValues initialValues=new ContentValues();
		initialValues.put(KEY_NAME, name);
		initialValues.put(KEY_EMAIL, email);
		return db.insert(DATABASE_TABLE, null, initialValues);
		
	}
	public boolean deleteContact(long rowId)
	{
		return db.delete(DATABASE_TABLE,KEY_ROWID+"="+rowId,null) >0;
	}
	public Cursor getAllContacts()
	{
		return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,
				KEY_EMAIL}, null, null, null, null, null);
	}
	public Cursor getContact(long rowId) throws SQLException
	{
		Cursor mCursor =
				db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
				KEY_NAME, KEY_EMAIL}, KEY_ROWID + "=" + rowId, null,
				null, null, null, null);
				if (mCursor != null) {
				mCursor.moveToFirst();
				}
				return mCursor;
	}
	public boolean updateContact(long rowId, String name, String email)
	{
	ContentValues args = new ContentValues();
	args.put(KEY_NAME, name);
	args.put(KEY_EMAIL, email);
	return db.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0;
	}
	
}

首先建议数据库表的一系列域(属性),然后再将创建数据库需要的sql语句写出来:

static final String KEY_ROWID = “_id”;
static final String KEY_NAME = “name”;
static final String KEY_EMAIL = “email”;
static final String TAG = “DBAdapter”;
static final String DATABASE_NAME = “MyDB”;
static final String DATABASE_TABLE = “contacts”;
static final int DATABASE_VERSION = 1;
static final String DATABASE_CREATE =
“create table contacts (_id integer primary key autoincrement, “
+ “name text not null, email text not null);”;

在DBAdapter类里面,我们创建了一个私有类--DatabaseHelper extends SQLiteOpenHelper,而SQLiteOpenHelper则是Android里面用于创建和管理数据库的类。我们重写了onCreate方法和onUpgrade方法:

private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db)
{
try {
db.execSQL(DATABASE_CREATE);
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
Log.w(TAG, “Upgrading database from version “ + oldVersion + “ to “
+ newVersion + “, which will destroy all old data”);
db.execSQL(“DROP TABLE IF EXISTS contacts”);
onCreate(db);
}
}
onCreate方法创建一个数据库(如果原来数据库不存在的话),onUpgrade当数据库要被更新的时候被调用。这里只是简单的删除和重新创建数据库。

最后我们创建了一系列的操作数据库的函数:

//---opens the database---
public DBAdapter open() throws SQLException
{
db = DBHelper.getWritableDatabase();
return this;
}
//---closes the database---
public void close()
{
DBHelper.close();
}
//---insert a contact into the database---
public long insertContact(String name, String email)
{
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_EMAIL, email);
return db.insert(DATABASE_TABLE, null, initialValues);
}
//---deletes a particular contact---
public boolean deleteContact(long rowId)
{
return db.delete(DATABASE_TABLE, KEY_ROWID + “=” + rowId, null) > 0;
}
//---retrieves all the contacts---
public Cursor getAllContacts()
{
return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME,
KEY_EMAIL}, null, null, null, null, null);
}
//---retrieves a particular contact---
public Cursor getContact(long rowId) throws SQLException
{
Cursor mCursor =
db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_NAME, KEY_EMAIL}, KEY_ROWID + “=” + rowId, null,
null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
//---updates a contact---
public boolean updateContact(long rowId, String name, String email)
{
ContentValues args = new ContentValues();
args.put(KEY_NAME, name);
args.put(KEY_EMAIL, email);
return db.update(DATABASE_TABLE, args, KEY_ROWID + “=” + rowId, null) > 0;
}

注意上面的cursor变量返回一个游标,可以让用户遍历全部数据库的内容。用ContentValue类可以存储一对name/value变量,它的put方法可以插入一对不同数据类型的name/value的值。

其实DBAdapter就是为了让DBhelper更加方便的给用户使用而构造的一个类。于是我们可以直接用DBAdapter来创建和管理数据库:
public DBAdapter(Context ctx)
{
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}

创建数据库=创建已给DBAdapter的实例。
其他操作同理。

1.3.2 往DB中添加查询数据


--往DB中添加数据操作。

        DBAdapter db = new DBAdapter(this);
	db.open();
        long id=db.insertContact("Wei-Meng Lee", "testData1");
        id=db.insertContact("Marry Jackson", "testData2");
        db.close();

先创建一个db例子,然后用其insertContact方法来往里插入数据即可。


--从DB中读取数据。

        db.open();
        Cursor c=db.getAllContacts();
        if(c.moveToFirst())
        {
        	do{
        		DisplayContact(c);
        		
        	}while(c.moveToNext());
        }
        

    public void DisplayContact(Cursor c)
    {
    	Toast.makeText(this, "id:"+c.getString(0)+"\n"+
						   "Name:"+c.getString(1)+"\n"+
						  "Email:"+c.getString(2)+"\n"
    			, Toast.LENGTH_SHORT).show();
    }

用cursor对象c可以遍历表中的每个triple,然后用moveToNext来去往下一个。在DisplayContact中用cursor的getString方法,传入列号,就能获得对应的数据。

--获取其中一个数据:

	db.open();
        Cursor c = db.getContact(number);
        if (c.moveToFirst())
        DisplayContact(c);
        else
        Toast.makeText(this, "No contact found", Toast.LENGTH_LONG).show();
        db.close();

可以用db的getContact方法来获取其中一行的数据,提供的是行号。然后cursor c里面就有一个对象昂,然后直接调用DisplayContact方法即可。注意行号从0开始。

--余下的还有删除和更新数据,这里只贴出来:

//更新数据
db.open();
if (db.updateContact(1, “Wei-Meng Lee”, “weimenglee@gmail.com”))
Toast.makeText(this, “Update successful.”, Toast.LENGTH_LONG).show();
else
Toast.makeText(this, “Update failed.”, Toast.LENGTH_LONG).show();
db.close();

//删除数据
db.open();
if (db.deleteContact(1))
Toast.makeText(this, “Delete successful.”, Toast.LENGTH_LONG).show();
else
Toast.makeText(this, “Delete failed.”, Toast.LENGTH_LONG).show();
db.close();

1.3.3预先创建数据库

--在实际的应用中,预先建立应用需要的数据库是比较高效的。可以用SQLite Database Browser等工具来帮助设计。
假设我们设计好了一个数据库文件mydb,只需要将其放入项目的assets文件夹中。再用如下代码读取即可:

DBAdapter db = newDBAdapter(this); 
try{
String destPath = “/data/data/”+ getPackageName() +
“/databases”;
File f = newFile(destPath);
if(!f.exists()) { 
f.mkdirs();
f.createNewFile();
//---copy the db from the assets folder into 
// the databases folder---
CopyDB(getBaseContext().getAssets().open(“mydb”),
newFileOutputStream(destPath + “/MyDB”));
}
} catch(FileNotFoundException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
}
用到的copyDb函数如下:
public voidCopyDB(InputStream inputStream, 
OutputStream outputStream) throwsIOException {
//---copy 1K bytes at a time---
byte[] buffer = new byte[1024];
int length;
while((length = inputStream.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
}
注意在这个函数里面直接用InputStream来读取源文件,并用outputStream来输出到指定文件里面。




Android12 解决应用使用Settings异常断电数据丢失的问题 后者通过精确控制需要保护的目录来减少性能损失,但是缺点是要非常清楚哪些目录需要保护,并 且不同文件系统的命令和标志都不一样,需要把你可能用到的所有文件系统的 ioctl 都写一遍,并且有 些文件系统可能不支持这种方式(f2fs 和 ext4 都是两种方式都支持,而 fat 则不支持 ioctl 方式)。在Android12中,Settings写操作在以下路径下,它是采用了AtomicFile的方式,这种方式可以避免断电文件损坏,但是避免不了刚写入的数据丢失。修改在最下面,主要修改底层文件系统。 阅读详情

相关推荐

QT在Android、Linux、windows平台采集声卡原始音频数据编码存储与播放

一、操作系统介绍 Linux系统:ubuntu18.04 64位 Android系统:Android 8.1/9.0 windows系统:win10 QT版本:5.12 声卡: win10电脑自带声卡、罗技USB摄像头声卡、Android手机自带声卡都可以获取声音数据 二、需求介绍 使用QT本身代码在linux平台、Android平台、windows平台实时获取声卡...

2521

Android数据存储的五种方法汇总

Android数据存储的五种方法汇总 本文介绍Android中的5种数据存储方式。 数据存储在开发中是使用最频繁的,在这里主要介绍Android平台中实现数据存储的5种方式,分别是: 1 使用SharedPreferences存储数据 2 文件存储数据 3 SQLite数据库存储数据 4 使用ContentProvider存储数据 5 网络存储数据 第一种: 使用SharedPreferences存储数据 SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置比

qq_45146250的博客 2万+

android外部存储

android外部存储案例,有private files public files

Android——数据文件保存

创建工具类FileSaveLogin,在该类中实现Login账号和密码的存储与读取功能,使用文件存储的方式,保存与读取账号与密码信息。运行程序,输入账号和密码信息,点击“登录”按钮,实现登录功能。通过线性布局和相对布局来搭建一个用户登录界面,界面可自主设计,但至少需要包括以下控件:1个ImageView控件、2个TextView控件;□验证性 □演示性 □设计性 R。下面的蓝色和红色的字是从data.txt中读取的信息。保存成功是将账号和密码成功存入data.txt中了。

qq_64628470的博客 2362

FPGA—ZYNQ_BD Block Design学习(一)

1.第一步:打开Vivado软件,创建或打开一个工程。2.第二步:在Block Design中添加arm核心,并将其配置为IP核。3.第三步:配置arm核心的外设信息,如DDR接口、时钟频率、UART接口等。4.第四步:完成Block Design中的模块互联,生成PL端的比特流。 5.第五步:导出PS端的硬件信息,包括arm IP核的外设、速度、引脚和地址等信息。1创建bd 需要有ddr以及uart和时钟配置外设io口,这里就是zynq的管脚规定好的,根据原理图用哪个个勾选哪一个。 对外设uart的配

m0_58129662的博客 1028

Mac OS X 工具技巧:一文搞定个性化命令行窗口(iTerm2 + zsh + oh-my-zsh 配置Powerline风格)

本不想写这篇文章的,因为在Linux下已经配置过很多次,想着mac应该找篇文章一次就搞定了,结果还是踩坑,看了很多文章,不是某一点没不清楚,就是截图有问题或者命令有问题,官网又难得找等等……花了3个多小时才搞定,所以,还是来总结一篇文章就能够搞定的教程。 为什么要用zsh?用了这么多年的bash,不能说bash不好,不过当我用了zsh后发现,什么叫更好用。 配置过程稍微有点繁琐,但是你看过效果...

mickjoust的技术小屋 1万+

Android 保存图片到本地 兼容Android 10+ ,12

适配Android12

qq_33209777的博客 2146

香橙派rk3588的rknn多线程推理RTSP流服务性能优化与资源分配策略

本文深入探讨了香橙派RK3588平台上的RKNN多线程推理在RTSP流服务中的性能优化与资源分配策略。通过分析硬件架构、设计多线程推理流水线、实现NPU核心负载均衡及优化RGA内存管理,显著提升了RTSP视频流的处理帧率和降低了延迟,为边缘计算视频分析应用提供了实用的优化方案。

7up55的博客 656

Android中实现长按照片弹出右键菜单

场景 效果 注: 博客:https://blog.youkuaiyun.com/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书、教程推送与免费下载。 实现 将布局改为LinearLayout,并通过android:orientation="vertical">设置为垂直布局。 然后添加一个ImageView,并设置id属性和图片源。 &lt...

BADAO_LIUMANG_QIZHI的博客 751

扩展卡尔曼滤波(EKF)理论讲解与实例(matlab、python和C++代码)

扩展卡尔曼滤波(EKF)理论讲解与实例(matlab、python和C++代码) 文章目录扩展卡尔曼滤波(EKF)理论讲解与实例(matlab、python和C++代码)理论讲解KF和EKF模型对比雅可比矩阵计算计算实例应用实例线性模型CV模型:CA模型非线性模型CTRV模型:CTRV实例(python)small demo抛物线demo飞机高度demoC++实例参考文献 我们上篇提到的卡尔曼滤波是用于线性系统,预测(运动)模型和观测模型是在假设高斯和线性情况下进行的。简单的卡尔曼滤波必须应用在符合高斯分布

O_MMMM_O的博客 10万+

电子电器架构 --- OEM企标中规定ECU启动时间的目的

电子电器架构 --- OEM企标中规定ECU启动时间的目的

Soly_kun的博客 223

Android11、12存储变化-不能在scard存文件怎么办-更换存储区域

Android11、12本地化存储变化-不能在scard存文件怎么办-更换存储区域

GUOZIJUN01的博客 2312

CPD (Coherent Point Drift) 算法实现点云配准 C++版

作为和icp并列的点集配准算法cpd为什么没有icp火,等我把整个方案跑完才明白。 首先下载cpd https://github.com/gadomski/cpd 包含的fgt https://github.com/gadomski/fgt 下载到win10的解压后 运行cmake来进行编译 这个时候会提醒缺少eigen3 fgt python 这几个都安装排除后 在build文件夹下会生成vs2017的源码,右键全部生成后在 .\cpd-master\build\Debug 下会生成一个cpd.li

uranus1992的博客 3510

【Android基础笔记12】Android数据存储——SharedPreferences

一、数据存储选项:Data Storage ——Storage Options【重点】 1、Shared PreferencesStore private primitive data in key-value pairs.保存简单的键值对数据。2、Internal StorageStore private data on the device memory.在手机内存中保存不对外共

黎姿从的博客 479

泰凌微8258入门教程——导读

基于泰凌微TLSR8258 Bluetooth® SIG Mesh 的入门系列教程

2万+

爬虫面试:关于爬虫破解验证码的13个经典面试题

​解释:验证码是一种用于区分人类用户和自动化程序(如爬虫)的技术,通常通过显示图形、文字或音频等方式,要求用户完成特定任务(如识别扭曲的字母、点击特定区域等)。​作用:防止恶意爬虫滥用网站资源、进行批量注册、登录破解等行为,保护网站的安全性和数据的完整性。

数据知道的博客 1万+

android开发基础12-数据存储

基础数据存储

blind cat 468

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

2017.12.25 Android数据存储方案

1.持久化技术: 数据持久化:将内存中的数据保存到文件,以使得设备在关机或者重启以后也能正常使用的一种技术; 内存态与持久态之间切换的代价:CPU,文件读取,文件写入,内核态的切换 2.Android内置的持久化技术: (1)文件系统; (2)SharedPreferences (3)SQLite 3.文件系统如何使用:文件持久化 如何把数据保存到文件中: ...

a18946983682的博客 92

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

我的android 第12天 - 嵌入式关系型SQLite数据库存储数据

嵌入式关系型SQLite数据库存储数据    除了可以使用文件或SharedPreferences存储数据,还可以选择使用SQLite数据库存储数据。 在Android平台上,集成了一个嵌入式关系型数据库—SQLite,SQLite3支持 NULL、INTEGER、REAL(浮点数字)、TEXT(字符串文本)和BLOB(二进制对象)数据类型,虽然它支持的类型只有五种,但实际上sqlit

u012458883的专栏 795

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android进阶#(5/12)独特高效的数据存储——SQLite数据库_Android中的数据库开发

(5/12)独特高效的数据存储——SQLite数据库_Android中的数据库开发

王基。 432

iphone同步助手_如何快速的将 Android 数据迁移到 iPhone

iPhone 12 发布在即,普及了买了新 iPhone 怎么最快的迁移数据,那么对于 Android 用户怎么快速的将数据迁移到 iPhone 呢?首先你要想好你是否真的要放弃 Android 阵营哈,在下定决心后,你就可以开始苹果之旅了。苹果公司自然也考虑到了这点,先去应用市场搜索「转移到 iOS」App。准备工作:在安卓设备上,确保 Wi-Fi 已开启。将新 iOS 设备和安卓设备插入电源。...

weixin_39738755的博客 896

【QCM2290】Android12 TA数据默认保存在data,修改保存至persist

【QCM2290】Android12 TA数据默认保存在data,修改保存至persist

sunnywalden 744

Android音视频【六】视频数据存储方式YUV

人间观察 这个社会很浮躁,千万不要考验人性 在视频中的数据保存和传输都是YUV数据格式。主要是为了降低数据大小,如果用argb格式的数据,1px可能需要4个字节,而用YUV可能就需要1.5个字节。 YUV简介 与RGB类似,YUV也是一种颜色编码方法,主要用于视频领域,它将亮度信息(Y)与色彩信息(UV)分离,没有UV信息一样可以显示完整的图像,只不过是黑白的,比如这样的设计解决了彩色电视机与黑白电视的兼容问题。 YUV,分为三个分量,“Y”表示的是明亮度(Luminance或Luma),也就是灰度值;.

顾修忠的博客 965

Android文件存储常用Api和对应存储位置

注:测试时的Android版本是12,如果实际地址不同可以用下面的测试代码自行重新看一遍。防止经常要用的时候记不住在哪个文件夹,要再去一一百度很麻烦,一次性测完了整理放在这里。

wodongx123的博客 1119
上一篇: Android -- (10),Gallery ImageView
下一篇: Android -- (13),Content Provider
theonlydk
博客等级 码龄14年 0粉丝 14原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值