I have written an activity that create and start IntentService,the IntenteService send BroadCast messege to the Activity which received it successfully, now I want to send back data from the Activity to the IntentService, I tried by implementing i tried to implimente BroadCastReciver, however, it didn't work, any tips ? Thanks in advance.
public class ListenActivity extends AppCompatActivity {
/*** Attributes ***/
private Intent intentListeningService;
private Intent intentVictim;
//BroadcastReceiver
private BroadcastReceiver_addVictim broadcastReceiver_addVictim;
//IntentFilters
private IntentFilter intentFilter_addVictim;
private SwipeRefreshLayout mSwipeRefreshLayout;
// for send to Service
public static final String ACTION_Refresh = "com.bmm.bmmratvandroid.ListenActivity.Refresh";
public static final String EXTRA_KEY_r = "EXTRA_r";
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listen);
/*****************************************************
* initialization get info from precedent activity *
***************************************************/
Intent i = getIntent();
serverPort = i.getIntExtra("port",6000);
/****************************
* start service listening *
***************************/
intentListeningService = new Intent(this, ListeningService.class);
intentListeningService.putExtra(ListeningService.EXTRA_KEY_PORT, serverPort);
Toast.makeText(getApplicationContext(), "Starting Service ..", Toast.LENGTH_LONG).show();
startService(intentListeningService);
/********************************
* Create BroadcastReceivers *
*******************************/
broadcastReceiver_addVictim = new BroadcastReceiver_addVictim();
/*******************************
* register BroadcastReceivers *
*******************************/
intentFilter_addVictim = new IntentFilter(ListeningService.ACTION_MyUpdate);
intentFilter_addVictim.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(broadcastReceiver_addVictim, intentFilter_addVictim);
// here shoud send message to Service what didn't work
Intent intentRefresh = new Intent();
intentRefresh.setAction(ACTION_Refresh);
intentRefresh.addCategory(Intent.CATEGORY_DEFAULT);
intentRefresh.putExtra(EXTRA_KEY_r, "REFRESH");
sendBroadcast(intentRefresh);
} // fin onCreate(Bundle savedInstanceState)
/*<<<<<<<<<<<<<<<<<<<<<<<<<<<<
< BroadcastReceiver Classes <
<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
public class BroadcastReceiver_addVictim extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
String ip =null;
ip = intent.getStringExtra(ListeningService.EXTRA_KEY_addVictim);
// Here I recive message from the IntentService
// process(ip)
}
}} // fin ListenActivity
public class ListeningService extends IntentService {
/*** Attributes ***/
public static final String ACTION_MyIntentService = "com.bmm.bmmratvandroid.ListeningService.RESPONSE";
public static final String ACTION_MyUpdate = "com.bmm.bmmratvandroid.ListeningService.UPDATE";
public static final String EXTRA_KEY_PORT = "EXTRA_PORT";
public static final String EXTRA_KEY_OUT = "EXTRA_OUT";
public static final String EXTRA_KEY_addVictim = "EXTRA_addVictim";
public static final String EXTRA_KEY_deleteVictim = "EXTRA_deleteVictim";
int serverPort;
private String serviceMsg;
private ServerSocket serverSocket;
private Hashtable<String,Socket> sockets;
private int timeOut;
//BroadcastReceiver
private BroadcastReceiver_reponse broadcastReceiver_r;
//IntentFilters
private IntentFilter intentFilter_r;
/*****************
* Constructeur *
* ***************/
public ListeningService() {
super("com.bmm.bmmratvandroid.ListeningService");
sockets = new Hashtable<String,Socket>();
timeOut = 3000;
}
/************************
* Communication method *
************************/
#Override
protected void onHandleIntent(#Nullable Intent intent) {
serverPort = intent.getIntExtra(EXTRA_KEY_PORT,6000); // get port
/********************************
* Create BroadcastReceivers *
*******************************/
broadcastReceiver_r = new BroadcastReceiver_reponse();
/*******************************
* register BroadcastReceivers *
*******************************/
intentFilter_r = new IntentFilter(ListenActivity.ACTION_Refresh);
intentFilter_r.addCategory(Intent.CATEGORY_DEFAULT);
registerReceiver(broadcastReceiver_r, intentFilter_r);
}
//*-*-*-*-*-*-*-*-*-*-*-**-*-*-*//
//\\ Calling ListenActivity //\\
//*-*-*-*-*-*-*-*-*-*-*-*-*-*-*//
private void addVictim(String ip) {
Intent intentUpdate = new Intent();
intentUpdate.setAction(ACTION_MyUpdate);
intentUpdate.addCategory(Intent.CATEGORY_DEFAULT);
intentUpdate.putExtra(EXTRA_KEY_addVictim, ip);
sendBroadcast(intentUpdate);
}
//###############################
// methode manipulating sokets #
//###############################
/*<<<<<<<<<<<<<<<<<<<<<<<<<<<<
< BroadcastReceiver Classes <
<<<<<<<<<<<<<<<<<<<<<<<<<<<<*/
public class BroadcastReceiver_reponse extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// HERE I SHOUD RECIVE THE MESSAGE !?
//???????????????????????????????????
intent.getStringExtra(ListenActivity.EXTRA_KEY_r);
}
}}
Related
I have updated target version from Android 8 to Android 10 after that I am facing an issue that broadcast receiver is not being called on devices(I have tested on Samsung s9(Pie), Mi Note 5(Oreo)) accept Google Pixel 2 XL device(Android 10) but it's working fine on Genymotion Samsung s9 emulator or any emulator. Can anybody tell what could be the possible issue?
There is Service called SipService inside that it registering the Intent filters and we trigger one of the Intent filter from one of the activity. Some codes are as below.
ACtivity
inside onCreate() method
registerReceiver(registrationStateReceiver, new IntentFilter(SipManager.ACTION_SIP_REGISTRATION_CHANGED));
bindService(new Intent(mContext, SipService.class), connection, Context.BIND_AUTO_CREATE);
And after some Webservice calls and operation we are calling one of the registered Intent Filter as below.
Intent intent = new Intent(SipManager.ACTION_SIP_REQUEST_RESTART);
sendBroadcast(intent);
Inside SipService class
private void registerBroadcasts() {
// Register own broadcast receiver
if (deviceStateReceiver == null) {
IntentFilter intentfilter = new IntentFilter();
intentfilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_CHANGED);
intentfilter.addAction(SipManager.ACTION_SIP_ACCOUNT_DELETED);
intentfilter.addAction(SipManager.ACTION_SIP_CAN_BE_STOPPED);
intentfilter.addAction(SipManager.ACTION_SIP_REQUEST_RESTART);
intentfilter.addAction(DynamicReceiver4.ACTION_VPN_CONNECTIVITY);
if (Compatibility.isCompatible(5)) {
deviceStateReceiver = new DynamicReceiver5(this);
} else {
deviceStateReceiver = new DynamicReceiver4(this);
}
registerReceiver(deviceStateReceiver, intentfilter);
deviceStateReceiver.startMonitoring();
}
}
Receiver Class
public class DynamicReceiver4 extends BroadcastReceiver {
private static final String THIS_FILE = "DynamicReceiver";
// Comes from android.net.vpn.VpnManager.java
// Action for broadcasting a connectivity state.
public static final String ACTION_VPN_CONNECTIVITY = "vpn.connectivity";
/** Key to the connectivity state of a connectivity broadcast event. */
public static final String BROADCAST_CONNECTION_STATE = "connection_state";
private SipService service;
// Store current state
private String mNetworkType;
private boolean mConnected = false;
private String mRoutes = "";
private boolean hasStartedWifi = false;
private Timer pollingTimer;
/**
* Check if the intent received is a sticky broadcast one
* A compat way
* #param it intent received
* #return true if it's an initial sticky broadcast
*/
public boolean compatIsInitialStickyBroadcast(Intent it) {
if(ConnectivityManager.CONNECTIVITY_ACTION.equals(it.getAction())) {
if(!hasStartedWifi) {
hasStartedWifi = true;
return true;
}
}
return false;
}
public DynamicReceiver4(SipService aService) {
service = aService;
}
#Override
public void onReceive(final Context context, final Intent intent) {
// Run the handler in SipServiceExecutor to be protected by wake lock
service.getExecutor().execute(new SipService.SipRunnable() {
public void doRun() throws SipService.SameThreadException {
onReceiveInternal(context, intent, compatIsInitialStickyBroadcast(intent));
}
});
}
/**
* Internal receiver that will run on sip executor thread
* #param context Application context
* #param intent Intent received
* #throws SameThreadException
*/
private void onReceiveInternal(Context context, Intent intent, boolean isSticky) throws SipService.SameThreadException {
String action = intent.getAction();
Log.d(THIS_FILE, "Internal receive " + action);
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
ConnectivityManager cm =
(ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
onConnectivityChanged(activeNetwork, isSticky);
} else if (action.equals(SipManager.ACTION_SIP_ACCOUNT_CHANGED)) {
final long accountId = intent.getLongExtra(SipProfile.FIELD_ID, SipProfile.INVALID_ID);
// Should that be threaded?
if (accountId != SipProfile.INVALID_ID) {
final SipProfile account = service.getAccount(accountId);
if (account != null) {
Log.d(THIS_FILE, "Enqueue set account registration");
service.setAccountRegistration(account, account.active ? 1 : 0, true);
}
}
} else if (action.equals(SipManager.ACTION_SIP_ACCOUNT_DELETED)){
final long accountId = intent.getLongExtra(SipProfile.FIELD_ID, SipProfile.INVALID_ID);
if(accountId != SipProfile.INVALID_ID) {
final SipProfile fakeProfile = new SipProfile();
fakeProfile.id = accountId;
service.setAccountRegistration(fakeProfile, 0, true);
}
} else if (action.equals(SipManager.ACTION_SIP_CAN_BE_STOPPED)) {
service.cleanStop();
} else if (action.equals(SipManager.ACTION_SIP_REQUEST_RESTART)){
service.restartSipStack();
} else if(action.equals(ACTION_VPN_CONNECTIVITY)) {
onConnectivityChanged(null, isSticky);
}
}
Actually, My bad it was basically ABI(Application Binary Interface) issue that causing the issue in bit 64 devices.
I want to detect address by reverse geocoding, so to achieve that I have created a class named FetchAddressIntentService that extends IntentService and also have done all the steps as on https://developer.android.com/training/location/display-address.html
When startService(intent) inside startIntentService() is called, exception(below) is thrown
java.lang.RuntimeException: Unable to instantiate service PACKAGENAME.MainActivity$FetchAddressIntentService: java.lang.InstantiationException: java.lang.Class PACKAGENAME.MainActivity$FetchAddressIntentService has no zero argument constructor
Defining intent service in my AndroidManifest.xml(below)
<service
android:name=".MainActivity$FetchAddressIntentService"
android:exported="false"/>
</application>
My startIntentService method(below)
void startIntentService(){
Intent intent = new Intent(this,FetchAddressIntentService.class);
intent.putExtra(FetchAddressIntentService.Constants.RECEIVER,myResultReciever);
intent.putExtra(FetchAddressIntentService.Constants.LOCATION_DATA_EXTRA,lastDetectedLocation);
startService(intent);
}
My FetchAddressIntentService class(below)
public class FetchAddressIntentService extends IntentService{
ResultReceiver myReciever;
public FetchAddressIntentService(){
super("Fetching address");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
Geocoder geocoder = new Geocoder(this, Locale.getDefault());
String errorMessage = "";
//Get the location passed to this serviec thorugh an extra
Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA);
List<Address> address = null;
try{
address = geocoder.getFromLocation(location.getLatitude(),location.getLongitude(),1);
}
catch(Exception e){
Toast.makeText(this,e.getMessage(), Toast.LENGTH_LONG).show();
}
//Handle the case when there is no location found
if(address == null || address.size() == 0){
Toast.makeText(this, "No address found", Toast.LENGTH_LONG).show();
deliverResulttoReciever(Constants.FAILURE_RESULT,"No address Found");
}
else{
Address currentAddress = address.get(0);
ArrayList<String> addressFragment = new ArrayList<String>();
//Fetch the address lines using getAddressLine
//join them and send them to the thread
for(int i = 0;i<=currentAddress.getMaxAddressLineIndex();i++)
{
addressFragment.add(currentAddress.getAddressLine(i));
}
deliverResulttoReciever(Constants.SUCCESS_RESULT, TextUtils.join(System.getProperty("line.saparator"),addressFragment));
}
}
private void deliverResulttoReciever(int resultCode, String message) {
Bundle bundle = new Bundle();
bundle.putString(Constants.RESULT_DATA_KEY,message);
myReciever.send(resultCode,bundle);
}
public final class Constants {
public static final int SUCCESS_RESULT = 0;
public static final int FAILURE_RESULT = 1;
public static final String PACKAGE_NAME =
"com.google.android.gms.location.sample.locationaddress";
public static final String RECEIVER = PACKAGE_NAME + ".RECEIVER";
public static final String RESULT_DATA_KEY = PACKAGE_NAME +
".RESULT_DATA_KEY";
public static final String LOCATION_DATA_EXTRA = PACKAGE_NAME +
".LOCATION_DATA_EXTRA";
}
}
Try this :
change your service class to static:
public static class FetchAddressIntentService extends IntentService {
public FetchAddressIntentService(){
super("Fetching address");
}
For more information Read this
Using the following code and when onReceive is fired,am getting the following error
Error receiving broadcast Intent { act=com.sample.service.ReminderActivityService flg=0x10 (has extras) }
in com.sample.common.UserActivity$1#41c2b4b0
The problem is this statement Looper.myLooper().quit();
How do I terminate my looper after receiving the broadcast in the code below?
public class UserActivity extends Thread implements
ConnectionCallbacks, OnConnectionFailedListener {
private String TAG;
// Constants that define the activity detection interval
public static final int MILLISECONDS_PER_SECOND = 1000;
public static final int DETECTION_INTERVAL_SECONDS = 30;
public static final int DETECTION_INTERVAL_MILLISECONDS = MILLISECONDS_PER_SECOND * DETECTION_INTERVAL_SECONDS;
IntentService is;
onActivityGot mCallback;
Handler mHandler;
Context mContext;
BroadcastReceiver br;
/*
* Store the PendingIntent used to send activity recognition events
* back to the app
*/
private PendingIntent mActivityRecognitionPendingIntent;
// Store the current activity recognition client
private ActivityRecognitionClient mActivityRecognitionClient;
public UserActivity(UserActivity.onActivityGot ints) {
is = (IntentService) ints;
mContext = is.getApplicationContext();
mHandler = new Handler();
TAG = this.getClass().getSimpleName();
// This makes sure that the container service has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (UserActivity.onActivityGot) ints;
} catch (ClassCastException e) {
throw new ClassCastException(ints.toString()
+ " must implement UserActivity.onActivityGot");
}
Log.i(TAG, "UserActivity constractor fired in activity");
}
#Override
public void run() {
if (servicesConnected()) {
Looper.prepare();
Log.i(TAG, "servicesConnected fired in activity");
/*
* Instantiate a new activity recognition client. Since the
* parent Activity implements the connection listener and
* connection failure listener, the constructor uses "this"
* to specify the values of those parameters.
*/
mActivityRecognitionClient =
new ActivityRecognitionClient(mContext, this, this);
// connect to the service
mActivityRecognitionClient.connect();
br = new BroadcastReceiver() {
#Override
public void onReceive(Context c, Intent i) {
//call calback with data
mCallback.activityKnown(i);
mActivityRecognitionClient.removeActivityUpdates(mActivityRecognitionPendingIntent);
mActivityRecognitionClient.disconnect();
mContext.unregisterReceiver(br);
Looper.myLooper().quit();
}
};
mContext.registerReceiver(br, new IntentFilter("com.sample.service.ReminderActivityService"));
Looper.loop();
}
}
#Override
public void onConnected(Bundle dataBundle) {
Log.i(TAG, "onConnected fired");
/*
* Create the PendingIntent that Location Services uses
* to send activity recognition updates back to this app.
*/
Intent intent = new Intent(
mContext, ReminderActivityService.class);
/*
* Return a PendingIntent that starts the IntentService.
*/
mActivityRecognitionPendingIntent =
PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
/*
* Request activity recognition updates using the preset
* detection interval and PendingIntent. This call is
* synchronous.
*/
mActivityRecognitionClient.requestActivityUpdates(
DETECTION_INTERVAL_MILLISECONDS,
mActivityRecognitionPendingIntent);
}
#Override
public void onDisconnected() {
// Delete the client
mActivityRecognitionClient = null;
Looper.myLooper().quit();
Log.i(TAG, "onDisconnected fired");
}
#Override
public void onConnectionFailed(ConnectionResult cr) {
mHandler.post(new UiToastCommunicaton(mContext,
is.getResources().getString(R.string.action_connfailed)));
mCallback.activityFail();
Looper.myLooper().quit();
Log.i(TAG, "onConnectionFailed fired");
}
private boolean servicesConnected() {
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.
isGooglePlayServicesAvailable(is.getBaseContext());
if (ConnectionResult.SUCCESS == resultCode) {// If Google Play services is available
// In debug mode, log the status
Log.d("Activity Recognition",
"Google Play services is available.");
// Continue
return true;
} else {// Google Play services was not available for some reason
mHandler.post(new UiToastCommunicaton(mContext,
is.getResources().getString(R.string.gpserv_notfound)));
return false;
}
}
public interface onActivityGot {
public void activityKnown(Intent i);
public void activityFail();
}
}
found a way by storing a handle to the looper in a static variable. view below.
declare the variable
public static Handler looperHandle;
set the variable after preparing looper
Looper.prepare();
looperHandle = new Handler();
since i had instantiated the class in an object i just called
object.looperHandle.getLooper().quit();
am not comfortable with this solution because of using a static variable.
if someone has a better solution please post it here.
I have a service that I'm using to send SOAP Webservice calls. Everything is working perfectly and it never crashes, but I kinda think it should.
My problem is that when I have long running queries (10-50 sec.) onDestroy() is called before my workerthread is done (and I call stopSelfResult). Could it be that System.out.println isn't executed right away/out of sync (cached) in the LogCat window?
The is how a start the service through QueryBase class:
QueryBase someService = new QueryBase(myActivity);
someService.execute(...);
My QueryBase Class
public class QueryBase {
private WeakReference<Activity> currentActivity = null;
private static class ResponseHandler extends Handler {
private QueryBase mQueryBase;
public ResponseHandler(QueryBase vQueryBase) {
mQueryBase = vQueryBase;
};
public void handleMessage(Message message) {
Bundle extras = message.getData();
mQueryBase.handleResult(message.arg1,message.arg2,extras.getInt("FRAMEID"),extras.getString("RESPONSE"));
mQueryBase=null;
};
};
public QueryBase(Activity vActivity) {
currentActivity = new WeakReference<Activity>(vActivity);
}
/***************************************************************************
* Start the service
**************************************************************************/
public boolean execute(Activity vActivity, int cmdID, int frameID, String serverAddress, int requestType, String request) {
// Valid activity
if (vActivity==null) return false;
// Test to see if network is connected
if (!isOnline(vActivity)) return false;
Intent webService = new Intent(vActivity, WebService.class);
final ResponseHandler responseHD = new ResponseHandler(this);
Messenger messenger = new Messenger(responseHD);
webService.putExtra("QUERYRESULT_MESSENGER",messenger);
webService.putExtra("CMDID", cmdID);
webService.putExtra("FRAMEID",frameID);
webService.putExtra("SERVER_ADDRESS",serverAddress);
webService.putExtra("REQUEST_TYPE",requestType);
webService.putExtra("REQUEST",request);
vActivity.startService(webService);
return true;
}
/***************************************************************************
* Is my Android connected?
**************************************************************************/
private Boolean isOnline(Activity vActivity) {
ConnectivityManager connMgr = (ConnectivityManager) vActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnected()) return true;
else return false;
}
/***************************************************************************
* Get current Activity
**************************************************************************/
public Activity getCurrentActivity() {
Activity ac = currentActivity.get();
if (ac!=null) {
if ((ac.isFinishing()) || (ac.activityDestroyed)) {
return null;
};
}
return ac;
};
/***************************************************************************
* XML result from webservice
**************************************************************************/
public void handleResult(int resultCode, int cmdID, int frameID, String response) {
System.out.println("DEFAULT HANDLER: ResultCode: " + resultCode);
};
}
My WebService Class
public class WebService extends Service {
public static final int WS_RT_BLOOSOAP = 0;
public static final int WS_RT_RSS = 1;
public static final int WS_RESULT_OK = 0;
public static final int WS_RESULT_UNABLE_TO_CONNECT = 2;
public static final int WS_RESULT_INVALID_REQUEST = 3;
public static final int WS_RESULT_UNKNOWN_ERROR = 999;
static private SparseBooleanArray workList=null; // Only one job with the same frameID is allowed to run
#Override
public void onCreate() {
System.out.println("#### WebService onCreate");
if (workList==null) workList = new SparseBooleanArray();
}
#Override
public void onDestroy() {
System.out.println("#### WebService onDestroy");
}
/***************************************************************************
* Start working
**************************************************************************/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("WebService Start ID=" + startId);
final int currentID = startId;
final Intent currentIntent = intent;
Runnable workerRunnable = new Runnable() {
public void run() {
System.out.println("WebService Thread Start - ID=" + currentID);
int resultCode;
Bundle responseExtras = new Bundle();
resultCode = serverRequest(currentIntent,responseExtras);
sendResponse(currentIntent,resultCode,responseExtras);
System.out.println("WebService Thread End - ID=" + currentID);
Bundle extras = currentIntent.getExtras();
if (extras != null) {
int frameID = extras.getInt("FRAMEID");
System.out.println(">>>>>>> PUT FALSE " + frameID);
workList.put(frameID, false);
};
stopSelfResult(currentID);
}
};
if (intent!=null) {
Bundle extras = intent.getExtras();
if (extras != null) {
int frameID = extras.getInt("FRAMEID");
Boolean found = workList.get(frameID,false);
if (!found) {
System.out.println(">>>>>>> PUT TRUE FRAMEID=" + frameID);
workList.put(frameID, true);
Thread workerThread = new Thread(workerRunnable);
workerThread.start();
} else {
System.out.println(">>>>>>> Allready running FRAMEID=" + frameID);
}
};
};
return Service.START_STICKY;
};
/***************************************************************************
* No binding
**************************************************************************/
#Override
public IBinder onBind(Intent intent) {
return null;
}
/***************************************************************************
* Send webservice request and return result in responseExtras
**************************************************************************/
private int serverRequest(Intent intent, Bundle responseExtras) {
...
};
/***************************************************************************
* Send response back to service caller using Messenger.send()
**************************************************************************/
private boolean sendResponse(Intent intent, int resultCode, Bundle responseExtras) {
...
};
Your service is stopped if you call stopSelfResult() with the latest startId. So if the service gets started with an intent for startId=1 and another intent with startId=2 and the second is finished before the first, you call stopSelfResult(2) before you finished for startId=1. The service gets destroyed immediately if you call stopSelfResult() with the latest startId and no other intents are pending.
Hold the latest startId. Add all startIds you wish to process in an array (e.g. List<Integer> runningStartIds) and remove them when you've finished processing them. After removing on finishing, compare the current startId with the latest one and do not call stopSelfResult() if runningStartIds is not empty. So you will end up calling stopSelfResult() only for the latest startId, when all intents were processed and no more intents are pending.
Should work, although I haven't posted an example.
.:EDIT:.
Explenation:
The next Intent may come in as fast as you return from onStartCommand() regardless of what you're doing in the background.
.:EDIT:.
Not an Improvement(Improvement:
Thinking about that, in fact you only have to keep the mLastStartId. Just skip calling stopSelfResult() until the finished startId matches mLastStartId.)
Unfortunately, it always can be happen. Actually, android application components' life cycle aren't synchronized w/ any type of worker threads as default.
So, you may need to check the status of Service manually, for example you can have one boolean flag to indicate if a service is working or not. Another handy approach is using IntentService instead of using normal service, it handles worker thread and life-cycle features by itself.
public class MyIntentService extends IntentService {
public MyIntentService() {
super("MyIntentService ");
}
#Override
protected void onHandleIntent(Intent intent) {
// This callback-method is called inside worker thread,
// so you can do some long-time network job here.
SystemClock.sleep(30000); // 30 seconds
// In this timing the service will be stopped automatically.
}
}
I have a method public void writeEntry(Activity ctx, Entry entry) which get some data and have to call a native method, which takes longer to finish.
So I created an AsyncTask which handles the ProgressDialog and the native method. It works great in an own Activity to test it, in that Activity I used a callback interface and so on.
In my case I have the above described method and have to execute the AsyncTask. The executing can't be in that method because it doesn't halt the further execution.
I need the result from the native method before I can continue with the execution.
Is there any possibility to wait for the AsyncTask til it is finished? The method wait() isn't an option because the UI Thread will wait, too and so the sense of a ProgressDialog will be lost.
Can I use the method runOnUiThread() from the given parameters or is the only solution to start an own Activity?
so I will try to explain as much as I can
Start your heavy process inside an AsyncTask, but whatever code you want to execute after completion of AsyncTask put it in a separate public method. Now once you finish with your heavy process call that separately created method in onPostExecute().
So psuuedo code will look like this,
class main extends Activity {
class Something extends AsyncTask<String, Integer, String> {
protected void onPreExecute() {
// Start your progress bar...
}
protected String doInBackground(String... params) {
// Do your heavy stuff...
return null;
}
protected void onPostExecute(String result) {
// close your progress dialog and than call method which has
// code you are wishing to execute after AsyncTask.
}
}
}
Hope this will help,
Good Luck!
My first solution was to use callback methods with an interface implementation see the example https://stackoverflow.com/a/6396376/390177.
After chatting a while in the Android chat I heard that there is a more praticable solution.
You can use of an IntentService in combination with a PendingIntent.
The communication is realized with Intent's.
If you want to use a ProgressDialog, you need an own Activity for it, which register for example a BroadcastReciever and the IntentService sends it actual status per Broadcast.
But lets start now.
First we create the Activity, which contains the ProgressDialog and a registered BroadcastReceiver. The BroadcastReceiver listen for messages about updating and finishing the dialog.
For the Activity we needs the layout ...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:background="#80000000">
</LinearLayout>
... and the related code:
public class ProgressActivity extends Activity {
/**
* ProgressDialog which is shown
*/
private ProgressDialog progessDialog_g;
/**
* Instance of the BroadcastReceiver
*/
private BroadcastReceiver receiver_g;
/**
* Identifier for the different settings of the ProgressDialog
*/
public static final String PROGRESS_DIALOG_BOOL_HORIZONTAL_BAR = "pbar_horizontal_bar";
public static final String PROGRESS_DIALOG_BOOL_CANCELABLE = "pbar_horizontal_cancelable";
public static final String PROGRESS_DIALOG_STR_MESSAGE = "pbar_message";
public static final String PROGRESS_DIALOG_INT_MAX = "pbar_max_bar";
public static final String PROGRESS_DIALOG_INT_VALUE = "pbar_value";
protected static final int PROGRESS_DIALOG_INT_MAX_VALUE = 100;
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.progress);
progessDialog_g = new ProgressDialog(this);
// Reads and sets the settings for the ProgressDialog
Intent i = getIntent();
progessDialog_g.setCancelable(i.getBooleanExtra(
PROGRESS_DIALOG_BOOL_CANCELABLE, false));
if (i.getBooleanExtra(
PROGRESS_DIALOG_BOOL_HORIZONTAL_BAR, false)) {
progessDialog_g.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
} else {
progessDialog_g.setProgressStyle(ProgressDialog.STYLE_SPINNER);
}
progessDialog_g
.setMessage(i
.getStringExtra(PROGRESS_DIALOG_STR_MESSAGE));
progessDialog_g.setMax(i.getIntExtra(
PROGRESS_DIALOG_INT_MAX, 100));
// Create the IntentFilter for the different broadcast messages
IntentFilter iFilter =
new IntentFilter(
ExampleProgressService.PROGRESS_DIALOG_BROADCAST_INIT);
iFilter.addAction(ExampleProgressService.PROGRESS_DIALOG_BROADCAST_UPDATE);
iFilter.addAction(ExampleProgressService.PROGRESS_DIALOG_BROADCAST_FINISH);
// Creates the BroadcastReceiver
receiver_g = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent){
Log.d(DefaultPreferences.DEBUG_PREFIX + "ProgressActivity",
intent.getAction());
if (ExampleProgressService.PROGRESS_DIALOG_BROADCAST_INIT
.equals(intent.getAction())) {
// Sets the ProgressDialog style
if (intent
.getBooleanExtra(
PROGRESS_DIALOG_BOOL_HORIZONTAL_BAR,
false)) {
progessDialog_g
.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
} else {
progessDialog_g
.setProgressStyle(ProgressDialog.STYLE_SPINNER);
}
// Shows the ProgressDialog
progessDialog_g.show();
} else if (ExampleProgressService.PROGRESS_DIALOG_BROADCAST_UPDATE
.equals(intent.getAction())) {
// Updates the ProgressDialog
int value =
intent.getIntExtra(
PROGRESS_DIALOG_INT_VALUE,
-1);
if (value != -1) {
progessDialog_g.setProgress(value);
}
} else if (ExampleProgressService.PROGRESS_DIALOG_BROADCAST_FINISH
.equals(intent.getAction())) {
// Finishs the ProgressDialog
progessDialog_g.cancel();
finish();
}
}
};
// Registers the BroadcastReceiver
registerReceiver(receiver_g, iFilter);
}
#Override
protected void onDestroy(){
unregisterReceiver(receiver_g);
super.onDestroy();
}
}
Now we want to use the Activity, so lets start with calling it:
final Intent i = new Intent(parentActivity, <packages>.ProgressActivity);
i.putExtra(ProgressActivity.PROGRESS_DIALOG_BOOL_CANCELABLE, cancelable_g);
i.putExtra(ProgressActivity.PROGRESS_DIALOG_BOOL_HORIZONTAL_BAR, showProgress_g);
i.putExtra(ProgressActivity.PROGRESS_DIALOG_STR_MESSAGE, message_g);
i.putExtra(ProgressActivity.PROGRESS_DIALOG_INT_MAX, ProgressActivity.PROGRESS_DIALOG_INT_MAX_VALUE);
parentActivity.startActivity(i);
So we habe a running ProgressActivity, which waits for different broadcasts. But first we need the IntentService, which sends the broadcasts.
So lets go:
public class ExampleProgressService extends IntentService {
/**
* PendingIntent for callback.
*/
protected PendingIntent pi_g = null;
private static final String DEBUG_TAG = "ExampleProgressService";
/**
* Message identifier for ProgressDialog init
*/
public static final String PROGRESS_DIALOG_BROADCAST_INIT = "Dialog.Progress.Init";
/**
* Message identifier for ProgressDialog finish
*/
public static final String PROGRESS_DIALOG_BROADCAST_FINISH = "Dialog.Progress.Finish";
/**
* Message identifier for ProgressDialog update
*/
public static final String PROGRESS_DIALOG_BROADCAST_UPDATE = "Dialog.Progress.Update";
/**
* Identifier of the result for intent content
*/
public static final String PROGRESS_DATA_RESULT = "Result";
/**
* Identifier of the result error for intent content
*/
public static final String PROGRESS_DATA_RESULT_ERROR_MESSAGE = "Result.Error.Message";
/**
* Identifier of the result error exception for intent content
*/
public static final String PROGRESS_DATA_RESULT_ERROR_EXCEPTION = "Result.Error.Exception";
/**
* Identifier of the result status for intent content
*/
public static final String PROGRESS_DATA_RESULT_STATUS_BOOL = "Result.Status.boolean";
/**
* Identifier of the pending intent for intent content
*/
public static final String PROGRESS_DATA_PENDING_RESULT = "PendingResult";
public ExampleProgressService() {
super("ExampleProgressService");
}
/**
* Send the finish message.
*/
private void closeProgressActivity() {
Intent intent = new Intent(PROGRESS_DIALOG_BROADCAST_FINISH);
sendBroadcast(intent);
}
/**
* Do some magic with the intent content
*/
private void extractVariablesFromIntentAndPrepare(Intent intent)
throws Exception {
pi_g = (PendingIntent) intent
.getParcelableExtra(PROGRESS_DATA_PENDING_RESULT);
if (pi_g == null) {
throw new Exception("There is no pending intent!");
}
/**
* Sends an error message.
*/
private void failed(Exception e, String message) {
Intent i = new Intent();
i.putExtra(PROGRESS_DATA_RESULT_ERROR_EXCEPTION, e);
i.putExtra(PROGRESS_DATA_RESULT_ERROR_MESSAGE, message);
send(i, false);
}
/**
* Sends the init message.
*/
private void initProgressActivity() {
Intent intent = new Intent(PROGRESS_DIALOG_BROADCAST_INIT);
intent.putExtra(PROGRESS_DIALOG_BOOL_HORIZONTAL_BAR,
multipart_g);
sendBroadcast(intent);
}
/**
* (non-Javadoc)
*
* #see android.app.IntentService#onHandleIntent(android.content.Intent)
*/
#Override
protected void onHandleIntent(Intent intent) {
extractVariablesFromIntentAndPrepare(intent);
initProgressActivity();
// do your calculation here and implements following code
Intent intent = new Intent(PROGRESS_DIALOG_BROADCAST_UPDATE);
intent.putExtra(PROGRESS_DIALOG_INT_VALUE, progressValue);
sendBroadcast(intent);
// If you finished, use one of the two methods to send the result or an error
success(result);
failed(exception, optionalMessage);
}
/**
* Sends the data to the calling Activity
*/
private void send(Intent resultData, boolean status) {
resultData.putExtra(PROGRESS_DATA_RESULT_STATUS_BOOL, status);
closeProgressActivity();
try {
pi_g.send(this, Activity.RESULT_OK, resultData);
} catch (PendingIntent.CanceledException e) {
Log.e(DEBUG_TAG,
"There is something wrong with the pending intent", e);
}
}
/**
* Sends the result message.
*/
private void success(String result) {
Intent i = new Intent();
i.putExtra(PROGRESS_DATA_RESULT, result);
send(i, true);
}
}
The result of the calculation progress shall be available in the parentActivity, so we create the PendingIntent in that Activity and call the IntentService.
// Some identifier for the call
int requestCode = 12345;
final Intent sI = new Intent(ExampleProgressService.PROGRESS_SERVICE_ACTION);
// Callback
sI.putExtra(ExampleProgressService.PROGRESS_DATA_PENDING_RESULT, parentActivity
.createPendingResult(requestCode, null,
PendingIntent.FLAG_CANCEL_CURRENT));
// Service start
parentActivity.startService(sI);
For receiving the results, we have to override the method onActivityResult(int requestCode, int resultCode, Intent data).
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
// Compares the requestCode with the requestCode from above
if (requestCode == ...) {
if (data.getBooleanExtra(ExampleProgressService.PROGRESS_DATA_RESULT_STATUS_BOOL, false)) {
// Calculation was success
data.getStringExtra(ExampleProgressService.PROGRESS_DATA_RESULT);
} else
{
// Calculation is failed
data.getStringExtra(ExampleProgressService.PROGRESS_DATA_RESULT_ERROR_MESSAGE);
((Exception) data.getSerializableExtra(ExampleProgressService.PROGRESS_DATA_RESULT_ERROR_EXCEPTION));
}
}
}
That was the magic, I hope it will help you.