I have a main activity and a service where i update some data taken from facebook in background every x hours.
In these days i'm finally moving to the new SDK 3.0.1 (from 2.x) and i have a problem with passing the facebook Session to the service:
If the service runs when the main activity is still running everything works fine, otherwise my session is null and so it crashes at if (session.isOpened()) { ...} because session==null.
here's my code:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("ServiceUpdate", "Received start id " + startId + ": " + intent);
session = Session.getActiveSession();
startservice();
return START_STICKY;
}
private void startservice() {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if (session.isOpened()) {
if (isOnline()) {
Log.i("ServiceUpdate", "start timer");
update();
Log.i("ServiceUpdate", "end timer");
}
}
}, 600000, 6 * 3600000);
}
Do i have to initialize the session with saved acces_token and expire_time as I used to do with SDK 2.x ?
Thank you!
The default behavior for Session is to save the token information in Android's SharedPreferences, so you should be able to just call Session.openActiveSessionFromCache in your service. If that returns null, then that means your user hasn't connected with Facebook yet.
Related
I am developing Android. I success to make Android Application login and logout that set by user. But when i deactivate the account user at back-end side (using boilerplate), the android application that use by user not logout. What I must to do.
You can create a Service that periodically check if the user is still valid. Along with that, you can check for the validity of the user when the user makes an action that access the server.
When the service or the action detects that the user is no longer valid. Then just bring the user back to the login page.
Example code of the Service
public class SessionService extends Service {
private AtomicBoolean isStopped = new AtomicBoolean(false);
private static final long CHECK_EVERY = 1000L; // check every 1 second
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
final Handler handler = new Handler(Looper.getMainLooper());
// session checker thread
new Thread(new Runnable() {
#Override
public void run() {
while (!isStopped.get()) {
try {
Thread.sleep(CHECK_EVERY);
// ask API if user is still valid
boolean isUserValid = APIDao.isUserValid();
if (!isUserValid) { // user is no longer valid
// kill all Activity then open the LoginActivity
handler.post(new Runnable() { // runOnUiThread
#Override
public void run() {
Intent intent = new Intent(this, LoginActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
// stop the thread
isStopped.set(true);
}
}
There's one more thing I can think of, using Push Notification. I haven't tried this but basically the server can send a message to the app. This is ussually used by messenger apps.
I'm trying to create a periodical service where I can validate certain values stored on my mobile with the data I have on my API Server. If a user's password gets changed or if the user gets deleted the API Server should send a response back so the mobile app knows the user should be signed out. The request/response is not the problem. Just having issues getting the service periodically.
MyService.class:
public class MyService extends IntentService {
private static final String TAG = "MyService";
public MyService() {
super("MyService");
}
#Override
protected void onHandleIntent(Intent intent) {
// Normally we would do some work here, like download a file.
// For our sample, we just sleep for 5 seconds.
try {
Thread.sleep(5000);
Log.d(TAG,"loop service");
} catch (InterruptedException e) {
// Restore interrupt status.
Thread.currentThread().interrupt();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
}
Inside the onCreate of another random class:
Context mContext = getApplicationContext();
Intent i= new Intent(mContext, MyService.class);
i.putExtra("KEY1", "Value to be used by the service");
mContext.startService(i);
The service does start (it shows me the Log.d), how do I continue from here to get it restarted or to make it start after certain time?
First things first: IntentService is designed only to execute a queue of tasks off the main thread. So you must create a new one every time you want to check for updates: for that see Alarm Manager
But this is not necessarily the best way to do it. Instead of you polling the server for changes, try the push notifications approach.
I am trying to create a Service that opens a stackoverflow site every 24 hours and my code looks like this:
public class StackServices extends Service {
private Handler mPeriodicEventHandler;
private static final String TAG = "Debug";
private static final long WAITING_TIME = 1000 * 60 * 60 * 24; //24 hours
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "Service Created");
mPeriodicEventHandler = new Handler();
}
#Override
public void onDestroy() {
mPeriodicEventHandler.removeCallbacks(doPeriodicConnect);
Log.d(TAG, "Service Destroyed");
super.onDestroy();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "Service started a command");
mPeriodicEventHandler.post(doPeriodicConnect); //in order to load url when service starts
return Service.START_STICKY;
}
private Runnable doPeriodicConnect = new Runnable() {
public void run()
{
String url = "http://www.stackOverflow.com";
loadURL(url);
mPeriodicEventHandler.postDelayed(doPeriodicConnect, WAITING_TIME);
}
};
private void loadURL(String url) {
Calendar calendar = Calendar.getInstance();
Log.d(TAG, String.format("Service loaded on: %s", calendar.getTime() ));
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.addFlags(intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
I am starting the Service by clicking on a button, and my Activity implements OnClickListener:
#Override
public void onClick(View view) {
int viewId = view.getId();
switch (viewId){
case R.id.btnStartStopService:
if (isServiceRunning){
this.stopService(new Intent(this, StackServices.class));
Toast.makeText(MainActivity.this, R.string.service_shutdown, Toast.LENGTH_SHORT).show();
btnStartStopService.setText(getString(R.string.btn_start_service));
Log.d("Debug","service is running and you clicked shutdown"); //TODO delete me
} else {
Toast.makeText(getApplicationContext(), R.string.service_started, Toast.LENGTH_SHORT).show();
this.startService(new Intent(this, StackServices.class));
Log.d("Debug", "service is not running and you clicked start"); //TODO delete me
finish();
}
break;
default:
Toast.makeText(MainActivity.this, R.string.dont_know_what_to_do, Toast.LENGTH_SHORT).show();
break;
}
}
So far so good, but something is wrong, When I start the service with the button and during the 24 hours period I open and close the application several times, my service is recreating and loads the site again. I am wondering why..?
That is not appropriate behavior for me. I wrote some Log.d() messages to see what happens.
Debug: Activity created
Debug: service running check
Debug: service is not running and you clicked start
Debug: Service Created
Debug: Service started a command
Debug: Service loaded on: Thu Feb 25 10:08:11 GMT+02:00 2016
Debug: Activity destroyed
Debug: Activity created
Debug: service running check
Debug: Activity destroyed
Debug: Activity created
Debug: service running check
Debug: Activity destroyed
Debug: Activity created
Debug: service running check
Debug: Service Created
Debug: Service started a command
Debug: Service loaded on: Thu Feb 25 10:08:53 GMT+02:00 2016
Your Service returns START_STICKY from onStartCommand().
This means although it may be destroyed by the system at some time it will then be recreated. One important detail:
if there are not any pending start commands to be delivered to the service, it will be called with a null intent object, so you must take care to check for this.
(From the documentation for START_STICKY)
To avoid frequent reloading of the webpage, you can change your onStartCommand() method like this:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null)
{
// only do this when started by activity
mPeriodicEventHandler.post(doPeriodicConnect);
}
return Service.START_STICKY;
}
But maybe you should also reconsider if you really need a Service running 24/7. Take a look at AlarmManager, see this guide
I have a class SendMessageService which extends from Service. This class sends chat messages to the server in the background.
Do I always have to call stopSelf() when I return from my service?
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null || !AppUtil.hasInternetConnection(this)) {
return START_STICKY;
}
startID = startId;
handler = new Handler();
messageDatabase = MessageDatabase.getInstance(this);
sendMessages();
return super.onStartCommand(intent, flags, startId);
}
Do I have to call stopself() above return START_STICKY?
private void sendMessages() {
// get all unsend messages
new Thread() {
#Override
public void run() {
synchronized (lock) {
final ArrayList<Message> messages = new ArrayList<Message>();
messageDatabase.getConditionBuilder().add(DatabaseHelper.KEY_MESSAGE_SENT + " = ?",
new String[] { String.valueOf(0) });
messageDatabase.getConditionBuilder().setSortOrder(DatabaseHelper.KEY_MESSAGE_LOCAL_TIME + " ASC");
messages.addAll(messageDatabase.getList());
// ...
sendMessageRecursive(0, messages);
}
}
}.start();
}
private void sendMessageRecursive(final int index, final ArrayList<Message> messages) {
if (index >= messages.size()) {
stopSelf(startID);
return;
}
// ...
}
Do I have to call stopSelf() in this situation?
Do I always have to call stopSelf() when I return from my service?
You call stopSelf() (or stopService() from outside the service) when you no longer want the service to be running. Do not just toss code into a Service without a very clear plan for when that service should and should not be running. Only have a service running when it is actively delivering value to the user.
In this case, I'm not quite certain why you didn't choose IntentService, considering that it handles your threading and "do I need to stop?" issues for you.
That being said, given your existing code, and assuming that there's nothing else to the service than what you have shown here, you should call stopSelf():
In onStartCommand() if you're not actually going to be doing any work (as you are then not actively delivering value to the user), rather than return START_STICKY
At the end of run(), when you are doing doing the work (and therefore will no longer be actively delivering value to the user)
Pleas let me explain the scenario fist.
I have been writing a Dropbox like android app, which automatically upload photos to the server. When user turn on upload service, it keeps running at the background as long as it can.
The Service is used to upload photos, but I also added a Content Observer to watch camera event, because I need to upload the newly taken photo instantly. The Content Observer will detect camera event and upload the new photo. Until now everything is alright.
As I passed START_STICKY to the Service to make it keep running and restore itself after OS has enough memory. But the Content Observer does not. And that made the problem.
I need to ensure Content Observer functions as long as the service does, because the user may take camera photo at any time. The Content Observer has to detect the camera event at the background and upload it.
I glistered the Content Observer in onCreate() method under Service class, and unregisterd it under onDestory() method. see the code
#Override
public void onCreate() {
Log.d(DEBUG_TAG, "onCreate");
// bind transfer service
Intent bIntent = new Intent(this, TransferService.class);
bindService(bIntent, mConnection, Context.BIND_AUTO_CREATE);
Log.d(DEBUG_TAG, "try bind TransferService");
this.getApplicationContext()
.getContentResolver()
.registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false,
cameraUploadObserver);
}
#Override
public void onDestroy() {
Log.d(DEBUG_TAG, "onDestroy");
cancelUploadTasks();
/*this.getApplicationContext().getContentResolver()
.unregisterContentObserver(cameraUploadObserver);
cameraUploadObserver = null;*/
if (mTransferService != null) {
unbindService(mConnection);
mTransferService = null;
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(DEBUG_TAG, "onStartCommand");
Log.d(DEBUG_TAG, "onStartCommand.intent: " + intent);
initializeCameraUploadPreference();
if (repoId != null && accountEmail != null) {
isCameraUpload = true;
account = new Account(accountServer, accountEmail, null, accountToken);
cUploadManager = new CameraUploadManager(account);
}
if (isCameraUpload) {
Log.d(DEBUG_TAG, "onStartCommand.startPhotoUploadTask");
ConcurrentAsyncTask.execute(new PhotoUploadTask());
}
return START_STICKY;
}
Below is how cameraUploadObserver was created and used
private CameraObserver cameraUploadObserver = new CameraObserver();
private class CameraObserver extends ContentObserver {
public CameraObserver() {
super(null);
}
#Override
public void onChange(boolean selfChange) {
super.onChange(selfChange);
Log.d(DEBUG_TAG, "CameraOberser onChange");
ConcurrentAsyncTask.execute(new CameraEventReceiverTask());
}
}
So anyone can tell me why the service can keep running but the Content Observer, is there any code I need to add to keep the Content Observer running? any comments, advice, reference from you will be appreciated.