Android基础之五大存储(SharedPreferences | SQLite | Content Provider | 文件存储 | 网络存储)详解,带实例 | 含源码地址

目录

五大存储

SharedPreferences本地存储

数据库之SQLite

ContentProvider内容提供者方式

文件存储方式

网络存储方式


五大存储

  • 持久化技术(文件存储/SharedPreference/数据库存储)就是指将内存中的那些瞬时数据保存到存储设备中

SharedPreferences本地存储

简介

  • 使用键值对的方式进行数据存储
  • Preferences只能在同一个包内使用,不能在不同的包之间使用

获取方式

  • Context类中的getSharedPreferences()方法

此方法接收两个参数,一个参数用于指定SharedPreferences文件的名称

如果指定的文件不存在则会创建一个 SharedPreferences文件都是存放在/data/data/<package name>/shared_prefs/目录下

第二个参数用于指定操作模式,目前只有MODE_PRIVATE这种模式,和直接传入0效果相同

SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age",13);
editor.putBoolean("married",false);
editor.apply();
  • Activity类中getPreferences()方法
这个方法和Context中的getSharedPreferences()方法很类似,不过它只接收一个操作模式
因为使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名
  • PreferenceManager类中的getDefaultSharedPreferences()方法
这是一个静态方法,它接收一个Context参数
并自动使用当前应用程序的包名作为前缀来命名文件,得到SharedPreferences对象之后,就可以开始向文件中存储数据了,主要分三步
    - 调用SharedPreferences对象的edit()方法来获取一个SharedPreferences.Editor对象
    - 向该对象中添加数据(putString()/putBoolean()...)
    - 调用apply()方法将添加的数据提交,从而完成数据存储操作

存取/读写操作

  • 存储
修改activity.xml
<Button
    android:id="@+id/save_data"
    android:layout_width="258dp"
    android:layout_height="56dp"
    android:text="@string/save_data"
    android:onClick="saveData"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
修改MainActivity代码
通过getSharedPreferences将文件名注册为data,并得到SharedPreferences.Editor对象,调用apply()进行提交
public void saveData(View view) {
    SharedPreferences.Editor editor = getSharedPreferences("data",MODE_PRIVATE).edit();
    editor.putString("name","Qricis Qi");
    editor.putInt("age",23);
    editor.putBoolean("married",false);
    editor.apply();
}
  • 读取
修改activity.xml
<Button
    android:id="@+id/save_data"
    android:layout_width="258dp"
    android:layout_height="56dp"
    android:onClick="saveData"
    android:text="@string/save_data"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.496"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.389" />

<Button
    android:id="@+id/restore_data"
    android:layout_width="258dp"
    android:layout_height="56dp"
    android:text="@string/restore_data"
    android:onClick="restoreData"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.498"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/save_data"
    app:layout_constraintVertical_bias="0.288" />
SharedPreferences对象提供了一系列的get方法,每个get对应一种put
get有两个参数,第一个参数是键,传入存储数据时使用的键,第二个参数是默认值,即表示当传入的键找不到对应的值时会以什么样的默认值返回
修改MainActivity
public void restoreData(View view) {
    SharedPreferences pref = getSharedPreferences("data",MODE_PRIVATE);
    String name = pref.getString("name","");
    int age = pref.getInt("age",0);
    boolean married = pref.getBoolean("married",false);

    Log.d("MainActivity","name is " + name);
    Log.d("MainActivity","age is " + age);
    Log.d("MainActivity","married is " + married);
}

