View is not getting displayed after starting a service - android

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.

Related

Android bluetooth: A thread started from UI thread blocks the UI thread

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.

Running AsyncTask with button performClick

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.

How to run service not on main thread?

I'm trying to launch service and then open socket to have connection with server.
On button click I create new Thread and then start service.
Thread t = new Thread(){
public void run(){
mIntent= new Intent(MainActivity.this, ConnectonService.class);
mIntent.putExtra("KEY1", "Value used by the service");
context.startService(mIntent);
}
};
t.start();
Then on service, I try to open socket and have connection with server
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//TODO do something useful
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
Scanner scanner = new Scanner(socket.getInputStream());
message = scanner.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
return Service.START_NOT_STICKY;
}
But when I call it, I have error
08-30 08:56:49.268: E/AndroidRuntime(3751): java.lang.RuntimeException: Unable to start service com.example.testofconnection.ConnectonService#40ef02a8 with Intent { cmp=com.example.testofconnection/.ConnectonService (has extras) }: android.os.NetworkOnMainThreadException*
I think problem is that service is on main thread, but I can't find how should I start service on new (independend) thread to keep connection alive?
You can use IntentService for this. Just launch it normally with an Intent from the main thread. onHandleIntent() method gets executed in background thread. Put your socket-code in there. Here is an example code.
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
// this method is called in background thread
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
In your activity you start the service as following.
startService(new Intent(this, MyIntentService.class));
If you need a long-lasting service, you can create a normal service and start a thread there. Here is an example. Make sure you launch it as "foreground" service. This will allow service to run longer without been killed by Android.
public class MyAsyncService extends Service {
private AtomicBoolean working = new AtomicBoolean(true)
private Runnable runnable = new Runnable() {
#Override
public void run() {
while(working.get()) {
// put your socket-code here
...
}
}
}
#Override
public void onCreate() {
// start new thread and you your work there
new Thread(runnable).start();
// prepare a notification for user and start service foreground
Notification notification = ...
// this will ensure your service won't be killed by Android
startForeground(R.id.notification, notification);
}
#Override
public onDestroy() {
working.set(false)
}
}
Move this code to your thread:
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
Scanner scanner = new Scanner(socket.getInputStream());
message = scanner.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
Just as an example (I'm not sure it this fits to your task):
Thread t = new Thread(){
public void run(){
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
Scanner scanner = new Scanner(socket.getInputStream());
message = scanner.nextLine();
} catch (IOException e) {
e.printStackTrace();
}
mIntent= new Intent(MainActivity.this, ConnectonService.class);
mIntent.putExtra("KEY1", "Value used by the service");
context.startService(mIntent);
}
};
t.start();
You should know that a service is running on the UI thread, so you got this error. Check this nice site for more information about various threading approaches in Android.

Holding android bluetooth connection through multiple activities

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();
}
}
});
}
});

Android System kill my serversocket in service

I'm writing an sample app to create a Server on Android and a client to connect to PC. I put the serversocket in a thread of a service. Everything goes perfectly, until a few minutes after the screen goes off. This may be Android kill my server, I tried to put a full wake lock to my code and it wont kill anymore, however, I DO want the screen go off as usual.
Here is my code:
public class MessageListener extends Service {
private ServerSocket serverSocket;
#Override
public void onCreate() {
Log.v("Test", "Create service");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
PowerManager.WakeLock wl=null;
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My Tag");
wl.acquire();
startServer();
if(wl!=null) wl.release();
return START_STICKY;
}
private Runnable thread = new Runnable() {
#Override
public synchronized void run() {
try {
serverSocket = new ServerSocket(Integer.parseInt(5000));
ObjectInputStream in = null;
while (true) {
Socket client = serverSocket.accept();
Log.v("TCP", "S: Receiving...");
try {
in = new ObjectInputStream(client.getInputStream());
DataInController data = new DataInController(
getApplicationContext());
data.processDataIn(in.readObject(), client);
} catch (ClassNotFoundException e) {
System.out.println("TCP S: Error in PC Server Listener");
e.printStackTrace();
} finally {
client.close();
}
}
} catch (IOException e) {
}
}
};
private Thread serverThread;
private synchronized void startServer() {
if (serverThread == null) {
serverThread = new Thread(thread);
serverThread.start();
}
}
private synchronized void stopServer() {
if(serverThread!=null){
Thread t=serverThread;
serverThread=null;
t.interrupt();
}
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v("TCP", "Killing Service!!!!!!!!!!!!!!!!!!!!!!!");
if (serverSocket != null) {
try {
serverSocket.close();
stopServer();
Log.v("TCP", "Closed server socket");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Two things that worked for us:
Wi-Fi lock
Set the Wi-Fi sleep policy to never. Some devices will power down the Wi-Fi radio without this setting, even when a program has a lock on the Wi-Fi radio.
I found the problem. That is the router lost the connection to Android. I've tried to ping it and it said "unreachable", after re connect to wifi, it works, but after a while, it comes again
Also try to keep WakeLock. Doing both works for me.

Categories

Resources