第一行代码 Ch.6

Ch.6

6.2 文件储存:

主要参考的这里

主要使用的是FileOutputStream() 方法, 这玩意返回一个文件输出流, 现在还没学到, 在java核心技术卷二中

	@Override
    public FileOutputStream openFileOutput(String name, int mode)
        throws FileNotFoundException {
        return mBase.openFileOutput(name, mode);
    }
  • 第一参数用于指定文件名:

    不能包含路径分隔符“/” , 即无法指定文件路径, 所有的文件都会被保存在/data/data/<package name>/files目录中

    如果文件不存在, Android 会自动创建它

  • 第二参数用于指定操作模式,有四种模式,分别为:

    • Context.MODE_PRIVATE = 0
      为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,

    • Context.MODE_APPEND = 32768
      模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。

    • Context.MODE_WORLD_READABLE = 1
      用来控制其他应用是否有权限读写该文件

      Android 4.2之后以弃用

    • Context.MODE_WORLD_WRITEABLE = 2
      表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入

      Android 4.2之后以弃用

常规の判断文件是否存在的方法:

	public boolean existsFile(String fileName){
     String path = this.getFilesDir().getPath()+"//";
        File file = new File(path+fileName);
        if(file.exists()){
            return true;
        }
        return false;
    }

读取文件の常规操作:

public String readFile(String fileName) throws IOException{
        FileInputStream fis = context.openFileInput(fileName);
        int len = fis.available();
        byte []buffer = new byte[len];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        while((fis.read(buffer))!=-1){
            baos.write(buffer);
        }
        byte []data = baos.toByteArray();
        baos.close();
        fis.close();
        return new String(data);
    }

写入文件の相关操作:

public void saveFile(String fileName, String str) throws IOException{
        FileOutputStream fos = context.openFileOutput(fileName, Activity.MODE_APPEND);
        byte[]bytes=str.getBytes();
        fos.write(bytes);
        fos.flush();
        fos.close();
    }

拓展: 读写缓冲器BufferedReader/BufferedWriter

主要作用是在程序与文件间建立缓冲区, 以减少文件的读写次数, 提高程序效率

例程:

package com.example.ch6test;

import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;

import androidx.appcompat.app.AppCompatActivity;

import java.io.*;

public class MainActivity extends AppCompatActivity {
    private EditText editText;
    private String editTextFileName = "editText";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        editText = findViewById(R.id.editText);
        editText.setText(readData());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        this.saveData(editTextFileName,editText.getText().toString());
    }

    private File findFile(String fileName) {
//        boolean fileExistFlag=false;
        String path = this.getFilesDir().getPath() + "//" + fileName;
        File file = new File(path);
        return file;
    }

    public String readData() {
        FileInputStream inputStream = null;
        BufferedReader bufferedReader = null;
        String data = null;
        try {
            inputStream = openFileInput(editTextFileName);
            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
            data = bufferedReader.readLine();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Log.d(this.getClass().getName(), editTextFileName + " Not exist.");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedReader != null) {
                    bufferedReader.close();
                }
            }catch(IOException e){
                e.printStackTrace();
            }
        }
        return data;
    }

    public void saveData(String fileName, String data) {
        FileOutputStream outStream = null;
        BufferedWriter buffer = null;
        try {
            outStream = openFileOutput(fileName, Context.MODE_PRIVATE);
            buffer = new BufferedWriter(new OutputStreamWriter(outStream));
            buffer.write(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (buffer != null) {
                    buffer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return;
    }
}

6.3 SharedPreferences储存

基础知识参考这里

获取SharedPreferences对象:

Android中主要提供了三种方法用于得到 SharedPreferences 对象

  1. Context 类中的 getSharedPreferences()方法:

    	@Override
        public SharedPreferences getSharedPreferences(String name, int mode) {
            return mBase.getSharedPreferences(name, mode);
        }
    

    与上头的openFileInput()方法相似的参数列表:

    • 第一参数用于指定文件名:

      不能包含路径分隔符“/” , 即无法指定文件路径, 所有的文件都会被保存在/data/data/<package name>/shared prefs/目录中

      如果文件不存在, Android 会自动创建它

    • 第二参数用于指定操作模式,有四种模式,分别为:

      • Context.MODE_PRIVATE = 0
        为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容,

      • Context.MODE_APPEND = 32768
        模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。

      • Context.MODE_WORLD_READABLE = 1
        用来控制其他应用是否有权限读写该文件

        Android 4.2之后以弃用

      • Context.MODE_WORLD_WRITEABLE = 2
        表示当前文件可以被其他应用读取;MODE_WORLD_WRITEABLE:表示当前文件可以被其他应用写入

        Android 4.2之后以弃用

  2. Activity 类中的 getPreferences()方法:

    这个方法和 Context 中的 getSharedPreferences()方法很相似,不过它只接收一个操作模式参数,

    文件名为当前活动的类名

  3. PreferenceManager 类中的 getDefaultSharedPreferences()方法:

    这是一个静态方法,它接收一个 Context 参数,并自动使用当前应用程序的包名作为前缀来命名 SharedPreferences 文件

    此方法已废弃

SharedPreferences使用:

SharedPreferences对象本身只能获取数据而不支持存储和修改

存储修改是通过SharedPreferences.edit()获取的内部接口Editor对象实现

写入数据常规操作:

  1. 创建SharedPreferences对象
  2. 调用SharedPreferences对象的 edit()方法来获取一个SharedPreferences.Editor对象。
  3. 向SharedPreferences.Editor对象中添加数据,比如添加一个布尔型数据就使用putBoolean()方法,添加一个字符串则使用putString()方法,以此类推。
  4. 调用apply()方法将添加的数据提交,从而完成数据存储操作

例程:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button saveData = (Button) findViewById(R.id.save_data);
        saveData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
                editor.putString("name", "Tom");
                editor.putInt("age", 28);
                editor.putBoolean("married", false);
                editor.apply();
            }
        });
    }
}

