PrintWriter#println() throws a NullPointerException in the Thread - android

I am trying to send a String message with PrintWriter#println(message) but it throws an exception as soon as I call the thread.
I don't know from where this message is coming from. I checked if the message is null but it is not the case.
import java.io.PrintWriter;
import android.util.Log;
public class Send_Client implements Runnable {
private PrintWriter out;
private String message=null;
public Send_Client(PrintWriter out,String message) {
this.out=out;
this.message=message;
}
#Override
public void run() {
// the message is different from null
// Problem to resolve
out.println(message);
out.flush();
Log.d(MainActivity.TAG, "The message has been sent from Send_Client");
}
}
And here is where I call the thread
public class ClientDataService extends IntentService {
private static Socket socket=null;
private static final int SOCKET_TIMEOUT = 5000;
public static final String ACTION_CLIENT="Action_Client";
public static final String EXTRAS_GROUP_OWNER_PORT = "go_port";
public static final String EXTRAS_GROUP_OWNER_ADDRESS = "go_host";
String message=null;
private BufferedReader in=null;
public PrintWriter out=null;
/** Command to the service to receive a message */
static final int MSG_CLIENT = 2;
public ClientDataService(String name) {
super(name);
}
public ClientDataService() {
super("ClientDataService");
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent.getAction().equals(ACTION_CLIENT)) {
int port = intent.getExtras().getInt(EXTRAS_GROUP_OWNER_PORT);
String host= intent.getExtras().getString(EXTRAS_GROUP_OWNER_ADDRESS);
try{
Log.d(MainActivity.TAG, "ask for connection to the server");
socket=new Socket();
socket.bind(null);
socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
Log.d(MainActivity.TAG, "connection socket has been established");
try{
in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
out= new PrintWriter(socket.getOutputStream());
Thread t1=new Thread(new Receive_Client(in));
t1.start();
}catch(IOException e){
Log.d(MainActivity.TAG, "the client disconnect");
}
}catch (UnknownHostException e) {
Log.d(MainActivity.TAG, "impossible to connect to the host"+socket.getLocalSocketAddress());
}catch (IOException e) {
Log.d(MainActivity.TAG, "No Server Listening the port"+socket.getLocalPort());
}
}
}
public PrintWriter getOutputstream(){
return out;
}
/**
* Handler of incoming messages from UI Thread.
*/
class IncomingHandler extends Handler {
ClientDataService mservice;
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_CLIENT:
mservice=new ClientDataService();
Log.d(MainActivity.TAG,"CDS has received the following message to send to the server"+(String)msg.obj);
Log.d(MainActivity.TAG, "Get the Out value"+mservice.getOutputstream().toString());
Thread t2= new Thread(new Send_Client(mservice.getOutputstream(),(String)msg.obj));
t2.start();
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
/**
* When binding to the service, we return an interface to our messenger
* for sending messages to the service.
*/
#Override
public IBinder onBind(Intent intent) {
Toast.makeText(getApplicationContext(), "binding client", Toast.LENGTH_SHORT).show();
return mMessenger.getBinder();
}
}

I'll post this as an answer as it is a bit long for a comment (even though I cannot provide a complete/direct solution).
The problem occurs because:
out is not yet set in ClientDataService when you created its object in IncomingHandler
so mservice.getOutputstream() return null
which you use in Send_Client.
You are misusing the ServerIntent, by directly creating a derived object from it using new ClientDataService().
Instead you need to start a services as follows, acording to the Android Service documentation:
Intent intent = new Intent(this, HelloService.class);
startService(intent);
So I guess you need to read more about android services (lots of information in the documentation that looks important to me) in order to properly use them in your application.

Related

Thread stops when creating TCP connection

