Android Local Service Architecture - android

I'm trying to implement a service to handle the communication with the server for the following code. I don't know much about the design architecture for these.
Here is my service class
public class BgService extends Service {
private static final String TAG = BgService.class.getSimpleName();
private Timer timer;
SendJsonRequest sjr;
private TimerTask updateTask = new TimerTask(){
#Override
public void run(){
try{
SendJsonRequest sjr = new SendJsonRequest();
sjr.carMake();
Log.i(TAG, "LOOK AT ME");
}
catch(Exception e){
Log.w(TAG,e);
}
}
};
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
super.onCreate();
Log.i(TAG, "Service creating");
timer = new Timer("Server listening timer");
timer.schedule(updateTask, 1000L, 60*1000L);
}
#Override
public void onDestroy(){
super.onDestroy();
Log.i(TAG, "Service Destroying");
timer.cancel();
timer = null;
}
}
Here is my SendJsonRequest class
public class SendJsonRequest{
private static final String TAG = "SendJsonRequest";
private static String URL = "xxxxxxxxx";
private static String infoRec;
public static void createJsonObj(String path, Map x){
infoRec = CreateJsonRequest.jsonRequest(URL+path, x );
System.out.println(infoRec);
}
public static void carMake(){
String path = "/CarMake";
Map<String, Object> z = new HashMap<String,Object>();
z.put("Name", "Ford");
z.put("Model", "Mustang");
createJsonObj(path, z);
}
}
Here is my CreateJsonObject class
public class CreateJsonRequest {
public static String jsonRequest(String URL, Map<String,Object> params){
try{
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(URL);
JSONObject holder = new JSONObject();
for (Map.Entry<String, Object> m : params.entrySet()){
try {
holder.put(m.getKey(), m.getValue());
}
catch (JSONException e) {
Log.e("Hmmmm", "JSONException : "+e);
}
}
StringEntity se;
se = new StringEntity(holder.toString());
httpPost.setEntity(se);
httpPost.setHeader("Accept", "text/json");
httpPost.setHeader("Content-type", "text/json");
HttpResponse response = (HttpResponse) httpClient.execute(httpPost);
HttpEntity entity = response.getEntity();
if(entity != null){
InputStream is = entity.getContent();
Header contentEncoding = response.getFirstHeader("Content-Encoding");
String result= convertToString(is);
is.close();
System.out.println(result);
return result;
}
}
catch(Exception e)
{
e.printStackTrace();
}
return null;
}
Sorry for the massive amount of code. How I implemented my service is obviously not correct, I just have no clue where to start to get a service handling the json requests to the server. Thanks in advance.
To be more clear, this did work on a button click, now I'm trying to get it to all run in the background with the service. So I guess my question is what goes where in the service?
My activity successfully starts the service, the service would work and print "look at me" to the logcat every minute. Then I added the try{ sjr.carMake()} and it catches an exception.

You can use a broadcast receiver. This is a way to have your code start at certain times indicated by Android OS - for example, you can have it start when Android finished booting up (this is where I run my services usually.
The best way is to use the AlarmManager class, and tell your service how often to run.
Tell us more about what you're trying to do, and what the problem is, and we can give you a more concise answer...
UPDATE:
Have you created an entry in the manifest.xml file for the service?
UPDATE
Here is how I'm doing it in my application. This is your "hook" to the OS. It's going to fire when it finishes booting (don't forget to make in entry in the manifest for this!)
public class TmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent bootintent) {
try{
Log.i("Taskmotion-ROBOT", "Robot Broadcast signal received on Boot. Trying to start Alarm scheduler");
Intent mServiceIntent = new Intent(context, ServiceAlarm.class);
context.startService(mServiceIntent);
}
catch(Exception e)
{
Log.i("Taskmotion", "Failed to start service...");
}
}
}
This Broadcast receiver calls a service that implements the AlarmManager class. The alarm manager sets up a schedule to run my service at a specified interval. Note that the alarms are deleted when the phone is shut down - but then recreated again when process is repeated as the phone boots back up and runs the BroadcastReceiver again.
public class ServiceAlarm extends Service {
private PendingIntent mAlarmSender;
#Override
public void onCreate() {
try{
Log.i("Taskmotion-ROBOT", "Setting Service Alarm Step 1");
mAlarmSender = PendingIntent.getService(this.getApplicationContext(),
0, new Intent(this.getApplicationContext(), BackgroundService.class), 0);
}
catch(Exception e)
{
Log.i("Taskmotion-ROBOT", "Problem at 1 :" + e.toString());
}
long firstTime = SystemClock.elapsedRealtime();
Log.i("Taskmotion-ROBOT", "Setting Service Alarm Step 2");
// Schedule the alarm!
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME,
firstTime, AlarmManager.INTERVAL_HOUR, mAlarmSender);
this.stopSelf();
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
I haven't refactored this code yet, it was my first go at it. I see now that I'm looking at it again that I could probably do the scheduling inside the BroadcastReceiver, but for the sake of getting you something that works, I'll continue.
As indicated by AlarmManager.INTERVAL_HOUR, my service will run once an hour. The service that I want to run is defined in the pendingIntent (BackgroundService.class). This is where you put your own service class.
I reworked your service class for you, and removed the timer (functionality replaced by the BroadcastReceiver & AlarmManager).
public class BgService extends Service {
private static final String TAG = BgService.class.getSimpleName();
SendJsonRequest sjr;
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
super.onCreate();
Log.i(TAG, "Service creating");
//DO YOUR WORK WITH YOUR JSON CLASS HERE
//**************************************
//Make sure to call stopSelf() or your service will run in the background, chewing up
//battery life like rocky mountain oysters!
this.stopSelf();
}
#Override
public void onDestroy(){
super.onDestroy();
}
}

Related

Android create thread for pusher to run at background

In my app, I am getting my messages instantly from my server via pusher. I have created a service designated to handle connections and firing broadcast messages to other activities in my app.
The problem that I face now is to have this service run in a new thread to have it still run even when my app goes to the background. I've found from this that I should create and connect it to the "service thread", but I cannot find examples for it with pusher.
If anyone can, could you please provide an example to do so? If not, insights to writing code with these "service threads" would be helpful as well. Thanks in advance for the help :D
PusherService.java
public class PusherService extends Service {
private static final String TAG = "PusherService";
private Pusher pusher = new Pusher("myKey");
private Channel channel = pusher.subscribe("cafe_channel");
private JSONObject pusherJSONObj;
private Order order;
public PusherService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//this service will run until we stop it
setupPusher();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
private void setupPusher() {
Log.d(TAG, System.currentTimeMillis()+"");
channel.bind("customer_order", new SubscriptionEventListener() {
#Override
public void onEvent(String channelName, String eventName, final String data) {
Intent broadcastIntent = new Intent();
try {
pusherJSONObj = new JSONObject(data);
order = new Order(pusherJSONObj);
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "success");
broadcastIntent.putExtra("order", order);
} catch (JSONException e) {
e.printStackTrace();
Log.d("Pusher", "conversion failed");
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "JSON conversion error");
}
sendBroadcast(broadcastIntent);
}
});
pusher.connect();
}
}
OrdersActivity.java
private BroadcastReceiver pusherReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("customer_order")) {
adapter.newOrder((Order) intent.getParcelableExtra("order"));
}
}
};
It turns out that multithreading on one process does not solve my problem.
So instead, I split the service into a new process, which will keep the service running independent of the status of the main thread & process. Tested and found that service does not stall when my activities go background.