读取数据常规操作:

SharedPreferences对象中提供了一系列的get 方法,用于对存储的数据进行读取,每种get方法都对应了SharedPreferences.Editor中的一种put 方法

这些get方法都接收两个参数

  • 第一个参数是键,传人存储数据时使用的键就可以得到相应的值了;

  • 第二个参数是默认值,即表示当传人的键找不到对应的值时会以什么样的默认值进行返回。

SharedPreferences sharedPreferences= getSharedPreferences("data", Context .MODE_PRIVATE);
String userId=sharedPreferences.getString("name","");

删除数据常规操作

//删除指定数据
editor.remove("name");
     editor.commit();

//删除所有数据
editor.clear();
     editor.commit();

测试程序:

package com.example.sharedpreferencestest;

import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private SharedPreferences sharedPreferences;
    private SharedPreferences.Editor editor;
    private EditText IntEditText;
    private EditText StringEditText;
    private EditText BooleanEditText;
    private TextView NumText;
    private TextView NameText;
    private TextView SexText;

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

        sharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
        editor = sharedPreferences.edit();

        IntEditText = findViewById(R.id.IntEditText);
        StringEditText = findViewById(R.id.StringEditText);
        BooleanEditText = findViewById(R.id.BooleanEditText);
        NumText = findViewById(R.id.NumText);
        NameText = findViewById(R.id.NameText);
        SexText = findViewById(R.id.SexText);

            IntEditText.setText(Integer.toString(sharedPreferences.getInt(NumText.getText().toString(), 114514)));
            StringEditText.setText(sharedPreferences.getString(NameText.getText().toString(), ""));

        final Button saveData = (Button) findViewById(R.id.SaveButton);
        saveData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                saveData();
            }
        });
    }

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

    private void saveData() {
        editor.putInt(NumText.getText().toString(),
                Integer.parseInt(IntEditText.getText().toString()));
        editor.putString(NameText.getText().toString(),
                StringEditText.getText().toString());
        editor.apply();
    }

}

6.4 SQLite 数据库储存:

创建数据库:

通常使用SQLiteOpenHelper对数据库进行创建和升级

SQLiteOpenHelper是抽象类, 需要给出自己的实现才能使用 ,其中有两个Abstract虚函数:

    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建数据库sql语句 并 执行
        String sql = "create table user(name varchar(20))";
        db.execSQL(sql);
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
 
    }

这两个方法用于创建和升级数据库, 可以在其中实现自己的逻辑

除此之外, 还需要有个构造函数
一共有三个版本的, 通常使用参数较少的即可

//带全部参数的构造函数,此构造函数必不可少
    public DatabaseHelper(@Nullable Context context, @Nullable String name,
                          @Nullable SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }
  • 第一个参数是Context,这个没什么好说的,必须要有它才能对数据库进行操作。

  • 第二个参数是数据库名,创建数据库时使用的就是这里指定的名称。

  • 第三个参数允许我们在查询数据的时候返回一个自定义的Cursor,一般都是传入null。

  • 第四个参数表示当前数据库的版本号,可用于对数据库进行升级操作

    当版本号大于上一个版本时, 会调用onUpgrade() 方法对数据库进行升级

  • 创建后的数据库位于/data/data/<package name>/databases/目录下

