Trong bài viết này, tôi xin giới thiệu cách thức để cài đặt một MIDlet lắng nghe các tin nhắn SMS đang đến. Luồng hoạt động ở bài viết này được thực hiện một cách bất đồng bộ:

1. Người dùng cài đặt ứng dụng SMSListenerMIDlet vào thiết bị và khởi động nó. Tạm gọi thiết bị này là thiết bị A. Lúc này,MIDlet vẫn chưa lắng nghe các message đang đến, do đó, nếu người dùng bây giờ đã gửi một tin nhắn từ một thiết bị khác – tạm gọi là thiết bị B – thì SMS sẽ không bị chặn bởi các MIDlet. Thay vào đó, nó sẽ đến trong inbox của thiết bị A.

2. Người dùng sẽ để MIDlet bắt đầu nghe các message SMS bằng cách chọn Options> Start listening. Bên trong MIDlet sẽ xảy ra các vấn đề sau:

  • Phương thức startListening() được gọi.
  • Phương thức này sẽ mở một connection với port chỉ định và đăng ký MIDlet để lắng nghe các tin nhắn đến.

3. Từ thiết bị B, người dùng bây giờ có thể gửi tin nhắn với port chỉ định tới thiết bị A, và MIDlet trong thiết bị A sẽ xảy ra các vấn đề sau:

  • Phương thức notifyIncomingMessage() được gọi là do thực thi WMA.
  • Để giảm thiểu thời gian phương thức notifyIncomingMessage(), một thread riêng biệt được tạo ra để tìm nạp message.
  • Một khi thread được bắt đầu, nó sẽ block phương thức receive() cho đến khi có một tin nhắn có sẵn. Điều này không nên dùng lâu dài, kể từ khi MIDlet đã được thông báo rằng có một tin nhắn đang chờ. Tuy nhiên, nó là cách thực hành tốt để đảm bảo rằng phương thức hệ thống notifyIncomingMessage() trả về nhanh chóng.
  • Khi một message SMS đã nhận được, phương thức processMessage() được gọi. Nó sẽ kiểm tra các kiểu tin nhắn thư (kiểu văn bản, nhị phân, hoặc tin nhắn multipart) và xử lý phù hợp. Trong thực tế, nó sẽ hiển thị nội dung tin nhắn lên màn hình. Ở ví dụ này, chỉ xử lý tin nhắn kiểu văn bản.

4. Lưu ý rằng các SMS được gửi với port chỉ định sẽ không bao giờ đến inbox của thiết bị A một khi MIDlet đang lắng nghe chúng. Và bây giờ, nếu người dùng ngừng việc lắng nghe (bằng cách chọn Options> Stop listening), các SMS tiếp theo với cổng được chỉ định sẽ rơi vào inbox của thiết bị A, thay vì MIDlet.

Code

import java.io.IOException;
import javax.microedition.io.Connector;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.StringItem;
import javax.microedition.midlet.MIDlet;
import javax.wireless.messaging.BinaryMessage;
import javax.wireless.messaging.Message;
import javax.wireless.messaging.MessageConnection;
import javax.wireless.messaging.MessageListener;
import javax.wireless.messaging.MultipartMessage;
import javax.wireless.messaging.TextMessage;

