First of all, I'm quite new to the Android and JAVA world (coming from C/C++/Objective-C).
I'm trying to integrate the Android bump API (3.0, latest version) but I'm running into trouble.
I copied the exemple, it is working fine under Android 2.2, bump services are started correctly, but as for Android 3.0 and upper it does not works.
I've got an exception (the network on main thread one) when loading my activity, I know this exception and how to avoid it, but in this case, Bump state that they run their API in their own thread so I don't really know why I got it. They said that you don't need to run a thread or tasks.
Here is a sample of my Activity
public class BumpActivity extends Activity {
private IBumpAPI api;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.bump);
bindService(new Intent(IBumpAPI.class.getName()), connection,
Context.BIND_AUTO_CREATE);
IntentFilter filter = new IntentFilter();
filter.addAction(BumpAPIIntents.CHANNEL_CONFIRMED);
filter.addAction(BumpAPIIntents.DATA_RECEIVED);
filter.addAction(BumpAPIIntents.NOT_MATCHED);
filter.addAction(BumpAPIIntents.MATCHED);
filter.addAction(BumpAPIIntents.CONNECTED);
registerReceiver(receiver, filter);
}
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
private final ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder binder) {
Log.i("BumpTest", "onServiceConnected");
api = IBumpAPI.Stub.asInterface(binder);
try {
api.configure("API_KEY", "Bump User");
} catch (RemoteException e) {
Log.w("BumpTest", e);
}
Log.d("Bump Test", "Service connected");
}
#Override
public void onServiceDisconnected(ComponentName className) {
Log.d("Bump Test", "Service disconnected");
}
};
}
Sound like the problem occur during the connection service on the api.configure....
Should I run it in a separate thread or in it's own AsynchTask, but then how and why ?
I stuck on this problem for a day or so... and literarily 2 minutes after posting it here I resolved it...
I just put the api.configure on a separate thread (shorter than a AsynchTask).
private final ServiceConnection connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName className, IBinder binder) {
Log.i("BumpTest", "onServiceConnected");
api = IBumpAPI.Stub.asInterface(binder);
new Thread() {
public void run() {
try {
api.configure("API_KEY",
"Bump User");
} catch (RemoteException e) {
Log.w("BumpTest", e);
}
}
}.start();
Log.d("Bump Test", "Service connected");
}
#Override
public void onServiceDisconnected(ComponentName className) {
Log.d("Bump Test", "Service disconnected");
}
};
Make request in background process.
The network on main thread one exception is happening in 2.2 and on 3.0 and above, the difference is that on 3.0 and above they force you to put everything that involves some heavy or slow ops in a different thread, as you said in an asyncTask.
You just have to create an inner asyncTask and on its onBackground method put your api.configure :)
class LoadBumpAsyncTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
api.configure("9b17d663752843a1bfa4cc72d309339e", "Bump User");
} catch (RemoteException e) {
Log.w("BumpTest", e);
}
return null;
}
}
Just call new LoadBumpAsyncTask().execute() on your service connected and will work.
Related
My Android application (targeting API 33) invokes a foreground service when the app is launched for the first time. The service is used for sending a 'heartbeat' / status to an API which writes the timestamp of the last heartbeat / status update to a database.
Starting the foreground service in the 'MainActivity':
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_activity);
if(!clientServiceRunning()) {
Intent serviceIntent = new Intent(this, ClientService.class);
this.startForegroundService(serviceIntent);
}
authenticateUser();
checkNetworkState();
storeTerminalId(TERMINAL_ID);
}
Code of the foreground service class:
public class ClientService extends Service {
#Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
public ClientService() {
super();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Thread that sends the heartbeat to the API
new Thread(
new Runnable() {
#Override
public void run() {
while (true) {
Log.d("Client Service", "Service is running");
// Sends request to API using Androids Volley library
sendHeartbeat();
try {
// Only send heartbeat every 2 minutes
Thread.sleep(120000);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
}
).start();
new Thread(
new Runnable() {
#Override
public void run() {
// Some other task
}
}
).start();
// Necessary notification details for foreground service
final String CHANNEL_ID = "Client Service ID";
NotificationChannel notificationChannel = new NotificationChannel(
CHANNEL_ID,
CHANNEL_ID,
NotificationManager.IMPORTANCE_LOW
);
getSystemService(NotificationManager.class).createNotificationChannel(notificationChannel);
Notification.Builder serviceNotification = new Notification.Builder(this, CHANNEL_ID)
.setContentText("Client Service running the background")
.setSmallIcon(R.drawable.launcher_foreground_icon);
startForeground(1, serviceNotification.build());
return super.onStartCommand(intent, flags, startId);
}
}
Method that makes the API heartbeat request:
(Note that JSONRequestNoResponse is a custom volley response class)
private void sendHeartbeat() {
try {
RequestQueue requestQueue = Volley.newRequestQueue(this);
String URL = "https://api.myapplication.com/device/heartbeat";
JSONObject jsonBody = new JSONObject();
jsonBody.put("terminalId", String.valueOf(terminalId));
jsonBody.put("scannerConnectionState", String.valueOf(isScannerConnected));
JSONRequestNoResponse jsonRequest = new JSONRequestNoResponse(Request.Method.PUT, URL, jsonBody,
new Response.Listener<>() {
#Override
public void onResponse(JSONObject response) {
Log.d("Volley", "API: Heartbeat sent");
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.w("Volley ERROR", "Service - Error Response");
}
}
);
requestQueue.add(jsonRequest);
} catch (JSONException e) {
e.printStackTrace();
}
}
When debugging and checking the Logcat output in Android Studio the Log.d() messages are displayed and I can see that in my database, the timestamp is renewed every two minutes since the service calls the API to initiate a this database update. The service also keeps running when the application is closed or when my device goes to sleep - as expected from a foreground service.
Now to my problem:
The service and the hence calling the API only works when my test device is connected to Android Studio. (The device and my PC are connected via a USB C cable). As soon as I disconnect the device from my PC, the service keeps running but the timestamp in the database is not updated every two minutes anymore.
When I reconnect my device to my PC and open Android Studio, the Logcat messages ("Client Service", "Service is running") resume and also the timestamp in the DB keeps updating regularly again.
So why does the service only really work when it is connected to a PC/Android Studio. I find this to be very peculiar.
I have written a code to connect my android app to mqtt server in android studio. I have added button "connect" after clicking it will show me connect message on screen. The moment I click the connect button, I get the message your android app keeps stopping. here is my code. I have uploaded necessary repositories and dependency and also permissions
public class MainActivity extends AppCompatActivity {
MqttAndroidClient client;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String clientId = MqttClient.generateClientId();
client =
new MqttAndroidClient(this.getApplicationContext(), "tcp://mqtt.eclipse.org:1883",
clientId);
}
public void test(View V){
try {
IMqttToken token = client.connect();
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
// We are connected
Toast.makeText(MainActivity.this,"connected",Toast.LENGTH_LONG).show();
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
Toast.makeText(MainActivity.this,"disconnected",Toast.LENGTH_LONG).show();
}
});
} catch (MqttException e) {
e.printStackTrace();
}
}
}
Error Solved. There was problem in local broadcast manager file.
I call this in an onClickListener
AsyncTaskBindService asyncTaskBindService = new AsyncTaskBindService(position, songList);
asyncTaskBindService.execute();
Asynctask class
public class AsyncTaskBindService extends AsyncTask<Void, Void, Void>{
private int songIndex;
private ArrayList<Song> songList;
public AsyncTaskBindService(int songIndex, ArrayList<Song> songList){
this.songIndex = songIndex;
this.songList = songList;
}
#Override
protected Void doInBackground(Void... voids) {
try {
if (getActivity() != null && !Main.mServiceIsBound) {
Main.bindMusicService(getActivity().getApplicationContext());
Log.i(TAG,"binding service!");
}
}catch (Exception e){
Log.e(TAG,"error binding Service!");
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
if (getActivity() != null && Main.mServiceIsBound) {
Main.mediaPlayerService.startActionPlay(getActivity().getApplicationContext(), songList, songIndex);
Log.i(TAG,"start song!");
}
}
Problem is in my doInBackground:
This line:
Main.bindMusicService(getActivity().getApplicationContext());
This method calls bindService in another class which then returns an instance to my Service.
But the problem is that Asynctask finished way to fast and when onPostExecute gets called it returns an NPE because mediaPlayerService is still null because onServiceConnected is slower than my Asynctask.
How to solve this?
Now i have to click a song 2 times before it starts playing.
EDIT
First i tried this, but my second if never gets called because it takes some time before onServiceConnected() is finished.
How can i immediately execute my second method if bindService is finished and my service is bound?
onClick Song
if (!Main.mServiceIsBound && getActivity() != null) {
Main.bindMusicService(getActivity().getApplicationContext());
Log.i(TAG,"Service is not bound yet!, binding Service...");
}
if (getActivity()!= null && Main.mServiceIsBound) {
Main.mediaPlayerService.startActionPlay(getActivity().getApplicationContext(), songList, position);
}
Main class
/**This establishes the connection to the MediaPlayerService. */
public static ServiceConnection serviceConnection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MediaPlayerService.MusicBinder binder = (MediaPlayerService.MusicBinder)service;
mediaPlayerService = binder.getService();
mServiceIsBound = true;
Log.i("Main","MediaPlayerService Connected!");
}
#Override
public void onServiceDisconnected(ComponentName name) {
mServiceIsBound = false;
Log.i("Main","MediaPlayerService Disconnected!");
}
};
public static void bindMusicService(Context c){
/*mediaPlayerServiceIntent binds our connection to the MediaPlayerService. */
try{
mediaPlayerServiceIntent = new Intent(c, MediaPlayerService.class);
c.bindService(mediaPlayerServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}catch (Exception e){
Log.e("Main", "Service is not bound!");
}
Log.i("Main","Service is bound!");
}
That's because AsyncTasks doInBackGround run asynchronously, yet when you bind a component to a Service by using bindService that also runs asynchronously causing Main.bindMusicService to return immediately.
An AsyncTask is not a good candidate for this sort of work. You should be binding to a service in the main thread (UI thread) and listen for the onServiceConnected callback of the ServiceConnection object.
I am using Job Scheduler API in my app to schedule a job for me after specific time interval. It runs fine when the app is running. But whenever the user closes the app or clears it from the recent task list the app stops and the scheduled job never executes afterwards until you open the app and it is rescheduled again from the time it is opened.
Now i want someone to help me to keep the jobs on executing even if the app is closed or cleared from the recent task list.
If there is any alternative solution please tell me.
i am looking for the solution from the past 3 days. Tried everything said by developers on StackOverFlow and other sites and none of them worked for me.
This is where is schedule the job!
ComponentName componentName = new
ComponentName(getActivity().getBaseContext(),WallpaperJobService.class);
JobInfo jobInfo = new JobInfo.Builder(777,componentName)
.setRequiresCharging(sharedPreferences.getBoolean("Charging",false))
.setRequiredNetworkType(sharedPreferences.getBoolean("Wifi",false) ?
JobInfo.NETWORK_TYPE_UNMETERED : JobInfo.NETWORK_TYPE_ANY)
.setPeriodic(sharedPreferences.getInt("Duration",15) * 60 *
1000)
.setPersisted(true)
.build();
JobScheduler scheduler = (JobScheduler)
getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
My Job Service Class:
public class WallpaperJobService extends JobService {
private boolean jobCancelled;
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor editor;
#Override
public boolean onStartJob(JobParameters params) {
Log.i("WallpaperJobService", "Job started!");
changeWallpaper(params);
return true;
}
private void changeWallpaper(final JobParameters params) {
final ArrayList<Image> images = (ArrayList<Image>)
MainActivity.favoritesRoomDatabase.roomDao().getAllFavoriteWallpapers();
sharedPreferences = getSharedPreferences("GridSize", MODE_PRIVATE);
editor = sharedPreferences.edit();
if (images != null && images.size() != 0) {
if (sharedPreferences.getInt("Index", 0) == images.size()) {
editor.putInt("Index", 0);
editor.commit();
}
Picasso.get().load(Constants.domain +
images.get(sharedPreferences.getInt("Index", 0)).getImage_url()).into(new
Target() {
#Override
public void onBitmapLoaded(final Bitmap bitmap,
Picasso.LoadedFrom from) {
new Thread(new Runnable() {
#Override
public void run() {
if (jobCancelled) {
Log.i("WallpaperJobService","Returned");
return;
}
try {
//Doing some work here
} catch (IOException e) {
e.printStackTrace();
}
Log.i("WallpaperJobService", "Job finished!");
jobFinished(params, false);
}
}).start();
}
#Override
public void onBitmapFailed(Exception e, Drawable errorDrawable)
{
Log.i("WallpaperJobService", "Bitmap load failed " +
e.getMessage());
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
} else {
Log.i("WallpaperJobService", "Favorite database is null!");
}
}
#Override
public boolean onStopJob(JobParameters params) {
Log.i("WallpaperJobService", "Job cancelled before completion!");
jobCancelled = true;
return true;
}
}
When doing stuff periodically in the background — JobScheduler, WorkManager, AlarmManager, FCM push messages, etc. — you have to take into account that your process might not be around when it is time for you to do your work. Android will fork a process for you, but it is "starting from scratch". Anything that your UI might have set up in memory, such as a database, would have been for some prior process and might not be set up in the new process.
UPDATE:
After adding the suggested methodes (doBindService() and doUnbindService()) along with calls to no avail) From here suggested by #Nick Campion
I've been trying for a while to get this service running but nothing seems to be working - I know I'm probably missing a semicolon or something :)
The program calls startNotificationService(), then the Log shows the log message... and the app continues to run without the Service showing up. I can't find the Service in Advance Task Killer. HELP!!!
XML (In Manifest) :
<service
android:icon="#drawable/icon"
android:label="Smart Spdate Service"
android:name="notifyService">
<intent-filter
android:label="FULL_PATH_NAME_HERE.updateService">
</intent-filter>
</service>
Service Call
Log.v("NOTICE", "Notification Service was not found running - starting");
//startService(new Intent(this, notifyService.class));
startService(new Intent(notifyService.class.getName()));
//startService(new Intent(TweetCollectorService.class.getName()));
/* FROM GOOGLE */
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
this.bindService(new Intent(this, updateService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
/* END OF GOOGLE CODE */
#Override
public void onDestroy() {
web.close();
doUnbindService(); // Added to `onDestroy` - suggested by Google page
super.onDestroy();
Log.v("NOTICE", "PROGRAM TERMINATED");
}
updateService.java
public class updateService extends Service {
private String TAG = "SERVICE";
public static final int INTERVAL = 60000;
private Timer timer = new Timer();
private static updateService Pointer;
public updateService() {
Pointer = updateService.this;
}
public static class LocalBinder extends Binder {
static updateService getService() {
return Pointer;
}
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
public void onDestroy() {
if (timer != null) {
timer.cancel();
}
super.onDestroy();
}
#Override
public void onStart(Intent intent, int startId) {
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
doStuff();
}
}, 0, INTERVAL);
super.onStart(intent, startId);
}
public void doStuff() {
Log.v(TAG, "doStuff");
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
}
I don't see anywhere where your client binds to your service. Take a look at the local service example.. The reason for using the bind pattern even though you call startService is because the startService call is asynchronous. You need to make an additional call to bind the service to make sure you get a call back once the startup is complete.
I've found that a really great example of a service client and service are available in the NPR Open Source App for you to learn from!