实例

  • 修改布局文件activity_login(使用了一个新控件checkBox,这是一个复选框键,用户可以通过点击的方式来进行选中和取消)
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
		    xmlns:app="http://schemas.android.com/apk/res-auto"
		    xmlns:tools="http://schemas.android.com/tools"
		    android:id="@+id/container"
		    android:layout_width="match_parent"
		    android:layout_height="match_parent"
		    android:paddingLeft="@dimen/activity_horizontal_margin"
		    android:paddingTop="@dimen/activity_vertical_margin"
		    android:paddingRight="@dimen/activity_horizontal_margin"
		    android:paddingBottom="@dimen/activity_vertical_margin"
		    tools:context=".LoginActivity">

		<EditText
		        android:id="@+id/username"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_marginStart="24dp"
		        android:layout_marginTop="96dp"
		        android:layout_marginEnd="24dp"
		        android:hint="@string/prompt_email"
		        android:inputType="textEmailAddress"
		        android:selectAllOnFocus="true"
		        app:layout_constraintEnd_toEndOf="parent"
		        app:layout_constraintStart_toStartOf="parent"
		        app:layout_constraintTop_toTopOf="parent" />

		<EditText
		        android:id="@+id/password"
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_marginStart="24dp"
		        android:layout_marginTop="8dp"
		        android:layout_marginEnd="24dp"
		        android:hint="@string/prompt_password"
		        android:imeActionLabel="@string/action_sign_in_short"
		        android:imeOptions="actionDone"
		        android:inputType="textPassword"
		        android:selectAllOnFocus="true"
		        app:layout_constraintEnd_toEndOf="parent"
		        app:layout_constraintStart_toStartOf="parent"
		        app:layout_constraintTop_toBottomOf="@+id/username" />

		<Button
		        android:id="@+id/login"
		        android:layout_width="wrap_content"
		        android:layout_height="wrap_content"
		        android:layout_gravity="start"
		        android:layout_marginStart="48dp"
		        android:layout_marginTop="16dp"
		        android:layout_marginEnd="48dp"
		        android:layout_marginBottom="64dp"
		        android:text="@string/action_sign_in"
		        android:onClick="loginButton"
		        app:layout_constraintBottom_toBottomOf="parent"
		        app:layout_constraintEnd_toEndOf="parent"
		        app:layout_constraintStart_toStartOf="parent"
		        app:layout_constraintTop_toBottomOf="@+id/password"
		        app:layout_constraintVertical_bias="0.2" />

		<LinearLayout
		        android:layout_width="0dp"
		        android:layout_height="wrap_content"
		        android:layout_marginTop="8dp"
		        android:orientation="horizontal"
		        app:layout_constraintBottom_toTopOf="@+id/login"
		        app:layout_constraintEnd_toEndOf="@+id/password"
		        app:layout_constraintHorizontal_bias="0.0"
		        app:layout_constraintStart_toStartOf="@+id/password"
		        app:layout_constraintTop_toBottomOf="@+id/password"
		        app:layout_constraintVertical_bias="0.215">

		        <CheckBox
		            android:id="@+id/remember_pass"
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content" />

		        <TextView
		            android:layout_width="wrap_content"
		            android:layout_height="wrap_content"
		            android:text="@string/remember_password" />

		    </LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
  • 修改LoginActivity代码(需要注意的是,将密码以明文的形式存储是非常不安全的,一定要结合加密算法)
public class LoginActivity extends AppCompatActivity {

  	private SharedPreferences pref;
  	private SharedPreferences.Editor editor;
 	 	private EditText usernameEdit;
  	private EditText passwordEdit;
  	private CheckBox rememberPass;

  	@Override
  	public void onCreate(Bundle savedInstanceState) {
    	super.onCreate(savedInstanceState);
    	setContentView(R.layout.activity_login);

    	pref = getSharedPreferences("data",MODE_PRIVATE);

    	// pref = PreferenceManager.getDefaultSharedPreferences(this);

    	usernameEdit = findViewById(R.id.username);
   	 	passwordEdit = findViewById(R.id.password);
    	rememberPass = findViewById(R.id.remember_pass);

    	boolean isRemember = pref.getBoolean("remember_password",false);

    	if (isRemember) {
      		// 将账号和密码都设置到文本框中
      		String username = pref.getString("username","");
      		String password = pref.getString("password","");

      		usernameEdit.setText(username);
      		passwordEdit.setText(password);
      		rememberPass.setChecked(true);
    	}
  	}

