Converting class that creates threads into Service - android

I have this class that creates threads, but I want to convert that class into Service hoping it wont be destroyed on orientation change.
Here is the class:
public class Getter {
private final String ip;
private final int amount, poolSize;
private Vector<Integer> results = new Vector<Integer>();
private final ExecutorService es;
private Collection<Future<?>> futures = new LinkedList<Future<?>>();
public Getter(String ip, int amount, int poolSize) {
this.ip = ip;
this.amount = amount;
this.poolSize = poolSize;
es = Executors.newFixedThreadPool(this.poolSize);
}
public boolean working() {
boolean work = false;
for (Future<?> future : futures) {
if (!future.isDone()) {
work = true;
}
}
return work;
}
public Vector<Integer> getResults() {
Collections.sort(results);
return results;
}
public int threads(){
return poolSize;
}
public void start() {
for (int i = 0; i <= amount; i++) {
futures.add(es.submit(new Get(ip)));
}
es.shutdown();
}
public void stop(){
for (Future<?> future : futures) {
future.cancel(true);
}
}
private class Get implements Runnable {
private String ip;
private Get(String ip) {
this.ip = ip;
}
public void run() {
try {
// network stuff
// adds result to results Vector.
} catch (Exception ex) {
}
}
}
}
so is this class possible to convert into Service so it would run on background no matter what, once its started?

you can add this to a manifest file that stops your app getting destroyed on orientation change:
android:configChanges="orientation"
but if you want to make a service then just copy this example:
http://developer.android.com/reference/android/app/Service.html
you could add the class to the service and then just add an accessor to your service connection.
You can also add something to the manifest file to let your service start when the device is turned on
see here
Trying to start a service on boot on Android

If you want a Service which runs "no matter what", you might want to set it as foreground.

Yes. I think this would make a good IntentService. Your next-best choice is probably AsyncTask.
More:
Generally, I would say, if the behavior of the background task is closely tied to whatever starts it (e.g., an Activity, Fragment, Service, whatever), then make it an AsyncTask. If it is self-contained and meant to serve several different components in a modular fashion, make it a Service. In either case, an AsyncTask or IntentService is very easy to make.

Related

Is there any app events on Xamarin.Android?

