I am learning Android bluetooth programming. I copied most of this code from Google's Android developer website for learning. The idea is listening for connection on server is done in a new thread without blocking the UI thread. When connection request is received then connection is done on another thread and finally communication is done on another thread.
The problem is when I start the listening thread from UI thread, it block automatically and no UI is displayed (freezes). Here is the sample code:
public void onCreate(Bundle savedInstance) {
super.onCreate(savedInstance);
setContentView(R.layout.activity_main);
...
badapter = BluetoothAdapter.getDefaultAdapter();
if (badapter == null) {
Toast.makeText(this, "No bluetooth device.", Toast.LENGTH_SHORT).show();
return;
}
if (!badapter.isEnabled()) {
Toast.makeText(this, "Bluetooth is disabled.", Toast.LENGTH_SHORT).show();
return;
}
pairedDevices = new HashMap<String, String>();
discoveredDevices = new HashMap<String, String>();
showDevices();
registerBroadcastReceiver();
//this thread blocks UI thread
ListenThread listen = new ListenThread();
listen.run();
}
And the listen thread:
public class ListenThread extends Thread {
MainActivity main;
CommunicateThread communicateThread;
private final BluetoothServerSocket serverSocket;
public ListenThread() {
main = MainActivity.getInstance();
BluetoothServerSocket tmp = null;
try {
tmp = main.badapter.listenUsingRfcommWithServiceRecord(main.NAME, main.MYUUID);
} catch (final IOException e) {
main.handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(main, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
serverSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
//keep listening until exception occurs or a socket is returned
while (true) {
try {
socket = serverSocket.accept();
} catch (final IOException e) {
main.handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(main, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
break;
}
// If a connection was accepted
if (socket != null) {
//call communication thread once connection is established
communicateThread = new CommunicateThread(socket);
communicateThread.run();
try {
serverSocket.close();
} catch (final IOException e) {
main.handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(main, "Error: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
break;
}
}
}
}
You are calling listen.run() on the main thread which makes it run on the main thread. You should call listen.start() which will spawn off a separate thread where the run() method will be executed.
The Runnable given to the handler will be executed on the main thread though as the Handler is for the main thread.
I had the same problem. What I understand is that every time you make a hardware call, in this case, the Bluetooth, you should do it in another thread. I moved the isEnabled() call to other thread and it solved the problem.
Related
I have a fragment that contains a Button btn_connect that when it is pressed a WiFi Direct connection is established between 2 devices. This fragment implements ConnectionInfoListener. So it has onConnectionInfoAvailable function where I want to execute an AsyncTask class. The problem that I have is that in one Activity, I am doing:
fragment.mContentView.findViewById(R.id.btn_connect).performClick();
And the button is being clicked and the connection is established so the code goes into the onConnectionInfoAvailable function but the AsyncTask is not being executed.
#Override
public void onConnectionInfoAvailable(final WifiP2pInfo info) {
//..code..
Log.d("Test 1", "Test 1");
new MasterServerTask().execute();
}
public class MasterServerTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
//**************
Log.d("IM INSIDE ASYNCTASK CLASS", "SOCKET");
try {
serverSocket = new ServerSocket(8090);
} catch (IOException e) {
e.printStackTrace();
}
while (true) {//wait for clients
Socket socket = null;
try {
socket = serverSocket.accept();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Log.d("ACCEPTED A SLAVE DEVICE "+num_clients, "ACCEPTED A SLAVE DEVICE "+num_clients);
num_clients++;
OutputStream os=null;
try {
os = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
proxy.addSlaveOutputStream(os);
}
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
}
}
mContentView.findViewById(R.id.btn_connect).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {//Phone that connects first is NOT the group owner
// port = Integer.parseInt(editTextPort.getText().toString());
Log.d("IM IN THE OTHER FRAGMENT", "Connect");
WifiP2pConfig config = new WifiP2pConfig();
config.groupOwnerIntent = 0;
config.deviceAddress = device.deviceAddress;
config.wps.setup = WpsInfo.PBC;
if (progressDialog != null && progressDialog.isShowing()) {
progressDialog.dismiss();
}
progressDialog = ProgressDialog.show(getActivity(), "Press back to cancel",
"Connecting to :" + device.deviceAddress, true, true
);
((DeviceActionListener) getActivity()).connect(config);
}
});
Is there an easy workaround solution for this?
Check how/where you are calling WifiP2pManager.initialize() to create the WifiP2pManager.Channel object. The Looper you provide it is the one which will receive all callbacks for your instance of WifiP2pManager.ConnectionInfoListener. If you are giving it a background thread then the AsyncTask will not execute - it must be started from the main (UI) thread.
The comments on the question were really helpful. The reason why the AsyncTask was not getting executed is because it was called from another task that is currently being executed. So in order for it to work, I replaced the AsyncTask with Thread classes. All the code in the doInBackground() was placed inside the thread's run() function. Now the performClick() executes a Thread, not an AsyncTask and it worked.
In my application, a client is connected to server. It waits until the connection to the server occurs. During that time the application is not responding. How can i solve this problem. Tried code snippet shows below
public Connection(){
client.SetParent(this);
this.context = g.getContext();
bConnected = false;
mNetworkRunner = new Runnable() {
public void run() {
try {
Log.e("", "mNetworkRunner...");
if( SendKeepAlive()){
Main.conStatus(1);
Log.e("", "SendKeepAlive...");
}
else {
Main.conStatus(0);
Log.e("", "No connection...");
g.log("Connection to server is lost... Trying to Connect...");
while(true){
Log.e("", "In while loop...");
if(!Connect()){
g.log("Trying...");
Log.e("", "In Connect no connect...");
Thread.sleep(2000);
}
else {
g.log("Connected");
break;
}
}
Main.conStatus(1);
}
mNetworkHandler.postDelayed(this, 30000);
}
catch (Exception e) {
e.printStackTrace();
}
}
};
}
//
private void CheckNetworkConnection(){
if( mNetworkHandler == null ){
mNetworkHandler = new Handler();
mNetworkHandler.post(mNetworkRunner);
Log.e("", "CheckNetworkConnection...");
}
}
You are doing a lot of time consuming work in UI Thread, which create problem. In this situation you should use AsyncTask.
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
//do your time consuming task here
}
protected void onProgressUpdate(Integer... progress) {
//setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
//showDialog("Downloaded " + result + " bytes");
}
}
Once created, a task is executed very simply:
new DownloadFilesTask().execute(url1, url2, url3);
mNetworkHandler = new Handler() will make Runnable execute on UI Thread, you need HandlerThread
private void CheckNetworkConnection(){
if( mNetworkHandler == null ){
HandlerThread handlerThread = new HandlerThread("thread");
handlerThread.start();
mNetworkHandler = new Handler(handlerThread.getLooper());
mNetworkHandler.post(mNetworkRunner);
Log.e("", "CheckNetworkConnection...");
}
}
Using socket connection i need to have two threads, one for reading and one for writing. I found other questions about socket connections but I don't understand how i can use the same socket in two different threads.
I have to create a socket in a different thread from the UI thread, so i need to start a thread to create the socket. Where can i start the two threads?
Sample code structure to give you an idea.
public class SocketActivity extends Activity {
Socket s;
OutputStream dout;
String ip = "127.0.0.1";
int port = 8080;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new Thread(new Runnable() {
#Override
public void run() {
try {
s = new Socket(ip, port);
new Thread(new ReaderRunnable(s));
new Thread(new WriteRunnable(s));
} catch (IOException e) {
e.printStackTrace();
//Handle error state
}
}
});
}
// You can put this class outside activity with public scope
class ReaderRunnable implements Runnable {
Socket socket;
public ReaderRunnable(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
if (socket != null && socket.isConnected()) {
try {
OutputStream out = new BufferedOutputStream(socket.getOutputStream());
//Do reader code
} catch (IOException e) {
e.printStackTrace();
}
} else {
//Handle error case
}
}
}
// You can put this class outside activity with public scope
class WriteRunnable implements Runnable {
Socket socket;
public WriteRunnable(Socket socket) {
this.socket = socket;
}
#Override
public void run() {
if (socket != null && socket.isConnected()) {
try {
InputStream out = new BufferedInputStream(socket.getInputStream());
//Do writer code
} catch (IOException e) {
e.printStackTrace();
}
} else {
//Handle error case
}
}
}
}
Judging by your question this is client side. You don't have to use the socket itself in two different threads. For the read thread you use the InputStream of the socket, and for the write thread you use the OutputStream.
That way you don't have to create a seperate thread just for the socket. Both the read and write threads can be started from the UI thread. For creating the threads i refer you to the Android Documentation Processes and Threads.
I'm very new with programming for android. I have two classes : main and btmanager. When i try to test my app on phone, all I get is a information that procees was killed. What am I doing wrong ?
Code implementation :
Main class :
public class MainActivity extends Activity {
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
Btmanager manager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (bluetooth == null)
{
Toast.makeText(this, "Bluetooth is not enabled on this device", Toast.LENGTH_LONG).show();
System.exit(0);
}
}
#Override
public void onStart()
{
super.onStart();
if (!bluetooth.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 2);
}
manager.run();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
public void closeApp (View view)
{
System.exit(0);
}
}
Btmanager class :
public class Btmanager extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public static final UUID myUUID = UUID.fromString("0x1101");
BluetoothAdapter bluetooth = BluetoothAdapter.getDefaultAdapter();
public Btmanager(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(myUUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
bluetooth.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) { }
return;
}
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
2 problems I see in your code:
You don't instantiate the Btmanager object, it is still null when you call run. (Will cause a NullPointerException - your app will crash).
You call the run method instead of the start method of the Btmanager. If you want the code in the run method to run in a new thread, you have to call start. Calling run will cause it to run in the same thread. This blocks your UI thread which may cause your app to crash, if it blocks for too long.
For test purporses, I don't use BTmanager class - in onStart() method I add new thread with connection implentation, but still without any results - app crash.
public void onStart()
{
super.onStart();
if (!bluetooth.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, 2);
}
new Thread(new Runnable()
{
public void run()
{
try {
//Create a Socket connection: need the server's UUID number of registered
socket = device.createRfcommSocketToServiceRecord(myUUID);
socket.connect();
Log.d("EF-BTBee", "Connectted");
}
catch (IOException e)
{
Log.e("EF-BTBee", "Error : ", e);
}
}
}).start();
}
I have an app in java which is playing the rolle of a server .For limiting the number of incoming connections I'm using a ThreadPool server.
But I have a few problems understanding a part of the code:
Here is y code:
protected ExecutorService threadPool =
Executors.newFixedThreadPool(5);
public ThreadPooledServer(BlockingQueue queue,int port) {
this.serverPort = port;
this.queue=queue;
}
while (!isStopped()) {
Socket clientSocket = null;
try {
System.out.println("Serverul asteapta clienti spre conectare la port" +serverPort);
clientSocket = serverSocket.accept();
clientconnection++;
System.out.println("Serverul a acceptat clientul cu numarul:"
+ clientconnection);
} catch (IOException e) {
if (isStopped()) {
System.out.println("Server Stopped.");
return;
}
throw new RuntimeException("Error accepting client connection",
e);
}
WorkerRunnable workerRunnable = new WorkerRunnable(queue,clientSocket);
this.threadPool.execute(workerRunnable);
}
this.threadPool.shutdown();
System.out.println("Server Stopped.");
}
private synchronized boolean isStopped() {
return this.isStopped;
}
public synchronized void stop() {
this.isStopped = true;
try {
this.serverSocket.close();
}
catch (IOException e) {
throw new RuntimeException("Error closing server", e);
}
}
private void openServerSocket() {
try {
InetSocketAddress serverAddr = new InetSocketAddress(SERVERIP,
serverPort);
serverSocket = new ServerSocket();
serverSocket.bind(serverAddr);
} catch (IOException e) {
throw new RuntimeException("Cannot open port", e);
}
}
WHAT I don't understand:
I'm using a ThreadPooledServer which accepts for 5 incoming connections....
The connection with the clients is done in a while() loop.
while (!isStopped()) {
}
isStopped is a boolean variable returned byt this function:
private synchronized boolean isStopped() {
return this.isStopped;
}
which I call as a condition for starting the loop.
This boolean variable is initially set to false.....and is set back to true in the here:
public synchronized void stop() {
this.isStopped = true;
}
When is setup back to true my while() loop ends and then I close up all the workers of my thread pool.
this.threadPool.shutdown();
The problem is that I never call for this function " stop() "
Question: Is the function called automatically when I close my server?????...or I should call for it somewhere????
You need to call it somewhere in your code to stop your server and close those connections. If you don't the system will eventually reclaim its resources as the server will be shutting down.
You should be able to register a shutdown hook in the JVM (which can call stop()) to help with reclaiming those yourself... Good luck!