How can I periodically run a service/thread in background even when the screen is locked?

I'm working on an app that can check web data every half an hour and I need to ensure it keeps running as long as the power is on.
For now, the structure of my app is like this:
main_activity:
AlarmManager in onCreate()
alarm_receiver:
start_service
acquire partial_wl for the service
service:
get network data using StrictMode
pop activity_2 if the data is expected
activity_2:
vibration
button to exit(activity_2.this.finish())
But in testing I find the service will stop(be killed) after the first 30 mins. In addition, if I start a thread for networking in service instead of using StrictMode, it will be killed in 5mins after the screen is locked.
Hope someone could give a suggestion for this. It's truly disturbing.
Many thanks.
common service lives no metter what is happen with activity. if you want it start periodically check out mine service:
https://bitbucket.org/kvrus/ocs-android/raw/036de7f0d3579b2a193bcb82309f7f82819508e6/app/src/main/java/koss/ru/oneclickrate/network/EcbEuropeService.java
/**
* Loads exchange rates form network periodically
* Returns results in broadcast message.
* Created by koss on 19.02.16.
* */
public class EcbEuropeService extends Service {
public static final String ECB_URL = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
public static final int UPDATE_PERIOD = 30000;
public static final int UPDATE_TICK = 1000;
public static final String NOTIFICATION = "koss.ru.oneclickrate.receiver";
public static final String EXTRA_CURRENCIES_MAP = "extra_currencies_map";
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
getUrlData();
return Service.START_NOT_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
public Cubes getUrlData() {
(new AsyncTask<Object, Object, Cubes>() {
Map<CurrencyType, BigDecimal> result = new EnumMap<CurrencyType, BigDecimal>(CurrencyType.class);
#Override
protected Cubes doInBackground(Object... params) {
Cubes cubes = new Cubes();
InputStream is = null;
HttpURLConnection urlConnection = null;
try {
URL url = new URL(ECB_URL);
urlConnection = (HttpURLConnection) url.openConnection();
is = urlConnection.getInputStream();
cubes = EcbEuropeResponseParser.parse(is);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(urlConnection!=null) IOUtils.close(urlConnection);
if(is!=null) IOUtils.closeQuietly(is);
return cubes;
}
}
#Override
protected void onPostExecute(Cubes map) {
super.onPostExecute(map);
sendBroadcastMessage(map);
startTimer();
}
}).execute();
return null;
}
/**
* Restarts timer
* */
public void startTimer() {
cdt.cancel();
cdt.start();
}
CountDownTimer cdt = new CountDownTimer(UPDATE_PERIOD, UPDATE_TICK) {
#Override
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
getUrlData();
}
};
private void sendBroadcastMessage(Cubes currenciesMap) {
Intent intent = new Intent(NOTIFICATION);
intent.putExtra(EXTRA_CURRENCIES_MAP, currenciesMap);
sendBroadcast(intent);
}
I have changed a few things and it works well now.
1.As my phone is 4.4.2(api=19), alarmmanager.setrepeating is inexact. So I turn to use .setExact (new method of .set()) and reschedule the alarm at the end of AsyncTask(network) in Service.
2.Make wakelock instance global, acquiring it in AlarmReceiver and releasing at the end of the AsyncTask. I used to put .release() in onDestroy() which releases the lock before the task is done.
3.There is a setting about protected-background applications in my phone and I didn't turn it on. That can allow system kill the application and disable the alarm manager.