而后, SQLiteOpenHelper中还有两个非常有用的方法getReadableDatabase()和getWritableDatabase()

源码:

public SQLiteDatabase getWritableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(true);
        }
    }
public SQLiteDatabase getReadableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(false);
        }
    }

创建或打开数据库, 并返回读/写对象

  • 创建数据库时调用onCreate()
  • 当数据不可写入时, getReadableDatabase()返回只读的数据库对象 ,getWritableDatabase()将出现异常

而后是创建数据库的常规操作

由于没有学过SQL, 所以这里只能现学现用了, SQLite常用语法参考这里

升级数据库:

在使用数据库时,我们会定义一个继承了SQLiteOpenHelper的子类,就比如刚才的DBHelper。我们通过版本号去更新一个数据库。我们更新一次数据库,当应用去访问数据库的时候就会读取数据库的版本号,如果和之前不一样,则会调用onUpgrade方法

具体可以参考这里, 其中有一个跨版本的例子

数据操作:

SQLite常用操作:

方法名称方法表示含义
openOrCreateDatabase(String path,SQLiteDatabase.CursorFactory factory)打开或创建数据库
insert(String table,String nullColumnHack,ContentValues values)插入一条记录
delete(String table,String whereClause,String[] whereArgs)删除一条记录
query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy)查询一条记录
update(String table,ContentValues values,String whereClause,String[] whereArgs)修改记录
execSQL(String sql)执行一条SQL语句
close()关闭数据库

insert:

	public long insert(String table, String nullColumnHack, ContentValues values) {
        try {
            return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
        } catch (SQLException e) {
            Log.e(TAG, "Error inserting " + values, e);
            return -1;
        }
    }
  • 第一个参数为表名
  • 第二个参数用于在未指定添加数据的情况下给某些可为空的列自动赋值NULL,一般我们用不到这个功能,直接传入null即可
  • 第三个参数是一个ContentValues对象,它提供了一系列的put()方法重载,用于向 ContentValues中添加数据,只需要将表中的每个列名以及相应的待添加数据传入即可

Update:

public int update(String table, ContentValues values, String whereClause,
                  String[] whereArgs) {
        return updateWithOnConflict(table, values, whereClause, 
                                    whereArgs, CONFLICT_NONE);
    }
  • 第一个参数和insert()方法一样,也是表名,在这里指定去更新哪张表里的数据

  • 第二个参数是ContentValues对象,要把更新数据在这里组装进去

  • 第三、第四个参数用于约束更新某一行或某几行中的数据,不指定的话默认就是更新所有行

    其中:

    第三个参数相当于SQL中的where, 点击这里现学现用 如需要修改name, 则"name=?"

    其中?是一个占位符, 与第四个参数配合使用

    第四个参数是String[]类型, 其中包含whereClause中需要的各个?, 与printf()类似, ?会被挨个替代

delete:

