Android: Creating a new thread invokes onResume? - java

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.

Related

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

disposeWhenFinished() causes crash on API 21

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.

AcceptThread is not returning to Activity on bluetooth file transfer app

I am making a Bluetooth based file transfer app and I am not sure how to work with Threads here.
When I start listening for incomming connections on the Server part of the app works fine but the thread I use to do this task and not block the UI is not returning to the Main Activity after being invoked on it. Here is part of the code:
public class AcceptThread extends Thread {
private BluetoothServerSocket mmServerSocket;
public BluetoothSocket mmBlueToothSocket;
private final BluetoothAdapter mmBluetoothAdapter;
/*Accept incomming connections on the server*/
public AcceptThread(BluetoothAdapter mBluetoothAdapter){
BluetoothServerSocket tmp = null;
this.mmBluetoothAdapter = mBluetoothAdapter;
//works well
try {
this.mmServerSocket = mmBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(BluetoothConfig.mServiceName,BluetoothConfig.mUUID);
} catch (IOException e) {
mmServerSocket = tmp;
Log.d("AcceptThreadConstructorErr:", e.getMessage());
}
}
public void run(){
//while (true){
try {
mmBlueToothSocket = this.mmServerSocket.accept();
Log.d("AcceptThreadRun:", "mmServerSocket reached");
} catch (IOException e) {
Log.d("AcceptThreadRunErr:", e.getMessage());
//break;
}
//}
//If I call the ConnectedThread part of the code here it raises an error so I am trying to call it from my Activity but is not returning there after executing the run part of this class
}//run
}//AcceptThread class
public class MainActivity extends Activity {
private BluetoothServerSocket mmServerSocket;
public BluetoothSocket mmBlueToothSocket;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//accept to listen for incomming connections
AcceptThread acceptThread = new AcceptThread(bConfig.getBluetoothAdapter());
acceptThread.start();
//After the start execution this part is never reached!!
connectedThread();
}
//Called from acceptThread once innitiated the connections listening in AcceptThread
public void connectedThread(){
socket = acceptThread.mmBlueToothSocket;
if (socket == null){
Log.d("acceptThread:", "socket Null in acceptThread");
}
if (socket != null){
try {
ConnectedThread connected = new ConnectedThread(socket);
connected.start();
Log.d("serverConnect", "connect.start() ok");
}catch (Exception e) {
Log.d("serverConnect:", e.getMessage());
}
}
}//connectedThread
}//Activity
Can you please tell me, what component of the Thread implementation I am missing (maybe a Handler?), to be able to reach the ConnectedThread part and make it to work?. There doesn't seem to be an error, is just the Thread exiting and not returning to the caller activity.
Thanks
eAs there is not all your code here we can't check it.
But I see something weird:
//accept to listen for incoming connections
AcceptThread acceptThread = new AcceptThread(bConfig.getBluetoothAdapter());
acceptThread.start();
//This part is never reached!!!!
Log.d("acceptThread:", "acceptThread.start() is executed");
This is called in your activity? Where in your activity?
You would normally put it somewhere like in the onCreate() method. This is not what you show. Otherwise your code should not even compile.
Indeed, I don't see any other reason, as the main purpose of a thread is to run "in parallel". So, immediately after acceptThread.start(); is called, Log.d("acceptThread:", "acceptThread.start() is executed"); should be executed. And in the same time the content of the run method of your thread should be executed.
Besides, you should be able to start your ConnectedThread in your AcceptThread without problem.
So I guess Nandeesh is right.
In order to return to the Activity from the AcceptThread I used this into its run method
(this.mmActivity).runOnUiThread(new Runnable() {
public void run() {
((MainActivity)mmActivity).connectedThread();
}
});
Where connectedThread method has been declared and implemented in the activity as the updated post show. Thank you.

wait() not signaled by notify() in callback

