I am building an Android app that communicates with an Arduino board via bluetooth, I have the bluetooth code in a class of it's own called BlueComms. To connect to the device I use the following methord:
public boolean connectDevice() {
CheckBt();
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
Log.d(TAG, "Connecting to ... " + device);
mBluetoothAdapter.cancelDiscovery();
try {
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
btSocket.connect();
outStream = btSocket.getOutputStream();
Log.d(TAG, "Connection made.");
return true;
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
Log.d(TAG, "Unable to end the connection");
return false;
}
Log.d(TAG, "Socket creation failed");
}
return false;
}
private void CheckBt() {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!mBluetoothAdapter.isEnabled()) {
System.out.println("Bt dsbld");
}
if (mBluetoothAdapter == null) {
System.out.println("Bt null");
}
}
This connects fine but as soon as I leave the activity I connected through it drops the connection, showing this through LogCat,
D/dalvikvm(21623): GC_CONCURRENT freed 103K, 10% free 2776K/3056K, paused 5ms+2ms, total 35ms
I can no longer connect to the device, but if I call killBt() it throws a fatal error and if I try to send data I get a 'Socket creation failed' error. My send message code is as follows:
public void sendData(String data, int recvAct) {
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
Log.d(TAG, "Bug BEFORE Sending stuff", e);
}
String message = data;
byte[] msgBuffer = message.getBytes();
try {
outStream.write(msgBuffer);
} catch (IOException e) {
Log.d(TAG, "Bug while sending stuff", e);
}
}
How should I go about preventing the connection from being paused by the activity I connect with when I switch a different activity, I am switching activities with this code:
Intent myIntent = new Intent(v.getContext(), Timelapse.class);
startActivityForResult(myIntent, 0);
Many Thanks,
Rozz
Where did you store the instance of your BlueComms class? If you put it in the first activity then the class instance would have been killed when that activity was destroyed as you left it and moved to the next activity (NB activities also get destroyed on screen rotation)
So you need to find a way to keep the instance of BlueComms class alive for as long as you need it. You could pass it between activities via public properties and store it in onRetainNonConfigurationInstance() during rotations.
An easier trick is to create a class that extends Application use it as the application delegate for your app and add public property to it to store the instance of BlueComms class within it. That way the instance of BlueComms class would be alive for the lifetime of you app.
Extend Application
import android.app.Application;
public class cBaseApplication extends Application {
public BlueComms myBlueComms;
#Override
public void onCreate()
{
super.onCreate();
myBlueComms = new BlueComms();
}
}
Make your class the application delegate in the app manifest
<application
android:name="your.app.namespace.cBaseApplication"
android:icon="#drawable/icon"
android:label="#string/app_name" >
Access the base app from any of your Activities like this
((cBaseApplication)this.getApplicationContext()).myBlueComms.SomeMethod();
What I have done is, Created a singleton class for BluetoothConnection.
So socket creation happens only for one time.
When onCreate method of any activity is created, it first fetch instance of BluetoothConnection class.
Handler is used to send messages from thread in BluetoothConnection class to the corresponding activity by settings Handler.
Like:
Class MyBTConnection{
private static MyBTConnection connectionObj;
private Handler mHandler;
public MyBTConnection() { //constructor }
public static MyBTConnection getInstance() {
if(connectionObj == null) {
connectionObj = new MyBTConnection();
}
return connectionObj;
}
}
public void setHandler(Handler handler) {
mHandler = handler;
}
..... Code for Bluetooth Connection ....
to send message :
mHandler.obtainMessage(what).sendToTarget();
}
// in first activity
class MainActivity extends Activity {
private MyBTConnection connectionObj;
public onCreate(....) {
/*
* Since this is first call for getInstance. A new object
* of MyBTConnection will be created and a connection to
* remote bluetooth device will be established.
*/
connectionObj = MyBTConnection.getInstance();
connectionObj.setHandler(mHandler);
}
private Handler mHandler = new Handler(){
public void onReceive(...) {
/// handle received messages here
}
};
}
// in second activity
class SecondActivity extends Activity {
private MyBTConnection connectionObj;
public onCreate(....) {
/*
* Since this is second call for getInstance.
* Object for MyBTConnection was already created in previous
* activity. So getInstance will return that previously
* created object and in that object, connection to remote
* bluetooth device is already established so you can
* continue your work here.
*/
connectionObj = MyBTConnection.getInstance();
connectionObj.setHandler(mHandler);
}
private Handler mHandler = new Handler(){
public void onReceive(...) {
/// handle received messages here
}
};
}
I'm currently having exactly the same issue and I was thinking of opening/closing the Bluetooth socket each time an Activity asks for it. Each Activity has it's own BlueComms instance.
Because my application will became a bit complex and there will be Bluetooth threaded requests from different activities, I'm thinking that this way will become very difficult to use and troubleshoot.
Another way I came across by reading here...
https://developer.android.com/guide/components/services.html
A Service can be created on the background having a Bluetooth socket always on. All Bluetooth requests can be made using Intent towards this service. This also creates some fair amount of complexity but feels a lot more tidy and organized.
I'm currently having this dilemma, either to use a thread for each activity or use a service. I don't know which way is actually better.
When you are Selecting A device to connect and when you are click on the device list item for requesting a connection to the device use AsyncTask
and put the connect method inside the AsyncTask like this :-
AsyncTask.execute(new Runnable() {
#Override
public void run() {
try {
bluetoothSocket = Globals.bluetoothDevice.createRfcommSocketToServiceRecord(Globals.DEFAULT_SPP_UUID);
bluetoothSocket.connect();
// After successful connect you can open InputStream
} catch (IOException e) {
e.printStackTrace();
}
**Here is the full code for the same problem that i have cracked :-**
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
lablelexconnected.setText("Connecting ...");
bdDevice = arrayListBluetoothDevices.get(position);
//bdClass = arrayListBluetoothDevices.get(position)
// Toast.makeText(getApplicationContext()," " + bdDevice.getAddress(),Toast.LENGTH_SHORT).show();
Log.i("Log", "The dvice : " + bdDevice.toString());
bdDevice = bluetoothAdapter.getRemoteDevice(bdDevice.getAddress());
Globals.bluetoothDevice = bluetoothAdapter.getRemoteDevice(bdDevice.getAddress());
System.out.println("Device in GPS Settings : " + bdDevice);
// startService(new Intent(getApplicationContext(),MyService.class));
/* Intent i = new Intent(GpsSettings.this, MyService.class);
startService(i);*/
// finish();
// connectDevice();
AsyncTask.execute(new Runnable() {
#Override
public void run() {
try {
bluetoothSocket = Globals.bluetoothDevice.createRfcommSocketToServiceRecord(Globals.DEFAULT_SPP_UUID);
bluetoothSocket.connect();
// After successful connect you can open InputStream
InputStream in = null;
in = bluetoothSocket.getInputStream();
InputStreamReader isr = new InputStreamReader(in);
br = new BufferedReader(isr);
while (found == 0) {
String nmeaMessage = br.readLine();
Log.d("NMEA", nmeaMessage);
// parse NMEA messages
sentence = nmeaMessage;
System.out.println("Sentence : " + sentence);
if (sentence.startsWith("$GPRMC")) {
String[] strValues = sentence.split(",");
System.out.println("StrValues : " + strValues[3] + " " + strValues[5] + " " + strValues[8]);
if (strValues[3].equals("") && strValues[5].equals("") && strValues[8].equals("")) {
Toast.makeText(getApplicationContext(), "Location Not Found !!! ", Toast.LENGTH_SHORT).show();
} else {
latitude = Double.parseDouble(strValues[3]);
if (strValues[4].charAt(0) == 'S') {
latitude = -latitude;
}
longitude = Double.parseDouble(strValues[5]);
if (strValues[6].charAt(0) == 'W') {
longitude = -longitude;
}
course = Double.parseDouble(strValues[8]);
// Toast.makeText(getApplicationContext(), "latitude=" + latitude + " ; longitude=" + longitude + " ; course = " + course, Toast.LENGTH_SHORT).show();
System.out.println("latitude=" + latitude + " ; longitude=" + longitude + " ; course = " + course);
// found = 1;
NMEAToDecimalConverter(latitude, longitude);
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
});
Related
So in my android application, I have an intent service which pings devices and finds whether they are online/offline.
When I start my IntentService my UI freezes(Debug points to when ping commands are being executed) in the Service.
Service is started from the parent activity after I get the response of a network call
loadFragment(printersFrag, Constants.CONTAINER_ACT_DASHBOARD, PrintersListingFragment.class.getSimpleName(), false, false, false);
serviceIntent = new Intent(this, PrinterPingIntentService.class);
serviceIntent.putExtra("PrinterList", printersResponse);
this.startService(serviceIntent);
The code for my IntentService is as follows:
public class PrinterPingIntentService extends IntentService {
/**
* The IP Address to ping
*/
private String msIPAddressToPing = null;
/**
* Countdown latch instance to decrement after the thread is done
*/
private CountDownLatch mCountDownLatch;
/**
* Handler to handle ping threads
*/
private PingHandler mPingThreadHandler = null;
/**
* Volatile count variable to manage the ping thread count
*/
private volatile int mnPingThreadCount = 0;
/**
* The currently list of valid IP Addresses
*/
private ConcurrentHashMap<String, Device> mPrinterMap = new ConcurrentHashMap<String, Device>();
public PrinterPingIntentService() {
super(PrinterPingIntentService.class.getName());
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Bundle bundle = intent.getExtras();
PrintersResponseBean printerResponse = bundle.getParcelable("PrinterList");
for (int i = 0; i < printerResponse.getDevices().size(); i++) {
mPrinterMap.put(printerResponse.getDevices().get(i).getDeviceIP(), printerResponse.getDevices().get(i));
}
validatePrinterIP();
}
#Override
public void onCreate() {
super.onCreate();
/*
* Fire up the Ping handler
*/
mPingThreadHandler = new PingHandler();
}
/**
* Validate the PrinterIPs by pinging them
*
* #author
*/
private void validatePrinterIP() {
try {
mnPingThreadCount = 0;
mCountDownLatch = new CountDownLatch(mPrinterMap.size());
for (String sIP : mPrinterMap.keySet()) {
PingRunnable runnable = new PingRunnable(sIP, mCountDownLatch);
Thread thread = new Thread(runnable);
++mnPingThreadCount;
Log.d("BAT", "validatePrinterIP - Thread count - " + mnPingThreadCount);
thread.start();
}
} catch (Exception e) {
Log.d("BAT", "Exception validatePrinterIP - " + e.getMessage());
}
}
/**
* Runnable to make a ping to the given Ip Address
*
* #author
*/
public class PingRunnable implements Runnable {
////////////////////////////////// CLASS MEMBERS ///////////////////////////////////////////
/**
* The IP Address to ping
*/
private String msIPAddressToPing = null;
/**
* Countdown latch instance to decrement after the thread is done
*/
private CountDownLatch mCountDownLatch;
////////////////////////////////// CLASS METHODS ///////////////////////////////////////////
public PingRunnable(String sIPAddress, CountDownLatch latch) {
msIPAddressToPing = sIPAddress;
mCountDownLatch = latch;
}
#Override
public void run() {
try {
/*
* If the destination is not reachable, remove the IP address
* from the printer map and set the bundle value accordingly
*/
if (!pingURL(msIPAddressToPing)) {
Log.d("BAT", "Could not ping " + msIPAddressToPing + ". Removing from Map");
mPrinterMap.remove(msIPAddressToPing);
} else {
Log.d("BAT", "Could ping " + msIPAddressToPing + ". Present in Map");
}
} catch (Exception e) {
Log.d("BAT", "Exception in Ping Runnable - " + e.getMessage());
} finally {
mPingThreadHandler.sendEmptyMessage(0);
mCountDownLatch.countDown();
}
}
}
/**
* Static Handler class to handle messsages.
* Reduce the count by one each time we receive a message to keep
* track that all threads have returned
*
* #author
*/
public class PingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
Log.d("BAT", "Returning thread..");
if (msg.what == 0) {
mnPingThreadCount--;
Log.d("BAT", "Thread Return count - " + mnPingThreadCount);
}
/*
Await Latch
*/
try {
mCountDownLatch.await();
} catch (InterruptedException e) {
Log.d("BAT", "InterruptedException PingHandler - " + e.getMessage());
}
if (mnPingThreadCount == 0) {
//////TEMP
Log.d("BAT", "All threads accounted for. Final Printer List...");
ArrayList<Device> onlinePrinters = new ArrayList<>();
for (String sIP : mPrinterMap.keySet()) {
onlinePrinters.add(mPrinterMap.get(sIP));
Log.d("BAT", "Printers Active " + sIP);
}
//send data back to fragment via localBroadcastReceiver
Intent localBroadcast = new Intent();
localBroadcast.putParcelableArrayListExtra("onlinePrinters", onlinePrinters);
localBroadcast.setAction("printer");
sendBroadcast(localBroadcast);
}
}
}
/**
* Ping a device. First we try the usual isReachable method. If that does not work,
* we go with the Ping command execution
*
* #param sURL THe uRL / IP Address to ping
* #author
*/
public boolean pingURL(String sURL) {
try {
Log.d("BAT", "Pinging IP sURL");
//First try with isReachable
if (Inet4Address.getByName(sURL).isReachable(1000)) {
Log.d("BAT", "Host Reachable by InetAddress " + sURL);
return true;
}
//else try and ping. If neither works, we return false
else {
Log.d("BAT", "Host Not Reachable by InetAddress. Pinging IP with RunTime... " + sURL);
StringBuffer echo = new StringBuffer();
Runtime runtime = Runtime.getRuntime();
Process proc = runtime.exec("ping -c 1 " + sURL);
// "/system/bin/ping -c 8 " + sURL
int nReturnVal = proc.waitFor();
Log.d("BAT", "Done Pinging - " + sURL + ((nReturnVal == 0) ? " Successful" : " Unsuccessful"));
return (nReturnVal == 0);
}
} catch (IOException e) {
Log.d("BAT", "IOEXception in pingURL - " + e.getMessage().toString());
} catch (InterruptedException e) {
Log.d("BAT", "InterruptedException in pingURL - " + e.getMessage());
} catch (Exception e) {
Log.d("BAT", "EXception in pingURL - " + e.getMessage());
}
return false;
}
}
From my intent service, I send back the data of active devices to my Fragment using:
//send data back to fragment via localBroadcastReceiver
Intent localBroadcast = new Intent();
localBroadcast.putParcelableArrayListExtra("onlinePrinters", onlinePrinters);
localBroadcast.setAction("printer");
sendBroadcast(localBroadcast);
and extract this info in my Fragment using:
IntentFilter filter = new IntentFilter();
filter.addAction("printer");
updateUIReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
//UI update here
Bundle bundle = intent.getExtras();
if (bundle.get("onlinePrinters") != null) {
onlinePrinters = (ArrayList) bundle.get("onlinePrinters");
setPrinterStatus();
}
}
};
As I'm using an IntentService a UI freeze should be unlikely as the task is performed on a worker thread and not in the Main Thread.
Not able to figure out the root cause of the UI freeze
onCreate() method of your service is called on the main thread.
PingHandler instance that you're creating there is associated with the main thread.
So handleMessage for this handler is also executed on the main thread. You seem to have blocking operations there which may be the cause of your problem.
New to Android and developing an app that will deliver Bluetooth data to several different activities. I have a ConnectActivity that performs discovery and presents the devices to the user and when the user selects a device, as suggested by someone in this discussion, starts a BluetoothService.
BluetoothService extends Service and starts a background thread which will eventually be used to block on an InputStream, and broadcast data to activities when it is available (i.e. after those activities poll via startService()).
Here is the Activity. Defines a receiver:
public class ConnectActivity extends AppCompatActivity {
...
// My test receiver just to see if things are working.
private BroadcastReceiver mDataReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(D)
Log.d(TAG, "Broadcast received.");
}
};
Starts the service onCreate():
protected void onCreate(Bundle savedInstanceState) {
...
IntentFilter dataFilter = new IntentFilter(BluetoothService.INCOMING_DATA);
registerReceiver(mDataReceiver, dataFilter);
// Start the service with an intent identifying the context ("this" is an Activity,
// which inherits from Context) and the recipient.
Intent i = new Intent(this, BluetoothService.class);
i.putExtra(BluetoothService.COMMAND, "MY_COMMAND"); // Add the command as payload to the intent.
if(D)
Log.d(TAG, "Starting service.");
startService(i);
}
And, finally, connect on a button press:
private void connect(){
if(D)
Log.d(TAG, "Connecting to device " + (mBluetoothDevices.get(mSelectedPos).toString()) + " at position " + mSelectedPos);
final ProgressDialog dialog = new ProgressDialog(this);
dialog.setTitle("Connecting");
dialog.setMessage("Please wait...");
dialog.show();
if(D)
Log.d(TAG, "Attempting to connect.");
mBluetoothService.connect(mBluetoothDevices.get(mSelectedPos), dialog);
Here is my service:
public class BluetoothService extends Service {
...
public void connect (BluetoothDevice device, ProgressDialog dialog){
....
// Start the thread to connect to the device
mConnectThread = new ConnectThread(device, dialog, this);
mConnectThread.start();
}
The ConnectThread connects successfully and starts a ConnectedThread which is intended to send data back to the acitvity as it comes in. I am including all of it since this is where my issue is:
public class ConnectedThread extends Thread {
private BluetoothSocket mmSocket;
private final String mmMYNAME = "ConnectedThread";
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private final Context mmContext;
public ConnectedThread(BluetoothSocket socket, Context context) {
mmSocket = socket;
mmContext = context;
InputStream tryIn = null;
OutputStream tryOut = null;
// Get the BluetoothSocket input and output streams
try {
tryIn = mmSocket.getInputStream();
tryOut = mmSocket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, mmMYNAME + " could not create streams.", e);
}
mmInStream = tryIn;
mmOutStream = tryOut;
mConnectedThreadEnabled = true;
}
#Override
public void run()
{
Thread.currentThread().setName(mmMYNAME);
Intent i = new Intent(INCOMING_DATA);
while(mConnectedThreadEnabled) {
SystemClock.sleep(1000);
if(D)
Log.d(TAG, "ConnectedThread is running.");
LocalBroadcastManager.getInstance(mmContext).sendBroadcast(i); //KABOOM
}
if(D)
Log.d(TAG, "Stopping ConnectedThread.");
}
public void cancel() {
if(D)
Log.d(TAG,"Canceling ConnectedThread.");
try {
if(D)
Log.d(TAG,"Shutting down existing socket.");
mConnectedThreadEnabled = false;
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
My problem is that on the line:
LocalBroadcastManager.getInstance(mmContext).sendBroadcast(i); //KABOOM
I am receiving:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
at android.content.ContextWrapper.getApplicationContext(ContextWrapper.java:107)
at android.support.v4.content.LocalBroadcastManager.getInstance(LocalBroadcastManager.java:102)
at com.company.me.app.BluetoothService$ConnectedThread.run(BluetoothService.java:255)
I am not clear on what the issue is, given that debug indicates that mmContext and i both seem to be properly created.
which performs a mBluetoothService = new BluetoothService();
Never create instances of components yourself. Get rid of this line. Anything that has been using mBluetoothService needs to be rewritten to:
be in the service itself, or
use service binding (e.g., bindService(), with the activity then calling methods on the supplied Binder), or
use some other pattern, to avoid the activity trying to have a direct reference to the service, let alone creating an instance of the service itself
I'm having a weird problem. I already lost a lot of time trying to understand
and solve this but nothing works.
I have an app that communicates with another device across bluetooth connection
to receive some sensor data. In that point, everything works fine, I can connect
to the device, receive and treat the messages.
But yesterday, I decided to create some kind of log file to directly save in the
internal memory the data received from the device without any kind of transformation from my app.
To receive the data from the device, I have a background thread:
public class CommunicationThread extends Thread {
private static final UUID UUID_DEVICE = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String TAG = CommunicationThread.class.getSimpleName();
private CommunicationListener mListener;
private boolean mRunning;
private BluetoothSocket mBluetoothSocket;
private InputStream mInputStream;
private OutputStream mOutputStream;
public interface CommunicationListener {
void onMessageReceived(String msg);
}
public CommunicationThread(
#NonNull BluetoothDevice device,
#Nullable CommunicationListener listener) throws IOException {
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(UUID_DEVICE);
socket.connect();
this.mBluetoothSocket = socket;
this.mInputStream = socket.getInputStream();
this.mOutputStream = socket.getOutputStream();
this.mListener = listener;
}
#Override
public void run() {
mRunning = true;
byte[] bytes = new byte[1024];
int length;
while (mRunning) {
try {
Log.d(TAG, "Waiting for message");
// read the message (block until receive)
length = mInputStream.read(bytes);
String msg = new String(bytes, 0, length);
Log.d(TAG, "Message: " + msg);
// Message received, inform the listener
if (mListener != null)
mListener.onMessageReceived(msg);
} catch (Exception e) {
Log.e(TAG, "Error reading the message", e);
}
}
}
public void sendCommand(String msg) {
try {
mOutputStream.write((msg).getBytes());
} catch (Exception e) {
Log.e(TAG, "Error to send message", e);
}
}
public void stopCommunication() {
mRunning = false;
mListener = null;
try {
if (mBluetoothSocket != null) {
mBluetoothSocket.close();
}
if (mInputStream != null) {
mInputStream.close();
}
if (mOutputStream != null) {
mOutputStream.close();
}
} catch (IOException e) {
Log.e(TAG, "Error to stop communication", e);
}
}
}
This thread works pretty fine and when a message is received, it informs the listener,
my Controller class. The first thing that I try to do when a message comes, is save it:
public class Controller implements CommunicationThread.CommunicationListener
...
#Override
public void onMessageReceived(final String msg) {
Log.d(TAG, "onMessageReceived(msg): " + msg);
mLogCreator.saveThis(msg);
....
}
}
Here is the LogCreator class:
public class LogCreator {
private static final String TAG = LogCreator.class.getSimpleName();
public static final String LOG_FILE_NAME = "log.txt";
private final Context mContext;
private volatile String mTempFullLog;
public LogCreator(Context context) {
mContext = context.getApplicationContext();
File dir = new File(mContext.getFilesDir(), "log_folder");
if (!dir.exists()) {
dir.mkdirs();
File file = new File(dir, LOG_FILE_NAME);
writeString(file, "");
Log.d(TAG, "empty file created");
}
}
public void saveThis(final String data) {
mTempFullLog += "\n" + data;
Log.d(TAG, "New log: " + data);
}
public void start() {
File dir = new File(mContext.getFilesDir(), "log_folder");
File file = new File(dir, LOG_FILE_NAME);
mTempFullLog = readString(file);
Log.d(TAG, "File: " + file);
Log.d(TAG, "Temp full log: " + mTempFullLog);
}
public void stop() {
File dir = new File(mContext.getFilesDir(), "log_folder");
File file = new File(dir, LOG_FILE_NAME);
writeString(file, mTempFullLog);
Log.d(TAG, "log saved: " + mTempFullLog);
}
}
The LogCreator class is already initialized and it works properly, because
if I try to read the file later, everything is there.
The real problem is the following: there is a lot of calls to Log.d during
this execution flow, and this makes very easy to me to understand the all process.
But, the logs are only printed in the logcat until this Log.d call, in the
CommunicationThread class:
Log.d(TAG, "Waiting for message);
After the message received, all code executes normally, but no logs are printed in
the logcat and I really dont know why.
Logs not printed:
CommunicationThread:
Log.d(TAG, "Message: " + msg);
Controller:
Log.d(TAG, "onMessageReceived(msg): " + msg);
LogCreator:
Log.d(TAG, "New log: " + data);
Like I said, I know that everything is working fine with the code because the log
file is created in internal memory even without the logcat prints. It cost me
some hours to realize that the problem is only with the log and not really in
my code.
For testing purpose, if I add this code in the saveThis method of LogCreator,
it executes normally:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(mContext, data, Toast.LENGTH_SHORT).show();
}
});
This makes me think that everything could be a thread problem, because the start
and stop methods of LogCreator are both called from the main thread not the CommunicationThread and both methods have their logs printed. Because of this, in the onMessageReceived method
of the Controller class, I tried this:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
mLogCreator.saveThis(msg);
}
});
But, unfortunately, the logs don't get printed in the logcat. The toast is still
executed and the data are still saved to the file.
If anyone has any idea of what might be causing this, I really want to know, thanks.
I finally find the solution myself. The reason why the following not work is not clear for me, and IMO it should be treated like a bug.
I compile the app in debug mode and discover that the string received from the device has a "\r" in the end.
Example: "15.50\r"
So, for some strange reason, if I try to do this:
Log.d(TAG, "New log: " + data);
Nothing prints and we don't receive no warnings at all.
But, if I do this instead:
Log.d(TAG, "New log: " + data.replace("\r", ""));
Where data is: "15.50\r"
Everything works and the logcat prints the message.
I'm trying to integrate the Twilio client into a larger application. Everything seems to work fine until I call device.connect(parameters, connectionListener). I get the 31100 Generic Malformed Request error and that's it.
On the same device, using the same Twilio account and the same Twilio application, the sample code supplied with the Twilio Android SDK (MonkeyPhone) works perfectly.
I can't find any more details about what the error means or what are the possible causes. While I'm assuming that I'm sending invalid data, I don't see how is that possible. The Capability Token is OK, I've verified it against the one generated in the MonkeyPhone sample app. Creating a Device works fine, no errors. The error is thrown even when I'm not sending any parameters in the connect() method. The onConnecting() method of the ConnectionListener gets called, but then the onDisconnected(Connection inConnection, int inErrorCode, String inErrorMessage) is called with the Malformed Request error.
The code for the Voice TwiML is working fine, it's just a simple PHP script generating the most simple <Dial> verb possible:
<Response>
<Dial>someone</Dial>
</Response>
Other specific information... I'm running another service in my application, used to do various other operations. Could this interfere in some way? Also, I'm using a trial account and I'm living in Romania, where calling real phone numbers is not supported (but I'm not using phone numbers anyway). Could this affect me in any way?
I apologize in advance for throwing the huge wall of code, but I hope a second pair of eyes can spot something wrong. This is the version of the code most similar to the MonkeyPhone sample. The only difference is that I'm using an AsyncTask to get the capability token (the JsonAsyncRequestWithError class.
public class MonkeyPhone implements Twilio.InitListener, DeviceListener {
private static final String TAG = "MonkeyPhone";
private Context context;
private Device device;
private Connection connection;
public MonkeyPhone(Context context) {
this.context = context;
Twilio.initialize(context, this /* Twilio.InitListener */);
}
#Override
/* Twilio.InitListener method */
public void onInitialized() {
Log.d(TAG, "Twilio SDK is ready");
// the Emulator has a somewhat unique "product" name
String clientName = "doug";
HttpGet get = new HttpGet("http://teamphoenix.zzl.org/capability.php?ClientName=" + clientName);
JsonAsyncRequestWithError asyncRequestWithError = new JsonAsyncRequestWithError(context, "test", new AsyncRequestWithErrorListener() {
#Override
public void onResult(AsyncRequestResponse response, Object destination) {
createDevice(response.getMessage());
}
#Override
public void onErrorResult(AsyncRequestResponse response, Object destination) {
}
});
asyncRequestWithError.execute(get);
}
public void createDevice(String token) {
try {
device = Twilio.createDevice(token, this /* DeviceListener */);
Intent intent = new Intent(context, SpringshotPhoneActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
device.setIncomingIntent(pendingIntent);
} catch (Exception e) {
Log.e(TAG, "", e);
}
}
#Override
/* Twilio.InitListener method */
public void onError(Exception e) {
Log.e(TAG, "Twilio SDK couldn't start: " + e.getLocalizedMessage());
}
#Override
/* DeviceListener method */
public void onStartListening(Device inDevice) {
Log.i(TAG, "Device is now listening for incoming connections");
}
#Override
/* DeviceListener method */
public void onStopListening(Device inDevice) {
Log.i(TAG, "Device is no longer listening for incoming connections");
}
#Override
/* DeviceListener method */
public void onStopListening(Device inDevice, int inErrorCode, String inErrorMessage) {
Log.i(TAG, "Device is no longer listening for incoming connections due to error " + inErrorCode + ": " + inErrorMessage);
}
#Override
/* DeviceListener method */
public boolean receivePresenceEvents(Device inDevice) {
return false; // indicate we don't care about presence events
}
#Override
/* DeviceListener method */
public void onPresenceChanged(Device inDevice, PresenceEvent inPresenceEvent) {
}
public void connect(String phoneNumber) {
Map<String, String> parameters = new HashMap<String, String>(1);
parameters.put("PhoneNumber", phoneNumber);
/// ---------------- THIS IS THE CALL THAT FAILS ------------------------------------//
connection = device.connect(parameters, null /* ConnectionListener */);
if (connection == null)
Log.w(TAG, "Failed to create new connection");
}
public void disconnect() {
if (connection != null) {
connection.disconnect();
connection = null;
}
}
public void handleIncomingConnection(Device inDevice, Connection inConnection) {
Log.i(TAG, "Device received incoming connection");
if (connection != null)
connection.disconnect();
connection = inConnection;
connection.accept();
}
#Override
protected void finalize() {
if (connection != null)
connection.disconnect();
if (device != null)
device.release();
}
}
Thank you very much!
I figured out the problem. Apparently, I had to read the InputStream from the server using the UTF-8 encoding (even if there are no special characters in the token).
char[] buf = new char[1024];
InputStream is = response.getEntity().getContent();
StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(is, "UTF-8");
int bin;
while ((bin = in.read(buf, 0, buf.length)) >= 0) {
out.append(buf, 0, bin);
}
return out.toString();
I am starting a service from activity.The problem here is the service gets started started but the activity is not getting displayed.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
startService(new Intent(this, ServerActivity1.class));
}
In the service I am opening a socket via a simple function like this by using a timer.The service gets started as I am able to see in logs but the view(R.layout.main) never gets displayed and after some time the force close pop is displayed.
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "sasa", Toast.LENGTH_SHORT).show();
timer.scheduleAtFixedRate( new TimerTask() {
public void run() {
read();
}
}, 0,50000);
Log.i("NoServer","Started1");
read();
}
#Override
public void onDestroy() {
}
#Override
public void onStart(Intent intent, int startid) {
Log.i("Home","Listening on IP: " + SERVERIP+"\n");
}
public void read()
{
SERVERIP = getLocalIpAddress();
Log.i("Home","Listening on IP: " + SERVERIP+"\n");
if (SERVERIP != null) {
Log.i("Home","Listening on IP: " + SERVERIP+"\n");
}
try {
serverSocket = new ServerSocket(SERVERPORT);
} catch (IOException e1) {
e1.printStackTrace();
}
while (true) {
Socket client;
Log.i("Home","Listening on IP: " + SERVERIP+"\n");
try {
client = serverSocket.accept();
Log.i("Home","Listening on IP: " + SERVERIP+"\n");
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
while ((line = in.readLine()) != null) {
serverSocket.close();
read();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Its because your UI thread (main) is being shared by service unless you define your service in a separate process in manifest. If you start your service in activity's onResume method, till then your service would be visible but still may cause ANR depending on the time (max 5 secs) it takes to complete requests in service.
Its better to put all the socket stuff (or any expensive calls) of your service in a separate thread. In that case, your app will not hang or crash due to ANR.
You should use ThreadHandler and Handler to execute Messages and/or Runnables in a separate thread inside Service.