I'm pretty new to this, so sorry if my question might be trivial, and I'm sure this is basic stuff, but really I couldn't find a solution.
I'd like to realize an autorefresh in an active activity. I have a BT-service running in background and need a confirmation for some received data through mHandler. If it receives the expected I want to change the string of a textview, right now I'm using an extra button, but it's the ugliest way.
So I need a loop inside the activity, but what should I use? which action listener?
ok what you can do is you can create a broadcast reciever in your activity. Write this code in you oncreate. fix it actually i am at home so you need to fix it i m giving you idea.
BroadcastReciever broadcast_obj = new BroadcastReciever (
#overrieded
onRecieve(Intent intent) {
String action = intent.getAction();
if(action == "my_bt_action") {
//UPDATE YOUR TEXTVIEW AND DO WHATEVER WORK YOU WANT.
}
});
Now you need to register your broadcast for that just put these line in your oncreate after creating Broadcast reciever which we have just done.
registerREciever(broadcast_obj, new IntentFilter("my_bt_action");
now you need to send your broadcast when your service will perform your calculation or your task for that it is simple.
Intent intent = new Intent (getApplictionContext,"my_bt_action");
sendBroadcast(intent);
from above code you can easily communicate between your activity and service.
hope it will work.
Maybe you can try a loop inside a thread
boolean update = false; // control the state to update textview
new Thread(new Runnable(){
void run(){
while(true){
if(!update){
...
textview.setText("something.");
...
update = true;
}
}
}
}.start();
Related
I have an Activity and a Service.
In my Activity, a button interacts with the Service to start/stop GPS logging.
My Service has 3 state indicators: One for being connected to Google Play Services, one for actively logging GPS, and one for processing what was logged.
When connected to Google Play Services the Service flow is this:
Ready -> Logging -> Processing -> Ready
The Service will broadcast these states as follows:
private void UpdateStatusBroadcast() {
//Save status variables to intent
Intent intent = new Intent(this.getString(R.string.BroadcastStatusIntent));
intent.putExtra(getString(R.string.BroadcastIsConnected), mIsConnected);
intent.putExtra(getString(R.string.BroadcastIsTripActive), mIsTripActive);
intent.putExtra(getString(R.string.BroadcastIsProcessing), mIsProcessing);
//Send the broadcast
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
My Activity receives the states as follows:
private class StatusReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
mIsConnected = intent.getBooleanExtra(getString(R.string.BroadcastIsConnected), false);
mIsTripActive = intent.getBooleanExtra(getString(R.string.BroadcastIsTripActive), false);
mIsProcessing = intent.getBooleanExtra(getString(R.string.BroadcastIsProcessing), false);
HandleConnectionStatus();
HandleTripStatus();
}
}
Then comes my problem. In HandleTripStatus(), posted below, i change the text and background of a button to reflect what the Service is currently doing. This works fine for the first and the third case. I never see the second background drawn however, in spite of receiving the correct boolean values.
private void HandleTripStatus() {
Button tripButton = (Button) findViewById(R.id.TripButton);
Button liveMapButton = (Button) findViewById(R.id.LiveMapButton);
if (mIsTripActive) {
tripButton.setText(R.string.TripButtonTitleStop);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_stop_shape));
liveMapButton.setEnabled(true);
} else if (mIsProcessing) {
tripButton.setText(R.string.TripButtonTitleStopping);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_stopping_shape));
liveMapButton.setEnabled(false);
} else {
tripButton.setText(R.string.TripButtonTitleStart);
tripButton.setBackground(ContextCompat.getDrawable(mContext, R.drawable.trip_button_start_shape));
liveMapButton.setEnabled(false);
}
}
To debug the issue i verified the following:
Text and background resource is correctly defined (i.e. trying to use
it instead of the first and third case works)
The if-else conditions runs when expected (i.e. the "else if" condition actually runs when I expect it to. Verified by breakpoint.)
No other if-else condition is used in the process. (i.e, only the correct condition is run.)
Some other code that could possibly be relevant:
This is how the Activity requests that the GPS logging should stop (Leading to the processing step before finishing)
private void EndTrip() {
//Create message to TripService with intent to run case for END_TRIP
Message message = Message.obtain(null, TripService.END_TRIP, 0, 0);
//Send the Message to the Service
try {
mMessenger.send(message);
Toast.makeText(mContext, R.string.TripStopToast, Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
Log.e("Debug", "Failed to contact TripService");
}
}
This is the structure of what happens in the Service after receiving the message from the Activity.
private void EndTrip() {
//Stop retrieving location updates
//Broadcast the updated status and begin processing the trip
mIsTripActive = false;
mIsProcessing = true;
UpdateStatusBroadcast();
//Processing the collected data
//Finish up
mIsProcessing = false;
UpdateStatusBroadcast();
stopForeground(true);
}
I am all out of ideas. What can the cause be? Why does the button background not change in the else-if?
After too many hours of trial and error, I found the cause to be thread-related.
What I learned:
My service doing its work (Processing) would hang up the UI thread until done
This was quite simply because the service was running on the UI thread
Android does not automatically run services in a thread seperate from the rest of your application.
It is possible to run your service on a different thread. To do this, add the following to your AndroidManifest, inside your service:
android:process=":WhateverNameYouLikeForYourThread"
Note that this of course broke the broadcasts i relied on. This was however easy to fix; The consequence is that I can no longer use LocalBroadcastManager
By example - Instead of
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
i now use
sendBroadcast(intent);
instead. This does however mean that the broadcasts are less private.
I'm creating an app that will also use NFC to read specific tags. I have a parent activity class where I would like to put function like:
public String scanTagId(){...}
I'm able to get actually tagID from tag the problem is it only occurs onNewIntent so actually first it will finish scanTagId() function and then invoke onNewIntent actually. So the question is how to write scanTagId() to actually wait for reading Tag ID from NFC tag and return string with this tag ID.
Maybe I need to approach this somehow differently or it's not possible - but right now I have a headache from trying to solve this :)
Normally NFC is handled via onNewIntentas you already said. It works like this: everytime a new NFC-Tag (that matches your filters) is detected by the phone the TAG gets connected and your app gets a reference to the TAG via the onNewIntent. Therefore you don't need an extra function scanTagId() since the device is already scaning for TAGs all the time.
If you want to disable that your app gets notified you can do it like this:
if (mNfcAdapter != null) {
mNfcAdapter.disableForegroundDispatch(this);
}
If you want to (re-)enable the notification about new TAG via onNewIntent you can use this code:
if (mNfcAdapter != null) {
mNfcAdapter.enableForegroundDispatch(this, mPendingIntent, mFilters, mTechLists);
}
If you still have to have a function like you mentioned, I'd store the TAG-Id in an instance-variable and implement the function like this:
protected String mLastTagId = "";
protected synchronized void setLastTagId(String id) {
mLastTagId = id;
}
public synchronized String scanTagId(){
return mLastTagId;
}
Please notice, that this function will not wait for a TAG to be connected, but will just return the ID of the last TAG that was found in the past.
Update
Another approach would be to synchronize onNewIntent and your scanTagId-function with wait() and notifyAll(). I assume you want to invoke your function from main-thread. In combination with synchronization this is a very dangerous thing to do and therefore I strictly advice against it. It will put your main-thread to sleep which is going to cause android to notify the user ("App is not responding") or even quit your app. At least your app won't be responding to UI-events.
You could avoid these effects if you'd use an AsyncTask. But I'd really advice to rethink your design and prepare it for async-events.
What are you trying to achieve in first place?
Update2
If you want to quit your Activity after a specific time has elapsed without a TAG being found, you can use a Timer:
protected String tag = null;
protected Timer timer = new Timer();
And in your onCreate-method activate the timer:
timer.schedule(new TimerTask() {
public void run() {
AlertDialog.Builder b = new AlertDialog.Builder(MyActivity.this);
b.setTitle("Timeout").setMessage("Nothing found");
b.setPositiveButton("OK", new OnClickListener() {
MyActivity.finish();
});
b.setCancelable(false);
b.create().show();
}, 10000);
This code hasn't been tested.
I have a Service with registered ContentObserver. When my ContentObserver detects changes it sets Service's boolean variable to true. I also have a Thread running in the service which sleeps for some time and wakes up to check that variable.
When it detects change it needs some time to process some other code and I need to show ProgressDialog during the delay. How can I do this?
You should use AsyncTask instead.
Here is the link to the library. It is fairly simple:
1) onPreExecute() = show ProgressDialog
2) doInBackground() = execute your code
3) onPostExecute() = dismiss ProgressDialog
DONE :-)
The essence of your question is that you want your service to send a message of some kind to your UI (to show a loading dialog).
There are four (or more) ways of going about this:
Intents: have your service send an intent to your activity
AIDL
Using the service object itself (as singleton)
Having your activity be a broadcast receiver
These options may seem familiar: How to have Android Service communicate with Activity
You'll have to read up on those options and take your pick.
AsyncTask is a good alternative, but still if you decided to go with threads, then in order to show the ProgressDialog on UI you will need to call runOnUiThread() method of the activity.
Let suppose you want to display the ProgressDialog in the MainActivity. Inside your Thread from Service you should have something like this:
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
// Display ProgressDialog here
}
});
Thanks everyone for answers.
I solve the problem using these steps
- broadcast Intent when my variable was changed
- create BroadcastReceiver for the intent( in Activity )
- inside BroadcastReceiver's method onReceive call runOnUiThread for my activity
I know this is an old thread but I have exactly what you needed because I just implemented this from a thread here. Please read Rachit Mishra's answer further down the page talking about a ProgressBar:
Communication between Activity and Service
I have this in my service:
public void sendMessage(int state) {
Message message = Message.obtain();
switch (state) {
case 1://SHOW:
message.arg1 = 1;
break;
case 0:
message.arg1 = 0;
break;
}
try {
messageHandler.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
Call sendMessage() with 1 or 0 to show or dismiss the ProgressDialog within your service.
And this is in my Main Activity:
private ProgressDialog progress;
public class MessageHandler extends Handler {
#Override
public void handleMessage(Message message) {
int state = message.arg1;
switch (state) {
case 0://HIDE
progress.dismiss();
break;
case 1://SHOW
progress = ProgressDialog.show(MainActivity.this, (getResources().getString(R.string.CONNECTING) + "..."), (getResources().getString(R.string.PLEASE_WAIT) + "!")); //show a progress dialog
break;
}
}
}
The ProgressDialog cannot be shown from the service, it must be called from the activity or fragment. I hope I added all the code you need and that it works well for your needs. To be honest I'm not sure how the message handler works but it works for me! The naming is probably not the best either lol. Sorry.
I have question regarding the service and activity.
I have one service which calls some other class to get contacts from phone, and activity where the contacts will be placed. What is the best thing to do:
1 this: On bound Activity "ask" in infinite loop for status from service like this:
Thread trdTest = new Thread(new Runnable() {
public void run() {
boolean done= true;
while(done){
if (service.status == Constants.GETTING_CONTACTS_DONE_OK){
handler.sendEmptyMessage(0);
done= false;
}else if (service.status == Constants.GETTING_CONTACTS_ERROR_NOTOK){
handler.sendEmptyMessage(1);
done= false;
}
}
}});
trdTest.start();
2: this: Create cistom event in service and fire event when getting users is done. Of course the bounded activity will listen for that event
Thanx for the answers.
Running loops like that will probably slow down many things and is not a good idea. The second approach seems to me like a much better idea.
I have a service which sends continously values to an activity through some custom event listeners.
Here everything works fine. Certain values are displayed in my activity as expected, but some others make the application to crash. This is because some of the incoming data is calculated inside a normal thread (that I cannot have access for changing it), and I know I have to use a handler here, but as far as I tried the app still crashing.
more graphically I would like to do the following
onValuesChanged(float val) {
myTextView.setText( Float.toString(val) )
}
where val is calculated in a normal thread, but of course it makes crash the app when doing the setText.
Any suggestions?
Use AsyncTask instead of Thread and in the onPostExecute() you can update the UI.
or use Activity.runOnUiThread(new Runnable() {
void run() {
// do something interesting.
}
});
hey u can send a custom broadcast from your service like this
Intent mintent = new Intent();
mintent.setAction("com.action");
mintent.putExtra("name", "value");
sendBroadcast(mintent);
and register a receiver in your activity which will get the value from incoming intent and then call the handler like this to update the UI ..plese parse the int to string at receiving
myTextView.post(new Runnable() {
public void run() {
myTextView.setText( Float.toString(val) )
}
});
Every time you send a broadcast to your activity and it will update the ui ..
However the above mentioned way is also right but if you have to stay with service then go for this way else above......