android.content.Context.getPackageName()' on a null object reference

I'm trying to call a service class from another service class but I get this error:
android.content.Context.getPackageName() on a null object reference
Do you know how I call service from another service?
When I setup my app to phone first, a broadcastreceiver is starting Alarm class and in Alarm class, I want to start another service in ReadGmail() method. But I get that null object reference error.
Here is my code:
public class Alarm extends Service {
private String userName;
private String password;
private String receivingHost;
Context context;
public int onStartCommand(Intent intent, int flags, int startId) {
final Handler handler = new Handler();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
String senderPassword=new String("password");
String senderUserName=new String("username#gmail.com");
Alarm newGmailClient=new Alarm();
newGmailClient.setAccountDetails(senderUserName, senderPassword);
newGmailClient.readGmail();
}
});
}
};
Timer timer = new Timer();
timer.schedule(doAsynchronousTask, 10, 120000);
return super.onStartCommand(intent, flags, startId);
};
public void setAccountDetails(String userName,String password){
this.userName=userName;//sender's email can also use as User Name
this.password=password;
}
public void readGmail(){
this.receivingHost="imap.gmail.com";//for imap protocol
Properties props2=System.getProperties();
props2.setProperty("mail.store.protocol", "imaps");
Session session2=Session.getInstance(props2, null);
try {
Store store=session2.getStore("imaps");
store.connect(this.receivingHost,this.userName, this.password);
Folder folder=store.getFolder("INBOX");//get inbox
folder.open(Folder.READ_ONLY);//open folder only to read
Message message[]=folder.getMessages();
String key= "Hey";
String subject;
for(int i=0;i<message.length;i++){
System.out.println(message[i].getSubject());
subject=message[i].getSubject();
if(subject.equals(key)){
System.out.println("inside");
Intent mTutorial = new Intent(Alarm.this, LaunchActivity.class);
this.startService(mTutorial);
//I want to call service class in here. LaunchActivity is my service class.
}
//Log.d(message[i].getSubject(),message[i].getSubject());
}
folder.close(true);
store.close();
} catch (Exception e) {
System.out.println(e.toString());
}
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
Log.d("", "FirstService destroyed");
}
}
This is your problem:
Alarm newGmailClient=new Alarm();
Your Alarm class extends Service. It is an Android Service. You cannot create an instance of an Android component with new. Only Android can create Android components. If you want to start a Service, you call startService.
Why do you want to start another Service? Please explain.
Use an interface your Service will use to communicate events:
public interface ServiceCallbacks {
void doSomething();
}

