为什么消息在传递给另一个线程时会丢失其内容?

时间:2017-11-05 17:02:06

标签: java android

在开发Android应用程序的过程中,我发现了(在我看来)非常奇怪的行为。 通过将消息对象(android.os.Message)传递到另一个线程,它将丢失其所有内容。有人可能会向我解释为什么会这样吗?我写了一个示例应用程序来说明问题:

MainActivity:

package com.test.bundletest;

import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

    private Messenger mOutMsger;

    private final ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mOutMsger = new Messenger(service);
            System.out.println("Connected");
            sendMultipleMessages(10);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startService(new Intent(this, MyService.class));
        bindService(new Intent(this, MyService.class), mConnection, BIND_AUTO_CREATE);
    }

    private void sendMultipleMessages(int messageCount){
        if (mOutMsger!=null){
            for (int i = 0; i<messageCount; i++){
                Message msg = Message.obtain();
                Bundle b = new Bundle();
                b.putString("test", "Just a test");
                msg.setData(b);
                msg.what = i;
                try {
                    mOutMsger.send(msg);
                    System.out.println("Message sent");
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }else {
            System.out.println("Messenger was null");
        }
    }
}

服务:

package com.test.bundletest;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyService extends Service {

    private ExecutorService mExe = Executors.newSingleThreadExecutor();

    @SuppressLint("HandlerLeak")
    private final Handler mInHandler = new Handler(){
        @Override
        public void handleMessage(final Message msg) {
            System.out.println(
                    new StringBuilder(" UI Thread - What? - ")
                            .append(msg.what)
                            .append("Data? - ")
                            .append(msg.peekData() == null)
                            .append(" KeySet size? - ")
                            .append(msg.getData().keySet().size()));
            mExe.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(
                            new StringBuilder(" NonUI Thread - What? - ")
                                    .append(msg.what)
                                    .append("Data? - ")
                                    .append(msg.peekData() == null)
                                    .append(" KeySet size? - ")
                                    .append(msg.getData().keySet().size()));
                }
            });
        }
    };

    private final Messenger mInMessenger = new Messenger(mInHandler);

    @Override
    public IBinder onBind(Intent intent) {
        return mInMessenger.getBinder();
    }
}

输出:

8751-8751/com.test.bundletest I/System.out: Connected
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out: Message sent
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 0Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 1Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 2Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 3Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 4Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 5Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 6Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 7Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 8Data? - false KeySet size? - 1
8751-8751/com.test.bundletest I/System.out:  UI Thread - What? - 9Data? - false KeySet size? - 1
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0
8751-8809/com.test.bundletest I/System.out:  NonUI Thread - What? - 0Data? - true KeySet size? - 0

如果我在处理之前复制消息,那就更奇怪了:这不会发生:

package com.test.bundletest;

import android.annotation.SuppressLint;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class MyService extends Service {

    private ExecutorService mExe = Executors.newSingleThreadExecutor();

    @SuppressLint("HandlerLeak")
    private final Handler mInHandler = new Handler(){
        @Override
        public void handleMessage(final Message msg) {
            System.out.println(
                    new StringBuilder(" UI Thread - What? - ")
                            .append(msg.what)
                            .append("Data? - ")
                            .append(msg.peekData() == null)
                            .append(" KeySet size? - ")
                            .append(msg.getData().keySet().size()));
            final Message msg2 = Message.obtain(msg);
            mExe.submit(new Runnable() {
                @Override
                public void run() {
                    System.out.println(
                            new StringBuilder(" NonUI Thread - What? - ")
                                    .append(msg2.what)
                                    .append("Data? - ")
                                    .append(msg2.peekData() == null)
                                    .append(" KeySet size? - ")
                                    .append(msg2.getData().keySet().size()));
                }
            });
        }
    };

    private final Messenger mInMessenger = new Messenger(mInHandler);

    @Override
    public IBinder onBind(Intent intent) {
        return mInMessenger.getBinder();
    }
}

以这种方式运行,UI和NON UI Thread的输出相同。

我的Java知识已经结束,希望有人可以理解为我解释这一点。

1 个答案:

答案 0 :(得分:1)

您使用Message.obtain();从全局池中返回一个新的Message实例。

创建新线程时,方法handleMessage(Message msg)完成执行,仍然属于全局池的对象Message正在回收。所以在新线程中这个对象已经是空的

在您的服务中,您可以这样做:

@Override
public void handleMessage(Message msg) {
    final Message message = new Message();
    message.copyFrom(msg);
    //your code
}

或者您可以将Message对象映射到您自己的自定义对象中。最好仅将Message对象用于通过Handler

进行数据传输