[安卓] 数据库的Android仲 – P5备份和导入
今天忙碌的几天对杂乱无章所以新来的一系列写在Android的数据库. 部分 5, 也是最后一部分,我会引导你如何备份和导入数据库的android.
步 1: 允许读取和写入数据,在AndroidManifest.xml中的存储卡
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="cachhoc.net.tut.demodatabase" > <!-- set can read and write on sd card--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".NoteActivity" android:label="@string/title_activity_note" > </activity> </application> </manifest>
帽山一文件menu_main.xml – 文件菜单CUA MainActivity
<menu 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" tools:context=".MainActivity"> <item android:id="@+id/menu_more" android:icon="@drawable/ic_more" android:title="Menu" app:showAsAction="always"> <menu> <item android:id="@+id/menu_backup" android:orderInCategory="100" android:title="@string/backup_data" app:showAsAction="never" /> <item android:id="@+id/menu_import" android:orderInCategory="100" android:title="@string/import_data" app:showAsAction="never" /> </menu> </item> </menu>
你自己也如上所述 1 项目 – 此产品是一个按钮 3 点 (溢出). 当你点击按钮 3 点会出现 2 菜单备份VA进口 (看视频) 是 2 它里面的物品.
MainActivity.java文件更新赶上事件菜单:
package cachhoc.net.tut.demodatabase; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.Toast; import java.util.ArrayList; import java.util.List; public class MainActivity extends AppCompatActivity implements View.OnClickListener, BackupData.OnBackupListener { private ItemNoteAdapter adapter; private List<Note> listNote = new ArrayList<>(); private Context context; private DatabaseHelper db; private BackupData backupData; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = this; db = new DatabaseHelper(context); backupData = new BackupData(context); backupData.setOnBackupListener(this); connectView(); } /** * connect java with xml view */ private void connectView() { // find Float Action Button findViewById(R.id.fab).setOnClickListener(this); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_note); // If the size of views will not change as the data changes. recyclerView.setHasFixedSize(true); // Setting the LayoutManager. RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); // Setting the adapter. adapter = new ItemNoteAdapter(context, listNote); recyclerView.setAdapter(adapter); } /** * update list note when resume (open app or finish NoteActivity) */ public void onResume() { super.onResume(); updateListNote(); } /** * select all note from database and set to ls * use for loop to add into listNote. * We must add all item in ls into listNote then adapter can update * we add reverse ls to show new note at top of list */ private void updateListNote() { // clear old list listNote.clear(); // add all notes from database, reverse list ArrayList<Note> ls = db.getListNote("SELECT * FROM " + DatabaseHelper.TABLE_NOTE); // reverse list for (int i = ls.size() - 1; i >= 0; i--) { listNote.add(ls.get(i)); } adapter.notifyDataSetChanged(); } /** * display note have id */ public static void showNote(Context context, long id) { Intent intent = new Intent(context, NoteActivity.class); // send id to NoteActivity intent.putExtra(NoteActivity.ID, id); context.startActivity(intent); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.fab: showNote(context, NoteActivity.NEW_NOTE); break; default: break; } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_backup: backupData.exportToSD(); break; case R.id.menu_import: backupData.importFromSD(); break; default: break; } return super.onOptionsItemSelected(item); } @Override public void onFinishExport(String error) { String notify = error; if (error == null) { notify = "Export success"; } Toast.makeText(context, notify, Toast.LENGTH_SHORT).show(); } @Override public void onFinishImport(String error) { String notify = error; if (error == null) { notify = "Import success"; updateListNote(); } Toast.makeText(context, notify, Toast.LENGTH_SHORT).show(); } }
你注意到我有 实现接口BackupData.OnBackupListener 在课堂上BackupData (在以后会写) 和覆盖 2 方法 onFinishExport 和 onFinishImport 其, 2 这种方法允许我们处理通知或执行备份和导入完成后,.
当您启动事件中调用菜单 2 方法 backupData.exportToSD 和 backupData.importFromSD 执行.
步 3: 执行写类BackupData
在前面描述她的脚步. 当用户选择backupdata那么我们将数据备份到位于某个文件夹的存储卡, 在此独自它保存在文件夹 MyNote. 该程序会自动创建该文件夹,如果它不存在. 备份文件的名称将是数据库的名称以及日期时间, 小时分秒作为第一视频后.
当用户选择导入数据, 如果你想导入之前备份您的现有数据,我们会问. 如果是这样,备份,然后列出导入先前的备份文件. 否则要导入的文件只只列表.
为了避免数据丢失,并避免在新的结构有些旧数据现有数据的情况下,错误, 我们删除现有的所有数据记录,然后将备份文件复制到一个临时数据库 (临时数据库) 然后复制表中的当前数据的整个记录和临时DATABSE. 例如你当前的数据表注, 此表列LAST_MODIFIED,在老DATABSE没有, 同时,如果直接复制导致了数据库,并没有last_modified列出错的应用.
这里是代码是比较明确的解释. 你可以阅读.
package cachhoc.net.tut.demodatabase; import android.app.ProgressDialog; import android.content.Context; import android.content.DialogInterface; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.Environment; import android.support.v4.app.FragmentActivity; import android.support.v7.app.AlertDialog; import android.view.LayoutInflater; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileChannel; import java.text.SimpleDateFormat; import java.util.Date; public class BackupData { // url for database private final String dataPath = "//data//cachhoc.net.tut.demodatabase//databases//"; // name of main data private final String dataName = DatabaseHelper.DATABASE_NAME; // data main private final String data = dataPath + dataName; // name of temp data private final String dataTempName = DatabaseHelper.DATABASE_NAME + "_temp"; // temp data for copy data from sd then copy data temp into main data private final String dataTemp = dataPath + dataTempName; // folder on sd to backup data private final String folderSD = Environment.getExternalStorageDirectory() + "/MyNote"; private Context context; public BackupData(Context context) { this.context = context; } // create folder if it not exist private void createFolder() { File sd = new File(folderSD); if (!sd.exists()) { sd.mkdir(); System.out.println("create folder"); } else { System.out.println("exits"); } } /** * Copy database to sd card * name of file = database name + time when copy * When finish, we call onFinishExport method to send notify for activity */ public void exportToSD() { String error = null; try { createFolder(); File sd = new File(folderSD); if (sd.canWrite()) { SimpleDateFormat formatTime = new SimpleDateFormat("yyyy_MM_dd__HH_mm_ss"); String backupDBPath = dataName + "_" + formatTime.format(new Date()); File currentDB = new File(Environment.getDataDirectory(), data); File backupDB = new File(sd, backupDBPath); if (currentDB.exists()) { FileChannel src = new FileInputStream(currentDB).getChannel(); FileChannel dst = new FileOutputStream(backupDB).getChannel(); dst.transferFrom(src, 0, src.size()); src.close(); dst.close(); } else { System.out.println("db not exist"); } } } catch (Exception e) { e.printStackTrace(); error = "Error backup"; } onBackupListener.onFinishExport(error); } /** * import data from file backup on sd card * we must create a temp database for copy file on sd card to it. * Then we copy all row of temp database into main database. * It will keep struct of curren database not change when struct backup database is old * * @param fileNameOnSD name of file database backup on sd card */ public void importData(String fileNameOnSD) { File sd = new File(folderSD); // create temp database SQLiteDatabase dbBackup = context.openOrCreateDatabase(dataTempName, SQLiteDatabase.CREATE_IF_NECESSARY, null); String error = null; if (sd.canWrite()) { File currentDB = new File(Environment.getDataDirectory(), dataTemp); File backupDB = new File(sd, fileNameOnSD); if (currentDB.exists()) { FileChannel src; try { src = new FileInputStream(backupDB).getChannel(); FileChannel dst = new FileOutputStream(currentDB) .getChannel(); dst.transferFrom(src, 0, src.size()); src.close(); dst.close(); } catch (FileNotFoundException e) { e.printStackTrace(); error = "Error load file"; } catch (IOException e) { error = "Error import"; } } } /** *when copy old database into temp database success * we copy all row of table into main database */ if (error == null) { new CopyDataAsyncTask(dbBackup).execute(); } else { onBackupListener.onFinishImport(error); } } /** * show dialog for select backup database before import database * if user select yes, we will export curren database * then show dialog to select old database to import * else we onoly show dialog to select old database to import */ public void importFromSD() { final AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppCompatAlertDialogStyle); builder.setTitle(R.string.backup_data).setIcon(R.mipmap.ic_launcher) .setMessage(R.string.backup_before_import); builder.setPositiveButton(R.string.no, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialogListFile(folderSD); } }); builder.setNegativeButton(R.string.yes, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { showDialogListFile(folderSD); exportToSD(); } }); builder.show(); } /** * show dialog list all backup file on sd card * @param forderPath folder conatain backup file */ private void showDialogListFile(String forderPath) { createFolder(); File forder = new File(forderPath); File[] listFile = forder.listFiles(); final String[] listFileName = new String[listFile.length]; for (int i = 0, j = listFile.length - 1; i < listFile.length; i++, j--) { listFileName[j] = listFile[i].getName(); } if (listFileName.length > 0) { // get layout for list LayoutInflater inflater = ((FragmentActivity) context).getLayoutInflater(); View convertView = (View) inflater.inflate(R.layout.list_backup_file, null); final AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppCompatAlertDialogStyle); // set view for dialog builder.setView(convertView); builder.setTitle(R.string.select_file).setIcon(R.mipmap.ic_launcher); final AlertDialog alert = builder.create(); ListView lv = (ListView) convertView.findViewById(R.id.lv_backup); ArrayAdapter<String> adapter = new ArrayAdapter<String>(context, android.R.layout.simple_list_item_1, listFileName); lv.setAdapter(adapter); lv.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { alert.dismiss(); importData(listFileName[position]); } }); alert.show(); } else { final AlertDialog.Builder builder = new AlertDialog.Builder(context, R.style.AppCompatAlertDialogStyle); builder.setTitle(R.string.delete).setIcon(R.mipmap.ic_launcher) .setMessage(R.string.backup_empty); builder.show(); } } /** * AsyncTask for copy data */ class CopyDataAsyncTask extends AsyncTask<Void, Void, Void> { ProgressDialog progress = new ProgressDialog(context); SQLiteDatabase db; public CopyDataAsyncTask(SQLiteDatabase dbBackup) { this.db = dbBackup; } /** * will call first */ @Override protected void onPreExecute() { // TODO Auto-generated method stub super.onPreExecute(); progress.setMessage("Importing..."); progress.show(); } @Override protected Void doInBackground(Void... params) { copyData(db); return null; } /** * end process */ @Override protected void onPostExecute(Void error) { // TODO Auto-generated method stub super.onPostExecute(error); if (progress.isShowing()) { progress.dismiss(); } onBackupListener.onFinishImport(null); } } /** * copy all row of temp database into main database * @param dbBackup */ private void copyData(SQLiteDatabase dbBackup) { DatabaseHelper db = new DatabaseHelper(context); db.deleteNote(null); /** copy all row of subject table */ Cursor cursor = dbBackup.query(true, DatabaseHelper.TABLE_NOTE, null, null, null, null, null, null, null); cursor.moveToFirst(); while (!cursor.isAfterLast()) { Note note = db.cursorToNote(cursor); db.insertNote(note); cursor.moveToNext(); } cursor.close(); context.deleteDatabase(dataTempName); } private OnBackupListener onBackupListener; public void setOnBackupListener(OnBackupListener onBackupListener) { this.onBackupListener = onBackupListener; } public interface OnBackupListener { public void onFinishExport(String error); public void onFinishImport(String error); } }
你需要注意 2 以下:
- 其用于导入数据库AsyncTask的,因为这个过程可能需要很长的时间. 这将有助于展示 1 仿佛没有用户放心的认为具有冷冻应用被处理对话框以指示用户.
-
在副本, 我所说的命令 注意注意= db.cursorToNote(光标); 以前cursorToNote秩序类和私营DatabaseHelper, 你现在要叫 修复公众 NHE.
该界面导入的文件列表
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <ListView android:id="@+id/lv_backup" android:layout_width="match_parent" android:layout_height="match_parent"> </ListView> </LinearLayout>
在文件string.xml一些变化
<resources> <string name="app_name">My Note</string> <string name="hello_world">Hello world!</string> <string name="action_settings">Settings</string> <string name="add">Add</string> <string name="delete">Delete</string> <string name="save">Save</string> <string name="yes">Yes</string> <string name="no">No</string> <string name="backup_data">Backup data</string> <string name="import_data">Import data</string> <string name="backup_before_import">Do you want backup curren database before import an old databas?</string> <string name="select_file">Select file</string> <string name="backup_empty">Backup file is empty</string> <!-- --> <string name="title_note">Title note</string> <string name="content">Content</string> <string name="title_activity_note">Create Note</string> </resources>
您可以 在这里下载源代码.
这就是它,然后. 祝你学习好.
在本教程的帖子 数据库的Android仲 由 nguyenvanquan7826
欢迎您,陆军,
首先,我们发现了一系列的Android你的数据库是非常有用的.
我又试了一次,你的指导下,代码共享的东西 5 备份和导入. 但我不备份和导入失败不, 我检查了代码几次也没有找到其中的错误.
他们在Eclipse代码, 这是你的源代码. 我希望你帮她修复错误. 如果导入库项目,然后重新启动该项目再次失败,直到错误信息是构建是.
http://www.mediafire.com/download/jq14bp7dw4poe0a/SQLite_Demo.rar
军事谢谢!
这种新的考验对不起太忙几天来帮助你.
满足你 2 错误:
– 写入字符串表创建错误:
当您添加KEY_LAST_MODIFIED_NOTE你没有在文本默认的空间,以便在新的安装将不再作记录.
– 错误备份,因为你错了行:
您必须复制从表中TABLE_NOTE不会从数据库中拷贝.
修复:
有错误我backupdata.java
在这条线
SQLiteDatabase DBBACKUP = context.openOrCreateDatabase(dataTempName,
SQLiteDatabase.CREATE_IF_NECESSARY, 空值);
什么是错误? 你可以在这里发布错误或拍照?
你好,
请您可以更新到的gradle Android版本 3. 我有在新版本上运行旧的程序太多的问题. 我是一个初学者,所以我不知道该怎么办的变化. 我在网上什么都试过了, 没有任何帮助.
感谢报告. 该项目在我的教程使用旧版本与您. 你能告诉我你的问题?
你可以做一个例子备份和使用客房持久库恢复? 我有难以适应这一功能使用客房持久库
私人无效了CopyData(SQLiteDatabase DBBACKUP) {
DatabaseHelper DB =新DatabaseHelper(背景);
db.deleteNote(空值);
/** 复制主题表的所有行 */
光标光标= dbBackup.query(真正, DatabaseHelper.TABLE_NOTE, 空值, 空值, 空值, 空值, 空值, 空值, 空值);
cursor.moveToFirst();
而 (!cursor.isAfterLast()) {
注意注意= db.cursorToNote(光标);
db.insertNote(注意);
cursor.moveToNext();
}
cursor.close();
context.deleteDatabase(dataTempName);
}
你好, 对不起它, 我不使用这个库, 所以我不知道,使它.
你好,
我正在使用您的代码创建备份. 从getExternalStorageDirectory开始() 已不推荐使用. 下面怎么写 .
私有的最终String文件夹SD = Environment.getExternalStorageDirectory() + “/MyNote”;
谢谢,
你好, 你可以在这里看到: https://stackoverflow.com/questions/57116335/environment-getexternalstoragedirectory-deprecated-in-api-level-29-java