  	public void loginButton(View view) {

    	String username = usernameEdit.getText().toString();
    	String password = passwordEdit.getText().toString();

    	// 如果账号是admin@royole.com且密码是123456就认为登录成功
    	if (username.equals("admin@royole.com") && password.equals("123456")) {
      		editor = pref.edit();

      		if (rememberPass.isChecked()) {
        		// 检查复选框是否被选中
        		editor.putString("username",username);
        		editor.putString("password",password);
        		editor.putBoolean("remember_password",true);
      		} else {
        		editor.clear();
      	    }
      		editor.apply();

      		Intent intent = new Intent(this,MainActivity.class);
      		startActivity(intent);
      		finish();
    	} else {
      		Toast.makeText(this,"account or password is invalid",Toast.LENGTH_SHORT).show();
    	}
  	}
}

源码地址

https://github.
com/qricis/DoSomeAndroidTest/tree/main/SharedPreferencesTest

数据库之SQLite

简介

  • Android专门提供了一个SQLiteOpenHelper帮助类来非常简单地对数据库进行创建和升级,它是一个抽象类,我们需要创建一个自己的帮助类去继承他
  • 其中有两个抽象方法,onCreate()和onUpgrade(),我们需要重写这两个方法去实现创建、升级数据库的逻辑
  • 有两个非常重要的实例方法,getReadableDatabase()和getWritableDatabase(),用来创建/打开一个现有数据库,并返回一个可对数据库进行读写操作的对象
  • 需要注意的是,当数据库不可写入时,getReadableDatabase返回的对象将以只读当时打开,而getWritableDatabase出现异常
  • 还有两个构造方法可以重写,一般选择参数少一点的那个,有四个参数,第一个参数是Context,第二个是数据库名,第三个是允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null,第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作
  • 数据库文件会存放在/data/data/<package name>/databases/目录下

查询方式

  • 使用adb shell来对数据库和表的创建情况进行检查

配置好环境之后(在Path中将platform-tools目录添加进去),在命令行输入adb shell

#:符号表示超级管理员的意思

$:符号表示普通管理员 你需要输入su命令变成超管

cd:进入到/data/data/<package name>/databases/目录下

ls:查看目录下文件

sqlite3 <数据库名>:来打开数据库

.table:来查看有哪些表

.scheme:来查看它们的建表语句

.exit/.quit:退出数据库

实例

  • 修改布局文件activity_main
<Button
        android:id="@+id/create_database"
        android:layout_width="268dp"
        android:layout_height="46dp"
        android:text="@string/create_database"
        android:onClick="createDatabase"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.235" />
  • 修改MainActivity
// 点击按钮创建数据库BookStore.db
public class MainActivity extends AppCompatActivity {
  
  	// 数据库版本号
    private static final int DATABASE_VERSION = 1;

    private MyDatabaseHelper mMyDatabaseHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mMyDatabaseHelper = new MyDatabaseHelper(this,"BookStore.db",null,DATABASE_VERSION);
    }

    public void createDatabase(View view) {
        mMyDatabaseHelper.getWritableDatabase();
    }
}
  • 新建一个MyDatabaseHelper继承自SQLiteOpenHelper
// integer整型、real浮点型、text文本类型、blob二进制类型、autoincrement表示id列自增长
public class MyDatabaseHelper extends SQLiteOpenHelper {
  
    public static final String CREATE_BOOK = "create table book (" +
	        "id integer primary key autoincrement," +
	        "author text," +
	        "price real," +
	        "pages integer," +
	        "name text)";

	private Context mContext;

    // 必须要实现的方法
	public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
	    super(context, name, factory, version);
	    mContext = context;
	}

    // 第一次创建数据库时 才会调用
	@Override
	public void onCreate(SQLiteDatabase sqLiteDatabase) {
	    sqLiteDatabase.execSQL(CREATE_BOOK);
	    Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {

	}
}

什么是SQLiteDatabase?

  • 一个SQLiteDatabase的实例代表了一个SQLite的数据库,通过SQLiteDatabase实例的一些方法,我们可以执行SQL语句对数据库进行增、删、查、改的操作
  • 需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的