Threads created on my service get killed when I close the activity

I created an activity that calls a service and the service creates a Thread that send and receive some data to/from the server, I can open other apps and the Service and the Thread run ok, but when I close the activity, the Service keeps running but the thread stops working. Why??? How can I keep the Thread running!!.
Code
Activity
package com.connectus.app;
public class ConnectUsActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_connect_us);
Intent startServiceIntent = new Intent(getApplicationContext(), ConnectUsService.class);
startService(startServiceIntent);
}
Service
package com.connectus.app;
public class ConnectUsService extends Service {
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
Thread t=new Thread(new Runnable() {
private DataInputStream in;
private BufferedReader br;
private DataOutputStream out;
#Override
public void run() {
Socket server=null;
try{
server=new Socket("10.10.40.58",4444);
in = new DataInputStream(server.getInputStream());
br=new BufferedReader(new InputStreamReader(in));
out = new DataOutputStream(server.getOutputStream());
while(true){
out.writeUTF("aaaaa");
String leido=in.readUTF();
out.writeUTF("asdf");
Thread.sleep(60000);
}
}catch(IOException ioe){
ioe.printStackTrace();
}catch (Exception e) {
e.printStackTrace();
}
}
});
t.start();
return START_STICKY;
}
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
}
This is just a part of my code, I hope it helps.
Thanks everyone, finally i find a solution. It was necesary to use the setForeground() method, I just added this code to my service Class:
Notification note=new Notification();
startForeground(1337, note);
According to my research, this code is used to prevent that the service get killed by itself.
best regards!

Google Play Service Activity recognition start and stops itself constantly

