Android - 序列化对象以通过蓝牙NotSerializableException传递它

时间:2014-12-31 11:42:42

标签: android object serialization bluetooth

我正在尝试通过我的Android应用程序中的Bluetooth传递对象。因此,我必须序列化它并将其从我的(GameViewHandler)活动传递到我的BluetoothConnectionService class.问题是对象(Ball)无法序列化并抛出NotSerializableExeption.

我尝试通过在此问题中实施serialize()方法来完成序列化:https://stackoverflow.com/a/14165417/4408098

以下是调用write()中的BluetoothConnectionService方法的活动:

public class GameActivityHandler extends Activity {

    private static final String TAG = "GameActivityHandler";

    private BluetoothConnectionService mConnService;
    private Ball myBall;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        View decorView = getWindow().getDecorView();
        // Hide the status bar.
        int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
        decorView.setSystemUiVisibility(uiOptions);
        // Remember that you should never show the action bar if the
        // status bar is hidden, so hide that too if necessary.
        ActionBar actionBar = getActionBar();
        actionBar.hide();

        setContentView(new GameView(this));

        Bundle extra = getIntent().getExtras();
        Boolean isHost = extra.getBoolean("host");
        if(isHost){
            mConnService = BluetoothHostActivity.mBtService;
        } else{
            mConnService = JoinGameActivity.mConnService;
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        final AlertDialog ad = new AlertDialog.Builder(this).create();
        ad.setMessage("Do you really want to disconnect?");
        ad.setCancelable(false);
        ad.setButton(AlertDialog.BUTTON_NEGATIVE, "No", new DialogInterface.OnClickListener() {                 
            @Override
            public void onClick(DialogInterface dialog, int which) {
                ad.dismiss();                       
            }
        });
        ad.setButton(AlertDialog.BUTTON_POSITIVE, "Yes", new DialogInterface.OnClickListener() {                    
            @Override
            public void onClick(DialogInterface dialog, int which) {
                mConnService.cancelConnection();
                Toast.makeText(GameActivityHandler.this, "Disconnected!", Toast.LENGTH_SHORT).show();
                Intent intent = new Intent(GameActivityHandler.this, MainActivity.class);
                startActivity(intent);  
                GameActivityHandler.this.finish();
            }
        });
        ad.show();

        return super.onKeyDown(keyCode, event);
    }

    /**
     * The Handler that gets information back from the BluetoothChatService
     */
    private final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    // just receive a Toast 
                    Toast.makeText(GameActivityHandler.this, msg.getData().getString("toast"),Toast.LENGTH_SHORT).show();                   
                    break;

                case 2: // 2 stands for the MESSAGE_READ Integer                    
                    Log.d(TAG, "Handler just received message from BluetoothConnectionService");
                    byte[] readBuf = (byte[]) msg.obj;
                    Ball readMessage = null;

                    try {
                        readMessage = deserialize(readBuf); // deserialize the received byte[] to convert it back to a Ball object
                    } catch (ClassNotFoundException | IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }

                    Log.d(TAG, "Received Ball: xCoordinate: " + readMessage.getX() + " yCoordinate: " + readMessage.getY());                    
                    break;
                case 3:
            }
        }
    };

    public static Ball deserialize(byte[] bytes) throws IOException, ClassNotFoundException {
        ByteArrayInputStream b = new ByteArrayInputStream(bytes);
        ObjectInputStream o = new ObjectInputStream(b);
        return (Ball) o.readObject();
    }

    public void invokeWriteDataToBtService(Ball send) throws IOException {

        // send a Ball object to the remote device through BluetoothConnectionService
        // but serialize it first because OutPutStream only accepts byte Arrays to be sent over Bluetooth
        mConnService.write(send.serialize());
    }
}
基本上,invokeWriteDataToBtService()方法很有意思。

我的BluetoothConnectionService:

public class BluetoothConnectionService {

     private static final String TAG = "BluetoothConnectionService";

     private static final String NAME = "ballGame";
     private static final UUID MY_UUID =
                UUID.fromString("9bf02da0-ff78-4a9d-a734-ef3e3324a4ec");