什么是SQLiteOpenHelper?

  • 这个类主要生成一个数据库,并对数据库的版本进行管理
  • SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的3个函数
  • onCreate(SQLiteDatabase):在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表
  • onUpgrade(SQLiteDatabase, int, int):当数据库需要升级时,Android会主动的调用这个方法。一般我们在这个方法里删除表,并建立新的表,当然是否还需要做其他的操作,完全取决于应用的需求
  • onOpen(SQLiteDatabase):这是当打开数据库时的回调函数,一般也不会用到
  • 升级数据库

若我们想再创建一个数据库table,可以在onCreate()里面添加

db.execSQL(CREATE_TABLE);
// 新创建一个数据库表
db.execSQL(CREATE_BOOK);
但是并不会运行成功,因为onCreate()只会在初始化的时候运行,因此我们需要用到onUpdate()方法
// 如果已经存在就删除,防止重复创建
db.execSQL("drop table if exists book");
// 再次执行onCreate 方法
onCreate(db);
但是onUpgrade默认不执行,若要执行它,则需要修改
// 将版本号 由 1 改为2
private static final int DATABASE_VERSION = 2;

完整代码 

// 修改MyDatabaseHelper
public class MyDatabaseHelper extends SQLiteOpenHelper {

	public static final String CREATE_BOOK = "create table Book (" +
	        "id integer primary key autoincrement," +
	        "author text," +
	        "price real," +
	        "pages integer," +
	        "name text)";

	public static final String CREATE_GATEGORY = "create table Category (" +
	        "id integer primary key autoincrement," +
	        "category_name text," +
	        "category_code integer)";

	private Context mContext;

	public MyDatabaseHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
	    super(context, name, factory, version);
	    mContext = context;
	}

	@Override
	public void onCreate(SQLiteDatabase sqLiteDatabase) {
	    sqLiteDatabase.execSQL(CREATE_BOOK);
	    sqLiteDatabase.execSQL(CREATE_GATEGORY);
	    Toast.makeText(mContext,"Create succeeded",Toast.LENGTH_SHORT).show();
	}

	@Override
	public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
	    sqLiteDatabase.execSQL("drop table if exists Book");
	    sqLiteDatabase.execSQL("drop table if exists Category");
	    onCreate(sqLiteDatabase);
	}
}

// 修改MainActivity
// 修改传参的版本号,当系统发现版本号变了,就会去调用数据库类的onUpgrade方法
private static final int DATABASE_VERSION = 2;

增删改查(CRUD)

getReadableDatabase()和getWritableDatabase(),这俩方法都可以创建或打开一个现有的数据库,并返回一个可对数据库进行读写操作的对象SQLiteDatabase,提供了insert()/update()/delete()/query()方法

insert():接收三个参数,第一个参数是表名,第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们不用,直接传null,第三个参数是一个ContentValues对象,提供了一系列put重载,用于向ContentValues中添加数据

update():接收四个参数,第一个参数表名,第二个参数ContentValues,第三、四个参数用于约束更新某一行或某几行二点数据,不指定的话默认就是更新所有行

delete():接收三个参数,第一个参数表名,第二、三个参数用来约束删除某一行或某几行数据,不指定就是删除所有行

query():接收七个参数,调用该方法后会返回一个Cursor对象,查询到的所有数据都将从这个对象取出

table

from table_name

指定查询的表名

columns

select column1,column2

指定查询的列名

selection

where column = value

指定where的约束条件

selectionArgs

-

为where中的占位符提供具体的值

groupBy

group by column

指定需要group by的列

having

having cloumn = value

对group by的结果进一步约束

orderBy

order by cloumn1,cloumn2

指定查询结果的排序方式

  • 插入数据

修改activity_main

<Button
        android:id="@+id/create_database"
        android:layout_width="268dp"
        android:layout_height="46dp"
        android:text="@string/create_database"
        android:onClick="createDatabase"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.235" />

<Button
        android:id="@+id/add_data"
        android:layout_width="268dp"
        android:layout_height="46dp"
        android:text="@string/add_data"
        android:onClick="addData"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/create_database"
        app:layout_constraintVertical_bias="0.182" />

修改MainActivity