public int delete(String table, String whereClause, String[] whereArgs) {
        acquireReference();
        try {
            SQLiteStatement statement =  new SQLiteStatement(this, "DELETE FROM " + table +
                    (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
            try {
                return statement.executeUpdateDelete();
            } finally {
                statement.close();
            }
        } finally {
            releaseReference();
        }
    }
  • 第一个参数仍然是表名,这个已经没什么好说的了

  • 第二、第三个参数又是用于约束删除某一行或某几行的数据,不指定的话默认就是删除所有行

    和上头的一模一样

数据查找:

使用SQLiteDatabase的query()方法进行查找

query()方法有4个重载版本, 使用7个参数的作为示例:

在这里插入图片描述

源码:

public Cursor query(String table, String[] columns, String selection,
            String[] selectionArgs, String groupBy, String having,
            String orderBy) {

        return query(false, table, columns, selection, selectionArgs, groupBy,
                having, orderBy, null /* limit */);
    }

各个参数详解:

在这里插入图片描述

其中的group by为SQL语法, 现学现用看这里

返回值为Cursor类型, 关于Cursor的常用操作看这里

Cursor的特点:

  • Cursor 是每行的集合

    意味着通常是一行一行操作的, 先定位到行, 在根据列的名称或下标定位到具体的值

  • 使用 moveToFirst() 定位第一行

  • 你必须知道每一列的名称

  • 你必须知道每一列的数据类型

  • Cursor 是一个随机的数据源

  • 所有的数据都是通过下标取得

Cursor获取数据的常规操作:

  1. 通过列名获得对应的index下标
  2. 将下标传入不同类型的get函数获得值
  3. 循环获取, 直到全部取完

Cursor 的一些重要方法:

  • close() 关闭Cursor对象,释放资源

  • copyStringToBuffer(int columnIndex, CharArrayBuffer buffer)
    检索请求的列文本并将其存储在提供的缓冲区中。
    如果缓冲区大小不够,将分配一个新的char缓冲区并将其分配给CharArrayBuffer.data

    在这里插入图片描述

  • getColumnCount() 返回列的总数

  • getColumnIndex(String columnName) 返回给定列名从零开始的索引,如果不存在返回-1

  • getColumnIndexOrThrow(String columnName)
    返回给定列名的从零开始的索引,如果该列不存在,则抛出IllegalArgumentException。如果您不确定某个列是否存在,那么使用getColumnIndex(java.lang.String)并检查-1,它比捕获异常更有效。

  • getColumnName(int columnIndex) 从给定的索引返回列名

  • getColumnNames() 返回一个字符串数组的列名

  • getCount() 返回Cursor 中的行数

  • moveToFirst()

    将光标移到第一行
    如果游标为空,则此方法将返回false。

  • moveToLast() 移动光标到最后一行

  • moveToNext()

    将光标移动到下一行
    如果游标已经经过结果集中的最后一项,则此方法将返回false。

  • moveToPosition(int position) 移动光标到一个绝对的位置

  • moveToPrevious() 移动光标到上一行

使用SQL操作数据库:

这里就是使用SQLiteDatabase的execSQL()方法执行SQL语句, 就是这样

由于SQL是现学现用的, 这个暂时不用

6.5 使用LitePal操作数据库:

参考这里, 讲的很充分, 下头仅仅是对这里的补充, 需要与此配合使用才能真正的复习

配置LitePal:

创建LitePal.xml配置信息时, 需要通过Android Studio进行创建:

在这里插入图片描述

创建成功后会在Android模式中显示, 而在文件管理器中创建的不会显示…(位置不对)

在这里插入图片描述

表明和版本号都在litepal.xml中:

<Litepal>
    <dbname value="Book"/>
    <version value="1"/>

    <list>
        这里需要注意包名, 并且不需要带上个package
        <mapping class="com.example.litepaldemo.Book" />
    </list>
</Litepal>

而后是在AndroidManifest.xml中配置一下LitaPalApplication:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.litepaldemo">

    <application
        添加这一个:         
        android:name="org.litepal.LitePalApplication"
                 
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

创建数据库:

首先是创建一个数据库信息类, extends LitePalSupport 用于使用此类对后头的数据进行操作

其中各个数据成员为表的列

并且如果需要使用id(自增), 其类型必须为int或long

package com.example.litepaldemo;

import org.litepal.crud.LitePalSupport;

/**
 * @author Janus
 * @create 2020-02-19 7:48
 */
public class Book extends LitePalSupport {
    private int id;
    private String name;
    private String author;
    private double price;
    private int page;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getPage() {
        return page;
    }

    public void setPage(int page) {
        this.page = page;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

而后, 在主代码中使用LitePal的static方法: getDatabase() 方法创建数据库

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

        bookStore=new Book();
        LitePal.getDatabase();
    //...
}       

由于表名和版本号上头都定义过了, 所以这个方法没有任何参数

其返回一个SQLiteDatabase对象, 可以实现上头6.4中的操作

操作数据库:

之前说到, 操作数据库需使数据库信息类继承extends LitePalSupport

但是这里不同的是, 不需要重写构造函数, 他的构造函数没有任何参数

    this.price = price;
}

public String getAuthor() {
    return author;
}

public void setAuthor(String author) {
    this.author = author;
}

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = name;
}

}


而后, 在主代码中使用LitePal的static方法: getDatabase() 方法创建数据库

```java
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bookStore=new Book();
        LitePal.getDatabase();
    //...
}       

由于表名和版本号上头都定义过了, 所以这个方法没有任何参数

其返回一个SQLiteDatabase对象, 可以实现上头6.4中的操作

操作数据库:

之前说到, 操作数据库需使数据库信息类继承extends LitePalSupport

但是这里不同的是, 不需要重写构造函数, 他的构造函数没有任何参数

而后就都是按照上头的教程来跑

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值