    // Member fields
        private final BluetoothAdapter mBluetoothAdapter;
        private final Handler mHandler;
        private ConnectThread mConnectThread;
        private ConnectedThread mConnectedThread;

    public BluetoothConnectionService(Context context, Handler handler) {
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = handler;
    }    

    public synchronized void accept(){
        Log.d(TAG, "accept device");

        // Start the thread to connect with the given device
        AcceptThread mAcceptThread = new AcceptThread();
        mAcceptThread.start();
    }

    public synchronized void connect(BluetoothDevice device){
        Log.d(TAG, "connect to: " + device);

        // Start the thread to connect with the given device
        mConnectThread = new ConnectThread(device);
        mConnectThread.start();
    }    

    /**
     * Start the ConnectedThread to begin managing a Bluetooth connection
     *
     * @param socket The BluetoothSocket on which the connection was made
     * @param device The BluetoothDevice that has been connected
     */
    public synchronized void connected(BluetoothDevice device, BluetoothSocket socket ) {
        Log.d(TAG, "connected()");

        // Cancel the thread that completed the connection
        if (mConnectThread != null) {
            mConnectThread.cancel();
            mConnectThread = null;
        }       

        // Cancel any thread currently running a connection
        if (mConnectedThread != null) {
            mConnectedThread.cancel();
            mConnectedThread = null;
        }


        // Start the thread to manage the connection and perform transmissions
        mConnectedThread = new ConnectedThread(socket);
        mConnectedThread.start(); 

        // Send the name of the connected device back to the UI Activity
        Message msg = mHandler.obtainMessage(1);
        Bundle bundle = new Bundle();
        bundle.putString("toast", "connected with: " + device.getName());
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }

    private class AcceptThread extends Thread {

        private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        // mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
            mmServerSocket = tmp;
        }

        public void run() {
            Log.d(TAG, "BEGIN mAcceptThread" + this);               

            BluetoothSocket socket = null;
            // Keep listening until exception occurs or a socket is returned
            while (true) {
                try {
                    socket = mmServerSocket.accept();
                } catch (IOException e) {
                    Log.e(TAG, "accept() failed", e);
                    break;
                }

                // If a connection was accepted
                if (socket != null) {
                    // Do work to manage the connection (in a separate thread)
                    synchronized (BluetoothConnectionService.this) {
                        connected(socket.getRemoteDevice(), socket);
                    }                   
                    try {
                        mmServerSocket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    break;
                }
            }
            Log.i(TAG, "END mAcceptThread");
        }
    }

    private class ConnectThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final BluetoothDevice mmDevice;

