First, download and install

Official download address: sqlitestudio.pl/ green software, unzip direct use.

Second, the configuration

(1) Install the Android plug-in

Click Tools -> Open Configuration Dialog

(2) Choose to install the plug-in

(3) Obtain jar files and configure them into the project

1. Click Tools -> Get Android Connector JAR file

2. Get the sqlitestudioreMote. jar file and configure it in your Android project.

4. Start SQLiteStudioService in code

  SQLiteStudioService.instance().start(this)
Copy the code

(5) Run SQLiteStudio to add database

1 Click the Add Database button in the upper left corner

2 you will see the following dialog box, select Android SQlite database type

3Click the folder button to the right of the Android Database URL

4You will see the following dialog box, and you can see that the lowest option box has identified the database, but you cannot click the “OK” button

I don’t know if it’s a software bug or something, but the reason why I can’t click is that the option list in the Database must be greater than 1. If it is greater than 1, I can click ok button

5. Click the green “+” button of the Batabase option at the bottom, name a database randomly, and click “OK” to create it

6 Then click “OK”, we can view and operate the database normally

Three, from the source analysis, to solve the problem of not finding the database

Mysql > select * from database where databases are located; mysql > select * from database where databases are located; / data/data/XXX. XXX. XXX/databases, if we have a custom project code storage paths, so SqliteStudio affirmation is to identify less than, if you don’t want to change the path of the database, it needs to analysis look at the source code, read from the source to go up and modify the path

Start with sqlitestudioreMote.jar

You can see there’s not a lot of code. The main principle is that APP starts Service and Service communicates with SQLiteStudio through Socket communication.

(II) Code analysis

1. Start with the code entry
  SQLiteStudioService.instance().start(this)// Start code
Copy the code

The startup code is one sentence, so start with SQLiteStudioService.

SQLiteStudioService is an inherited Service. This Service implements one more singleton method

  public static SQLiteStudioService instance(a) {
        if (staticInstance == null) {
            staticInstance = new SQLiteStudioService();
        }

        return staticInstance;
    }
Copy the code

Then look at the start method as follows:

    public void start(Context context) {
        if (!this.running) {
            this.listener = new SQLiteStudioListener(context);
            this.listener.setPort(this.port);
            this.listener.setPassword(this.password);
            this.listener.setIpBlackList(this.ipBlackList);
            this.listener.setIpWhiteList(this.ipWhiteList);
            this.listenerThread = new Thread(this.listener);
            this.listenerThread.start();
            this.running = true;
            Log.d(Utils.LOG_TAG, "Started instance on port " + this.port); }}Copy the code