The problem is simple. On Android I have a method that needs to call a library function. The call will signal a callback that I must handle. Before I return from my method, I must wait for that callback to be signaled.
I thought the wait() and notify() methods on a monitor object would do the trick. It does not.
Basically the monitoring object is called Connection which I instantiate in the calling method. The method then does a loop where it calls an Android library method to 'unregister' an object. Unfortunately the response to this method is given in some callback. So I use the Connection.wait(10000) method to wait for the callback, and in the callback I use connection.notify() to signal when it gets done (all synchronized, of course). However, the connection.notify() does not release the connection.wait(10000). I can see from the Android logcat that the unregistration succeeds BUT I always have to wait 10 seconds before the next unregisration task is attempted.
The code for the calling method and the callback are below. What stupid assumption am I making in my reasoning that this fails. As far as I can see the calling method (thread) definitely owns the monitoring Object and gives it up to the callback on the connection.wait(10000)!
Maybe I am using an entirely incorrect approach for this problem? (What I want is the method to block the caller until all the unregistrations are done.)
public void clearRegistrations()
{
connection = new Connection();
// Tell the callback to notify() when a result is obtained
connection.setUseNotify(true);
for(BluetoothHealthAppConfiguration btConfig : btHealthAppConfigs)
{
// Initialize Connection object to not connected
connection.setConnectionState(false);
if(btHealth.unregisterAppConfiguration(btConfig))
{
try
{
synchronized (connection)
{
connection.wait(10000);
// See if we were signaled or timed out
if(!connection.getConnectionState())
{
Log.i(TAG, "Unregistration attempt timed out or failed; trying next un-registration");
}
}
}
// This should not happen
catch(InterruptedException ie)
{
Log.i(TAG, "The InterrupedException is signaled.");
}
// This should not happen.
catch(IllegalMonitorStateException ime)
{
Log.i(TAG, "wait() method threw an IllegalMonitorStateException. Message: " + ime.getMessage());
}
}
else
{
Toast.makeText(context, "Un-Registration API returned failure", Toast.LENGTH_SHORT).show();
}
}
btHealthAppConfigs.clear();
connection.setConnectionState(false);
connection.setUseNotify(false);
}
The callback is as follows and it is in the same class as the method above but it is one of those 'onSomeEvent()' that is so popular in Android:
public void onHealthAppConfigurationStatusChange(BluetoothHealthAppConfiguration btAppConfig, int status)
{
if (status == BluetoothHealth.APP_CONFIG_UNREGISTRATION_FAILURE)
{
Log.i(TAG, "Un-Registration of the Bluetooth Health Application failed");
if(connection.useNotify() == true)
{
synchronized (connection)
{
Log.i(TAG, "Signal unregistration failure");
// just indicate signaled
connection.setConnectionState(true);
connection.notify();
}
}
}
else if(status == BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS)
{
Log.i(TAG, "Un-Registration of the Bluetooth Health Application successful");
if(connection.useNotify() == true)
{
synchronized (connection)
{
Log.i(TAG, "Signal unregistration success");
connection.setConnectionState(true);
connection.notify();
}
}
}
}
There are a bunch of things that might be wrong with this code: assylias got most of them. The obvious thing is that you definitely need to use notifyAll, instead of notify. notify restarts some thread waiting on a lock; notifyAll restarts them all.
useNotify and setConnectionState also need to be synchronized. Also you need to guarantee that the instance of connection on which you are locking, is the same in both sections, and is the same one on which you call notify. Lastly, you need to guarantee that the call to notify actually happens after the call to wait.
As far as approach, anytime you use such low-level tools, you ought to wonder if there isn't a better way. There are several higher-level constructs that could do this more simply. I would suggest that you try to re-design you code so that you don't wait on the callback. Make the call, park the state somewhere, and then handle the callback when it happens.

Decompressing .zip file in service makes app freeze until it finish decompressing, why?

