Aplikacja zawiesza się w momencie czyszczenia bufora

0

Piszę małą aplikację do odbierania paru znaków przez bluetooth.

public void run() {
 
           byte[] buffer = new byte[1024];
           int bytes;
           final StringBuffer sBuffer = new StringBuffer("");
         
           while (true) 
           {
               try 
               {
                    // Read from the InputStream
                    
            	   bytes = mmInStream.read(buffer);
                   
                    if(bytes > 0)
                    {   
                    	byte[] newbuffer = new byte[bytes];
                    	
                    	for(int i = 0; i < bytes; i++)
                    		newbuffer[i] = buffer[i]; 
                    	
                    	final String data2 = new String(newbuffer, "US-ASCII");
                    	handler.post(new Runnable()
	                    {
	                        public void run()
	                        {	
	                        	if(data2.indexOf('>')==-1)
	                        	{
	                        		sBuffer.append(data2);
		                       }
	                        	else
	                        	{
	                        		sBuffer.append(data2);
	                        		
		                        	String data=new String(sBuffer);
	                        	
		                        	data=data.substring(data.indexOf('<')+1,data.indexOf('>'));
		                        	String[] lista = data.split("#");
		                        	tvVoltage.setText(lista[0]+"."+lista[1]+"V"+sBuffer.length());
		                        	
		                        	sBuffer.delete(0, sBuffer.length()); //Gdy ta linijka jest zakomentowana aplikacja nie zawiesza się.
	                        	}
	                        }
	                    });
                    }
               } 
               catch (IOException e) 
                {
                	Log.e("BT", "watcher", e);
                	break;
                }
            }
        }

Wszystko by było ok, ale się po prostu zawiesza. Gdy zakomentuję linijkę z metodą delete, aplikacja działa dalej, nie zawiesza się, a do bufora wciąż napływają nowe znaki, a sprawdzam to metodą sBuffer.length();. Na moje oko nie ma tu nic podejrzanego, a jednak się zawiesza?

0

A jak zrobisz zwykłe setLength(0)?

0

Też się zawiesza. Próbowałem jeszcze

sBuffer.replace(0, sBuffer.lenght(), "")

ale efekt był taki sam.

0

A to musi być string buffer? Bo string buffer jest synchronizowany więc twoje "zawieszanie" się zapewne wynika z tego że jakis inny wątek trzyma locka z jakiegoś powodu. Nie możesz użyć string buildera? BTW ten kod wygląda koszmarnie ;]

0

Jak tylko będę miał dostęp do swojego komputera to sprawdzę jak apka się zachowuje ze StringBuilderem. Co jest koszmarnego w tym kodzie? Chętnie poprawię.

0

A to proste. Pokaż ten kod swojej mamie albo siostrze i spytaj czy rozumie co się w nim dzieje. Nie rozumie? To jest nieczytelny. Albo spróbuj napisać UnitTest ze 100% pokrycia dla tego kodu. Osiwiejesz prędzej ;]

0

Wróciłem znowu do tego. Aplikacja nadal się zawiesza ze StringBuilderem. Co może być jeszcze nie tak?

0

Dasz radę udostępnić cały projekt?

0

Tak to wygląda. Tak jak pisał Shalom kod jest koszmarny. Pisany na szybko, aby sprawdzić komunikacje.

package com.example.bt1;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Set;
import java.util.UUID;

import android.support.v7.app.ActionBarActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.content.DialogInterface.OnClickListener;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;


public class MainActivity extends ActionBarActivity implements SensorEventListener{

	private static final int GET_DEVICE = 1;
	
	//status aplikacji
	private static final int DISCONNECTED = 1;
	private static final int CONNECTED = 2;
	private static final int CONNECTION_ERROR = 3;
	private static int STATE = 1;
	