You can see from the code that the start() method basically starts a thread. SQLiteStudioListener should be a class that implements the Runnable interface and creates a new thread to execute it. So let’s look at what the SQLiteStudioListener class does. (ps: this code is very confusing, obviously inherited Service, not a Service startup process to use. You have to adjust start yourself. Is actually used as a singleton helper class.

public class SQLiteStudioListener implements Runnable.ClientJobContainer {... omitpublic void run(a) {
        if (this.init()) {
            Log.d(Utils.LOG_TAG, "Listening for clients...");

            while(this.isRunning()) {
                try {
                    Socket clientSocket = this.serverSocket.accept();
                    ClientHandler clientHandler = new ClientHandler(clientSocket, this.context, this.this.authService);
                    this.clientJobs.add(clientHandler);
                    this.threadPool.execute(clientHandler);
                } catch (IOException var3) {
                }
            }

            Log.d(Utils.LOG_TAG, "Listener thread finished."); }}private boolean init(a) {
        try {
            this.serverSocket = new ServerSocket(this.port, 5);
            this.serverSocket.setSoTimeout(1000);
        } catch (IOException var2) {
            Log.e(Utils.LOG_TAG, "Error while opening listening socket: " + var2.getMessage(), var2);
            return false;
        }

        this.jobsQueue = new LinkedBlockingDeque(1);
        this.clientJobs = new CopyOnWriteArrayList();
        this.threadPool = new ThreadPoolExecutor(20.20.10L, TimeUnit.SECONDS, this.jobsQueue);
        this.authService = new AuthServiceImpl(this.password, this.ipBlackList, this.ipWhiteList);
        return true; }... Omit}Copy the code

I’m just going to look at the run method and init method, and I’m going to look at the code and I’m going to look at the run method and I’m going to initialize it, and I’m going to return if it was successfully initialized, and then I’m going to look at what init does, and I’m going to create a Socket server, serverSocket, and I’m going to hold clientJobs, The threadPool threadPool, authService is used for authentication (because security passwords can be set). Then we look at the Run method, which is the while loop that waits for the client to connect, gets the socket object, and creates the ClientHandler. While this. ThreadPool. Execute (clientHandler); As you can see, it is the thread pool that executes a Runnable object, so ClientHandler is also a class that implements the Runnable interface. So next we need to see what ClientHandler does.

public class ClientHandler implements Runnable {... omitpublic void run(a) {
        //1. Obtain IP address
        String ip = this.clientSocket.getInetAddress().getHostAddress();
        Log.d(Utils.LOG_TAG, "New client from " + ip);
        if (!this.authService.isIpAllowed(ip)) {
            //2. Check whether the connection can be made
            Log.e(Utils.LOG_TAG, "Client's IP address not allowed: " + ip + ", disconnecting.");
            this.cleanUp();
        } else if (!this.init()) {
            //3. Initialize and check whether the initialization is successful
            Log.e(Utils.LOG_TAG, "Could not initialize handler for the client.");
            this.cleanUp();
        } else {
            //4. Start a loop to receive messages
            while(this.isRunning() && !this.denyAccess) {
                this.readClientChannel();
            }

            this.cleanUp();
            Log.d(Utils.LOG_TAG, "Disconnected client "+ ip); }}}Copy the code

The code for ClientHandler starts with the run method. The first step is to obtain the IP address, the second step is to judge whether the connection is allowed, the third step is to initialize and judge whether the initialization is successful, and the fourth step is to start the loop to receive messages. So let’s just look at what init() does in step 3, okay?

    private boolean init(a) {
        try {
            this.inputStream = this.clientSocket.getInputStream();
            this.outputStream = this.clientSocket.getOutputStream();
        } catch (IOException var2) {
            return false;
        }

        this.dataInputStream = new DataInputStream(this.inputStream);
        return true;
    }
Copy the code

Init () basically creates an I\O interface for reading and writing, and then I’ll look at what the readClientChannel() method does.

    private void readClientChannel(a) {
        if (!this.clientSocket.isConnected()) {
            // Determine whether to connect
            this.close();
        } else {
            try {
            // Read the content
                switch(this.currentState) {
                case READING_SIZE:
                    this.dataInputStream.readFully(this.sizeBuffer);
                    break;
                case READING_DATA:
                    this.dataInputStream.readFully(this.dataBuffer); }}catch (EOFException var4) {
                this.close();
                return;
            } catch (IOException var5) {
                Log.e(Utils.LOG_TAG, "Error while reading input from client: " + var5.getMessage(), var5);
                this.sendError(ClientHandler.Error.ERROR_READING_FROM_CLIENT);
                return;
            }

            switch(this.currentState) {
            case READING_SIZE:
                int size = ByteBuffer.wrap(this.sizeBuffer).order(ByteOrder.LITTLE_ENDIAN).getInt();
                if (size > 10485760) {
                    Log.e(Utils.LOG_TAG, "Error while reading input from client: maximum size exceeded: " + size);
                    this.sendError(ClientHandler.Error.ERROR_READING_FROM_CLIENT);
                    return;
                }

                this.currentState = ClientHandler.State.READING_DATA;
                this.dataBuffer = new byte[size];
                break;
            case READING_DATA:
                String str = null;

                try {
                    // Convert the message content to a string
                    str = new String(this.dataBuffer, "UTF-8");
                } catch (UnsupportedEncodingException var3) {
                    Log.e(Utils.LOG_TAG, "Error while reading data from client: " + var3.getMessage(), var3);
                    this.sendError(ClientHandler.Error.ERROR_READING_FROM_CLIENT);
                    return;
                }
                // Handle the accept string message
                this.handleRequest(str);
                this.currentState = ClientHandler.State.READING_SIZE; }}}Copy the code

From the long list above, find the key code this.handlerequest (STR); Here is the code for the final processing of the message. So what does handleRequest() do

    private void handleRequest(String data) {
        JSONObject json;
        try {
            // Convert the string message to JSON
            json = new JSONObject(data);
        } catch (JSONException var7) {
            this.sendError(ClientHandler.Error.INVALID_FORMAT);
            return;
        }

        HashMap<String, Object> map = (HashMap)JsonConverter.fromJsonValue(json);
        if (!this.authorized) {
            this.authorize(map);
        } else if(! map.containsKey("cmd")) {
            this.sendError(ClientHandler.Error.NO_COMMAND_SPECIFIED);
        } else {
            ClientHandler.Command cmd;
            try {
                // Get instructions
                cmd = ClientHandler.Command.valueOf("" + map.get("cmd"));
            } catch (IllegalArgumentException var6) {
                this.sendError(ClientHandler.Error.UNKNOWN_COMMAND);
                return;
            }

            switch(cmd) {
            case LIST:
               // Database list
                this.send("list".this.dbService.getDbList());
                break;
            case QUERY:
                / / query
                this.execAndRespond(map.get("db"), "" + map.get("query"));
                break;
            case DELETE_DB:
                / / delete
                this.deleteDbAndRespond(map.get("db")); }}}Copy the code

As you can see from the code, the communication content of SQLiteStudio is a JSON string, in the form of instructions to perform the query task. The key code “this.dbservice.getdblist ()” can be seen in the code above to see how SQLiteStudio knows about the database in our app. Let’s take a look at what this.dbService.getdblist () does.

    public List<String> getDbList(a) {
        List<String> filteredList = new ArrayList();
        String[] var2 = this.context.databaseList();
        int var3 = var2.length;

        for(int var4 = 0; var4 < var3; ++var4) {
            String dbFile = var2[var4];
            if(! dbFile.endsWith("-journal")) { filteredList.add(dbFile); }}return filteredList;
    }
Copy the code

From this. Now be clear at a glance, SQLiteStudio context. DatabaseList () access to the database. So we just need to change the getDbList method in SQLiteStudioDbService to return our own database path.

(3) Summary

Just change the getDbList method in the SQLiteStudioDbService class to return your own database path.