        public ConnectThread(BluetoothDevice device) {
            // Use a temporary object that is later assigned to mmSocket,
            // because mmSocket is final
            BluetoothSocket tmp = null;
            mmDevice = device;

            // Get a BluetoothSocket to connect with the given BluetoothDevice
            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) { }
            mmSocket = tmp;
        }

        public void run() {
            // Cancel discovery because it will slow down the connection
            mBluetoothAdapter.cancelDiscovery();

            try {
                // Connect the device through the socket. This will block
                // until it succeeds or throws an exception
                mmSocket.connect();
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) { }
                connectionFailed();
                return;
            }

            // Reset the ConnectThread because we're done
            synchronized (BluetoothConnectionService.this) {
                mConnectThread = null;
            }

            // Do work to manage the connection (in a separate thread)
            connected(mmDevice, mmSocket);
        }

        /** Will cancel an in-progress connection, and close the socket */
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) { }
        }
    }

    public void cancelConnection(){
        ConnectedThread r;
        synchronized (this) {
            r = mConnectedThread;
        }
        r.cancel();
    }

    /**
     * Indicate that the connection attempt failed and notify the UI Activity.
     */
    private void connectionFailed() {
        // Send a failure message back to the Activity
        Message msg = mHandler.obtainMessage(1);
        Bundle bundle = new Bundle();
        bundle.putString("toast", "Unable to connect device");
        msg.setData(bundle);
        mHandler.sendMessage(msg);
    }

    /**
     * Indicate that the connection was lost and notify the UI Activity.
     */
    private void connectionLost() {
        // Send a failure message back to the Activity
        Message msg = mHandler.obtainMessage(1);
        Bundle bundle = new Bundle();
        bundle.putString("toast", "Device connection was lost");
        msg.setData(bundle);
        mHandler.sendMessage(msg);

        // Start the service over to restart listening mode
//        BluetoothConnectionService.this.start();
    }

    /**
     * Write to the ConnectedThread in an unsynchronized manner
     *
     * @param out The bytes to write
     * @see ConnectedThread#write(byte[])
     */    
    // this method gets called from the GameActivityHandler Activity
    public void write(byte[] out) {
        Log.d(TAG, "write() in BluetoothConnectionService executed!");      
        // Create temporary object
        ConnectedThread r;
        // Synchronize a copy of the ConnectedThread
        synchronized (this) {
            r = mConnectedThread;
        }
        // Perform the write unsynchronized in ConnectedThread
        r.write(out);
    }

    /**
     * This thread runs during a connection with a remote device.
     * It handles all incoming and outgoing transmissions.
     */
    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;

        public ConnectedThread(BluetoothSocket socket) {
            Log.d(TAG, "create ConnectedThread");
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the BluetoothSocket input and output streams
            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "temp sockets not created", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            Log.i(TAG, "BEGIN mConnectedThread");
            byte[] buffer = new byte[1024];
            int bytes;

            // Keep listening to the InputStream while connected
            while (true) {
                try {
                    Log.d(TAG, "Read InputStream...");
                    // Read from the InputStream
                    bytes = mmInStream.read(buffer);

                    // Send the obtained bytes to the UI Activity
                    mHandler.obtainMessage(2, bytes, -1, buffer)
                            .sendToTarget();

                    Log.d(TAG, "Just sent message to Handler");
                } catch (IOException e) {
                    Log.e(TAG, "disconnected", e);
                    connectionLost();
                    // Start the service over to restart listening mode
//                    BluetoothConnectionService.this.start();
                    break;
                }
            }
        }

        /**
         * Write to the connected OutStream.
         *
         * @param buffer The bytes to write
         */
        // the actual write method
        public void write(byte[] buffer) {
            Log.d(TAG, "write() in ConnectedThread executed!");
            try {
                mmOutStream.write(buffer);

                // Share the sent message back to the UI Activity
//                mHandler.obtainMessage(3, -1, -1, buffer)
//                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Exception during write", e);
            }
        }

        public void cancel() {
             if (mmInStream != null) {
                 try {mmInStream.close();} catch (Exception e) {}
             }

             if (mmOutStream != null) {
                try {
                    mmOutStream.close();
                } catch (Exception e) {}
             }

             if (mmSocket != null) {
                try {
                    mmSocket.close();
                } catch (Exception e) {}
             }

             try {
                 mmSocket.close();
             } catch (IOException e) {
                 Log.e(TAG, "close() of connect socket failed", e);
             }
        }
    }
}

应该序列化的Ball类。它包含一些floatIntegerbooleanBitmap

public class Ball {

    private int x, y;
    private Bitmap bitmap;
    private float currVelocityX = 0, currVelocityY = 0, lastVelocityY = 0;
    private boolean touched = false;

    public Ball(Resources resources, int x_pos, int y_pos) {
        x = x_pos;
        y = y_pos;
        bitmap = BitmapFactory.decodeResource(resources, R.drawable.ball1);
    }

    public void drawBall(Canvas canvas){
        canvas.drawBitmap(bitmap, x, y, null);
    }

    // some methods

    public byte[] serialize() throws IOException {
        ByteArrayOutputStream b = new ByteArrayOutputStream();
        ObjectOutputStream o = new ObjectOutputStream(b);
        o.writeObject(this);
        Log.d("TAG", "Ball gets serialized()");
        return b.toByteArray();
    }
}