I am building an application that starts a Connection via a Thread but when i initialize the Connection the Application doesn't connect and stops (it continues to work but it does nothing).
1 week ago it did work without any troubles, but when i updated Android Studio it started causing me this problem.
This is the abstract class of the Connection:
public abstract class Connection implements Runnable{
private Socket socket;
private PrintWriter writer;
private BufferedReader reader;
private Semaphore semaphore;
private ArrayList<String> messageQueue;
private Thread connection;
//private String state; // CONNECTED,RUNNING,STOPPED,RESTARTED,CLOSED
Connection(InetAddress address, int port) {
try {
socket = new Socket(address,port);
onConnectionEstablished(socket);
writer = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
semaphore = new Semaphore(1);
messageQueue = new ArrayList<>();
} catch (Exception e) {
close();
}
}
#Override
public void run() {
connection = Thread.currentThread();
while(!connection.isInterrupted()){
this.read();
}
}
// Checks if connection is established
boolean isConnectionEstablished(){
return socket != null && !socket.isClosed();
}
public abstract Socket onConnectionEstablished(Socket socket);
// Restarts the connection
public abstract void onConnectionClose();
// Launched when message is received
public abstract String onMessageReceived(String message);
// Launched when message is posted
public abstract String onMessagePosted(String message);
// Launched when message is sent
public abstract String onMessageSent(String message);
// Post message
public void postMessage(String message){
messageQueue.add(message);
sendMessage();
}
// Send Message
private void sendMessage() {
final String message = messageQueue.get(0);
onMessagePosted(message);
new Thread(new Runnable() {
#Override
public void run() {
try {
semaphore.acquire();
System.out.println(Colors.color("[*] Sending: \'"+message+"\' [*]","blue"));
writer.print(message);
writer.flush();
messageQueue.remove(0);
semaphore.release();
} catch (Exception e) {
close();
}
}
}).start();
onMessageSent(message);
}
// Read messages
private void read(){
try {
String message = reader.readLine();
if(message != null && message.length() > 0)
onMessageReceived(message);
else
throw new Exception();
} catch (Exception e) {
close();
}
}
// Close the connection
private void close(){
try{
connection.interrupt();
this.writer.close();
this.reader.close();
this.socket.close();
}catch(Exception e){
System.out.println(Colors.color("[X] Error while closing [X]","red"));
}
onConnectionClose();
}
// Restart the connection
void restart(){
new Thread(new Runnable() {
#Override
public void run() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
System.out.println(Colors.color("[X] Error while restarting [X]","red"));
}
new Thread(new ConnectionInitializer()).start();
}
}).start();
}
}
While doing some debugging i found out that it apparently blocks when the socket is instantiated.

WebSocket with Service has create new Connection every time startService