I have a Xamarin.Android app with several activities and fragments. The app uses SignalR, connected with a .net core backend web app. There are several activities that may require visual modifications depending on the events called by the server. Is there any kind of in-app events that activities may subscribe to on creation that handles those required visual changes?
For example:
I am on an activity that shows 5 images related to a publication, and then the server sends a notification that the publication has been edited so the images have changed. In this case i would want that the SignalR client triggered some in-app event that updates the changed images on created activities of this kind.
I have came up with some kind of solution. I created a class called EventSubscriber that acts exactly how i wanted to.
public class EventSubscriber<T> : IEventSubscriber
{
#region Private members
private List<Tuple<T, Func<T, bool>>> Subscribtions { get; set; }
private ConcurrentDictionary<int, Func<T, bool>> WaitingObjectSubscribtions { get; set; }
#endregion
public EventSubscriber()
{
Subscribtions = new List<Tuple<T, Func<T, bool>>>();
WaitingObjectSubscribtions = new ConcurrentDictionary<int, Func<T, bool>>();
}
#region Subscribe
public void Subscribe(T adapter, Func<T, bool> function)
{
lock (Subscribtions)
{
Subscribtions.Add(new Tuple<T, Func<T, bool>>(adapter, function));
}
}
public int Subscribe(Func<T, bool> function)
{
lock (WaitingObjectSubscribtions)
{
int id = WaitingObjectSubscribtions.Count;
WaitingObjectSubscribtions.TryAdd(id, function);
return id;
}
}
#endregion
#region Unsubscribe
public void UnSubscribe(Tuple<T, Func<T, bool>> item)
{
lock (Subscribtions)
{
Subscribtions.Remove(item);
}
}
public void UnSubscribe(int id)
{
lock (WaitingObjectSubscribtions)
{
Func<T, bool> func;
WaitingObjectSubscribtions.TryRemove(id, out func);
}
}
public void UnSubscribeAll()
{
lock (Subscribtions)
{
Subscribtions.Clear();
}
WaitingObjectSubscribtions.Clear();
}
#endregion
#region Call Subscribed
public void CallSubscribed()
{
lock (Subscribtions)
{
foreach (var item in Subscribtions)
{
(var adapter, var function) = item;
if (!function(adapter))
{
Log.Debug("[EventSubscriber]", "Failed to notify adapter, will be automatically unsubscribed from this event");
UnSubscribe(item);
}
}
}
}
public void CallSubscribedWith(T adapter)
{
foreach (var id in WaitingObjectSubscribtions.Keys)
{
var function = WaitingObjectSubscribtions[id];
try
{
if (!function(adapter))
{
Log.Debug("[EventSubscriber]", "Failed to execute function");
UnSubscribe(id);
}
}
catch (Exception)
{
Log.Debug("[EventSubscriber]", "Failed to execute function");
UnSubscribe(id);
}
}
}
#endregion
}
To manage several EventSubscribed used in-app i created a static class Accessible to every Activity or Fragment which contains all the needed events:
public static class EventBoard
{
#region Products and Favorites
public static EventSubscriber<SwipeRefreshLayout> FinishedLoadingProducts = new EventSubscriber<SwipeRefreshLayout>();
public static EventSubscriber<List<Product>> SuccessfullyLoadedProducts = new EventSubscriber<List<Product>>();
public static EventSubscriber<RecyclerView.Adapter> UnsuccessfullyLoadedProducts = new EventSubscriber<RecyclerView.Adapter>();
//Search
public static EventSubscriber<SwipeRefreshLayout> FinishedLoadingProductsForSearch = new EventSubscriber<SwipeRefreshLayout>();
public static EventSubscriber<RecyclerView.Adapter> SuccessfullyLoadedProductsForSearch = new EventSubscriber<RecyclerView.Adapter>();
public static EventSubscriber<RecyclerView.Adapter> UnsuccessfullyLoadedProductsForSearch = new EventSubscriber<RecyclerView.Adapter>();
public static EventSubscriber<RecyclerView.Adapter> SearchNotFound = new EventSubscriber<RecyclerView.Adapter>();
//favorites
public static EventSubscriber<LocalProduct> ConfirmNewFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<LocalProduct> ConfirmRemoveFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<LocalProduct> NewFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<LocalProduct> RemoveFavoriteEvent = new EventSubscriber<LocalProduct>();
public static EventSubscriber<List<LocalProduct>> SetFavoritesEvent = new EventSubscriber<List<LocalProduct>>();
#endregion
}
So basically if a new activity is created it can subscribe to an EventSubscriber of its preference. Moreover, it can provide a function that receives any kind of object so it can be as flexible as it can.
The only thing that raise my concern, be sure you unsubscribe your events when the activity or the fragment View is destroyed because they can be a good source of memory leaks.
To communicate with the real-time server, you would have to use methods from the server's package. From researching, you can use the HubConnection.On method. Signalr registers the send and receive methods when you built the hub:
public class MyHub : Hub{
public async Task Send(string user, string message){
// Receive is the name of the listener method
await Clients.All.SendAsync("Receive", user, message);
}
}
Then, in your Android activities, you can register to the HubConnection (assuming you've sent the message about the edited image) of your listener method using the On method:
myHubConnection.On<string, string>("Receive", (user, message) =>
{
/* change UI here */
});

How to hold an Object as long as a service live?

I have an Object that i use in all my activities which holds a lot of complex data in it, it would be quite an hassle to use Android framework of saving the object state and passing it around from activity to activity, so i thought it would be possible to make a Singleton that manages this object and makes it live as long as the application lives.
Tried to use regular Java Singleton scheme, with normal class and normal static instance, but the instance becomes null after a while (which is very confusing, why would an Object that is still referenced be turned to null and garbage collected?). so i decided to flow with Android designers and created a Service to manage this Object, the Service looks something like that :
public class DataService extends Service {
private Data data;
private static DataService instance;
#Override
public void onCreate() {
instance = this;
data= new Data(...);
instance.data.addProgressListener(listener);
(new Thread() {
#Override
public void run() {
data.doInitProgress();
listener = null;
};
}).start();
}
public static void listenToInitDataProcess(final ProgressBar progressBar,final Runnable onDone) {
listener = new ProgressListener() {
private int progress;
private int max;
#Override
public void onUpdateProgress(final long i) {
progressBar.setProgress(progress+=i);
}
#Override
public void onProgressEndComputed(final long n) {
progressBar.setMax(max=(int) n);
}
#Override
public void onDone() {
progressBar.setProgress(max);
onDone.run();
}
};
if (instance!=null) instance.data.addProgressListener(listener);
}
public static Data getData() {
return instance.data;
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
now the problem with that is that after a while that the app is on i get NPE caused by instance is null... notice that i was listenning to the data object creation and i was trying to get it only after it was once inited, so no way that instance was suppose to be null...
how to do this right then?
If you want an Object that lives as long as your application lives (i.e. as long as its process is not killed by OS) you can extend android.app.Application, put your 'global' data there and use that subclass as your app context (needs to be declared in manifest)
However many argue that singletons provide essentially the same result as custom context e.g.
Singletons vs. Application Context in Android?
but the instance becomes null after a while
First, understand Effective Java's singleton recommendations, and how something wouldn't become null:
// Singleton with public final field
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { ... }
public void leaveTheBuilding() { ... }
}
But a Service is different, since there is no public constructor there. So, first check if the Service is running (as referenced in this post: Check if Service is running from a Broadcast Receiver):
private boolean isMyServiceRunning(Context context) {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (DataService.class.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
Then define some methods in your Service to return your needed state variables.

Is it right if I read view on other thread,Android UI

Android can't update view direct on non-ui thread,but if I just read/get some information for ui?
For example I have a method updateModel() like
void updateModel() {
dailyReport.log.setValue(editLog.getText().toString());
dailyReport.plan.setValue(editPlan.getText().toString());
dailyReport.question.setValue(editQuestion.getText().toString());
}
Is it a problem if I run this method on non-ui thread.
Example below helped me solve this problem. Hope this will help you too
runOnUiThread(new Runnable() {
#Override
public void run() {
//do your job
}
});
Is it a problem if I run this method on non-ui thread?
With the assumption that dailyPlan is a model class and its methods do not modify the UI, then no, it is not a problem, Android will not complain and you will not receive any runtime errors. However, I would not follow this approach as in general it's a bad practice to access directly one threads data from another thread - you never know who is modifying what, read/write issues can occur and so on. These are usually solved by synchronizing the data, but if you put synchronized code in UI thread you made things even worse!
For your kind of problem, why don't you pass the data from UI controls to the thread that uses above logic? When you create it, pass the 3 strings:
editLog.getText().toString()
editPlan.getText().toString()
editQuestion.getText().toString()
Example:
private EditText editLog;
private EditText editPlan;
private EditText editQuestions;
private void activityMethodThatStartsThread() {
String log = editLog.getText().toString();
String plan = editPlan.getText().toString();
String questions = editQuestions.getText().toString();
DailyReportModel model = new DailyReportModel(log, plan, questions);
model.start();
}
public class DailyReportModel extends Thread {
private String log;
private String plan;
private String questions;
public DailyReportModel(String log, String plan, String questions) {
super();
this.log = log;
this.plan = plan;
this.questions = questions;
}
void updateModel() {
dailyReport.log.setValue(log);
dailyReport.plan.setValue(plan);
dailyReport.question.setValue(questions);
}
}
Yes you can Update the UI from a Non UI thread. There are two ways of doing this.
1) Do this with activity object (easy option get and Update)
2) Do this using Message Handler (a bit difficult and Update only)
Going for the 1st one all you need to do is Pass the Activity into the constructor. Here is Thread snippet
public class MyThread extends Thread {
private Activity _activity;
public MyThread(Activity activity) {
this._activity = activity;
}
#Override
public void run() {
super.run();
//do all what you want. and at the end to Update the Views Do this
if(!this._activity.isFinishing())
{ // executed if the activity is not finishing
this._activity.runOnUiThread(new Runnable() {
#Override
public void run() {
//set the public variables UI Variables like this
dailyReport.log.setValue(this._activity.editLog.getText().toString());
dailyReport.plan.setValue(this._activity.editPlan.getText().toString());
dailyReport.question.setValue(this._activity.editQuestion.getText().toString());
});
}
}
}
Now in the Activity
MyThread thread = new MyThread(this);
thread.start;

Using Resources in Exceptions

I have an app that uses custom Exceptions, such as this:
public class SomeException extends Exception{
private int iCode;
private String iMessage;
public SomeException(){
iCode = 201;
iMessage = **//Get the localized string R.string.error_201??**
}
#Override
public String getMessage() {
return iMessage;
}
#Override
public int getCode() {
return iCode;
}
}
Obviously, I want lo localize the error message. I have possible solutions but non of them satisfy me.
1) Pass "Context" to the constructor, and do ctx.getString(R.string.error_201)
--> Fail, as this Exceptions are sometimes thrown from MODEL classes, so they don't have a Context
2) Pass "Context" when retriveing the message in getMessage() function,
--> Fail, It's necesary to override the super method, to work as all other Exceptions.
Solution I have now: All activities in my app have this onCreate:
public void onCreate(...){
Utils.RESOURCES = getResources();
...
}
Very dirty code... I don't like the solution. My question is then,: is there a way to access the resources without the Context? And most important, How would an application such as mine solve this problem?
What about
public class MyException extends Exception {
private int iCode;
public MyException(int code) {
this.iCode = code;
}
#Override
public String getMessage() {
return "MyException code " + String.valueOf(iCode);
}
public String getLocalizedMessage(Context ctx) {
String message;
if (iCode == 201)
message = ctx.getString(R.string.error_201);
else if (iCode == 202)
message = ctx.getString(R.string.error_202);
// ...
}
}
Even if there was way to access context in different way, you should not do it. If you need to emit exceptions where you cannot pass Context, you should be able to access context before you display such error. I cannot see reason why you should create localized error messages from constructor. You can log to logcat not localized versions if you need. And where you want to display something in UI, you should have context at hand.
You can access only system wide resources without Context.
You need a Context, so I would suggest You to get it as soon as possible, and make it available through a static method or variable. You do the same thing in every Activity, but there is a cleaner method. You should make a custom Application, and override its onCreate() to make the resources public:
public class App extends Application {
private static Resources myResources;
#Override
public void onCreate() {
myResources = getBaseContext().getResources();
super.onCreate();
}
public static Resources getMyResources(){
return myResources;
}
}
The other thing you have to do is to set the Application in your manifest:
<application
android:name="{your_package}.App"
...
Now you can access the resources in all of your Activity without any preparation. Your custom Exception class could also use the externalized resources.

Android: Detect when an application as a whole (not individual Activities) is paused/exited?

One of the Activities in my app starts/binds to a service (also part of my app). I would like that service to continue running as long as the app as a whole is still in the foreground, regardless of which Activity is active. But I need to make sure that the service is stopped when the app as a whole is paused (home button/back button).
How can I do that on an application level rather than an Activity level?
The easiest way is to have a singleton which keeps a track of the state of each activity, e.g showing just one activity as an example:
public class ActivityStates {
private static ActivityStates ref = null;
private static int firstAct = ACTIVITY_GONE;
public static synchronized ActivityStates getInstance() {
if (ref == null) {
ref = new ActivityStates();
}
return ref;
}
public int getFirstAct() {
return firstAct;
}
public void setFirstAct(int arg) {
this.firstAct = arg;
}
}
..
and define some static constants that you can import
public static final int ACTIVITY_GONE = 0;
public static final int ACTIVITY_BACKGROUND = 1;
public static final int ACTIVITY_FOREGROUND = 2;
then in each activity have a method
private void setActivityState(int state){
ActivityStates as = ActivityStates.getInstance();
as.setFirstAct(state);
}
Then in your onResume(), onPause, onDestroy() you can set the activitiy's state when you enter these methods, e.g in onResume have
setActivityState(ACTIVITY_FOREGROUND)
in onDestroy() have
setActivityState(ACTIVITY_GONE)
Then in you service, or wherever you want , you can use the get methods to find out the state of each activity and decide what to do.

Categories

Resources