	ListView lvDevices;
	Button bConnect, bSend;
	TextView tvPwm_left, tvPwm_right, tvVoltage;
	private int ile = 0;
	private int j = 0;
	private int ii;
	private int x, y, pwm_left, pwm_right;
	private int flag, flag2;
	private BluetoothAdapter mBluetoothAdapter;
	private BluetoothDevice mDevice;
	private BluetoothSocket mSocket;
	private ArrayList<String> mDevices;
	private ArrayAdapter<String> listAdapter;
	private static final UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); //uuid dla wymiany danych
	final Handler handler = new Handler(); 
	private ConnectedThread mConnectedThread;
	private final static int REQUEST_ENABLE_BT = 1;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);
       
       lvDevices = (ListView)findViewById(R.id.listView1);
       mDevices = new ArrayList<String>();
       listAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, 0);
       bConnect = (Button) findViewById(R.id.button1);
       bSend = (Button) findViewById(R.id.button2);
       tvPwm_left = (TextView) findViewById(R.id.tvPWM_left);
       tvPwm_right = (TextView) findViewById(R.id.tvPWM_right);
       tvVoltage = (TextView) findViewById(R.id.tvVoltage);
       
       mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();        
	     if (mBluetoothAdapter == null) {
	         Toast.makeText(this, "Bluetooth jest niedostępny!", Toast.LENGTH_LONG).show();
	         finish();
	         return;
	     }
	     
	     if(!mBluetoothAdapter.isEnabled()){
	     	Toast.makeText(this, "Bluetooth jest wyłączony!", Toast.LENGTH_LONG).show();
	     	Intent enableBtIntent=new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
	     	startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
	        finish();
	        return;
	     }        
	        
	     Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
	     if (pairedDevices.size() > 0) {
	    	    // Loop through paired devices
	    	    for (BluetoothDevice device : pairedDevices) {
	    	        // Add the name and address to an array adapter to show in a ListView
	    	       listAdapter.add(device.getName() + " " + device.getAddress());
	    	       mDevice = device;
	    	    }
	    	     lvDevices.setAdapter(listAdapter);
	     }
	     else if(pairedDevices.size() == 0){
	      	Toast.makeText(this, "Brak sparowanych urządzeń!", Toast.LENGTH_LONG).show();
	        finish();
	        return;
	     }   
	    /* lvDevices.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
				Toast.makeText(getApplicationContext(), arg2, Toast.LENGTH_SHORT).show();
				
			}
		});*/
	     bConnect.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				btConnect(mDevice.getAddress());
				Toast.makeText(getApplicationContext(), "Bluetooth Connected", Toast.LENGTH_SHORT).show(); 
			}
		});
	     bSend.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				// TODO Auto-generated method stub
				/*String s;
				if(j == 0) s = "<1#0#0#>";
				else if(j == 1) s = "<1#60#60#>";
				else s = "<1#100#100#>";
				mConnectedThread.write(s);
				j++;
				if(j == 3) j = 0;*/
				flag ^= 0x01;
			}
		});
	    SensorManager sm = (SensorManager)getSystemService(SENSOR_SERVICE);
	    sm.registerListener(this, sm.getDefaultSensor(Sensor.TYPE_ORIENTATION), 0, null);
    }

	@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
 private class ConnectedThread extends Thread {
    	
    	Thread workerThread;
    	byte[] readBuffer;
    	int readBufferPosition;
    	int counter;
    	volatile boolean stopWorker;
        private InputStream mmInStream = null;
        private OutputStream mmOutStream = null;

        public ConnectedThread(BluetoothSocket socket) {
        	try
        	{
        		mmInStream = socket.getInputStream();
        		mmOutStream = socket.getOutputStream();
        	}
        	catch(IOException e)
        	{
        		 handler.post(new Runnable()
                 {
                     public void run()
                     {
                     	btDisconnect();
              			changeState(CONNECTION_ERROR);
              			mConnectedThread = null;
                     }
                 });
        	}
         
        }
      
        
       public void run() 
       {
 
           byte[] buffer = new byte[1024];
           int bytes;
           final StringBuilder sBuffer = new StringBuilder("");
           //final StringBuffer sBuffer = new StringBuffer("");
         
           while (true) 
           {
               try 
               {
                    // Read from the InputStream
                    
            	   bytes = mmInStream.read(buffer);
                   
                    if(bytes > 0)
                    {   
                    	byte[] newbuffer = new byte[bytes];
                    	
                    	for(int i = 0; i < bytes; i++)
                    		newbuffer[i] = buffer[i]; 

                    	
                    	final String data2 = new String(newbuffer, "US-ASCII");
                    	handler.post(new Runnable()
	                    {
	                        public void run()
	                        {	
	                        	if(data2.indexOf('>')==-1)
	                        	{
	                        		sBuffer.append(data2);
		                       }
	                        	else
	                        	{
	                        		sBuffer.append(data2);
	                        		
		                        	String data=new String(sBuffer);
	                        	
		                        	data=data.substring(data.indexOf('<')+1,data.indexOf('>'));
		                        	String[] lista = data.split("#");
		                        	tvVoltage.setText(" ");
		                        	tvVoltage.setText(lista[0]);
		                        	sBuffer.setLength(0); // Gdy zakomentuję tę linię aplikacja nie zawiesza się
	                        	}
	                        }
	                    });
                    }
               } 
               catch (IOException e) 
                {
                	Log.e("BT", "watcher", e);
                	//break;
                	
                }
            }
        }
    
        
       
        void write(int one)
        {
        	 if(STATE != CONNECTED)
          	   return;
             
             try{
            	 mmOutStream.write(one);
             } catch(IOException e){
            	 handler.post(new Runnable()
                 {
                     public void run()
                     {
                     	btDisconnect();
              			changeState(CONNECTION_ERROR);
              			mConnectedThread = null;
                     }
                 });
             }    
        }
        
        void write(String str)
        {
           if(STATE != CONNECTED)
        	   return;
           
           try
           {
        	   mmOutStream.write(str.getBytes());
           } 
           catch(IOException e)
           {
        	  
        	   synchronized(MainActivity.this)
        	   {
       				btDisconnect();
       				changeState(CONNECTION_ERROR);
       				mConnectedThread = null;
       		   }
           }
           
        }   
    }
 
 ////////////////////
 
	 private void btConnect()    
	 {
	 	if(mDevice == null)
	 		return;    	
	 	try{   	  
	     	mSocket = mDevice.createRfcommSocketToServiceRecord(uuid);
	     	mSocket.connect();
	 	
	 	} catch(IOException e){
	 		Log.e("BT","point1", e); 		
	 		
	 		btDisconnect();    		
	 		changeState(CONNECTION_ERROR);
	 		return;
	 	}
	 	
	 	mConnectedThread = new ConnectedThread(mSocket);
	 	mConnectedThread.start();
	 
	 	changeState(CONNECTED);	
	 }

	 private void btConnect(String address)
	 {    	
	 	mDevice = mBluetoothAdapter.getRemoteDevice(address);	 
	 	btConnect();
	 }
 
 private void btDisconnect() 
	 {
	 	if(mSocket == null)
	 		return;
	 	
	 	if(mConnectedThread != null)
	 	{
	 		mConnectedThread.stop();
	 		mConnectedThread = null;
	 	}
	 	
	 	try{ 
	     	mSocket.close();    	
	 	} catch(IOException e){
	 		Log.e("BT","point3", e);  		
	 	}  	
	 	
	 	mSocket = null;
	 	
	 	changeState(DISCONNECTED);
	 }
 
	 private void changeState(int iState)
	 {
	 	
	 	STATE = iState;
	 	
	 	switch(iState)
	 	{
	 	case CONNECTED:	
	     	//status.setTextColor(Color.BLUE);
	     	//status.setText("Połączony z " + mDevice.getName());        	
	     	break;        	
	 	case DISCONNECTED:    		
	 		//status.setTextColor(Color.BLACK);
	 		//status.setText("Rozłączony");        	
	     	break;
	 	case CONNECTION_ERROR:
	 		//status.setTextColor(Color.RED);
	 		//status.setText("Błąd połączenia");
	     	break;
	 	
	 	}  	
 	}

	@Override
	public void onAccuracyChanged(Sensor sensor, int accuracy) 
	{
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onSensorChanged(SensorEvent event) 
	{

	}
}
0