I am beginner with using service and webSocket.
I am trying to connect and send String on webSocket using Service in Android but when calling StartService(), it gives me anew Connection every time.I need to send text string every time without creating a New Connection.
The code I used is as follows
public class SocketServices extends Service {
DataOutputStream toServer = null;
DataInputStream fromServer = null;
String line = null;
char frmSe;
WebSocketClient webSocketClient;
OutputStream os;
static Socket socket;
String message = null;
public static final String NOTIFICATION = "MyService";
public static final String TAG = "SOCKETS";
URI uri;
static final int MSG_SAY_HELLO = 1;
private static final String MESSENGER = "MESSENGER";
boolean isWebSocketConnect = false;
private static final String PATHNAME = "PATHNAME";
/**
* Looper associated with the HandlerThread.
*/
private volatile Looper mServiceLooper;
private ServiceHandler mServiceHandler;
/**
* Factory method to make the desired Intent.
*/
public static Intent makeIntent(Context context, String url, Handler downloadHandler) {
return new Intent(context,
SocketServices.class
.putExtra("js",url)
.putExtra(MESSENGER,
new Messenger(downloadHandler));
}
public SocketServices() {
}
#Override
public void onCreate() {
super.onCreate();
// Create and start a background HandlerThread since by
// default a Service runs in the UI Thread, which we don't
// want to block.
try {
uri = new URI("ws://localhost:port");
} catch (URISyntaxException e) {
e.printStackTrace();
}
HandlerThread thread =
new HandlerThread("DownloadService");
thread.start();
// Get the HandlerThread's Looper and use it for our Handler.
mServiceLooper = thread.getLooper();
if(isWebSocketConnect == false){
mServiceHandler =
new ServiceHandler(mServiceLooper);}
else {
isWebSocketConnect=true;
}
#Override
public int onStartCommand(final Intent intent, int flags, int startId) {
Message message = mServiceHandler.makeDownloadMessage(intent);
// Send the Message to ServiceHandler to retrieve an image
// based on contents of the Intent.
mServiceHandler.sendMessage(message);
return START_STICKY;
}
// here use class handeller With Messenger to connect with activity and do process
private final class ServiceHandler extends Handler {
/**
* Class constructor initializes the Looper.
*
* #param looper
* The Looper that we borrow from HandlerThread.
*/
public ServiceHandler(Looper looper) {
super(looper);
}
private Message makeDownloadMessage(Intent intent) {
Message message = Message.obtain();
message.obj = intent;
return message;
}
public void handleMessage(Message message) {
// Get the intent from the message.
Intent intent = (Intent) message.obj;
webSocket(intent);
}
}
// method to connect with web Socket
public void webSocket(final Intent intent) {
final String request = intent.getStringExtra("jss");
webSocketClient = new WebSocketClient(uri) {
#Override
public void onOpen(ServerHandshake handshakedata) {
send(request);
}
#Override
public void onMessage(String message) {
sendPath(intent, message);
}
#Override
public void onClose(int code, String reason, boolean remote) {
}
#Override
public void onError(Exception ex) {
// System.err.println("an error occurred:" + ex)
}
};
webSocketClient.connect();
}
ActivityClass
Intent intent= SocketServices.makeIntent(MainActivity.this,String.valueOf(object),mDownloadHandler);
try {
startService(intent);
} catch(Exception e) {
e.printStackTrace();
}

Android - how to run a socket in a long lives background thread

I'm building a simple chat that connects between an android client and a java server (running on my pc). The user can send and receive messages to/from the android app and the desktop server.
I'm dealing now with the question of how to run the client-socket in a different thread than the UI Thread.
I saw solutions using AsyncTask, but as the user may communicate using the app for a long sequential time, AsyncTask looks like a bad approach.
AsyncTasks should ideally be used for short operations (a few seconds at the most.) API
Because i need the client socket to consistently listen for messages from the desktop server, i thought of creating new Thread receiving a Runnable implementing class.
My questions
1. In which "thread mechanism" to place the client socket rows (Thread, IntentService)?
Socket client = new Socket(host, port);
InputStreamReader inputStreamReader = new InputStreamReader(client.getInputStream());
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
while ((messageFromServer = bufferedReader.readLine()) != null) { //... }
2. How can the client socket (running from a different thread than the main thread) post the messageFromServer to an TextView?
How will i send the user messages from the app to the server (using the client-socket ofcourse), upon user entering text and clicking a button?
Thanks!
I've created a similar app and I used a Service which runs in the background.
I've copied the code from the IntentService clas and updated handleMessage(Message msg) method and removed stopSelf(msg.arg1); line. In this way you have a service that runs in the background. After it I used a Thread for the connection.
You have two choices here. Store the data into the database and the GUI refreshes itself. Or use LocalBroadcastManager.
Here you can also store the data into the db or Start the service with a special intent.
Here is my implementation. I hope you will understand the code.
public class KeepAliveService extends Service {
/**
* The source of the log message.
*/
private static final String TAG = "KeepAliveService";
private static final long INTERVAL_KEEP_ALIVE = 1000 * 60 * 4;
private static final long INTERVAL_INITIAL_RETRY = 1000 * 10;
private static final long INTERVAL_MAXIMUM_RETRY = 1000 * 60 * 2;
private ConnectivityManager mConnMan;
protected NotificationManager mNotifMan;
protected AlarmManager mAlarmManager;
private boolean mStarted;
private boolean mLoggedIn;
protected static ConnectionThread mConnection;
protected static SharedPreferences mPrefs;
private final int maxSize = 212000;
private Handler mHandler;
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private final class ServiceHandler extends Handler {
public ServiceHandler(final Looper looper) {
super(looper);
}
#Override
public void handleMessage(final Message msg) {
onHandleIntent((Intent) msg.obj);
}
}
public static void actionStart(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_START)));
}
public static void actionStop(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_STOP)));
}
public static void actionPing(final Context context) {
context.startService(SystemHelper.createExplicitFromImplicitIntent(context, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER)));
}
#Override
public void onCreate() {
Log.i(TAG, "onCreate called.");
super.onCreate();
mPrefs = getSharedPreferences("KeepAliveService", MODE_PRIVATE);
mConnMan = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
mNotifMan = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
mHandler = new Handler();
final HandlerThread thread = new HandlerThread("IntentService[KeepAliveService]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
// If our process was reaped by the system for any reason we need to
// restore our state with merely a
// call to onCreate.
// We record the last "started" value and restore it here if necessary.
handleCrashedService();
}
#Override
public void onDestroy() {
Log.i(TAG, "Service destroyed (started=" + mStarted + ")");
if (mStarted) {
stop();
}
mServiceLooper.quit();
}
private void handleCrashedService() {
Log.i(TAG, "handleCrashedService called.");
if (isStarted()) {
// We probably didn't get a chance to clean up gracefully, so do it now.
stopKeepAlives();
// Formally start and attempt connection.
start();
}
}
/**
* Returns the last known value saved in the database.
*/
private boolean isStarted() {
return mStarted;
}
private void setStarted(final boolean started) {
Log.i(TAG, "setStarted called with value: " + started);
mStarted = started;
}
protected void setLoggedIn(final boolean value) {
Log.i(TAG, "setLoggedIn called with value: " + value);
mLoggedIn = value;
}
protected boolean isLoggedIn() {
return mLoggedIn;
}
public static boolean isConnected() {
return mConnection != null;
}
#Override
public void onStart(final Intent intent, final int startId) {
final Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
#Override
public int onStartCommand(final Intent intent, final int flags, final int startId) {
Log.i(TAG, "Service started with intent : " + intent);
onStart(intent, startId);
return START_NOT_STICKY;
}
private void onHandleIntent(final Intent intent) {
if (IntentActions.KEEP_ALIVE_SERVICE_STOP.equals(intent.getAction())) {
stop();
stopSelf();
} else if (IntentActions.KEEP_ALIVE_SERVICE_START.equals(intent.getAction())) {
start();
} else if (IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER.equals(intent.getAction())) {
keepAlive(false);
}
}
#Override
public IBinder onBind(final Intent intent) {
return null;
}
private synchronized void start() {
if (mStarted) {
Log.w(TAG, "Attempt to start connection that is already active");
setStarted(true);
return;
}
try {
registerReceiver(mConnectivityChanged, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to register the receiver.", e);
}
if (mConnection == null) {
Log.i(TAG, "Connecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private synchronized void stop() {
if (mConnection != null) {
mConnection.abort(true);
mConnection = null;
}
setStarted(false);
try {
unregisterReceiver(mConnectivityChanged);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred while trying to unregister the receiver.", e);
}
cancelReconnect();
}
/**
* Sends the keep-alive message if the service is started and we have a
* connection with it.
*/
private synchronized void keepAlive(final Boolean forced) {
try {
if (mStarted && isConnected() && isLoggedIn()) {
mConnection.sendKeepAlive(forced);
}
} catch (final IOException e) {
Log.w(TAG, "Error occurred while sending the keep alive message.", e);
} catch (final JSONException e) {
Log.w(TAG, "JSON error occurred while sending the keep alive message.", e);
}
}
/**
* Uses the {#link android.app.AlarmManager} to start the keep alive service in every {#value #INTERVAL_KEEP_ALIVE} milliseconds.
*/
private void startKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + INTERVAL_KEEP_ALIVE, INTERVAL_KEEP_ALIVE, pi);
}
/**
* Removes the repeating alarm which was started by the {#link #startKeepAlives()} function.
*/
private void stopKeepAlives() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_PING_SERVER), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
public void scheduleReconnect(final long startTime) {
long interval = mPrefs.getLong("retryInterval", INTERVAL_INITIAL_RETRY);
final long now = System.currentTimeMillis();
final long elapsed = now - startTime;
if (elapsed < interval) {
interval = Math.min(interval * 4, INTERVAL_MAXIMUM_RETRY);
} else {
interval = INTERVAL_INITIAL_RETRY;
}
Log.i(TAG, "Rescheduling connection in " + interval + "ms.");
mPrefs.edit().putLong("retryInterval", interval).apply();
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.set(AlarmManager.RTC_WAKEUP, now + interval, pi);
}
public void cancelReconnect() {
final PendingIntent pi = PendingIntent.getService(this, 0, new Intent(IntentActions.KEEP_ALIVE_SERVICE_RECONNECT), PendingIntent.FLAG_UPDATE_CURRENT);
mAlarmManager.cancel(pi);
}
private synchronized void reconnectIfNecessary() {
if (mStarted && !isConnected()) {
Log.i(TAG, "Reconnecting...");
mConnection = new ConnectionThread(Config.PLUGIN_BASE_HOST, Config.PLUGIN_BASE_PORT);
mConnection.start();
}
}
private final BroadcastReceiver mConnectivityChanged = new BroadcastReceiver() {
#Override
public void onReceive(final Context context, final Intent intent) {
final NetworkInfo info = mConnMan.getActiveNetworkInfo(); // (NetworkInfo) intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
final boolean hasConnectivity = info != null && info.isConnected();
Log.i(TAG, "Connecting changed: connected=" + hasConnectivity);
if (hasConnectivity) {
reconnectIfNecessary();
} else if (mConnection != null) {
mConnection.abort(false);
mConnection = null;
}
}
};
protected class ConnectionThread extends Thread {
private final Socket mSocket;
private final String mHost;
private final int mPort;
private volatile boolean mAbort = false;
public ConnectionThread(final String host, final int port) {
mHost = host;
mPort = port;
mSocket = new Socket();
}
/**
* Returns whether we have an active internet connection or not.
*
* #return <code>true</code> if there is an active internet connection.
* <code>false</code> otherwise.
*/
private boolean isNetworkAvailable() {
final NetworkInfo info = mConnMan.getActiveNetworkInfo();
return info != null && info.isConnected();
}
#Override
public void run() {
final Socket s = mSocket;
final long startTime = System.currentTimeMillis();
try {
// Now we can say that the service is started.
setStarted(true);
// Connect to server.
s.connect(new InetSocketAddress(mHost, mPort), 20000);
Log.i(TAG, "Connection established to " + s.getInetAddress() + ":" + mPort);
// Start keep alive alarm.
startKeepAlives();
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
// Send the login data.
final JSONObject login = new JSONObject();
// Send the login message.
dos.write((login.toString() + "\r\n").getBytes());
// Wait until we receive something from the server.
String receivedMessage;
while ((receivedMessage = in.readLine()) != null) {
Log.i(TAG, "Received data: " + receivedMessage);
processMessagesFromServer(dos, receivedMessage);
}
if (!mAbort) {
Log.i(TAG, "Server closed connection unexpectedly.");
}
} catch (final IOException e) {
Log.e(TAG, "Unexpected I/O error.", e);
} catch (final Exception e) {
Log.e(TAG, "Exception occurred.", e);
} finally {
setLoggedIn(false);
stopKeepAlives();
if (mAbort) {
Log.i(TAG, "Connection aborted, shutting down.");
} else {
try {
s.close();
} catch (final IOException e) {
// Do nothing.
}
synchronized (KeepAliveService.this) {
mConnection = null;
}
if (isNetworkAvailable()) {
scheduleReconnect(startTime);
}
}
}
}
/**
* Sends the PING word to the server.
*
* #throws java.io.IOException if an error occurs while writing to this stream.
* #throws org.json.JSONException
*/
public void sendKeepAlive(final Boolean forced) throws IOException, JSONException {
final JSONObject ping = new JSONObject();
final Socket s = mSocket;
s.getOutputStream().write((ping.toString() + "\r\n").getBytes());
}
/**
* Aborts the connection with the server.
*/
public void abort(boolean manual) {
mAbort = manual;
try {
// Close the output stream.
mSocket.shutdownOutput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the input stream.
mSocket.shutdownInput();
} catch (final IOException e) {
// Do nothing.
}
try {
// Close the socket.
mSocket.close();
} catch (final IOException e) {
// Do nothing.
}
while (true) {
try {
join();
break;
} catch (final InterruptedException e) {
// Do nothing.
}
}
}
}
public void processMessagesFromServer(final DataOutputStream dos, final String receivedMessage) throws IOException {
}
}
You can start the service by calling KeepAliveService.actionStart() and you can also define custom functions.
Please note that the service will be stopped only if you call KeepAliveService.actionStop(). Otherwise it will run forever. If you call e.g. KeepAliveService.actionSendMessage(String message) then the intent will be passed to the service and you can handle it easily.
EDIT:
The SystemHelper class is only a utility class which contains static methods.
public class SystemHelper {
/**
* Android Lollipop, API 21 introduced a new problem when trying to invoke implicit intent,
* "java.lang.IllegalArgumentException: Service Intent must be explicit"
*
* If you are using an implicit intent, and know only 1 target would answer this intent,
* This method will help you turn the implicit intent into the explicit form.
*
* Inspired from SO answer: http://stackoverflow.com/a/26318757/1446466
* #param context the application context
* #param implicitIntent - The original implicit intent
* #return Explicit Intent created from the implicit original intent
*/
public static Intent createExplicitFromImplicitIntent(Context context, Intent implicitIntent) {
Log.i(TAG, "createExplicitFromImplicitIntent ... called with intent: " + implicitIntent);
// Retrieve all services that can match the given intent
PackageManager pm = context.getPackageManager();
List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);
// Make sure only one match was found
if (resolveInfo == null || resolveInfo.size() != 1) {
Log.i(TAG, "createExplicitFromImplicitIntent ... resolveInfo is null or there are more than one element.");
return null;
}
// Get component info and create ComponentName
ResolveInfo serviceInfo = resolveInfo.get(0);
String packageName = serviceInfo.serviceInfo.packageName;
String className = serviceInfo.serviceInfo.name;
ComponentName component = new ComponentName(packageName, className);
Log.i(TAG, "createExplicitFromImplicitIntent ... found package name:" + packageName + ", class name: " + className + ".");
// Create a new intent. Use the old one for extras and such reuse
Intent explicitIntent = new Intent(implicitIntent);
// Set the component to be explicit
explicitIntent.setComponent(component);
return explicitIntent;
}
}
The Config class.
public class Config {
public static final String PACKAGE_NAME = "com.yourapp.package";
public static final String PLUGIN_BASE_HOST = "test.yoursite.com";
public static final int PLUGIN_BASE_PORT = 10000;
}
And the IntentActions class.
public class IntentActions {
public static final String KEEP_ALIVE_SERVICE_START = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_START";
public static final String KEEP_ALIVE_SERVICE_STOP = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_STOP";
public static final String KEEP_ALIVE_SERVICE_PING_SERVER = Config.PACKAGE_NAME + ".intent.action.KEEP_ALIVE_SERVICE_PING_SERVER";
}
In the AndroidManifest file the service is defined in the following way:
<service android:name="com.yourapp.package.services.KeepAliveService"
android:exported="false">
<intent-filter>
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_START" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_STOP" />
<action android:name="com.yourapp.package.intent.action.KEEP_ALIVE_SERVICE_PING_SERVER" />
</intent-filter>
</service>
I suggest you take a look at the Android documentation for background services. Personally I would use the IntentService as it's a well established pattern within Android.
http://developer.android.com/training/run-background-service/index.html