I implemented the code to work with a async task and it works perfectly, but if user exits app it will get killed very fast, so I decided to try it with a service, it works perfectly, but it makes the app freeze.
So here's my decompress class:
public class Decompress {
private String _zipFile;
private String _location;
ZipEntry ze = null;
public Decompress(String zipFile, String location) {
_zipFile = zipFile;
_location = location;
_dirChecker("");
}
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
while ((ze = zin.getNextEntry()) != null) {
//Log.v("Decompress", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch(Exception e) {
Log.e("Decompress", "unzip", e);
}
}
private void _dirChecker(String dir) {
File f = new File(_location + dir);
if(!f.isDirectory()) {
f.mkdirs();
}
}
}
Here is my service call for unzip:
#Override
public void onStart(Intent intent, int startid)
{
try
{
zipFile = intent.getStringExtra("zipFile");
zipLocation = intent.getStringExtra("unzipLocation");
String fileS = intent.getStringExtra("file");
file = new File(fileS);
fin = new FileInputStream(zipFile);
zin = new ZipInputStream(fin);
while (zin.getNextEntry() != null) {
numFiles++;
}
}
catch (FileNotFoundException e)
{}
catch (IOException e)
{}
d = new Decompress(zipFile, zipLocation);
d.unzip();
}
Now here's how I ussed to call it with async task:
#Override
protected Void doInBackground(Void... params) {
d.unzip();
return null;
}
now my question is, why with async tsk my app don't get freeze and it will keep unzipping letting me cancel it with a button, but with service it makes the app lags? I even got a message about MyApp not responding, Would you like to close it?
EDIT: My service call for Start
#Override
protected Void doInBackground(Void... params) {
Intent intent = new Intent(DownloadFiles.this, MyService.class);
String unzipLocation = Environment.getExternalStorageDirectory().toString()+"/Android/data/";
String zipFile = Environment.getExternalStorageDirectory().toString()+"/Android/data/test.zip";
intent.putExtra("zipFile", zipFile);
intent.putExtra("unzipLocation", unzipLocation);
intent.putExtra("file", Environment.getExternalStorageDirectory().toString()+"/Android/data/");
startService(intent);
try {
FileInputStream fin = new FileInputStream(zipFile);
ZipInputStream zin = new ZipInputStream(fin);
while (zin.getNextEntry() != null) {
numFiles++;
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
Services also run in the UI (main) Thread, so you need to implement an AsyncTask or sepearate Thread approach there too.
The docs say it all under What is a Service?
A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
Edit: Note that if you start a Service from a background thread, the service is still running in the main thread. As per this SO answer. This should make sense as the docs say:
When a Service component is actually created, for either of these
reasons, all that the system actually does is instantiate the
component and call its onCreate() and any other appropriate callbacks
on the main thread. It is up to the Service to implement these with
the appropriate behavior, such as creating a secondary thread in which
it does its work.
This ultimately means that you should always implement a seperate AsyncTask/Thread approach in Services as well, no matter how you start the Service.
Try running it in a separate background thread if you don't need the onPreExecute() and onPostExecute() methods of the AsyncTask, but still have a problem with the operation blocking the UI Thread.
Thread t = new Thread() {
public void run() {
d = new Decompress(zipFile, zipLocation);
d.unzip();
}
};
t.start();
Simply starting the service from a background thread doesn't mean that it will be started off of the main UI thread. That is the default for its start, and you must create a new thread within the Service to get around it.
Expanding on A--C's point:
You need to create the background thread to unzip the file from inside the service, because the service is created and runs on the main thread, whether you start it inside another thread or not.
You basically need to do in the service exactly what you did outside the service (i.e. put the 'unzip' call inside the AsyncTask, and execute the task).
(Addendum)
The point of using a service is not to create a separate thread, but rather to split time-consuming processing from a UI-based application. It means that the UI can be destroyed by the OS and the resources recovered, and all the while the service is still running.
Thus, the decision about whether to use an AsyncTask (or thread) within the app itself vs within a service is really about whether the action should continue independently of the app interface or not. Restoring apps using TitaniumBackup is a good example of this: once you've started a restore, the app UI isn't really needed any more.

Categories

Resources