Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Implementing a Basic Handler for Intra-Process Communication

Tech 1

A handler is primarily used for intra-process communication.

First, let's create a handler:

public class Handler {
    public void handleMessage(Message msg) {
    }
    public void sendMessage(Message msg) {
        msg.setHandler(this);
        Looper.messageQueue.enqueueMessage(msg);
    }
}

These are the two main methods: one for sending and one for receiving.

public class Message {
    String content;
    Handler target;
    Message next;
    public Message(String content) {
        this.content = content;
    }
    public void setHandler(Handler handler) {
        this.target = handler;
    }
    public Handler getHandler() {
        return this.target;
    }
    @Override
    public String toString() {
        return "Message{" +
                "content='" + content + '\'' +
                '}';
    }
}

This is a simplified Message class.

When sending and receiving data, if we only send one piece of data, we can directly use the handler's methods. However, with large amounts of data, using send repeatedly could block the thread. To avoid this, we add a queue to store data to be sent and process it gradually.

public class MessageQueue {
    Message head = null;
    public void enqueueMessage(Message msg) {
        Message current = head;
        if (current == null) {
            msg.next = current;
            head = msg;
        } else {
            Message previous;
            for (;;) {
                previous = current;
                current = current.next;
                if (current == null) {
                    break;
                }
            }
            msg.next = current;
            previous.next = msg;
        }
        Log.d("HandlerDemo", "enqueueMessage: " + msg.toString());
        Log.d("HandlerDemo", "enqueueMessage: " + head.toString());
    }
    public Message next() {
        synchronized (this) {
            if (head == null) {
                return null;
            }
            Message msg = head;
            head = msg.next;
            msg.next = null;
            return msg;
        }
    }
}

With messages added to the queue, we need a class to execute them, which leads to the Looper.

A question arises: can there be multiple loopers? Consider this method:

public static void loop() {
    for (;;) {
        final Looper me = threadLocal.get();
        Message msg = me.messageQueue.next();
        if (msg == null) {
            break;
        }
        msg.target.handleMessage(msg);
    }
}

This is a simplified loop method with an infinite for-loop. If there were multiple loopers, the first loop might not finish, causing subsequent ones to block, rendering the queue ineffective. To ensure only one looper exists, we use ThreadLocal. Look at its set method:

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

The key is the current thread, and we can store a looper as the value, ensuring a unique looper per thread.

Here's the implementation:

public class Looper {
    public static MessageQueue messageQueue = new MessageQueue();
    static final ThreadLocal<Looper> threadLocal = new ThreadLocal<Looper>();
    public static void prepare() {
        if (threadLocal.get() != null) {
            // Handle existing looper
        }
        threadLocal.set(new Looper());
    }
    public static void loop() {
        for (;;) {
            final Looper me = threadLocal.get();
            Message msg = me.messageQueue.next();
            if (msg == null) {
                break;
            }
            msg.target.handleMessage(msg);
        }
    }
}

In prepare(), we ensure only one looper exists, and loop() executes it.

Finally, here's the usage example:

public class MainActivity extends Activity {
    Handler handler = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("HandlerDemo", "onCreate: activity_main");
        Looper.prepare();
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("HandlerDemo", "thread");
                handler = new Handler() {
                    @Override
                    public void handleMessage(Message msg) {
                        Log.d("HandlerDemo", "handleMessage: " + msg.toString());
                    }
                };
            }
        }).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        Log.d("HandlerDemo", "onCreate: send");
        handler.sendMessage(new Message("message1"));
        handler.sendMessage(new Message("message2"));
        handler.sendMessage(new Message("message3"));
        Looper.loop();
    }
}

This implements a basic handler communication system.

Note: Memory leaks can occur due to long-lived objects holding references to short-lived ones (e.g., ThreadLocal -> Queue -> Message -> Handler -> Activity). A simple solution is to use static methods to mitigate this.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.