public void addData(View view) {

    SQLiteDatabase db = mMyDatabaseHelper.getWritableDatabase();
    ContentValues values = new ContentValues();

    // 开始组装第一条数据
    values.put("name","The Da Vinci Code");
    values.put("author","Dan Brown");
    values.put("pages",454);
    values.put("price",16.96);
    
    // 插入第一条数据
    db.insert("Book",null,values);
    values.clear();

    // 插入第二条数据
    values.put("name","The Lost Symbol");
    values.put("author","Dan Brown");
    values.put("pages",510);
    values.put("price",19.95);
    
    // 插入第一条数据
    db.insert("Book",null,values);
    values.clear();
}
  • 更新数据

修改xml

<Button
        android:id="@+id/update_data"
        android:layout_width="268dp"
        android:layout_height="46dp"
        android:layout_marginTop="33dp"
        android:onClick="updateData"
        android:text="@string/update_data"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/add_data"
        app:layout_constraintVertical_bias="0.0" />

修改MainActivity

public void updateData(View view) {

    SQLiteDatabase db = mMyDatabaseHelper.getWritableDatabase();
    ContentValues values = new ContentValues();

    values.put("price",29.89);

    db.update("Book",values,"name = ?",new String[]{"The Da Vinci Code"});
    values.clear();
}
  • 删除数据

修改xml

<Button
        android:id="@+id/delete_data"
        android:layout_width="268dp"
        android:layout_height="46dp"
        android:onClick="deleteData"
        android:text="@string/delete_data"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.496"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/update_data"
        app:layout_constraintVertical_bias="0.127" />

修改MainActivity

public void deleteData(View view) {

    SQLiteDatabase db = mMyDatabaseHelper.getWritableDatabase();
    db.delete("Book","id > ?",new String[]{"2"});

} 
  • 查询数据

修改xml

<Button
        android:id="@+id/query_data"
        android:layout_width="268dp"
        android:layout_height="46dp"
        android:onClick="queryData"
        android:text="@string/query_data"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.503"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/delete_data"
        app:layout_constraintVertical_bias="0.161" />

修改MainActivity

public void queryData(View view) {

    SQLiteDatabase db = mMyDatabaseHelper.getWritableDatabase();

    //查询Book表中的所有数据
    Cursor cursor = db.query("Book",null,null,null,null,null,null);

    if (cursor.moveToFirst()) {
        do {
            //遍历Cuosor对象,取出数据并打印
            String name = cursor.getString(cursor.getColumnIndex("name"));
            String author = cursor.getString(cursor.getColumnIndex("author"));
            int pages = cursor.getInt(cursor.getColumnIndex("pages"));
            double price = cursor.getDouble(cursor.getColumnIndex("price"));

            Log.d("MainActivity","book name is " + name);
            Log.d("MainActivity","book author is " + author);
            Log.d("MainActivity","book pages is " + pages);
            Log.d("MainActivity","book price is " + price);
        }while (cursor.moveToNext());
    }
    cursor.close();
}

使用SQL操作数据库

// 插入
db.execSQL("insert into Book (name,author,pages,price) values(?,?,?,?)",new String[] {"The Da Vinci Code","Dan Brown","454","44.23"});
// 更新
db.execSQL("update Book set price = ? where name = ?",new String[] {"29.9","The Da Vinci Code"});
// 删除
db.execSQL("delete from Book where id > ?",new String[] {"2"});
// 查询
Cursor cursor = db.rawQuery("select * from Book",null);

源码地址

https://github.com/qricis/DoSomeAndroidTest/tree/main/DatabaseTest

ContentProvider内容提供者方式

简介

  • 上面四大组件中有详细说明,在这里不再多做赘述

实例

  • 新建一个类去继承ContentProvider,并重写其中的6个抽象方法
public class MyProvider extends ContentProvider {

    // 初始化内容提供器的时候调用,通常在这里完成对数据库的创建和升级,返回true表示成功,false表示失败
    @Override
    public boolean onCreate() {
        return false;
    }

    // 从内容提供器中添加一条数据,uri确定表,projection确定列,selection确定where,selectionArgs确定where参数,sortOrder确定排序方式
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    // 向内容提供器添加数据
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    // 更新内容提供器中已有的数据
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }

    // 从内容提供器中删除数据
    @Override
    public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    // 根据传入的内容URI返回相应的MIME类型
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }
}
  • UriMatcher类

