disposeWhenFinished() causes crash on API 21 - java

following onDestroy method is making my app crash when running on API 21 (for example when i rotate the screen):
#Override
protected void onDestroy() {
super.onDestroy();
if (mHelper != null) {
mHelper.disposeWhenFinished();
mHelper = null;
}
}
The method is from the IabHelper class which Google provides in its in-app-billing tutorial.
/**
* Disposes of object, releasing resources. If there is an in-progress async operation, this
* method will queue the dispose to occur after the operation has finished.
*/
public void disposeWhenFinished() {
synchronized (mAsyncInProgressLock) {
if (mAsyncInProgress) {
logDebug("Will dispose after async operation finishes.");
mDisposeAfterAsync = true;
} else {
try {
dispose();
} catch (IabAsyncInProgressException e) {
// Should never be thrown, because we call dispose() only after checking that
// there's not already an async operation in progress.
}
}
}
}
This is the error message:
java.lang.RuntimeException: Unable to destroy activity {package name}: java.lang.IllegalArgumentException: Service not registered: packagename.util.IabHelper$1#3bf48617
I couldnt find a solution for this, which wonders me, because this method is needed for in-app billing.

You need to release your IabHelper before onDestroy() super call or some where in onPause() ensuring that your activity will finish using isFinishing() method, because there is no guarantee that the code after onDestroy() super call will be excuted.
Disposal should be done before activity context unbinds from the play billing service. You are getting that message because you might be hitting the case when your app has released the connection the service.

Related

mapFrag.getMapAsync() problems in a while loop

I really need help for a problem I don´t know how to solve.
I´m trying to make an application for my father, and I will work with Map.
The problem is specific in a mapFrag.getMapAsync() inside a while loop.
#Override
public void onResume(){
super.onResume();
new Thread(){
public void run(){
while ( mapFrag.getMapAsync() == null){
try {
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
}
runOnUiThread(new Runnable() {
#Override
public void run() {
configMap();
}
});
}
}.start();
}
while ( mapFrag.getMapAsync(), this line gets an error that says'
getMapAsync(OnMapReadyCallback) in SupportMapFragment cannot be applied to ()
The OnMapReadyCallback was implemented on a MainActivity.
According documentation for MapFragment.getMapAsync():
public void getMapAsync (OnMapReadyCallback callback)
Sets a callback object which will be triggered when the GoogleMap
instance is ready to be used.
Note that:
This method must be called from the main thread.
The callback will be executed in the main thread.
In the case where Google Play services is not installed on the user's device, the callback will not be triggered until the user
installs it.
In the rare case where the GoogleMap is destroyed immediately after creation, the callback is not triggered.
The GoogleMap object provided by the callback is non-null.
Especially this:
Parameters callback The callback object that will be triggered when
the map is ready to be used.
In your call mapFrag.getMapAsync() there is no OnMapReadyCallback parameter. So, you should add it:
...
mapFrag.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(GoogleMap googleMap) {
googleMap.moveCamera(cameraUpdate);
}
})
...
And you shouldn't call getMapAsync() in separate thread, because of (see above link to documentation):
This method must be called from the main thread.
The callback will be executed in the main thread.
So, please read carefully official Get Started page.

Cordova Plugin - Java: wait for callback

I have a plugin method that acts on remote hardware via Bluetooth.
It sends a command to the hardware, which executes some action.
After the hardware action finishes a callback defined outside of my method is called.
I only want to call CallbackContext.success(...) or CallbackContext.error(...) after the callback is called, so i want to wait for my callback to be called.
How would i go about this?
E.g. part of CordovaPlugin-class:
public void actOnHardware(CallbackContext callbackContext)
{
this.verifiyBluetoothEnabled();
this.hardwareConnection.doSomething()
// Now wait for the callback to complete before calling
// callbackContext.success() or error()
callbackContext.error("Not implemented.");
}
#Override
public void hardwareActionCallback(result)
{
// Notify actOnHardware() that we're finished.
}
This seems to be more of a Java thing, but i can't get my head to wrap around it.
Is using Object.wait() and Object.notify() a viable option or does calling wait() prevent the callback from getting called due to thread stuff? If so - how to solve this?
E.g. is it sufficient to just do:
private Object lockObj;
private boolean actionFinished;
public void actOnHardware(CallbackContext callbackContext)
{
this.verifiyBluetoothEnabled();
this.actionFinished = false;
this.hardwareConnection.doSomething()
while(!this.actionFinished)
this.lockObj.wait();
callbackContext.error("Not implemented.");
}
#Override
public void hardwareActionCallback(result)
{
this.actionFinished = true;
this.lockObj.notify();
}
Kind Regards

Service not running after closing app