日志说的是什么:

12-31 11:44:35.792: W/System.err(7956): java.io.NotSerializableException: com.mhpproductions.ballgame.Ball
12-31 11:44:35.792: W/System.err(7956):     at java.io.ObjectOutputStream.writeNewObject(ObjectOutputStream.java:1364)
12-31 11:44:35.792: W/System.err(7956):     at java.io.ObjectOutputStream.writeObjectInternal(ObjectOutputStream.java:1671)
12-31 11:44:35.792: W/System.err(7956):     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1517)
12-31 11:44:35.792: W/System.err(7956):     at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:1481)
12-31 11:44:35.792: W/System.err(7956):     at com.mhpproductions.ballgame.Ball.serialize(Ball.java:104)
12-31 11:44:35.792: W/System.err(7956):     at com.mhpproductions.ballgame.GameActivityHandler.invokeWriteDataToBtService(GameActivityHandler.java:125)
12-31 11:44:35.792: W/System.err(7956):     at com.mhpproductions.ballgame.GameView.onTouchEvent(GameView.java:131)
12-31 11:44:35.792: W/System.err(7956):     at android.view.View.dispatchTouchEvent(View.java:7713)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:2216)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:1959)
12-31 11:44:35.792: W/System.err(7956):     at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchTouchEvent(PhoneWindow.java:2329)
12-31 11:44:35.792: W/System.err(7956):     at com.android.internal.policy.impl.PhoneWindow.superDispatchTouchEvent(PhoneWindow.java:1568)
12-31 11:44:35.792: W/System.err(7956):     at android.app.Activity.dispatchTouchEvent(Activity.java:2458)
12-31 11:44:35.792: W/System.err(7956):     at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchTouchEvent(PhoneWindow.java:2277)
12-31 11:44:35.792: W/System.err(7956):     at android.view.View.dispatchPointerEvent(View.java:7893)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$ViewPostImeInputStage.processPointerEvent(ViewRootImpl.java:3950)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:3829)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3395)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3445)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3414)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:3521)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3422)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:3578)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3395)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:3445)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:3414)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:3422)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:3395)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl.deliverInputEvent(ViewRootImpl.java:5535)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl.doProcessInputEvents(ViewRootImpl.java:5515)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl.enqueueInputEvent(ViewRootImpl.java:5486)
12-31 11:44:35.792: W/System.err(7956):     at android.view.ViewRootImpl$WindowInputEventReceiver.onInputEvent(ViewRootImpl.java:5615)
12-31 11:44:35.792: W/System.err(7956):     at android.view.InputEventReceiver.dispatchInputEvent(InputEventReceiver.java:185)
12-31 11:44:35.792: W/System.err(7956):     at android.os.MessageQueue.nativePollOnce(Native Method)
12-31 11:44:35.792: W/System.err(7956):     at android.os.MessageQueue.next(MessageQueue.java:138)
12-31 11:44:35.792: W/System.err(7956):     at android.os.Looper.loop(Looper.java:123)
12-31 11:44:35.792: W/System.err(7956):     at android.app.ActivityThread.main(ActivityThread.java:5146)
12-31 11:44:35.792: W/System.err(7956):     at java.lang.reflect.Method.invokeNative(Native Method)
12-31 11:44:35.792: W/System.err(7956):     at java.lang.reflect.Method.invoke(Method.java:515)
12-31 11:44:35.792: W/System.err(7956):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:796)
12-31 11:44:35.792: W/System.err(7956):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:612)
12-31 11:44:35.792: W/System.err(7956):     at dalvik.system.NativeStart.main(Native Method)

因此,GameActivityHandler中的第125行是

mConnService.write(send.serialize());

导致这个例外。

那么如何序列化我的Ball对象,发送它,在远程设备上接收它并再次反序列化它?

2 个答案:

答案 0 :(得分:1)

  

那么如何序列化我的球

很简单:

import java.io.Serializable;

...

public class Ball implements Serializable

答案 1 :(得分:0)

添加以下内容

class Ball implements Serializable{
//Your code
}