com.tencent.wcdb.sample.repairdb.MainActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.tencent.wcdb.sample.repairdb.MainActivity.java

Source

/*
 * Tencent is pleased to support the open source community by making
 * WCDB available.
 *
 * Copyright (C) 2017 THL A29 Limited, a Tencent company.
 * All rights reserved.
 *
 * Licensed under the BSD 3-Clause License (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 *       https://opensource.org/licenses/BSD-3-Clause
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.tencent.wcdb.sample.repairdb;

import android.database.Cursor;
import android.os.AsyncTask;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;

import com.tencent.wcdb.database.SQLiteDatabase;
import com.tencent.wcdb.database.SQLiteException;
import com.tencent.wcdb.database.SQLiteOpenHelper;
import com.tencent.wcdb.repair.RepairKit;
import com.tencent.wcdb.sample.repairdb.R;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "WCDB.RepairDBSample";

    private SQLiteDatabase mDB;
    private final SQLiteOpenHelper mDBHelper = new DBHelper(this);

    private ListView mListView;
    private SimpleCursorAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Extract test database from assets.
        new AsyncTask<Void, Void, Void>() {
            @Override
            protected Void doInBackground(Void... params) {
                File dbFile = getDatabasePath(DBHelper.DATABASE_NAME);

                if (!dbFile.exists()) {
                    dbFile.getParentFile().mkdirs();

                    InputStream in = null;
                    OutputStream out = null;
                    try {
                        byte[] buffer = new byte[1024];
                        in = getAssets().open(DBHelper.DATABASE_NAME);
                        out = new FileOutputStream(dbFile);
                        int len;
                        while ((len = in.read(buffer)) != -1) {
                            out.write(buffer, 0, len);
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    } finally {
                        if (in != null)
                            try {
                                in.close();
                            } catch (IOException e) {
                            }
                        if (out != null)
                            try {
                                out.close();
                            } catch (IOException e) {
                            }
                    }
                }
                return null;
            }
        }.execute();

        setContentView(R.layout.activity_main);

        mListView = (ListView) findViewById(R.id.list);
        mAdapter = new SimpleCursorAdapter(this, R.layout.main_listitem, null, new String[] { "a", "b" },
                new int[] { R.id.list_tv_a, R.id.list_tv_b }, 0);

        mListView.setAdapter(mAdapter);

        findViewById(R.id.btn_init_db).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AsyncTask<Void, Void, SQLiteException>() {
                    @Override
                    protected void onPreExecute() {
                        mAdapter.changeCursor(null);
                    }

                    @Override
                    protected SQLiteException doInBackground(Void... params) {
                        if (mDB != null && mDB.isOpen()) {
                            mDBHelper.close();
                            mDB = null;
                        }

                        try {
                            mDBHelper.setWriteAheadLoggingEnabled(true);
                            mDB = mDBHelper.getWritableDatabase();

                            // After successfully opened the database, backup its master info.
                            RepairKit.MasterInfo.save(mDB, mDB.getPath() + "-mbak", DBHelper.PASSPHRASE);
                        } catch (SQLiteException e) {
                            // Failed to open database, probably due to corruption.
                            mDB = null;
                            return e;
                        }

                        return null;
                    }

                    @Override
                    protected void onPostExecute(SQLiteException e) {
                        if (e == null) {
                            // Database is successfully opened, query and refresh ListView.
                            Cursor cursor = mDB.rawQuery("SELECT rowid as _id, a, b FROM t1;", null);
                            mAdapter.changeCursor(cursor);

                            Toast.makeText(MainActivity.this, "Database is successfully opened.",
                                    Toast.LENGTH_SHORT).show();
                        } else {
                            // Database could not be opened, show toast.
                            Toast.makeText(MainActivity.this,
                                    "Database cannot be opened, exception: " + e.getMessage(), Toast.LENGTH_LONG)
                                    .show();
                        }
                    }
                }.execute();
            }
        });

        findViewById(R.id.btn_corrupt_db).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new AsyncTask<Void, Void, Exception>() {
                    @Override
                    protected void onPreExecute() {
                        mAdapter.changeCursor(null);
                    }

                    @Override
                    protected Exception doInBackground(Void... params) {
                        if (mDB != null && mDB.isOpen()) {
                            mDBHelper.close();
                            mDB = null;
                        }

                        // Write random noise to the first page to corrupt the database.
                        RandomAccessFile raf = null;
                        try {
                            File dbFile = getDatabasePath(DBHelper.DATABASE_NAME);
                            raf = new RandomAccessFile(dbFile, "rw");
                            byte[] buffer = new byte[1024];
                            new Random().nextBytes(buffer);
                            raf.seek(0);
                            raf.write(buffer);
                        } catch (IOException e) {
                            return e;
                        } finally {
                            if (raf != null)
                                try {
                                    raf.close();
                                } catch (IOException e) {
                                }
                        }

                        return null;
                    }

                    @Override
                    protected void onPostExecute(Exception e) {
                        if (e == null) {
                            Toast.makeText(MainActivity.this, "Database is now CORRUPTED!", Toast.LENGTH_SHORT)
                                    .show();
                        } else {
                            Toast.makeText(MainActivity.this, "Unable to overwrite database: " + e.getMessage(),
                                    Toast.LENGTH_LONG).show();
                        }
                    }
                }.execute();
            }
        });

        findViewById(R.id.btn_repair_db).setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                new AsyncTask<Void, Void, SQLiteException>() {
                    @Override
                    protected void onPreExecute() {
                        mAdapter.changeCursor(null);
                    }

                    @Override
                    protected SQLiteException doInBackground(Void... params) {
                        if (mDB != null && mDB.isOpen()) {
                            mDBHelper.close();
                            mDB = null;
                        }

                        RepairKit.MasterInfo master = null;
                        File dbFile = getDatabasePath(DBHelper.DATABASE_NAME);
                        File masterFile = new File(dbFile.getPath() + "-mbak");
                        File newDbFile = getDatabasePath(DBHelper.DATABASE_NAME + "-recover");

                        if (masterFile.exists()) {
                            try {
                                master = RepairKit.MasterInfo.load(masterFile.getPath(), DBHelper.PASSPHRASE, null);
                            } catch (SQLiteException e) {
                                // Could not load master info. Maybe backup file itself corrupted?
                            }
                        }

                        RepairKit repair = null;
                        try {
                            repair = new RepairKit(dbFile.getPath(), // corrupted database file
                                    DBHelper.PASSPHRASE, // passphrase to the database
                                    DBHelper.CIPHER_SPEC, // cipher spec to the database
                                    master // backup master info just loaded
                            );

                            if (newDbFile.exists())
                                newDbFile.delete();

                            SQLiteDatabase newDb = SQLiteDatabase.openOrCreateDatabase(newDbFile,
                                    DBHelper.PASSPHRASE, DBHelper.CIPHER_SPEC, null, DBHelper.ERROR_HANDLER);
                            boolean result = repair.output(newDb, 0);
                            if (!result) {
                                throw new SQLiteException("Repair returns false on output.");
                            }

                            newDb.setVersion(DBHelper.DATABASE_VERSION);
                            newDb.close();
                            repair.release();
                            repair = null;

                            if (!dbFile.delete() || !newDbFile.renameTo(dbFile))
                                throw new SQLiteException("Cannot rename database.");
                        } catch (SQLiteException e) {
                            return e;
                        } finally {
                            if (repair != null)
                                repair.release();
                        }

                        return null;
                    }

                    @Override
                    protected void onPostExecute(SQLiteException e) {
                        if (e == null) {
                            Toast.makeText(MainActivity.this, "Repair succeeded.", Toast.LENGTH_SHORT).show();
                        } else {
                            Toast.makeText(MainActivity.this, "Repair failed: " + e.getMessage(), Toast.LENGTH_LONG)
                                    .show();
                        }
                    }
                }.execute();
            }
        });
    }
}