public class SMSListenerMIDlet
        extends MIDlet
        implements CommandListener, MessageListener {
 
    // The port which is listened for incoming messages
    private final String PORT = "5000";
 
    private Form mainForm;
    private Command startCommand;
    private Command stopCommand;
    private Command exitCommand;
    private MessageConnection connection;
    private boolean listening;
 
    /**
     * Constructor. Constructs the object and initializes displayables.
     */
    public SMSListenerMIDlet() {
        mainForm = new Form("SMS Listener");
 
        startCommand = new Command("Start listening", Command.ITEM, 0);
        mainForm.addCommand(startCommand);
 
        stopCommand = new Command("Stop listening", Command.ITEM, 1);
        mainForm.addCommand(stopCommand);
 
        exitCommand = new Command("Exit", Command.EXIT, 1);
        mainForm.addCommand(exitCommand);
 
        mainForm.setCommandListener(this);
    }
 
    /**
     * From MIDlet.
     * Called when the MIDlet is started.
     */
    public void startApp() {
        // The initial display is the main form
        Display.getDisplay(this).setCurrent(mainForm);
    }
 
    /**
     * From MIDlet.
     * Called to signal the MIDlet to enter the Paused state.
     */
    public void pauseApp() {
        // No implementation required
    }
 
    /**
     * From MIDlet.
     * Called to signal the MIDlet to terminate.
     * @param unconditional whether the MIDlet has to be unconditionally
     * terminated
     */
    public void destroyApp(boolean unconditional) {
        // Stop listening
        stopListening();
    }
 
    /**
     * From CommandListener.
     * Called by the system to indicate that a command has been invoked on a
     * particular displayable.
     * @param command the command that was invoked
     * @param displayable the displayable where the command was invoked
     */
    public void commandAction(Command command, Displayable displayable) {
        if (command == exitCommand) {
            // Exit the MIDlet
            destroyApp(true);
            notifyDestroyed();
        } else if (command == startCommand) {
            startListening();
        } else if (command == stopCommand) {
            stopListening();
        }
    }
 
    /**
     * Starts listening for incoming messages.
     */
    private void startListening() {
        // If we are already listening, no need to start again
        if (listening) {
            return;
        }
 
        try {
            // Open the connection to the specified port
            connection = (MessageConnection)Connector.open("sms://:" + PORT);
 
            // Register this MIDlet as the listener that should be notified
            // whenever messages arrive
            connection.setMessageListener(this);
        } catch (IOException ex) {
            return;
        }
 
        listening = true;       
        mainForm.append("Listener started.\n");
    }
 
    /**
     * Stops listening for incoming messages.
     */
    private void stopListening() {
        // If we are not listening, no need to do anything
        if (!listening) {
            return;
        }
 
        if (connection != null) {
            try {
                // Deregister the message listener and close the connection
                connection.setMessageListener(null);
                connection.close();
                connection = null;
            } catch (IOException ex) {
                // TODO: Exception handling
            }
        }
 
        listening = false;
        mainForm.append("Listener stopped.\n");
    }
 
    /**
     * Asynchronous callback method for receiving incoming messages.
     * Called by the WMA implementation when a new message is ready.
     * @param connection the message connection with incoming messages
     */
    public void notifyIncomingMessage(final MessageConnection conn) {
        // Because this method is called by the platform, it is good practice to
        // minimize the processing done here, on the system thread. Therefore,
        // let's create a new thread for reading the message. 
        Thread smsThread = new Thread() {
            public void run() {
                try {
                    // Receive all incoming messages to the specified
                    // connection. The receive() method will block until there
                    // is a message available.
                    Message message = conn.receive();
                    if (message != null) {
                        mainForm.append("Message received.\n");
                        processMessage(message);
                    }
                } catch (IOException ex) {
                    // Stop listening
                    stopListening();
                }
            }
        };
        smsThread.start();
    }
 
    /**
     * Processes the received message according to its type.
     * @param message the received message
     */
    private void processMessage(Message message) {
        if (message instanceof TextMessage) {
            processTextMessage((TextMessage)message);
        } else if (message instanceof BinaryMessage) {
            processBinaryMessage((BinaryMessage)message);
        } else if (message instanceof MultipartMessage) {
            processMultipartMessage((MultipartMessage)message);
        }
    }
 
    /**
     * Processes a text message.
     */
    private void processTextMessage(TextMessage message) {
        String text = message.getPayloadText();
        StringItem textItem = new StringItem("Text", text);
        mainForm.append(textItem);
    }
 
    /**
     * Processes a binary message.
     */
    private void processBinaryMessage(BinaryMessage binaryMessage) {
        // Not implemented
    }
     /**
     * Processes a multipart message.
     */
    private void processMultipartMessage(MultipartMessage multipartMessage) {
        // Not implemented
    }
}

Lưu ý: Khi bạn gửi SMS, bạn phải đảm bảo rằng port giống như listener. Nếu không, thread listener sẽ không thông báo SMS. Đoạn code dưới đây mô tả gửi tin nhắn SMS có chỉ định port:

// Prepare the text message
TextMessage message = (TextMessage)connection.newMessage(
    MessageConnection.TEXT_MESSAGE);
 // Obtain the address from a text field, append a port number to it, and set the
// whole thing as the destination address for the message. The port number is
// the same as above.
String address = "sms://" + smsAddress.getString() + ":" + PORT;
message.setAddress(address);
 // Obtain the specified text and set it as the payload
String text = smsText.getString();
message.setPayloadText(text);