I am trying to make background service that will run 15 sec after user closes tha app, I have done service that runs 15 sec (loop with Logs), bud when I close tha app, then it stopes
and another problem is, when I try to stop it from main activity by stopService(intent); then the onDestroy method is called, but thread with loop continues
.. please can someone help me?
*sorry for my english - no native :D
public class NotificationService extends Service {
final private class MyThread implements Runnable {
int service_id;
MyThread(int service_id) {
this.service_id = service_id;
}
#Override
public void run() {
synchronized (this) {
for (int i = 0; i < 15; i++) {
try {
wait(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e("onStartCommand", "loop:" + i);
}
stopSelf(service_id);
}
}
}
Thread thread;
#Override
public void onCreate() {
super.onCreate();
Toast.makeText(this, "onCreate", Toast.LENGTH_SHORT).show();
}
#Override
public int onStartCommand(#Nullable Intent intent, int flags, int startId) {
Log.e("onStartCommand", "started");
Toast.makeText(this, "onStartCommand", Toast.LENGTH_SHORT).show();
thread = new Thread(new MyThread(startId));
thread.start();
return START_STICKY;
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
Log.e("onDestroy", "onDestroy");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
super.onDestroy();
}
}
I am trying to make background service that will run 15 sec after user closes tha app, I have done service that runs 15 sec (loop with Logs), bud when I close tha app, then it stopes
Your code only starts the loop thread when startService(yourNotificationService)is called on the Activity or Broadcast Receiverthat is responsible for calling it does so. It then kills itself with stopSelf(service_id).
If, after you have returned from onStartCommand(), you immediately kill the app without calling stopSelf(service_id) (i.e. your 15 seconds is not up), then your Service will MOST LIKELY restart itself given the START_STICKY return value. However, after you call stopSelf(service_id) you are telling the Service to kill itself; after you close your app, there is nothing to tell your Service to restart through the onStartCommand() call.
and another proble is, when I try to stop it from main activity by stopService(intent); then the onDestroy method is called, but thred with loop continues
A Service is an Android component; it is not another process or thread, it runs in the same process and thread as the main UI thread unless you specify otherwise, as seen here.
Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
In your case, calling stopService(intent) tells the Service to stop itself, which it does. It does not stop the Thread you started (the MyThread instance). To do that, you must first make your Thread interruptible; see here to do that. Once you do that, you need to change your onDestroy() code to actually interrupt the MyThread instance, as here
#Override
public void onDestroy() {
Log.e("onDestroy", "onDestroy");
Toast.makeText(this, "onDestroy", Toast.LENGTH_SHORT).show();
thread.interrupt();
super.onDestroy();
}

Android error: recreate() must be called from main thread

I am getting an Android error, even though the error message is quite obvious, i can't figure out how to make it work properly.
The error message is:
java.lang.IllegalStateException: Must be called from main thread
at android.app.Activity.recreate(Activity.java:4193)
In my app, a notification is sent to log out a user (when his token expires).
On older Android versions i am having no problems to do so, however from SDK 11 and up, i have to use the recreate() method. I get the error that it has to be called from the Main thread.
I moved the recreate() statement to the MainActivity class, this doesn't work when i call the method from the IntentService. I still get the same error. The messaging part is working just fine, just the handling of the logout message is resulting in this error.
Here are some snippets:
inside GcmIntentService.java
if (logout!=null) {
VarControl.ma.logout();
}
inside MainActivity.java
public void logout() {
deleteToken();
closeWebView();
restartApp();
}
public void restartApp() {
if (Build.VERSION.SDK_INT >= 11) {
this.recreate(); // THE ERROR OCCURS HERE
}
else{
//left out this part because its not relevant
}
}
How can i call recreate from the Main thread (but the code has to be handled on receiving the intent) ??
If you want to run sthg on the main thread you can still do :
public void restartApp() {
if (Build.VERSION.SDK_INT >= 11) {
runOnUiThread(new Runnable() {
#Override
public void run() {
recreate();
}
});
}
else{
//left out this part because its not relevant
}
}
Make a List that is accessible by both threads. Secure it using synchronized or anything like that. Whenever the Intent is received add a Runnable with the fitting code to the List.
In the main thread check if the list is non-empty on a regular basis. If it's not empty, pop the first Runnable from the list and run it. Do that until the List is empty.
That is generally a good way to handle passing code from one thread to the other.

Android: Creating a new thread invokes onResume?

I guess this is a more generic question, but my Android program seems to call onResume in the main UI thread in between these two thread-creation-related function calls. This causes other invocations that I don't want to happen, and so far the only way around it that I've found is setting global flags (which I don't like, and is bad programming practice in my opinion). It looks rather like this:
mConnectThread = new ConnectThread(bd);
mConnectThread.start();
Somehow, in between these calls (which are made from the UI thread by a BluetoothCommHandler object), onResume gets called. If anyone could point me to a good source of when onResume and other activity lifecycle events are triggered, I would be much obliged. Further, I checked this: http://developer.android.com/reference/android/app/Activity.html, and it didn't seem to have any hints that I could find.
A last note - the onResume ALWAYS gets called in between those two commands getting called, and that makes me think it's not really a thread-switching issue.
I've also just noticed that the onResume gets called as the pair of an onPause that gets called WAY earlier - still no idea why it happens precisely between these two function calls though.
EDIT: Code is included below.
Invocation of the bluetooth handler object:
mBComm = new BluetoothCommHandler(this, mHandler);
The onResume function in the main UI thread (the mNoRestartFlag is so that this particular bit gets called only when I want it to. It's NOT the flag I'm referring to above - it handles another case that I'm not speaking about here):
#Override
protected void onResume() {
super.onResume();
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent,
mFilters, mTechLists);
Log.i(TAG, "OnResume called.");
if(mBComm != null && !mNoRestartFlag) {
mBComm.start();
}
}
Activity OptionsHandler (same as DeviceListActivity) declaration in the AndroidManifest (note that it's a Theme.Dialog styled activity, which pops on top of the UI thread, causing the onPause I've referred to above):
activity android:name=".OptionsHandler"
android:label="#string/select_device"
android:theme="#android:style/Theme.Dialog"
android:configChanges="orientation|keyboardHidden" />
The actual connectThread gets created:
public synchronized void connect(BluetoothDevice bd) {
Log.i(TAG, "connect called from inside BluetoothCommHandler");
if (mAcceptThread == null) {
Log.i(TAG, "Creating an AcceptThread");
mAcceptThread = new AcceptThread();
mAcceptThread.start();
}
mConnectThread = new ConnectThread(bd);
mConnectThread.start();
}
The creation and running of the ConnectThread (the mDontKill flag IS the flag I am mentioning above that I use to bypass the onResume symptoms):
public ConnectThread(BluetoothDevice bd) {
Log.i(TAG, "created ConnectThread");
mBD = bd;
BluetoothSocket bs = null;
try {
bs = mBD.createInsecureRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
Log.i(TAG, "Could not create an RFCOMM socket!", e);
}
mBS = bs;
if (mBS != null) Log.i(TAG, "BluetoothSocket acquired");
else Log.i(TAG, "BluetoothSocket null!");
mDontKillFlag = true;
}
public void run() {
Log.i(TAG, "BEGIN ConnectThread");
// Always cancel discovery because it will slow down a connection
mBluetoothAdapter.cancelDiscovery();
mDontKillFlag = false;
// Make a connection to the BluetoothSocket
try {
// This is a blocking call and will only return on a
// successful connection or an exception
mBS.connect();
Log.i(TAG, "Connected to BluetoothDevice");
} catch (IOException e) {
Log.i(TAG, e.toString());
// Close the socket
try {
mBS.close();
} catch (IOException e2) {
Log.i(TAG, "unable to close RFCOMM socket", e2);
}
Log.i(TAG, "About to call connectionFailed");
connectionFailed();
return;
}
// Reset the ConnectThread because we're done
synchronized (BluetoothCommHandler.this) {
mConnectThread = null;
}
// Start the connected thread
connected(mBS, mBD);
}
The actual start() function that causes the issues:
public synchronized void start() {
if (D) Log.i(TAG, "start called from inside BluetoothCommHandler");
// Cancel any thread attempting to make a connection
if (mConnectThread != null && !mDontKillFlag)
{mConnectThread.cancel(); mConnectThread = null;}
// Cancel any thread currently running a connection
if (mConnectedThread != null)
{mConnectedThread.cancel(); mConnectedThread = null;}
if (mAcceptThread == null) {
Log.i(TAG, "Creating an AcceptThread");
mAcceptThread = new AcceptThread();
mAcceptThread.start();
}
}
Legend: mBS is a member variable that is a BluetoothSocket and mDB is a member variable that is a BluetoothDevice.
To summarize, I create a BluetoothCommHandler object on the UI Thread, this tries to create a ConnectThread and then while calling the accept() command on a bluetooth socket, it fails because the cancel() function for the thread has been called (that simply has a try-catch that closes the socket). This cancel is called from the start() function that is listed above, that is called by the onResume function. The onResume is the complement of the onPause that is getting called because of a selector dialog appearing over the main UI activity. This onResume seems to always get called in between the first two lines of code that I mentioned pre-EDIT. I'm trying to figure out why it always happens exactly THERE, so that I can have an accept() occur without the socket being closed.

Categories

Resources