Co to znaczy "zawiesza się"? Masz na myśli ANR? Leci Ci coś na StackTrace? Próbowałeś to debugować?

0

A teraz bierzemy na tapetę metodę setLength z klasy AbstractStringBuilder:

public void setLength(int newLength) {
        if (newLength < 0)
            throw new StringIndexOutOfBoundsException(newLength);
        ensureCapacityInternal(newLength);

        if (count < newLength) {
            Arrays.fill(value, count, newLength, '\0');
        }

        count = newLength;
    }

i po przeklikaniu się dalej przez ensureCapacityInternal trafiamy na System.arraycopy, który jest zapewne blokujący w ramach handlera, ale pewności nie mam, bo androida od strony ichniejszego JVMa znam gorzej niż słabo.

Opisz co chcesz zrobić, bo może się zafiksowaliśmy tu na tym kodzie, a rozwiązanie będzie prostsze.

0

Chcę odbierać kilka liczb oddzielonych znakiem #.
Ten problem chyba już rozwiązałem. Tworzę StringBuildera w tym miejscu

final String data2 = new String(newbuffer, "US-ASCII");
                    	final StringBuilder sBuffer = new StringBuilder("");
                    	handler.post(new Runnable()

Dzięki czemu nie muszę nigdzie go czyścić.

1

Być może coś namieszałeś podczas operacji na bajtach. Zamiast wykonywać te operacje "ręcznie" może spróbuj skorzystać z jakiejś biblioteki, która daje taką możliwość. Np. Guava lub Apache Commons (konkretnie: commons-io). W takich "niskopoziomowych" operacjach łatwo o błąd.

1 użytkowników online, w tym zalogowanych: 0, gości: 1