I've developed a small Android application which uses an AlarmManager to call a Handler Thread that synchronizes some information from the mobile device with a remote server. This process happens every 15 minutes.
When I use the HandlerThread without the aid of the AlarmManager, everything ALWAYS works fine. But when I try to put these two to work together, SOMETIMES it works and SOMETIMES I get the following error :
W/System.err(10557): java.lang.NullPointerException
W/System.err(10557):at Synchronizer.queueSyncEverything(Synchronizer.java:109)
W/System.err(10557): at SyncService.onHandleIntent(SyncService.java:33)
W/System.err(10557): at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
W/System.err(10557): at android.os.Handler.dispatchMessage(Handler.java:99)<BR/>
W/System.err(10557): at android.os.Looper.loop(Looper.java:137)
W/System.err(10557): at android.os.HandlerThread.run(HandlerThread.java:60)
The code is really simple as you can see in the following snippets:
//Method from SyncService, a class which extends IntentService
protected void onHandleIntent(Intent intent) {
ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
if(cm.getActiveNetworkInfo() == null){
return;
}
if(mSynchronizer == null){
mSynchronizer = new Synchronizer(getApplicationContext(), this);
mSynchronizer.start();
mSynchronizer.getLooper();
}
mSynchronizer.queueSyncEverything();
}
//Method from Synchronizer, a class which extends HandlerThread
public void queueSyncEverything(){
try{
mHandler.obtainMessage(MESSAGE_SYNC_EVERYTHING).sendToTarget();
}
catch(Exception e){
mListener.onError();
e.printStackTrace();
}
}
I already checked if the Handler or the Looper are null, but they're all fine.
EDIT:
As #DavidWasser suggested, I did some more testing and discovered that sometimes mHandler is null. This variable is set inside the method onLooperPrepared from Synchronizer (which extends from HandlerThread) as you can see here:
#Override
protected void onLooperPrepared(){
mHandler = new Handler(){
#Override
public void handleMessage(Message msg){
if(msg.what == MESSAGE_SYNC_EVERYTHING){
handleSyncEverything();
mListener.onFinished();
}
}
};
}
The constructor of this class, as #DavidWasser asked, is just:
public Synchronizer(Synchronizer.Listener listener){
super(TAG);
mListener = listener;
}
mListener is just an delegate-like object who receives events sent by the Synchronizer class, like when the Synchronization is finished. TAG is just a name to make it easier to debug. Maybe my main difficulty is to understand how the class HandlerThread works and when is it the right time to set its Handler object.
P.S.: This way of setting up the HandlerThread I got by reading the book "Android Programming: The Big Nerd Ranch Guide".
Don't initialize mHandler in onLooperPrepared() as this method could be called after the call to getLooper() returns. This is a small timing window that could bite you.
Instead create a method in Synchronizer like this:
public void createHandler() {
mHander = new Handler(getLooper()) {
#Override
public void handleMessage(Message msg){
if(msg.what == MESSAGE_SYNC_EVERYTHING){
handleSyncEverything();
mListener.onFinished();
}
}
};
}
and call this method in onHandleIntent() instead of calling mSynchronizer.getLooper().
This will ensure that mHandler is initialized before you call queueSyncEverything().
NOTE: You don't need to override onLooperPrepared() at all.
Related
I am trying to save some data from a file to a database. Everything is fine without using an intentService. But, my app crash when i use IntentService and i am getting an error om my asyncTask as:
java.lang.RuntimeException: An error occured while executing doInBackground()
Caused by: java.lang.NullPointerException
at com.example.carl.myTest.AsyncTask.doInBackground(AsyncTask.java:44)
and the line 44 is: HttpResponse response = httpClient.execute(httpPost);
My problem is why is everything is fine without a service, and chaos after using it?!!! What is going wrong?- Any help is very appreciated. I show my intentService class:
public class TheIntentService extends IntentService {
MainActivityMyTest t = new MainActivityMyTest ();
private Handler handler = new Handler();
public MyIntentService() {
super("TheIntentService ");
}
#Override
protected void onHandleIntent(Intent intent) {
t.writeToTable();
handler.post(showResult);
}
private Runnable showResult = new Runnable() {
public void run() {
Context c = TheIntentService .this.getApplicationContext();
Toast.makeText(c, "Mission accomplished", Toast.LENGTH_SHORT).show();
}
};
#Override
public void onDestroy() {
Context c = this.getApplicationContext();
Toast.makeText(c, "Exits service", Toast.LENGTH_SHORT).show();
super.onDestroy();
}
}
and from the MainActivity i start the service like this:
Intent i = new Intent(MainActivityMyTest.this, TheIntentService.class);
startService(i);
why is everything is fine without a service, and chaos after using it?!!!
Because you created your own instance of MainActivityMyTest, which appears to be an Activity. NEVER create an instance of an Activity, or Service, or ContentProvider yourself. Android's framework creates those, not you. Presumably, MainActivityMyTest creates httpclient somewhere in its lifecycle (e.g., onCreate()), and that is not happening.
Please move the code associated with writeToTable() into the IntentService itself.
I have an Android app from which I receive BLE data (every 62ms via notifications). The app can save data via a BufferedWriter to a file. Upon each onCharacteristicChanged() callback, I call either an AsyncTask, Thread or an IntentService to do a file write if the user enabled file save.
The AsyncTask seems to work fine. But the docs say execute must be invoked on the UI thread, and I'm calling it from the BLE callback. Is that a problem? And how should I fix it?
Using Thread causes this error: GKI_exception out of buffers https://code.google.com/p/android/issues/detail?id=65455 (except my code is not scanning but receiving notifications) and if the file save is long, I need to power cycle the Nexus 7 (the app and BLE become totally unresponsive). Why does the Thread not work and how can I fix it?
The IntentService never goes to the onHandleIntent(). What are the issues here?
Here is some code:
...
_context = this.getApplicationContext();
...
private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
...
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
...
int mode = 1;
if (mode==0) // Asynctask
new doFileWriteTask().execute(strBuild.toString());
else if (mode==1) // Thread
{
final String str = strBuild.toString();
new Thread(new Runnable() {
public void run() {
try {
_writer.write(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
else if (mode==2) // intentService
{
Intent mServiceIntent = new Intent(_context, writeFileService.class);
mServiceIntent.putExtra("foo", strBuild.toString());
startService(mServiceIntent);
}
}
...
};
private class doFileWriteTask extends AsyncTask<String, Void, Void> {
#Override
protected Void doInBackground(String... strings) {
try {
_writer.write(strings[0]);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private class writeFileService extends IntentService {
public writeFileService() {
super("writeFileService");
}
#Override
protected void onHandleIntent(Intent workIntent) {
String dataString = workIntent.getStringExtra("foo");
try {
_writer.write(dataString);
} catch (Exception e) {
e.printStackTrace();
}
}
}
...
But the docs say execute must be invoked on the UI thread, and I'm calling it from the BLE callback. Is that a problem? And how should I fix it?
The framework triggers the AsyncTask callback methods on the same thread it was called from (presumed to be the main thread). It doesn't really affect the background work, but you could see problems if you started trying to use onPostExecute() and the like. AsyncTask probably isn't the best choice to be called from a thread that you don't have control over.
Why does the Thread not work and how can I fix it?
I can't say exactly why you are still seeing errors, through spawning a series of private unsynchronized threads will probably lead to other headaches. If you want to use a single worker thread, a better choice would be to use a single HandlerThread that you can post to from your event callbacks using a Handler, something like:
…
_workerThread = new HandlerThread("Worker");
_workerThread.start();
_handler = new Handler(_workerThread.getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
String str = (String) msg.obj;
_writer.write(str);
return true;
}
});
…
#Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
…
Message msg = Message.obtain(_handler, 0, strBuild.toString());
_handler.sendMessage(msg);
…
}
That solution is quite a bit more code, but given the frequency of writes this is probably the most efficient choice.
The IntentService never goes to the onHandleIntent(). What are the issues here?
You should pretty much never implement a top level Android component (activity, service, content provider, receiver) as an inner class, because they have to be declared in your manifest as well (and the XML syntax for inner classes is ugly). If your service does not have a matching entry in the manifest, then you will never see it start. You might want to have a look at the docs on using services.
At a minimum, a Service written as an inner class must be public static to work. Otherwise the framework cannot see it and cannot instantiate it using a default constructor (non-static inner classes mess with the constructor). Unless you are calling startService() inside of a try/catch right now, I'm surprised it isn't crashing when you attempt this.
IntentService is probably the simplest of your three choices because it is the most decoupled and the framework will handle queueing up work and tearing down the threads when all the incoming work is done.
I'm currently trying to show a toast from IntentService, if a device detects an accelerometer. To do so, I searched and learned that I can implement an Handler. However, it is not quite working. The code compiles and runs on an emulator without any error, but the toast doesn't show. I was wondering if I could get some help to spot mistakes in my code. The code is shown below.
Any help would be appreciated!
public class AccelService extends IntentService implements SensorEventListener{
private SensorManager mySensorManager;
private Handler toastHandler;
public AccelService(){
super("AccelerometerIntentService");
}
...
private class ToastRunnable implements Runnable{
String toastText;
public ToastRunnable(String text){
toastText = text;
}
#Override
public void run(){
Toast.makeText(getApplicationContext(), toastText, Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onHandleIntent(Intent intent){
toastHandler = new Handler();
initialize();
}
public void initialize(){
mySensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
if(mySensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) != null){
toastHandler.post(new ToastRunnable("Accelerometer Detected!"));
}
}
...
}
Creating the handler for the Toast messages in onHandleIntent binds it to the wrong thread:
This method is invoked on the worker thread with a request to process.
Either explicitly set the handler's thread, with, e.g., new Handler(getMainLooper()) or create the handler in onCreate.
I have an android application with different activities and they all pull data from a web source. This is done by implementing Runnable and creating a thread with the activity as object. The basic class looks like this:
public ActivityX extends Activity implements Runnable {
#Override
public onResume() {
super.onResume();
Thread someThread = new Thread(this);
someThread.start();
}
#Override
public run() {
try {
// pull web content
}
catch(TimeOutException e) {
// >>> create dialog here <<<
// go back to another activity
}
}
}
I tried to create a dialog helper class with a static method that returns the timeout dialog and then call show() like this:
HelperClass.getTimeOutDialog().show();
but the problem is, I can't call it from inside the run() method, as it's in a different thread. If I try to, I will get a runtime exception stating:
Can't create handler inside thread that has not called Looper.prepare()
I need to do this dialog for nearly a dozen of activities and I really want to get around using a Handler objects and sending a message to call the dialog every time. Isn't there an easier way to do this? I just can't think of any right now unfortunately.
My code would look something like this:
handler.handleEmptyMessage(1);
This is to call the handler. And the following would handle the message:
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
// show dialog here
}
}
};
Cheers
#Override
public run() {
try {
// pull web content
}
catch(TimeOutException e) {
runOnUiThread(new Runnable(){
#Override
public void run() {
// >>> create dialog here <<<
// go back to another activity
}
}
}
}
Try the one above if you don't want to use Handler.
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
// show dialog here
}
}
};
Is this code a part of your activity and not in a thread? If it is a part of your non Ui thread, it would give you the error message. Make sure the handler instance is created in your UI thread because a handler contains an implicit reference to the thread they get created in.
I am calling from a method:
myHandler.postDelayed(mMyRunnableHide, 6000);
which calls:
public Runnable mMyRunnableHide = new Runnable()
{
public void run()
{
mTextDisplay.setText("");
DisplayX();
}
};
if a button on screen is clicked I want to stop the runnable:
Button next = (Button) findViewById(R.id.Breaction);
next.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
myHandler.removeCallbacks(mMyRunnableHide);
mTextDisplay.setText("");
DisplayX();
}
});
}
the removecallbacks is not stopping the runnable. What am I doing wrong? Am I using the correct method? I just want the runnable to "Not Run" when the user clicks the button.
Thanks for any help.
It appears to me that removeCallbacks(..) only stops pending messages (Runnables). If your runnable has already started, then there's no stopping it (at least not this way).
Alternatively, you can extend the Runnable class and give it some kind of kill switch like this:
public class MyRunnable implements Runnable
{
private boolean killMe = false;
private void run()
{
if(killMe)
return;
/* do your work */
}
private void killRunnable()
{
killMe = true;
}
}
This will only prevent it from starting, but you could occasionally check killMe and bail out. If you are looping the runnable (like some kind of background thread) you can say:
while(!killMe) {
/* do work */
}
Hope this helps
EDIT I just wanted to post an update on this. Since this original post, Google has come up with a great class called AsyncTask that handles all of this stuff for you. Anyone reading this really should look into it because it is the correct way of doing things.
You can read about it here
Handler.removeCallback is synchronous and will work nicely provided:
You call postDelayed always in the main thread.
You call removeCallback always in the main thread
You don't call postDelayed again after having removed callbacks.
So in your case removeCallbacks is called from a button handler, which runs in the main thread. But you didn't show in your code the point from where you call postDelayed. If you call it from a background thread thats where your problem is.
If you are sure you don't call any of these methods from background threads, and the order of the calls is correct, then you might be leaving uncancelled tasks unadvertedly alive due to activity recreation on config changes (screen rotation, etc). Always make sure to call removeCallbacks again in the onDestroy method to prevent this kind of problems.
Here is another way to accomplish what mtmurdock is describing. This class will allow editing of instance variables in any class that your Runnable is defined as an anonymous inner class.
package support;
/**
* Runnable that can be stopped from executing
*/
public abstract class KillableRunnable implements Runnable{
private boolean isKilled=false;
/**
* Instead of Overriding run(), override this method to perform a Runnable operation.
* This will allow editing instance variables in the class that this Runnable is defined
*/
public abstract void doWork();
//The handler that posts this Runnable will call this method.
//By default, check if it has been killed. doWork() will now be the method
//override to implement this Runnable
#Override
final public void run(){
if(!isKilled){
doWork();
}
}
final public void kill(){
isKilled=true;
}
}
I don't think that removeCallbacks(..) only stops pending messages (Runnables) ,I think removeCallbacks(..) not working have other cause,but i don‘t know. because postDelayed(..) and removeCallbacks(..) is in the same thread
the following has worked for me. Place it in onResume.
mService= null;
public void onServiceConnected(ComponentName name, IBinder service) {
Log.i(TAG, "OnServiceConnected");
ContadorFG.LocalBinder binder = (ContadorFG.LocalBinder) service;
mService = binder.getService();
connected = true;
synchronized (lock){
lock.notifyAll();
}
}
public void onResume() {
super.onResume();
loopDelayed();
}
private void loopDelayed(){
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (mService != null) {
----
----
----
return;
}else{
//auto call
loopDelayed();
}
}
}, 10);
}