我们再借助UriMatcher这个类就可以轻松实现匹配内容URI的功能

提供了一个addURI()方法,接收3个参数(authority/path/自定义代码)

当调用match()时,就可以将一个URI对象传入,返回值你是某个能够匹配这个Uri对象所对应的自定义代码

public class MyProvider extends ContentProvider {

    public static final int TABLE1_DIR = 0;
    public static final int TABLE1_ITEM = 1;
    public static final int TABLE2_DIR = 2;
    public static final int TABLE2_ITEM = 3;
    private static UriMatcher mUriMatcher;

    static {
        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI("com.example.contactstest.provider","table1",TABLE1_DIR);
        mUriMatcher.addURI("com.example.contactstest.provider","table1",TABLE1_ITEM);
        mUriMatcher.addURI("com.example.contactstest.provider","table2",TABLE2_DIR);
        mUriMatcher.addURI("com.example.contactstest.provider","table2",TABLE2_ITEM);

    }

    // 初始化内容提供器的时候调用,通常在这里完成对数据库的创建和升级,返回true表示成功,false表示失败
    @Override
    public boolean onCreate() {
        return false;
    }

    // 从内容提供器中添加一条数据,uri确定表,projection确定列,selection确定where,selectionArgs确定where参数,sortOrder确定排序方式
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {

        switch (mUriMatcher.match(uri)) {
            case TABLE1_DIR:
                //查询table1表中的所有数据
                break;
            case TABLE1_ITEM:
                //查询table1表中的单条数据
                break;
            case TABLE2_DIR:
                //查询table2表中的所有数据
                break;
            case TABLE2_ITEM:
                //查询table2表中的单条数据
                break;
            default:
                break;
        }

        return null;
    }

    // 向内容提供器添加数据
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    // 更新内容提供器中已有的数据
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, String selection, String[] selectionArgs) {
        return 0;
    }

    // 从内容提供器中删除数据
    @Override
    public int delete(@NonNull Uri uri, String selection, String[] selectionArgs) {
        return 0;
    }

    // 根据传入的内容URI返回相应的MIME类型,一个内容URI所对应的MIME由三部分组成
    // 必须以vnd开头
    // 如果内容URI以路径结尾,则后接android.cursor.dir/,如果以id结尾,则后接android.cursor.item/
    // 最后接上vnd.<authority>.<path>
    // 对应content://com.example.contactstest.provider/table1,写成vnd.android.dir/vnd.content://com.example.contactstest.provider/table1
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {

        switch (mUriMatcher.match(uri)) {
            case TABLE1_DIR:
                return "vnd.android.dir/vnd.content://com.example.contactstest.provider/table1";
            case TABLE1_ITEM:
                return "vnd.android.item/vnd.content://com.example.contactstest.provider/table1";
            case TABLE2_DIR:
                return "vnd.android.dir/vnd.content://com.example.contactstest.provider/table2";
            case TABLE2_ITEM:
                return "vnd.android.item/vnd.content://com.example.contactstest.provider/table2";
            default:
                break; 
        }

        return null;
    }
}

源码地址

https://github.com/qricis/DoSomeAndroidTest/tree/main/ContentProvider

文件存储方式

简介

  • Android中,Context提供了openFileInput 和 openFileOuput 方法读取设备上的文件
  • 如果调用FileOutputStream 时指定的文件不存在,Android 会自动创建它
  • 文件的操作模式有两种(MODE_PRIVATE/MODE_APPEND),前者是默认的,表示当存在时覆盖,后者表示存在时追加内容
  • 比较适合用于一些简单的文本数据或二进制数据
  • 所有的文件都是默认存储到/data/data/<packagename>/files/目录下的

读/写操作

// 确定要操作文件的文件名
String FILE_NAME = "tempfile.tmp";
// 初始化 第一个参数文件名,第二个参数文件的操作模式
FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); 
// 创建写入流
FileInputStream fis = openFileInput(FILE_NAME); 