Android -(Thread) app will hang when I run a new thread

I have 3 activities in my android Application. In the first activity, on the click of a bluetooth device from the list of paired devices, I'm starting a service to keep the bluetooth connection visible to all the actives. In the service class I'm reading data continuously from the bluetooth device and I'm binding the second activity to the service class to read the data received.
I'm not able to get the instance of the binder outside the onServiceConnected() method of service connection method. So I'm calling a user-defined thread from onServiceConnected() method. In this way I'm getting values continuously from the service class. But the app will not respond after few seconds of successful execution.
It is blocking the main thread I think. But I'm not getting where I need to modify my code. The code below is my second Activity(MainActivity). "bluetoothManager" is my service class. I need to do a similar task in third activity also.
I'm not getting whether the problem is with binding or the thread. I need to call the thread outside of the Service connection class. If I do so, I'll get a null pointer exception. So I'm calling the thread from onServiceConnected() function where the binder object is not null. I have to use the boolean mIsBound for the while loop. But now it will be always true. Please help me. I'm new to android.
bluetoothManager.class
public class bluetoothManager extends Service{
final int handlerState = 0; // used to identify handler message
private BluetoothAdapter btAdapter = null;
private BluetoothSocket btSocket = null;
private StringBuilder recDataString = new StringBuilder();
public ConnectedThread mConnectedThread;
static Handler bluetoothIn;
int bp;
String sensor0,sensor1;
static Handler mHandler;
// SPP UUID service - this should work for most devices
private static final UUID BTMODULEUUID = UUID
.fromString("00001101-0000-1000-8000-00805F9B34FB");
IBinder mBinder = new LocalBinder();
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
public class LocalBinder extends Binder {
bluetoothManager getService() {
return bluetoothManager.this;
}
}
#Override
public void onCreate() {
/// Toast.makeText(this, " MyService Created ", Toast.LENGTH_LONG).show();
// flag="created";
}
private BluetoothSocket createBluetoothSocket(BluetoothDevice device) throws IOException {
return device.createRfcommSocketToServiceRecord(BTMODULEUUID);
// creates secure outgoing connecetion with BT device using UUID
}
public String getBPM(){
return sensor1;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, " MyService Started", Toast.LENGTH_LONG).show();
final String address=intent.getStringExtra("address");
final int currentId = startId;
if(address!=null)
{
btAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice device = btAdapter.getRemoteDevice(address);
try {
btSocket = createBluetoothSocket(device);
} catch (IOException e) {
Toast.makeText(getBaseContext(), "Socket creation failed",
Toast.LENGTH_LONG).show();
}
// Establish the Bluetooth socket connection.
try {
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
// insert code to deal with this
}
}
mConnectedThread = new ConnectedThread(btSocket);
mConnectedThread.start();
// I send a character when resuming.beginning transmission to check
// device is connected
// If it is not an exception will be thrown in the write method and
// finish() will be called
mConnectedThread.write("x");
}
bluetoothIn = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) { // if message is what we want
String readMessage = (String) msg.obj; // msg.arg1 = bytes
// from connect
// thread
recDataString.append(readMessage); // keep appending to
// string until ~
int endOfLineIndex = recDataString.indexOf("~"); // determine
// the
// end-of-line
if (endOfLineIndex > 0) { // make sure there data before ~
String dataInPrint = recDataString.substring(0,
endOfLineIndex); // extract string
//txtString.setText("Data Received = " + dataInPrint);
/*int dataLength = */dataInPrint.length(); // get length of
// data received
/*txtStringLength.setText("String Length = "
+ String.valueOf(dataLength));*/
if (recDataString.charAt(0) == '#') // if it starts with
// # we know it is
// what we are
// looking for
{
sensor0 = recDataString.substring(1,3);
// get
sensor1=sensor0;
Log.d("bpm", sensor0);
}
recDataString.delete(0, recDataString.length()); // clear
// all
// string
// data
// strIncom =" ";
dataInPrint = " ";
}
}
}
};
// get Bluetooth
// adapter
return currentId;
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final OutputStream mmOutStream;
// creation of the connect thread
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
// Create I/O streams for connection
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[256];
int bytes;
// Keep looping to listen for received messages
while (true) {
try {
bytes = mmInStream.read(buffer); // read bytes from input
// buffer
String readMessage = new String(buffer, 0, bytes);
// Send the obtained bytes to the UI Activity via handler
bluetoothIn.obtainMessage(handlerState, bytes, -1,
readMessage).sendToTarget();
} catch (IOException e) {
break;
}
}
}
// write method
public void write(String input) {
byte[] msgBuffer = input.getBytes(); // converts entered String into
// bytes
try {
mmOutStream.write(msgBuffer); // write bytes over BT connection
// via outstream
} catch (IOException e) {
// if you cannot write, close the application
Toast.makeText(getBaseContext(), "Connection Failure",
Toast.LENGTH_LONG).show();
}
}
}
#Override
public void onRebind(Intent intent) {
Log.v("myservice", "in onRebind");
super.onRebind(intent);
}
#Override
public boolean onUnbind(Intent intent) {
Log.v("myapp", "in onUnbind");
return true;
}
#Override
public void onDestroy() {
super.onDestroy();
Log.v("myservice", "in onDestroy");
}
}
MainActivity.java
public class MainActivity extends Activity {
private ServiceConnection mConnection;
TextView sensorView0;
boolean mIsBound;
bluetoothManager bm;
private Handler bpmHandler;
private ServiceConnection mConnection;
final int handlerState = 0; // used to identify handler message
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
sensorView0 = (TextView) findViewById(R.id.bpm);
bpmHandler=new Handler(){
public void handleMessage(android.os.Message msg) {
if (msg.what == handlerState) {
String s=(String)msg.obj;
sensorView0.setText("BPM="+s);
}
}
};
}
#Override
public void onResume() {
super.onResume();
mConnection= new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mIsBound = false;
bm=null;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
LocalBinder myBinder = (LocalBinder)service;
mIsBound = true;
bm=myBinder.getService();
mConnectedService=new ConnectedService(mIsBound);
mConnectedService.start();
}
};
Intent intent = new Intent(this, bluetoothManager.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
private class ConnectedService extends Thread {
final boolean bound;
public ConnectedService(boolean mIsBound){
bound =mIsBound;
}
public void run() {
String s;
while (bound) {
s= bm.getBPM();
Message msg = new Message();
msg.what =handlerState ;
msg.obj=s; MainActivity.this.bpmHandler.sendMessage(msg);
}
}
};
#Override
public void onPause() {
super.onPause();
unbindService(mConnection);
mIsBound = false;
}
}
I feel the connectedservice thread code is causing the issue. Instead of continuously racing grtBPM method, why don't you post the message only when there is a change. You can use local broadcast manager to broadcast the message from service and catch that in activity and update UI accordingly. The connectedservice thread runs continuously and keep posting the message to handler which is causing load on the main thread.

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

