[安卓] 数据库的Andr​​oid仲 – P5备份和导入

今天忙碌的几天对杂乱无章所以新来的一系列写在Android的数据库. 部分 5, 也是最后一部分,我会引导你如何备份和导入数据库的android.

步 1: 允许读取和写入数据,在AndroidManifest.xml中的存储​​卡

AndroidManifest.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?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>

步 2: 创建菜单中MainActivity允许备份和导入

帽山一文件menu_main.xml – 文件菜单CUA MainActivity

res/menu/menu_main.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<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文件更新赶上事件菜单:

MainActivity.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
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 方法 onFinishExportonFinishImport 其, 2 这种方法允许我们处理通知或执行备份和导入完成后,.

当您启动事件中调用菜单 2 方法 backupData.exportToSDbackupData.importFromSD 执行.

步 3: 执行写类BackupData

在前面描述她的脚步. 当用户选择backupdata那么我们将数据备份到位于某个文件夹的存储卡, 在此独自它保存在文件夹 MyNote. 该程序会自动创建该文件夹,如果它不存在. 备份文件的名称将是数据库的名称以及日期时间, 小时分秒作为第一视频后.

当用户选择导入数据, 如果你想导入之前备份您的现有数据,我们会问. 如果是这样,备份,然后列出导入先前的备份文件. 否则要导入的文件只只列表.

为了避免数据丢失,并避免在新的结构有些旧数据现有数据的情况下,错误, 我们删除现有的所有数据记录,然后将备份文件复制到一个临时数据库 (临时数据库) 然后复制表中的当前数据的整个记录​​和临时DATABSE. 例如你当前的数据表注, 此表列LAST_MODIFIED,在老DATABSE没有, 同时,如果直接复制导致了数据库,并没有last_modified列出错的应用.

这里是代码是比较明确的解释. 你可以阅读.

BackupData.java
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
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.

该界面导入的文件列表

res/layout/list_backup_file.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
<?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一些变化

res/values/string.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<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>

您可以 在这里下载源代码.

这就是它,然后. 祝你学习好.

在本教程的帖子 数据库的Andr​​oid仲nguyenvanquan7826