实例

  • 修改activity_main.xml的代码
<EditText
          android:id="@+id/edit"
          android:layout_width="263dp"
          android:layout_height="50dp"
          android:ems="10"
          android:hint="@string/edit"
          android:inputType="textPersonName"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toTopOf="parent"
          app:layout_constraintVertical_bias="0.336"
          android:autofillHints="Just so so " />
  • 修改MainActivity代码之存储数据
通过openFileOutput得到一个FileOutputStream对象,然后再借助它构建出一个OutputStreamWriter对象
接着再使用OutputStreamWriter构建出一个BufferedWriter对象,我们通过该对象将文本内容写入到文件中
public class MainActivity extends AppCompatActivity {

		private EditText editText;

		@Override
		protected void onCreate(Bundle savedInstanceState) {
		    super.onCreate(savedInstanceState);
		    setContentView(R.layout.activity_main);

		    editText = findViewById(R.id.edit);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        String input = editText.getText().toString();
        save(input);
		}

		public void save(String inputText) {
		    FileOutputStream out = null;
		    BufferedWriter writer = null;

		    try {
		        out = openFileOutput("data", Context.MODE_PRIVATE);
		        writer = new BufferedWriter(new OutputStreamWriter(out));
		        writer.write(inputText);
		    } catch (IOException e) {
		        e.printStackTrace();
		    } finally {
		        try {
		            if (writer != null) {
		                writer.close();
		            }
		        } catch (IOException e) {
		            e.printStackTrace();
		        }
		    }
		}
}
  • 读取数据
// Context提供了一个openFileInput方法,用于从文件中读取数据
// 只接收一个参数,即要读取文件的文件名,系统会自动到上面的目录下去加载这个文件,并返回一个FileInputStream对象
// 在onCreate中调用load方法,如果读取的内容不为null,就调用EditText的setText方法,将内容填充到EditText里,并调用setSelection方法将输入光标移动到文本的末尾位置以便继续输入,然后弹出提示
// 非空判断使用了TextUtils.isEmpty方法,它可以一次性进行两种非空判断,当传入的字符串=null或=空字符串时。返回true
public class MainActivity extends AppCompatActivity {

	private EditText editText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        editText = findViewById(R.id.edit);

		String inputText = load();

		if (!TextUtils.isEmpty(inputText)) {
		    editText.setText(inputText);
		    editText.setSelection(inputText.length());
		    Toast.makeText(this,"Restoring succeed",Toast.LENGTH_SHORT).show();
		}

	}

	@Override
	protected void onDestroy() {
	    super.onDestroy();

	    String input = editText.getText().toString();
	    save(input);
	}

    // 保存文件内容
	public void save(String inputText) {
	    FileOutputStream out = null;
	    BufferedWriter writer = null;

	    try {
	        out = openFileOutput("data", Context.MODE_PRIVATE);
	        writer = new BufferedWriter(new OutputStreamWriter(out));
	        writer.write(inputText);
	    } catch (IOException e) {
	        e.printStackTrace();
	    } finally {
	        try {
	            if (writer != null) {
	                writer.close();
	            }
	        } catch (IOException e) {
	            e.printStackTrace();
	        }
	    }
	}

    // 加载文件内容,显示出来
	public String load() {
	    FileInputStream in = null;
	    BufferedReader reader = null;
	    StringBuilder content = new StringBuilder();
	    try {
	        in = openFileInput("data");
	        reader = new BufferedReader(new InputStreamReader(in));
	        String line = "";
	        while ((line = reader.readLine()) != null) {
	            content.append(line);
	        }
	    } catch (IOException e) {
	        e.printStackTrace();
	    } finally {
	        if (reader != null) {
	            try {
	                reader.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        }
	    }
	    return content.toString();
	}
}

源码地址

https://github.com/qricis/DoSomeAndroidTest/tree/main/FilePersistenceTest

网络存储方式

简介

  • 顾名思义,通过网络存取信息
  • 网上有很多实例,目前最火的应该是Rxjava + Retrofit + Okhttp进行数据的传输和处理
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值