五种数据存储方式
- 文件存储是一种较常用的方法,与 Java的文件存储类似,都是通过 I/O 流的形式存储数据。
- SharedPreferences 时 Android提供的用来存储一些简单的配置信息的一种机制。
- SQLite数据库是 Android自带的一个轻量级数据库,支持基本 SQL语法。
- ContentProvider是 Android四大组件之一,可以将自己的数据共享给其他应用程序。
- 网络存储是通过网络提供的存储空间来存储 / 获取数据信息。
读取各目录下的文件
- 操作sd卡文件、读写文件
- 操作assets目录下的文件
- 操作raw目录下的文件
- 操作res目录下的文件
void testAssets() throws IOException {
// 第一种,直接读取路径
WebView webView = new WebView(this);
webView.loadUrl("file:///android_asset/test.html");
try {
// open的只能是文件,不能是文件夹
InputStream inputStream = getResources().getAssets().open("test.html");
} catch (IOException e) {
e.printStackTrace();
}
// 读列表
String[] filenames = getAssets().list("images");
// 读图片
InputStream inputStream = getAssets().open("images/test.jpg");
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
// 读音乐
AssetFileDescriptor assetFileDescriptor =
getAssets().openFd("test.mp3");
MediaPlayer player = new MediaPlayer();
player.reset();
player.setDataSource(
assetFileDescriptor.getFileDescriptor(),
assetFileDescriptor.getStartOffset(),
assetFileDescriptor.getLength());
player.prepare();
player.start();
}
void testResFile() {
InputStream inputStream = getResources().openRawResource(R.id.文件名);
getResources().getColor(R.color.);
getResources().getString(R.string.);
getResources().getDrawable(R.drawable.);
}
void testSDCard() {
File file = new File("/sdcard/test/a.txt");
String filePath = Environment.getExternalStorageDirectory().getAbsolutePath();
Environment.getDataDirectory(); // 获取Android中的data数据目录
Environment.getDownloadCacheDirectory(); // 获取下载的缓存
}
1、文件存储
-
文件存储是Android中最基本的一种数据存储方式,Android中的文件存储分为内部存储和外部存储。
- 内部存储:将应用程序中的数据以文件方式存储到设备的内部(位于data/data//files/目录),当创建的应用程序被卸载时,其内部存储文件也随之被删除。
-
外部存储:是将文件存储到一些外部设备上,例如SD卡或者设备内嵌的存储卡(通常位于mnt/sdcard目录下,不同厂商生产的手机路径可能不同)。属于永久性存储方式。
— 获取外部存储的权限:android.permission.WRITE_EXTERNAL_STORAGE
-
1.1 首先创建一个Module,然后在 activity_main.xml文件中添加组件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity"> <TextView android:id="@+id/tv_hello" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="文件读取案例..." /> <Button android:id="@+id/btn_save" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="保存"/> <Button android:id="@+id/btn_read" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="读取" /> </LinearLayout>
-
1.2 在MainActivity中实现View.OnClickListener接口,并为组件实例化和设置监听事件
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView mTextView; // 声明 TextView private Button mSaveButton; private Button mReadButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView = findViewById(R.id.tv_hello); mSaveButton = findViewById(R.id.btn_save); mReadButton = findViewById(R.id.btn_read); // 设置保存和读取按钮的点击事件 mSaveButton.setOnClickListener(this); mReadButton.setOnClickListener(this); } // 保存文件 public void saveFileInApplication() { String fileName = "data.txt"; String content = "这是我们演示的文件存储示例"; FileOutputStream fos; try { // 以追加模式创建 data.txt文件,并写入内容保存文件到应用程序下 // openFileOutput(文件名, 操作模式),MODE_PRIVATE:私有覆盖模式,后写入的内容会覆盖前面写入的内容 fos = openFileOutput(fileName, MODE_PRIVATE); // 写入获取字的符串的字节流 fos.write(content.getBytes()); fos.close(); Toast.makeText(this, "文件保存成功", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "文件保存失败", Toast.LENGTH_SHORT).show(); } } // 读取文件 public void readFileInApplication() { String content = ""; FileInputStream fis; try { // 读取"data.txt"文件中的字节流,并将返回的文件输入流赋值给 fis fis = openFileInput("data.txt"); // 创建字节数组,通过 fis.available()方法获取 fis的可读取字节数,并作为字节数组的最大长度 byte[] buffer = new byte[fis.available()]; // 将读取的内容存储到字节数组当中 fis.read(buffer); content = new String(buffer); fis.close(); // 将读取的内容设置给TextView显示出来 mTextView.setText(content); Toast.makeText(this, "读取成功", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, "读取失败", Toast.LENGTH_SHORT).show(); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_save: saveFileInApplication(); break; case R.id.btn_read: readFileInApplication(); break; } } }
File存储核心语句:
- openFileOutput(String name, int mode) : 保存文件内容,打开指定的私有文件输出流;
- 返回参数类型为FileOutputStream;
- name : 为要打开的文件名,不包含路径分隔符;
- mode : 为文件操作模式。
Mode的取值:
- Environment.MODE_PRIVATE: 为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下写入的内容会覆盖原文件的内容;
- Environment.MODE_APPEND: 检查文件是否存在,存在就往文件追加内容,否则就创建新文件。
-
1.3 运行Module,在Android Studio的右下角Device File Explorer中的data/data/文件夹下可以找到我们新创建的data.txt文件,同样也可以打开DDMS查看,关于Android Studio3.0之后如何打开DDMS?
2、使用 SharedPreferences 方便的存储数据
-
SharedPreferences 是什么?
SharedPreferences 是 Android平台上一个轻量级的存储类。
SharedPreferences 中存储的数据是以 key/value 键值对的形式保存在XML文件中,该文件位于“data/data//shared_prefs”文件夹中。
-
SharedPreferences 适用于哪些地方?
存储一些简单的值,应用程序的配置参数,如用户名、密码等。
-
2.1 使用SharedPreferences 保存数据的方法 :
- 1、使用Activity类的getSharedPreferences (String name, int mode) 方法获得SharedPreferences 对象,其中参数 name 指定存储键值对的文件名称,mode 则指定文件操作模式。
- 2、使用SharedPreferences 的edit() 方法获得SharedPreferences.Editor对象。
- 3、通过SharedPreferences.Editor的putXxx(String key, Xxx value) 方法写入键值对。
- 4、通过SharedPreferences.Editor的commit()方法提交要保存的键值对。
-
2.2 首先创建一个Module,并在activity_main.xml文件中通过约束布局快速搭建界面
-
2.3 创建一个SPSaveQQ类,并在该类中创建两个方法用于保存和读取账号密码
public class SPSaveQQ { public static final String ACCOUNT = "account"; public static final String PASSWORD = "password"; // 保存QQ账号和密码到 data.xml文件中 public static boolean saveUserInfo(Context context, String account, String password) { // 创建 SharedPreferences对象。参数 data是文件名,Context.MODE_PRIVATE是文件操作模式 SharedPreferences sp = context.getSharedPreferences("data", Context.MODE_PRIVATE); // 创建 Editor对象 SharedPreferences.Editor editor = sp.edit(); // 保存用户名和密码 editor.putString(ACCOUNT, account); editor.putString(PASSWORD, password); // 编辑结束,调用该方法提交 // editor.commit(); // 和网络相关,IO操作相关的,都要用异步 // 后台写数据,另开线程 editor.apply(); return true; } // 从 data.xml文件中获取存储的QQ账号和密码 public static Map<String, String> getUserInfo(Context context) { SharedPreferences sp = context.getSharedPreferences("data", Context.MODE_PRIVATE); // 使用getString()方法读取用户名和密码。第一个参数为 key,第二个参数为 defValue String account = sp.getString(ACCOUNT, null); String password = sp.getString(PASSWORD, null); // 创建 HashMap用来存储用户登录信息 Map<String, String> userMap = new HashMap<>(); // 将刚才读取的用户名和密码值添加到 userMap中 userMap.put(ACCOUNT, account); userMap.put(PASSWORD, password); return userMap; } }
-
2.4 在MainActivity中初始化EditView和Button组件,并为登录按钮设置点击事件
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private EditText mAccount; private EditText mPassword; private Button mLogin; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化界面 initView(); // 调用SPSaveQQ.getUserInfo()方法获取保存在SharedPreferences文件当中的账号和密码 Map<String, String> userInfo = SPSaveQQ.getUserInfo(this); if (userInfo != null) { mAccount.setText(userInfo.get(SPSaveQQ.ACCOUNT)); mPassword.setText(userInfo.get(SPSaveQQ.PASSWORD)); } } public void initView() { mAccount = findViewById(R.id.et_account); mPassword = findViewById(R.id.et_password); mLogin = findViewById(R.id.btn_login); // 为登录按钮设置点击事件 mLogin.setOnClickListener(this); } @Override public void onClick(View v) { // 点击登录按钮时,获取文本框中的账号和密码 String account = mAccount.getText().toString(); String password = mPassword.getText().toString(); // 校验用户是否输入账号和密码 if (TextUtils.isEmpty(account)) { Toast.makeText(this, "请输入QQ账号", Toast.LENGTH_SHORT).show(); return; } if (TextUtils.isEmpty(password)) { Toast.makeText(this, "请输入密码", Toast.LENGTH_SHORT).show(); return; } // 登录成功 Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show(); // 保存用户输入的QQ账号和密码 boolean isSaveSuccess = SPSaveQQ.saveUserInfo(this, account, password); if (isSaveSuccess) { Toast.makeText(this, "保存成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "保存失败", Toast.LENGTH_SHORT).show(); } } }
-
2.5 运行Module,在Android Studio的右下角Device File Explorer中的data/data//shared_prefs文件夹下可以看到我们创建的data.xml
3、SQLite数据库简介
SQLite 是一个轻量级数据库,占用资源非常低,在内存中只需占用几百KB的存储空间。
SQLite 是遵守ACID的关系型数据库管理系统,ACID是指数据库事务正确执行的四个基本要素,即原子性(Atomicity)、一致性(Consistency)、隔离性(lsolation)、持久性(Durability)。
- 特色:轻量级、独立、隔离、跨平台、多语言接口、安全性
增删改查示例
-
3.1 新建一个Module,并添加一个DatabaseHelper类
/** * DatabaseHelper作为一个访问 SQLite的助手类,提供两个方面的功能: * 第一:getReadableDatabase(), getWritableDatabase()可以获得 SQLiteDatabase对象,通过该对象可以对数据库进行操作 * 第二:提供了 onCreate()和 onUpgrade()两个回调函数,允许我们在创建和升级数据库时,进行自己的操作 */ public class DatabaseHelper extends SQLiteOpenHelper { public static final String DATABASE_NAME = "test.db"; public DatabaseHelper(@Nullable Context context) { super(context, DATABASE_NAME, null, 1); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("create table tb_user(id INTEGER primary key autoincrement," + "username varchar(20) not null, password varchar(20) not null)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 当数据库的版本号增加时调用 } }
-
3.2 在activity_main.xml中添加一个Button按钮并设置点击事件
<Button android:id="@+id/btn_index_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAllCaps="false" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" />
-
点击按钮跳转到DatabaseButtonActivity界面
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.btn_index_main); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(MainActivity.this, DatabaseButtonActivity.class); startActivity(intent); } }); } }
-
-
3.2 创建activity_database.xml和DatabaseButtonActivity,并在布局文件中添加组件
-
在DatabaseButtonActivity中为四个按钮设置点击事件【别忘了加入AndroidManifest.xml中】
public class DatabaseButtonActivity extends AppCompatActivity { private static final String TAG = "MySQLite"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_database); initView(); } public void initView() { Button insertButton = findViewById(R.id.btn_insert); Button deleteButton = findViewById(R.id.btn_delete); Button updateButton = findViewById(R.id.btn_update); Button selectButton = findViewById(R.id.btn_select); insertButton.setOnClickListener(new InsertListener()); deleteButton.setOnClickListener(new DeleteListener()); updateButton.setOnClickListener(new UpdateListener()); selectButton.setOnClickListener(new SelectListener()); } /** * 插入数据按钮的监听器 */ public class InsertListener implements View.OnClickListener { @Override public void onClick(View v) { // 1、创建 DatabaseHelper助手类对象 DatabaseHelper databaseHelper = new DatabaseHelper(DatabaseButtonActivity.this); // 2、创建 SQLiteDatabase对象 SQLiteDatabase db = databaseHelper.getWritableDatabase(); // 3、创建 ContentValues对象 ContentValues values = new ContentValues(); // ContentValues类似于 Map类,通过键值对的形式存储数据 values.put("username", "admin"); values.put("password", "admin123"); // 插入数据 long id = db.insert("tb_user", null, values); db.close(); if (id != -1) { Log.i(TAG, "插入成功"); } } } /** * 删除数据按钮的监听器 */ public class DeleteListener implements View.OnClickListener { @Override public void onClick(View v) { DatabaseHelper databaseHelper = new DatabaseHelper(DatabaseButtonActivity.this); SQLiteDatabase db = databaseHelper.getWritableDatabase(); String whereClause = "id=?"; String[] whereArgs = {"1"}; int number = db.delete("tb_user", whereClause, whereArgs); db.close(); Log.i(TAG, "删除:" + number + "行受影响"); } } /** * 修改数据按钮的监听器 */ public class UpdateListener implements View.OnClickListener { @Override public void onClick(View v) { DatabaseHelper databaseHelper = new DatabaseHelper(DatabaseButtonActivity.this); SQLiteDatabase db = databaseHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("username", "zhangsan"); String whereClause = "id=?"; String[] whereArgs = {"1"}; int number = db.update("tb_user", values, whereClause, whereArgs); db.close(); Log.i(TAG, "修改:" + number + "行受影响"); } } /** * 查询数据按钮的监听器 */ public class SelectListener implements View.OnClickListener { @Override public void onClick(View v) { DatabaseHelper databaseHelper = new DatabaseHelper(DatabaseButtonActivity.this); SQLiteDatabase db = databaseHelper.getWritableDatabase(); String whereClause = "id=?"; String[] whereArgs = {"1"}; // Cursor是一个游标接口,提供了遍历查询结果的方法。query()方法返回的是一个行数集合 Cursor Cursor cursor = db.query("tb_user", null, whereClause, whereArgs, null, null, null); boolean result = cursor.moveToNext(); if (result) { // 根据列名获得列索引 // getString()方法获取记录值,getColumnIndex()方法获取列的索引 String username = cursor.getString(cursor.getColumnIndex("username")); String password = cursor.getString(cursor.getColumnIndex("password")); cursor.close(); db.close(); Log.i(TAG, "查询记录----用户名:" + username + ", 密码:" + password); } else { Log.i(TAG, "查询记录为空"); } } } }
-
3.3 运行效果预览【在线预览】
4、ContentProvider
什么是Content Provider
- 应用程序间共享数据的一种方式
- 为存储和获取数据提供了统一的接口
- Android为常见的一些数据提供了默认的ContentProvider
- 四大组件之一
Google是怎么定义Content Provider的?
- 内容提供者将一些特定的应用程序提供给其他应用程序使用。
- 数据可以存储于文件系统、SQLite数据库或其他方式。
- 内容提供者继承于ContentProvider基类,为其他应用程序取用和存储它管理的数据实现了一套标准方法。
- 应用程序并不直接调用这些方法,而是使用ContentResolver对象,调用它的方法作为替代。
- ContentResolver可以与任意内容提供者进行会话,与其合作来对所有相关交互通讯进行管理。
ContentProvider的实现过程
- Database
- Uri
- UriMatcher
- ContentProvider
- query/insert/update/delete
- AndroidManifest.xml
5、网络编程数据处理
5.1 如何请求网络数据
-
在AndroidManifest.xml中申请权限
<uses-permission android:name="android.permission.INTERNET"/>
-
需要异步的获取数据,否则会直接闪退,因为不能在主线程中获取数据
5.2 请求百度数据,并把数据显示出来
-
5.2.1 我们新建一个项目,在activity_main.xml中添加按钮,并在MainActivity中设置点击事件
<Button android:id="@+id/btn_index_main" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAllCaps="false" android:text="NetWork"/>
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = findViewById(R.id.btn_index_main); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent = new Intent(); intent.setClass(MainActivity.this, NetWorkActivity.class); startActivity(intent); } }); }
-
5.2.2 创建一个NetWorkActivity和activity_network.xml布局文件,并添加到AndroidManifest中
public class NetWorkActivity extends AppCompatActivity implements View.OnClickListener { private EditText mHttpEditText; private TextView mShowResultTextView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_network); initView(); } public void initView() { mHttpEditText = findViewById(R.id.et_http); Button mGetDataButton = findViewById(R.id.btn_getData); mShowResultTextView =findViewById(R.id.tv_showResult); mGetDataButton.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_getData: String url = getEditTextUrl(); // 请求网络数据,需要在 AndroidManifest中注册网络权限 new RequestNetworkDataTask().execute(url); //String content = requestData(url); //mShowResultTextView.setText(content); break; } } // 获取 Url地址 public String getEditTextUrl() { return mHttpEditText != null ? mHttpEditText.getText().toString() : ""; } // 返回请求数据 public String requestData(String urlString) { try { URL url = new URL(urlString); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); // 设置连接超时 connection.setConnectTimeout(30000); // 设置请求方法 connection.setRequestMethod("GET"); // 建立连接 connection.connect(); int responseCode = connection.getResponseCode(); String responseMessage = connection.getResponseMessage(); InputStream inputStream = connection.getInputStream(); Reader reader = new InputStreamReader(inputStream); char[] buffer = new char[1024]; reader.read(buffer); String content = new String(buffer); return content; } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } // 异步任务处理 public class RequestNetworkDataTask extends AsyncTask<String, Integer, String> { @Override protected void onPreExecute() { // 在后台 work之前,在主线程中 super.onPreExecute(); Toast.makeText(NetWorkActivity.this, "加载中...", Toast.LENGTH_SHORT).show(); } @Override protected String doInBackground(String... strings) { String result = requestData(strings[0]); return result; } @Override protected void onPostExecute(String s) { // 执行完之后再主线程中 super.onPostExecute(s); mShowResultTextView.setText(s); System.out.println(s); Toast.makeText(NetWorkActivity.this, "加载完成", Toast.LENGTH_SHORT).show(); } } }
-
5.2.3 效果预览
注意:Genymotion需要进行网络连接哦!
5.3 数据解析
-
XML
- SAX : 基于事件驱动的
- PULL : android系统本身自适用的
- DOM : 文件流的形式,会把整个文件加载出来
-
JSON
-
JSONObject
-
JSONArray
-
other…
-
GSON
-
fastjson
-
网络状态处理及常用网络开源库
- 网络状态处理
- ConnectivityManager
- NetworkInfo
- 常用网络开源库
- android-async-http
- Volley
- OKHttp
- Retrofit