I need to call the Google activity recognition service through a service (not activity) and run it in the background, of course when the user starts the app, which has an activity (But the service does not called directly from activity).
Therefore I have created a service class (ActivitySensor) and another class (ActivityRecognitionScan).
When I install the app on my Galaxy Nexus S device, the service starts calling onCreate and onDestroy automatically. Even without doing anything in the GUI
It is very strange behaviour. Does anybody has the same experience or solution for it?
I mean I get something as follows in the debug console:
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
Activity-Logging --- onCreate
Activity-Logging --- onDestroy
...
Here are my two classes:
public class ActivitySensor extends IntentService {
private ActivityRecognitionScan myascan;
private Intent inIntent;
private static long ACTIVITY_LOG_INTERVAL = 30000L;
private static JsonEncodeDecode jsonencoder = new JsonEncodeDecode();
public ActivitySensor() {
super("ActivitySensor");
}
#Override
public void onCreate(){
super.onCreate();
Log.d("Activity-Logging", "--- onCreate");
try {
myascan = new ActivityRecognitionScan(getApplicationContext());
myascan.startActivityRecognitionScan();
} catch (Exception e) {
Log.e("[Activity-Logging]","----------Error:"+e.getLocalizedMessage());
e.printStackTrace();
}
}
#Override
public void readSensor() {
// Log.e("Activity-Logging", "ActivityRecognitionResult.hasResult: "+String.valueOf(ActivityRecognitionResult.hasResult(inIntent)));
if (ActivityRecognitionResult.hasResult(inIntent)) {
ActivityRecognitionResult result = ActivityRecognitionResult.extractResult(inIntent);
DetectedActivity activity = result.getMostProbableActivity();
final int type = activity.getType();
String strType = new String();
switch(type){
case DetectedActivity.IN_VEHICLE:
strType = "invehicle";
break;
case DetectedActivity.ON_BICYCLE:
strType ="onbicycle";
break;
case DetectedActivity.ON_FOOT:
strType = "onfoot";
break;
case DetectedActivity.STILL:
strType = "still";
break;
case DetectedActivity.TILTING:
strType ="tilting";
break;
case DetectedActivity.UNKNOWN:
strType ="unknown";
break;
}
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
Editor edt = prefs.edit();
String previousActv = prefs.getString("PREVIOUS_ACTIVIY","");
long previousDate = prefs.getLong("PREVIOUS_DATE", 0);
if (previousActv.length()==0){ // nothing was in the string and it is the first time just initialize
previousActv = strType;
previousDate = new Date().getTime();
// Log.e("-----FIRST TIME: type:", previousActv+" date:"+String.valueOf(previousDate));
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", previousDate);
edt.commit();
}else {
if (!strType.equalsIgnoreCase(previousActv)){
Date readablePrevDate = new Date(previousDate);
Date nowDate = new Date();
String jsonstr = jsonencoder.EncodeActivity("Activity", readablePrevDate, nowDate, strType, activity.getConfidence());
// Log.e("[Activity-Logging] ----->",jsonstr);
edt.putString("PREVIOUS_ACTIVIY", strType);
edt.putLong("PREVIOUS_DATE", nowDate.getTime());
edt.commit();
DataAcquisitor.dataBuff.add(jsonstr);
}
}
}
}
#Override
protected void onHandleIntent(Intent intent) {
Log.d("Activity-Logging", "--- onHandleIntent"+ "---"+intent.getAction());
intent.putExtra("LOG_INTERVAL",ACTIVITY_LOG_INTERVAL );
intent.putExtra("STOP",false);
inIntent = intent;
readSensor();
}
#Override
public void onDestroy(){
Log.d("Activity-Logging", "--- onDestroy");
myascan.stopActivityRecognitionScan();
myascan=null;
//super.onDestroy();
}
}
This is the class that calls the Google Activity Recognition Service:
ActivityRecognitionScan implements GooglePlayServicesClient.ConnectionCallbacks, GooglePlayServicesClient.OnConnectionFailedListener {
private Context ctx;
private static final String TAG = "ActivityRecognition";
private static ActivityRecognitionClient actrecClient;
private static PendingIntent callbackIntent;
private long ACTIVITY_LOG_INTERVAL=30000;
public ActivityRecognitionScan(Context context) {
ctx=context;
}
public void startActivityRecognitionScan(){
int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(ctx);
if(resp == ConnectionResult.SUCCESS){
actrecClient = new ActivityRecognitionClient(ctx, this, this);
if (!actrecClient.isConnected()){
actrecClient.connect();
} else{
Log.e("ActivityRecognitionScan"," ---Activity recognition client is already connected");
}
}else{
Log.e("[Activity-Logging]", "Google Play Service hasn't installed");
}
}
public void stopActivityRecognitionScan(){
try{
if (actrecClient.isConnected() || actrecClient.isConnecting() ){
actrecClient.removeActivityUpdates(callbackIntent);
actrecClient.disconnect();
}
} catch (Exception e){
e.printStackTrace();
}
}
#Override
public void onConnectionFailed(ConnectionResult result) {
Log.e("[ActivityRecognitionScan]", "Connection Failed");
}
#Override
public void onConnected(Bundle connectionHint) {
try{
Intent intent = new Intent(ctx, ActivitySensor.class);
Bundle bundle = intent.getExtras();
callbackIntent = PendingIntent.getService(ctx, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
long interval = 5000;
if ( null!= bundle && bundle.containsKey("LOG_INTERVAL") ){
interval = bundle.getLong("LOG_INTERVAL");
}
actrecClient.requestActivityUpdates(interval, callbackIntent);
actrecClient.disconnect();
}catch(Exception ex){
Log.e("[Activity-Logging]","Error in requesting Activity update "+ex.getMessage());
ex.printStackTrace();
}
}
#Override
public void onDisconnected() {
callbackIntent.cancel();
actrecClient = null;
Log.e("[ActivityRecognitionScan]","---onDisconnected");
}
}
IntentService automatically stops itself on completion of onHandleIntent as per the source code (see ServiceHandler.handleMessage()) as per the description of an IntentService:
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.
Use a Service if you want it to run continuously in the background.
You have 2 issues with your code that is causing the problem you are experiencing.
When activity is detected, the pending intent that is called calls (and creates, since it is an IntentService) ActivitySensor. The onCreate will connect another ActivityRecognitionClient, which is unnecessary. This causes another activity to be detected which causes your logging loop.
You should separate the creation of the ActivityRecognitionClient from the handling of the detected activity. You don't need to keep recreating it as subsequent detections will use the same PendingIntent. This will prevent the logging loop.

Categories

Resources