I've got problem and I don't know how to resolve it. I wanted create service which will sent sensor changes to client (thread).
I've got thread where inside I start services, and client thread receive answers and sent them through bluetooth. The problem is I can't handle service.
public class SensorMsgService extends Service implements SensorEventListener{
public static final int MSG_SAY_HELLO = 1;
public static final int MSG_REGISTER_CLIENT = 1;
public static final int MSG_UNREGISTER_CLIENT = 2;
public static final int MSG_SET_VALUE = 3;
static final String TAG = "Sensor Msg Service";
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
private SensorManager mSensorManager;
private Sensor mSensor;
private ArrayList<Float> temp;
private Looper mServiceLooper;
public class IncomingHandler extends Handler{
public IncomingHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
//mValue = msg.arg1;
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
/*mClients.get(i).send(
Message.obtain(null, MSG_SET_VALUE, 1, 0));*/
Log.d(TAG, "Message from client");
}
//catch (RemoteException e) {
catch (Exception e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
break;
default:
super.handleMessage(msg);
}
}
//Toast.makeText(getApplicationContext(), "Hello service test", Toast.LENGTH_SHORT).show();
}
final Messenger mMessenger = new Messenger(new IncomingHandler(mServiceLooper));
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
Log.d(TAG, "binding");
//return null;
return mMessenger.getBinder();
}
#Override
public boolean bindService(Intent service, ServiceConnection conn, int flags) {
// TODO Auto-generated method stub
super.bindService(service, conn, flags);
// mServiceLooper.prepare();
temp = new ArrayList<Float>();
mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL);
return true;
}
#Override
public void unbindService(ServiceConnection conn) {
// TODO Auto-generated method stub
super.unbindService(conn);
mSensorManager.unregisterListener(this);
}
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
// TODO Auto-generated method stub
temp.add(event.values[0]);
temp.add(event.values[1]);
temp.add(event.values[2]);
for (int i = mClients.size() - 1; i >= 0; i--) {
try {
mClients.get(i).send(
Message.obtain(null, MSG_SET_VALUE, temp));
Log.d(TAG, "Message service to client sending");
}
//catch (RemoteException e) {
catch (Exception e) {
// The client is dead. Remove it from the list;
// we are going through the list from back to front
// so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
}
I've initiated this service as I said from thread:
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private Context mContext;
/** Messenger for communicating with service. */
Messenger mService = null;
/** Flag indicating whether we have called bind on the service. */
boolean mIsBound;
ArrayList<Float> temp;
private Looper mServiceLooper;
public ConnectedThread(BluetoothSocket socket, Context ctxx) {
Log.d("ConnectedThread", "constructor ConnectedThread");
mContext = ctxx;
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
temp = new ArrayList<Float>();
// Get the BluetoothSocket input and output streams
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e("ConnectedThread", "it was trying create input and output sockets", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.i("ConnectedThread", "run mConnectedThread");
byte[] buffer = new byte[1024];
/*int i = 0;
while(true)
{
Log.i("ConnectedThread", "sending int test value");
write(i++);
}*/
doBindService();
}
/**
* Write to the connected OutStream.
* #param buffer The bytes to write
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) {
Log.e("ConnectedThread", "Exception during write", e);
}
}
public void write(int out) {
try {
mmOutStream.write(out);
} catch (IOException e) {
Log.e("ConnectedThread", "Exception during write", e);
}
}
public void cancel() {
try {
//mmOutStream.write(EXIT_CMD);
mmSocket.close();
} catch (IOException e) {
Log.e("ConnectedThread", "close() of connect socket failed", e);
}
}
///------------------------------
/**
* Handler of incoming messages from service.
*/
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SensorMsgService.MSG_SET_VALUE:
temp = (ArrayList<Float>) msg.obj;
//mCallbackText.setText("Received from service: " + msg.arg1);
Log.d("Handle message from service", temp.get(0) + " " + temp.get(1) + " " + temp.get(2) + "\n");
break;
default:
super.handleMessage(msg);
}
}
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = new Messenger(service);
//mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
Message msg = Message.obtain(null,
SensorMsgService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
// Give it some value as an example.
msg = Message.obtain(null, SensorMsgService.MSG_SET_VALUE, this
.hashCode(), 0);
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(mContext, "Service connected",
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
//mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(mContext, "Service disconnedcted",
Toast.LENGTH_SHORT).show();
}
};
/**
* Target we publish for clients to send messages to IncomingHandler.
*/
final Messenger mMessenger = new Messenger(new IncomingHandler());
// do Bind
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because there is no reason to be able to let other
// applications replace our component.
mContext.bindService(new Intent(mContext, SensorMsgService.class),
mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
Log.d("ConnectedThread", "doBindService");
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null,
SensorMsgService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
mContext.unbindService(mConnection);
mIsBound = false;
//mCallbackText.setText("Unbinding.");
}
}
}
The problem is when I wanted run this thread:
04-04 01:36:26.853: W/dalvikvm(29341): threadid=12: thread exiting with uncaught exception (group=0x420372a0)
04-04 01:36:26.853: E/AndroidRuntime(29341): FATAL EXCEPTION: Thread-11596
04-04 01:36:26.853: E/AndroidRuntime(29341): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
04-04 01:36:26.853: E/AndroidRuntime(29341): at android.os.Handler.<init> (Handler.java:121)
04-04 01:36:26.853: E/AndroidRuntime(29341): at com.nauka.bluetooth.ConnectedThread$IncomingHandler.<init>(ConnectedThread.java:127)
04-04 01:36:26.853: E/AndroidRuntime(29341): at com.nauka.bluetooth.ConnectedThread.<init>(ConnectedThread.java:200)
Could you show me how to handle this issue? thx

Categories

Resources