android debug database 源碼解析

我們今天分析下android debug database 的源碼:

項目地址:

https://github.com/amitshekhariitbhu/Android-Debug-Database

具體作用:

可以在瀏覽器裏面,觀看編輯數據庫以及SP.

如何做到android 無侵入初始化

public class DebugDBEncryptInitProvider extends ContentProvider {


    public DebugDBEncryptInitProvider() {
    }

    @Override
    public boolean onCreate() {
        DebugDB.initialize(getContext(), new DebugDBEncryptFactory());
        return true;
    }

我們看到,很聰明的使用了android 啓動 會調用ContentProvider onCreate 這一現象 ,也就是你直接引入包即可,不用寫任何的install 代碼。

使用ServerSocket 監聽端口

public class ClientServer implements Runnable {

    private static final String TAG = "ClientServer";

    private final int mPort;
    private final RequestHandler mRequestHandler;
    private boolean mIsRunning;
    private ServerSocket mServerSocket;

    public ClientServer(Context context, int port, DBFactory dbFactory) {
        mRequestHandler = new RequestHandler(context, dbFactory);
        mPort = port;
    }

    public void start() {
        mIsRunning = true;
        new Thread(this).start();
    }

    public void stop() {
        try {
            mIsRunning = false;
            if (null != mServerSocket) {
                mServerSocket.close();
                mServerSocket = null;
            }
        } catch (Exception e) {
            Log.e(TAG, "Error closing the server socket.", e);
        }
    }

    @Override
    public void run() {
        try {
            mServerSocket = new ServerSocket(mPort);
            while (mIsRunning) {
                Socket socket = mServerSocket.accept();
                mRequestHandler.handle(socket);
                socket.close();
            }
        } catch (SocketException e) {
            // The server was stopped; ignore.
            Log.e(TAG, "Web SocketException.", e);
        } catch (IOException e) {
            Log.e(TAG, "Web server error.", e);
        } catch (Exception ignore) {
            Log.e(TAG, "Exception.", ignore);
        }
    }

    public void setCustomDatabaseFiles(HashMap<String, Pair<File, String>> customDatabaseFiles) {
        mRequestHandler.setCustomDatabaseFiles(customDatabaseFiles);
    }

    public void setInMemoryRoomDatabases(HashMap<String, SupportSQLiteDatabase> databases) {
        mRequestHandler.setInMemoryRoomDatabases(databases);
    }

    public boolean isRunning() {
        return mIsRunning;
    }
}

這一塊是socket 編程相關代碼。沒有什麼太大的技術難度,但是也是很有用的一塊api,沒有練習過的,可以看下,自己聯繫下。紙上得來終覺淺。

具體處理業務看:

   public void handle(Socket socket) throws IOException {
        BufferedReader reader = null;
        PrintStream output = null;
        try {
            String route = null;

            // Read HTTP headers and parse out the route.
            reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String line;
            while (!TextUtils.isEmpty(line = reader.readLine())) {
                if (line.startsWith("GET /")) {
                    int start = line.indexOf('/') + 1;
                    int end = line.indexOf(' ', start);
                    route = line.substring(start, end);
                    break;
                }
            }

            // Output stream that we send the response to
            output = new PrintStream(socket.getOutputStream());

            if (route == null || route.isEmpty()) {
                route = "index.html";
            }

            byte[] bytes;
            Log.d("RequestHandler",route);
            LogFile.d("RequestHandler",route);
            if (route.startsWith("getDbList")) {
                final String response = getDBListResponse();
                bytes = response.getBytes();
            } else if (route.startsWith("getAllDataFromTheTable")) {
                final String response = getAllDataFromTheTableResponse(route);
                bytes = response.getBytes();
            } else if (route.startsWith("getTableList")) {
                final String response = getTableListResponse(route);
                bytes = response.getBytes();
            } else if (route.startsWith("addTableData")) {
                final String response = addTableDataAndGetResponse(route);
                bytes = response.getBytes();
            } else if (route.startsWith("updateTableData")) {
                final String response = updateTableDataAndGetResponse(route);
                bytes = response.getBytes();
            } else if (route.startsWith("deleteTableData")) {
                final String response = deleteTableDataAndGetResponse(route);
                bytes = response.getBytes();
            } else if (route.startsWith("query")) {
                final String response = executeQueryAndGetResponse(route);
                bytes = response.getBytes();
            } else if (route.startsWith("deleteDb")) {
                final String response = deleteSelectedDatabaseAndGetResponse();
                bytes = response.getBytes();
            } else if (route.startsWith("downloadDb")) {
                bytes = Utils.getDatabase(mSelectedDatabase, mDatabaseFiles);
            } else {
                bytes = Utils.loadContent(route, mAssets);
            }

            if (null == bytes) {
                writeServerError(output);
                return;
            }

            // Send out the content.
            output.println("HTTP/1.0 200 OK");
            output.println("Content-Type: " + Utils.detectMimeType(route));

            if (route.startsWith("downloadDb")) {
                output.println("Content-Disposition: attachment; filename=" + mSelectedDatabase);
            } else {
                output.println("Content-Length: " + bytes.length);
            }
            output.println();
            output.write(bytes);
            output.flush();
        } finally {
            try {
                if (null != output) {
                    output.close();
                }
                if (null != reader) {
                    reader.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

其實就是去讀流,然後處理完結果,輸出流。

asset下面存放的是我們的資源,也就是html,css,js 等

這一塊相關的,有興趣可以自己研究下。相當於html 的編寫。

有關css的加載:

 else {
                bytes = Utils.loadContent(route, mAssets);
            }

    public static byte[] loadContent(String fileName, AssetManager assetManager) throws IOException {
        InputStream input = null;
        try {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            input = assetManager.open(fileName);
            byte[] buffer = new byte[1024];
            int size;
            while (-1 != (size = input.read(buffer))) {
                output.write(buffer, 0, size);
            }
            output.flush();
            return output.toByteArray();
        } catch (FileNotFoundException e) {
            return null;
        } finally {
            try {
                if (null != input) {
                    input.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

都是根據文件名讀取asset 下的東西。

從build.gradle 裏面讀取string:

 buildTypes {
        debug {
            resValue("string", "PORT_NUMBER", "8080")
            resValue("string", "DB_PASSWORD_PERSON", "a_password")
        }

使用的時候:

 portNumber = Integer.valueOf(context.getString(R.string.PORT_NUMBER));

可是BuildConfig 完全可以替代,不過這也是一條路

獲取手機ip地址:

    public static String getAddressLog(Context context, int port) {
        WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
        @SuppressLint("DefaultLocale")
        final String formattedIpAddress = String.format("%d.%d.%d.%d",
                (ipAddress & 0xff),
                (ipAddress >> 8 & 0xff),
                (ipAddress >> 16 & 0xff),
                (ipAddress >> 24 & 0xff));
        return "Open http://" + formattedIpAddress + ":" + port + " in your browser";
    }

怎麼拿到數據庫的?用文件遍歷嗎?

Context context.databaseList()

android 有提供接口

怎麼拿到所有的sp? 遍歷嗎?

    public static List<String> getSharedPreferenceTags(Context context) {

        ArrayList<String> tags = new ArrayList<>();

        String rootPath = context.getApplicationInfo().dataDir + "/shared_prefs";
        File root = new File(rootPath);
        if (root.exists()) {
            for (File file : root.listFiles()) {
                String fileName = file.getName();
                if (fileName.endsWith(PREFS_SUFFIX)) {
                    tags.add(fileName.substring(0, fileName.length() - PREFS_SUFFIX.length()));
                }
            }
        }

        Collections.sort(tags);

        return tags;
    }

是的,只能遍歷。

sp 裏面的數據,是自己解析xml?

 SharedPreferences preferences = context.getSharedPreferences(tag, Context.MODE_PRIVATE);
        Map<String, ?> allEntries = preferences.getAll();
			if (entry.getValue() instanceof String) {
                    valueColumnData.dataType = DataType.TEXT;
                } else if (entry.getValue() instanceof Integer) {

我們看到,map<String,?> 雖然我們不知道類型,但是我們可以用這種形式。而且不需要自己解析,android 有先有的api.

怎麼查詢數據庫表的所有列名:

        if (tableName != null) {
            final String pragmaQuery = "PRAGMA table_info(" + quotedTableName + ")";
            tableData.tableInfos = getTableInfo(db, pragmaQuery);
        }

上面的sql 即可,最終SQl 想這種:

PRAGMA table_info([cars])
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章