I have an IntentService which updates a preference like this:
SharedPreferences.Editor editor = userPrefs.edit();
editor.putInt("COUNT", intCount);
editor.commit();
In my main activity I am listening for preference changes and update a TextView
userPrefsListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if(key.equals("COUNT")) {
final TextView txvCounter = (TextView) findViewById(R.id.TXV_COUNTER);
if(txvCounter != null) {
SharedPreferences userPrefs = getSharedPreferences("USER_SCORE", 0);
int intCount = userPrefs.getInt("COUNT", 0);
txvFishcounter.setText(String.format("%03d",intCount));
}
}
}
};
userPrefs.registerOnSharedPreferenceChangeListener(userPrefsListener);
For Android 2.3 everything works fine, but for 2.2 I'm getting CalledFromWrongThreadException everytime the OnSharedListener is triggered.
Thanks for your Help!
The thread running the onSharedPreferenceChanged() callback is not the main UI thread in the 2.2 device which give you CalledFromWrongThreadException (therefore violating the second of the Android UI thread access rules of only calling UI toolkit from the UI thread). A straightforward way to get code to run on the UI thread is to use Activity.runOnUiThread(). This could be done simply by wrapping the body of your code in a new Runnable:
activity.runOnUiThread(new Runnable() {
public void run() {
// Code which updates UI controls goes here.
}
});
A short discussion of the change between 2.2 and 2.3 and code snippets of why it occurred can be found here.
The reason why the CalledFromWrongThreadException is raised, is because the OnChangeListener is not supposed to be called from the IntentService.
What you could do though, is sending a Broadcast (where you can actually include the value as well).
In case you are only using the SharedPreference for communication, you can replace it entirely (and I would recommend this, as SharedPreferences are wasting Write Cycles).
You could use a code like this for sending a Broadcast:
/**
* Send an Intent with the Broadcast, a permission and a Bundle
*
* #param context
* A context to use
* #param broadcast
* String to use, eg. "de.bulling.smstalk.ENABLE"
* #param permission
* Permission needed for receiving
* #param bundle
* Extras to attach
*/
public static void send_broadcast(Context context, String broadcast, String permission, Bundle bundle) {
//SettingsClass.log_me(tag, "Sending broadcast " + broadcast);
Intent i = new Intent();
i.setAction(broadcast);
if (bundle != null) {
i.putExtras(bundle);
}
if (permission != null) {
context.sendBroadcast(i, permission);
} else {
context.sendBroadcast(i);
}
}
You should include a custom permission as well, so other apps don't get the Broadcast, but it is not necessarily needed.
To receive the Broadcast, register a Receiver in your Activity, eg
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle b = intent.getExtras();
doSomething();
}
};
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
IntentFilter filter = new IntentFilter();
filter.addAction(YOURBROADCAST);
registerReceiver(receiver, filter);
[...]
}
#Override
public void onDestroy() {
unregisterReceiver(receiver);
super.onDestroy();
}
Also you should consider using Handlers, so your methods in the OnChangeListener call are run by the MainUI thread.
Example:
Handler mHandler = new Handler();
Runnable myCode = new Runnable(){
#Override
protected void onRun() {
yourCode();
}
};
mHandler.run(myCode);
This has also the advantage of running the code delayed with runDelayed(), so you don't have to use sleep, and you UI will be still responding.
Related
I created a Broadcast Receiver (BR) in a service that will react to incoming SMS with specific number and body. I only need it to receive for a few seconds/minutes after user action, that's why I didn't registered it in manifest or activity (user may close it). BR has two parts, automatic (which works fine) and manual which should launch MainActivity and start a Dialog. I know that Dialog can't be started from BR and thats why I created a Listener, but my problem is that it is always null after service starts. It has value in onCreate of my MainActivity, but when service starts it changes to null, and I understand why (serivce re-initalize the Listener listener). I even tryed to put initialised listener value to SharedPrefs and restore it after, but when I try to store it with json it only stores null again. So how do I make my listener != null??? These are the relevant parts of my code:
MainActivity
onCreate {
SMSService smsReceiver = new SMSService();
smsReceiver.setListener(new SMSService.Listener() { //here listener from service is != null
#Override
public void onTextReceived(String s) {
dialogS(s); // totaly different dialog
}
});
...
mDialogBuilder = new AlertDialog.Builder(this);
...
.setPositiveButton(new OnClick...
Intent servisIntent = new Intent(MainActivity.this, SMSService.class);
startService(servisIntent);
...
}
SMSService
private Listener listener; // and here it get null which is the problem
public int onStartCommand(Intent intent, int flags, int startId) {
...
SMSReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Intent i = new Intent(context, MainActivity.class);
context.startActivity(i);
if (listener != null) {
listener.onTextReceived("4333");
}
}
void setListener(Listener listener) {
this.listener = listener; }
interface Listener {
void onTextReceived(String text);
}
Btw I also tried to put smsReceiver.setListener block of code in my Dialog .setPossitive onClickListener after calling startService hoping it would initiate after service but nothing
Installing a listener mechanism with setter method in service is bad practice. You can use ResultReceiver to receive callback results from service. It is Parcelable, so it can be passed in an intent before service started
My intention is to have download service created when the app first runs and checks for update every 24 hours. I originally had everything running my main activity but it seems to much to run everything on one thread and one class. So this is my attempt to move it to another class and into service. It suppose to run and check for an update ever 24 hours and if there is no internet try again in 4 hours. I specifically want to involve any recursive problems, having two or three same services checking update, just one every 24 hours. But having problem with integrating my code into service, what am I doing wrong?
public class DownloadService extends IntentService {
// TODO 0 - Define your Download Service as Android component in
// AndroidManifest.xml
private int result = Activity.RESULT_CANCELED;
public DownloadService() {
super("DownloadService");
}
// Will be called asynchronously be Android
#Override
protected void onHandleIntent(Intent intent) {
private final Runnable mUpdateUi = new Runnable(){
public void run(){
check();
}
};
private void start(){
new Thread(
new Runnable(){
public void run(){
Log.d(TAG, "inside start");
Looper.prepare();
mHandler = new Handler();
check();
Looper.loop();
}
}
).run();
}
private void check(){
if (isNetworkAvailable()== true){
try {
new checkupdate().execute();
delayTime = 86400000;
Toast.makeText(DownloadService.this, "Daily update check!", Toast.LENGTH_SHORT).show();
}
catch (IOException e) {
e.printStackTrace();
delayTime = 21600000;
}
}else{
delayTime = 21600000;
Toast.makeText(DownloadService.this, "No internet for Daily update check, try again in little!", Toast.LENGTH_SHORT).show();
}
reCheck();
}
private void reCheck(){
mHandler.postDelayed(mUpdateUi, delayTime);
}
}
IntentService already handles setting up a worker thread and queue, and termination when the queue is empty. Which makes it a very good candidate for something like a download service to manage the actual work of downloading data, but not really a great candidate for a time scheduler.
I'd suggest using an AlarmManager to schedule your work instead. What you want is to trigger an Intent to start your DownloadService, by sending it intent with an Action indicating what to do.
Note also that if you want to cancel an IntentService with an Action, you will need to implement onStartCommand in addition to the usual onHandleIntent, so that you can respond to the action immediately -- you cannot do this from onHandleIntent, since the intent won't be sent to that until the current task in the queue is completed. Here's a quick example:
public class DownloadService extends IntentService {
private static final String TAG = "DownloadService";
////////////////////////////////////////////////////////////////////////
// Actions
public static final String ACTION_CANCEL = "package.name.DownloadService.action.CANCEL";
public static final String ACTION_DOWNLOAD = "package.name.DownloadService.action.DOWNLOAD";
////////////////////////////////////////////////////////////////////////
// Broadcasts
public static final String BROADCAST_DOWNLOADED = "package.name.DownloadService.broadcast.DOWNLOADED";
public static final String BROADCAST_ERROR = "package.name.DownloadService.broadcast.ERROR";
////////////////////////////////////////////////////////////////////////
// Extras
public static final String MESSAGE = "package.name.DownloadService.extra.MESSAGE";
// etc.
private boolean isCancelled;
// usual stuff omitted
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null) {
String action = intent.getAction();
Log.v(TAG, "onStartCommand() - action: "+action);
if(ACTION_CANCEL.equals(action)) {
isCancelled = true;
// insert code here to signal any objects to cancel
// their work, etc.
stopSelf();
}
}
return super.onStartCommand(intent, flags, startId);
}
#Override
protected void onHandleIntent(Intent intent) {
if(intent != null) {
final String action = intent.getAction();
Log.v(TAG, "onHandleIntent() - action: "+action);
if(ACTION_DOWNLOAD.equals(action)) {
handleDownloading(intent);
}
else if(ACTION_CANCEL.equals(action)) {
// nothing to do here, handled in onStartCommand
}
}
}
////////////////////////////////////////////////////////////////////
private void handleDownloading(Intent intent) {
// get stuff you need from the intent using intent.getStringExtra(), etc.
if(!isCancelled) {
// do downloading, call broadcastDownloaded() when done
}
else {
// stop work, send broadcast to report cancellation, etc.
}
}
// send a broadcast to a BroadcastReceiver (e.g. in your activity)
// to report that the download completed
private void broadcastDownloaded() {
Log.v(TAG, "broadcastDownloaded()");
Intent broadcastIntent = new Intent();
if (broadcastIntent != null) {
broadcastIntent.setAction(BROADCAST_DOWNLOADED);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
sendBroadcast(broadcastIntent);
}
}
private void broadcastError(String message) {
Log.v(TAG, "broadcastError(), message: "+message);
Intent broadcastIntent = new Intent();
if (broadcastIntent != null) {
broadcastIntent.setAction(BROADCAST_ERROR);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
if(message != null) {
broadcastIntent.putExtra(MESSAGE, message);
}
sendBroadcast(broadcastIntent);
}
}
}
This is not how IntentService is meant to be used. As per the documentation, IntentService already creates its own worker threads. You should not be creating your own:
Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
Apart from the fact that your code as shown here won't compile (your start method is inside the onHandleIntent method), your general approach seems to be to start your own worker thread. What would happen in this approach is that you would start the thread, onHandleIntent would complete and then the service would be stopped. In addition to not actually working, this approach is also a bad idea because (at best if you're lucky) the service would be running continually 24/7.
What you should do instead is actually do your main work in onHandleIntent which IntentService will queue on a worker thread for you. Then instead of using postDelayed use AlarmManager to set an alarm to send an Intent to start the service again in 24 hours or 4 hours.
I created a Handler in my activity. The handler will be stored in the application object.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.action_activity);
appData = (AttachApplication) getApplication();
Handler updateHandler = new Handler() {
public void handlerMessage(Message msg) {
Log.d( TAG, "handle message " );
}
};
appData.setUpdateHandler( updateHandler );
}
My plan is that this handleMessage will be called when i setEmtpyMessage in my service. The service retrieves the handler from the application object.
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand of attachService");
List<Job> jobList = DBManager.getInstance().getAllOpenJobs();
appData = (AttachApplication) getApplication();
updateHandler = appData.getUpdateHandler();
updateHandler.sendEmptyMessage( 101 );
I checked the logs, but there is no handle message so that it seems that my plan does not work. I want to update a textfield each time my service did its job.
In Your case You shoild use BroadcastReceiver like this:
define receiver in your Activity class:
public class DataUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(MainService.REFRESH_DATA_INTENT)) {
//do something
}
}
}
on your onCreate or onStart method you must register receiver:
DataUpdateReceiver dataUpdateReceiver = new DataUpdateReceiver();
IntentFilter intentFilter = new IntentFilter(MainService.REFRESH_DATA_INTENT);
registerReceiver(dataUpdateReceiver, intentFilter);
on your service add this:
public static final String REFRESH_DATA_INTENT = "done";
and when you done all staff you must send brocast like this:
sendBroadcast(new Intent(MainService.REFRESH_DATA_INTENT));
Your code snippet says public void handlerMessage(Message msg), but I think you mean public void handleMessage(Message msg), without the r. You can avoid these problems by using the #Override tag when you intent to override methods from a superclass; so your snippet would be rendered #Override public void handleMessage(Message msg), whereas #Override public void handlerMessage(Message msg) would be an error.
What are you trying to do? I really don't see the point of instantiating a Handler in an Activity, since all you're doing is getting Messages from the MessageQueue. You certainly don't want to fool around with any of the Messages that Android posts, and there are much better ways of sending messages to the Activity.
Of course, you don't include the code for AttachApplication, so I can only speculate.
You're also trying to access this Handler from a Service. Something is going on, but I'm not sure what.
If you want to update a TextView every time your Service does its job, send a broadcast Intent from the Service to the Activity, and use a broadcast receiver in the Activity. You should also consider using an IntentService instead of a Service.
I have an app in which I'm trying to detect WHEN the Internet connection appears and when it disappears.
At the moment, when it appears, I'm starting a new thread (different from the UI) which connects my app to a remote server.
For that I'm hardly trying to implement a broadcast receiver which LISTENS for connectivity, but I'm having problems in understanding the concept.
In my onCreate() I have somethig like:
onCreate()
{
cThread = new Thread(new ClientThread(syncToken));
cThread.start();
}
When there is connection to the Internet I'm sending data through the socket, when there is not I'm storing the data in a database. And when the Internet appears I'm restarting my thread to reconnect and send the old data (which hasn't been sent because of network crashing) and the new one.
Let's say I would implement something like this:
DoRefreshBroadcastReceiver refreshBroadcastReceiver;
...
onResume() {
// register the refreshing complete broadcast receiver
IntentFilter intentFilter = new IntentFilter(DO_REFRESH);
refreshBroadcastReceiver = new doRefreshBroadcastReceiver();
registerReceiver(refreshBroadcastReceiver, intentFilter);
}
public class DoRefreshBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// call method to run fetch code...
}
}
Does this mean that when the Internet connection is detected my onReceive() gets called? And I could start my thread there?
What is the concept of using an intent? Because I really don't get it. How to use it, and what its purpose?
THE IDEA: I don't really know how to use this intent in this case or how to use it in my app!
Would this thing detect the connection to the Internet even when I'm not in this activity?
EDIT:
Here is how my onReceive looks like:
onCreate()
{
cThread = new Thread(new ClientThread(syncToken));
// cThread.start();
connIntentFilter = new IntentFilter(
"android.net.conn.CONNECTIVITY_CHANGE");
connListener = new MyConnectivityListener();
}
public void onReceive(Context context, Intent intent)
{
mNetworkInfo = (NetworkInfo) intent
.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (mNetworkInfo != null && mNetworkInfo.isConnected())
{
/*
* if(mNetworkInfo.getType()==ConnectivityManager.TYPE_WIFI);
*
*
* else
*/
cThread.start();
}
else {
System.out.println("There is no internet connection!");
try {
cThread.stop();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
mNetworkInfo != null && mNetworkInfo.isConnected()
Does this mean it's connected or should I verify for a certain type of connection on the emulator?
*I think that I should start my thread directly in onReceive(). As soon as my app starts it detects the Internet connection and BroadcastReceiver gets fired, doesn't it?
Try something like this...
public class MyActivity extends Activity {
private MyConnectivityListener connListener = null;
private IntentFiler connIntentFilter = null;
private Boolean connIntentFilterIsRegistered = false;
#Override
protected void onCreate(...) {
...
connIntentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
connListener = new MyConnectivityListener();
}
#Override
protected void onResume() {
...
if (!connIntentFilterIsRegistered) {
registerReceiver(connListener, connIntentFilter);
connIntentFilterIsRegistered = true;
}
}
#Override
protected void onPause() {
...
if (connIntentFilterIsRegistered) {
unregisterReceiver(connListener);
connIntentFilterIsRegistered = false;
}
}
protected class MyConnectivityListener extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// The NetworkInfo for the affected network is sent
// as an extra; it should be consulted to see what
// kind of connectivity event occurred.
}
}
}
A BroadcastReceiver is effectively a 'listener' which listens for events either sent by the system or, in some cases, by your own application components.
In this case, the system broadcasts android.net.conn.CONNECTIVITY_CHANGE whenever there is a connection change (connected/disconnected). By registering your BroadcastReceiver to 'listen' for that event, you can get the extra included in the Intent from your BroadcastReceiver's onReceive(...) method and do whatever you need to do accordingly. The extra is a `NetworkInfo object which will contain information about the particular network and whether it is connected or not.
In my android application, I am using the tab view and so I have two tabs: parameters and results.
the user enters the various parameters on the first tab and then switches to the second tab to view the results.
i have a service that performs some long-running calculations. the user enters parameters on the first tab and hits 'calculate'. They can make adjustments and hit 'recalculate' and the service is updated with the new parameters.
As these calculations progress, I want the user to be able to switch to the results tab to view the results of the latest calculation. They would then view the results and be able to switch back to the parameters tab to make adjustments.
I can think of two approaches:
-register the 'results tab' with the service and when the service reaches a milestone, it calls directly to the 'results tab'.
-have a timer running in the 'results tab' and have it query against the bound service on a regular interval and update accordingly.
Do people have comments or recommendations for these two approaches?
AsyncTask has a publishProgress method that should make it really painless to push updates from your background task to the UI thread.
Using broadcast Receiver
public class Detail extends GDActivity {
private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(DownloadService.CUSTOM_INTENT)) {
mProgressDialog.setProgress(intent.getFlags());
}
}
};
// Flag if receiver is registered
private boolean mReceiversRegistered = false;
// Define a handler and a broadcast receiver
private final Handler mHandler = new Handler();
#Override
protected void onResume() {
super.onResume();
// Register Sync Recievers
IntentFilter intentToReceiveFilter = new IntentFilter();
intentToReceiveFilter.addAction(DownloadService.CUSTOM_INTENT);
this.registerReceiver(mIntentReceiver, intentToReceiveFilter, null, mHandler);
mReceiversRegistered = true;
}
#Override
public void onPause() {
super.onPause();
// Make sure you unregister your receivers when you pause your activity
if(mReceiversRegistered) {
unregisterReceiver(mIntentReceiver);
mReceiversRegistered = false;
}
}
}
}
and the Sender
#Override
protected void onProgressUpdate(Integer... progress) {
Intent i = new Intent();
i.setAction(CUSTOM_INTENT);
i.setFlags(progress[0]);
ctx.sendBroadcast(i);
}