I have to communicate between an Android Activity and another Java class. In a very, very stripped down version of what I want, I want the Java Class to run, and when it's done, set some information. To get a more specific idea of what I want to happen:
Activity {
CallJavaClass(); // Doesn't return anything, would be very bad form
GetInfoFromJavaClass() // Once the JavaClass has finished what needs to be done
}
What I could do:
Set a global variable in JavaClass that my Activity can access. I'd rather not do it this way, as I would have to implement some kind of OnChangedListener on that object in the JavaClass.
Use an Interface with Setters/ Getters. The problem with this is my JavaClass is a Singleton, and most of its methods are static, which is a no-go when working with an Interface.
Create a separate class that handles these variables. I would rather keep it simple and not have to use a bunch of different classes to do this, though.
So what do you think would be the best solution? If needed (and probably will be), I can provide more information about what exactly I want done. Thanks for your help in advance.
Sounds like something you could use AsyncTask for http://developer.android.com/reference/android/os/AsyncTask.html
then again, it depends on the specifics of what you're going for
AsyncTask should resolve your problem:
private class myTask extends AsyncTask<Void, Void, Boolean> {
protected void onPreExecute() {
super.onPreExecute();
// do something before starting the AsyncTask
}
#Override
protected Boolean doInBackground(Void... params) {
// do what you want to do
return false;
}
protected void onPostExecute(Boolean success)
{
super.onPostExecute(success);
// do something right after you finish the "doInBackground"
}
}
Related
I'm using an ASyncTask in my app to get some data (a short URL) via a REST API from a web service (Bitly).
When the ASyncTask completes I want to pass the result back to my MainActivity.
Getting the data back to the MainActivity is acheievd by using the onPostExecute method of the AsyncTask.
I've read and read and read about how to do this and there seem to be two general approaches.
Originally I was using a 'WeakReference' approach whereby at the start of the AsyncTask class you create a weak reference to your MainActivity as follows:
private class getShortURL extends AsyncTask<String, Void, String> {
private WeakReference<MainActivity> mainActivityWeakReference;
myASyncTask(MainActivity activity) {
mainActivityWeakReference = new WeakReference<>(activity);
}
{etc etc}
With this approach your AsyncTask class sits outside of your MainActivity class and so a lot of things need to be referenced via the weak reference.
This worked fine (except I suspected - possibly incorrectly - that this weak reference may have been the cause of occassional NPEs), but I then found another way of doing things.
This second approach involved moving the ASyncTask class inside of the MainActivity class.
This way I was able to access everything that was accessible in the MainActivity class directly, inlcuding UI elements and methods defined in the MainActivity. It also means that I can access resources such as strings etc and can generate toasts to advise the user what is happening.
In this case the whole of the WeakReference code above can be removed and the AsyncTask class can be made private.
I am also then able to do things like this directly in onPostExecute or to keep this in a method within the MainActivity that I can call directly from onPostExecute:
shorten_progress_bar.setIndeterminate(false);
shorten_progress_bar.setVisibility(View.INVISIBLE);
if (!shortURL.equals("")) {
// Set the link URL to the new short URL
short_link_url.setText(shortURL);
} else {
CommonFuncs.showMessage(getApplicationContext(), getString(R.string.unable_to_shorten_link));
short_link_url.setHint(R.string.unable_to_shorten_link);
}
(note that CommonFuncs.showMessage() is my own wrapper around the toast function to make it easier to call).
BUT, Android Studio then gives a warning that "the AsyncTask class should be static or leaks might occur".
If I make the method static I then get a warning that the method from the MainActivity that I want to call from onPostExecute cannot be called as it is non-static.
If I make that method from MainActivity a static method, then it cannot access string resources and any other methods that are non static - and down the rabbit hole I go!
The same is true, as you would expect, if I just move the code from the method in the MainActivity into the onPostExecute method.
So...
Is having an AsyncTask as a non-static method really a bad thing? (My
app seems to work fine with this warning in AS, but I obviously don't
want to be creating a memory leak in my app.
Is the WeakReference appraoch actually a more correct and safer approach?
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string
resources etc from the MainActivity?
I read somewhere about creating an interface but got a bit lost and couldn't find that again. Also would this not have the same kind of reliance on the MainActivity that a WeakReference does and is that a bad thing?
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
Is having an AsyncTask as a non-static method really a bad thing? (My app seems to work fine with this warning in AS, but I obviously don't want to be creating a memory leak in my app.
Yes, your Views and your Context will leak.
Enough rotations and your app will crash.
Is the WeakReference approach actually a more correct and safer approach?
It's lipstick on a dead pig, WeakReference in this scenario is more-so a hack than a solution, definitely not the correct solution.
What you're looking for is a form of event bus from something that outlives the Activity.
You can use either retained fragments* or Android Architecture Component ViewModel for that.
And you'll probably need to introduce Observer pattern (but not necessarily LiveData).
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string resources etc from the MainActivity?
Don't run that sort of thing in doInBackground().
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
The simplest way to do that would be to use this library (or write something that does the same thing yourself, up to you), put the EventEmitter into a ViewModel, then subscribe/unsubscribe to this EventEmitter inside your Activity.
public class MyViewModel: ViewModel() {
private final EventEmitter<String> testFullUrlReachableEmitter = new EventEmitter<>();
public final EventSource<String> getTestFullUrlReachable() {
return testFullUrlReachableEmitter;
}
public void checkReachable() {
new testFullURLreachable().execute()
}
private class testFullURLreachable extends AsyncTask<Void, Void, String> {
...
#Override
public void onPostExecute(String result) {
testFullUrlReachableEmitter.emit(result);
}
}
}
And in your Activity/Fragment
private MyViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// ...
}
private EventSource.NotificationToken subscription;
#Override
protected void onStart() {
super.onStart();
subscription = viewModel.getTestFullUrlReachable().startListening((result) -> {
// do `onPostExecute` things here
});
}
#Override
protected void onStop() {
super.onStop();
if(subscription != null) {
subscription.stopListening();
subscription = null;
}
}
This is likely an issue with me not properly understanding threading, however I often try and group code into a seperate class and become hindered by some it not being on the UI thread. I can't find anything that properly explains this however. As an example:
public class MyActivity extends Activity {
#Override
public void onCreate (Bundle bundle) {
HelperClass = new HelperClass(context);
}
}
public class HelperClass extends ContextWrapper {
someMethod () {
// Do stuff
}
}
When someMethod is called, if it's trying to do something thread dependant, I will get an error. I've had this with trying to keep some WebView logic in a seperate class as well as trying to access a realm database.
Using runnables seems very messy and I clearly don't understand exactly what's happening to beable to properly structure my code.
Can anyone explain why? and what the best soloution is?
I currently have developed an app with some GUI and network operations, but I need to make it more of a library without the GUI.
I know that there is a "is library" option under Properties/Android. But the question is: how to move the GUI elements out of the project to a different app, so that the library/project will have only java code; any suggestion ?
Thanks.
If you are making code into a library, you want to try and decouple it as much as you can from anything else. This makes it much more portable so that more people can use the library how they wish. Even though you are using this library just for yourself right now, later on you may wish to release it to others.
For example, maybe your code is like this currently:
public void computeSum(int a, int b) {
int sum = a + b;
mTextView.setText(String.valueOf(sum));
}
Right now this code is tightly coupled with mTextView. Instead, it makes sense to rewrite the code like this:
//In library
public int computeSum(int a, int b) {
return a+b;
}
and
//Somewhere in your app
mTextView.setText(String.valueOf(computeSum(3,4)));
This is a small change, but you can see that computeSum() is no longer coupled with mTextView. This makes it much easier to use throughout your project and even other projects. Now the computeSum() method is part of an API.
So for your network calls, try to decouple them from your GUI stuff either by using callbacks or return values.
In regards to your latest comment:
You could create a wrapper like so:
public class UIWrapper {
public Runnable runnable;
public SomeUiCallback callback;
}
And then use this in your AsyncTask:
public class YourTask extends AsyncTask<UIWrapper, Void, Void> {
SomeUiCallback mCallback;
protected void doInBackground(UIWrapper... wrapper) {
mCallback = UiWrapper.callback;
UIWrapper.runnable.run();
}
protected void onProgressUpdate() {
}
protected void onPostExecute() {
mCallback.runYourUiStuff();
}
}
I wrote that code quickly so it likely won't compile, but hopefully you get the idea. I think something like this would work, not sure if it's the most elegant solution. You can replace the Runnable with whatever you want to run in the thread.
So both UIWrapper and YourTask would reside in your library. You would create the UIWrapper and then use that in the YourTask.
I've created a few minor apps for Android while learning. Being a PHP developer, it's a challenge to get used to it.
I'm especially wondering how I could define a couple of "general" functions in a separate class. Eg I have a function that checks if network connection is available, and if not, shows a dialog saying that the user should enable it. Currently, that function exists in several of my activities. Of course that seems strange - I suppose it would be more logical to define it once and include it in the activites where needed.
I tried putting it in a new class, and included that class in the original activity. But that failed since eg getBaseContext() is not accepted anymore.
I'm wondering how to go ahead. What should I be Google-ing for ? What is this mechanism called?
You need to create class with static methods. Like this
public class HelperUtils {
public static void checkNetworkConnection(Context ctx) {...}
}
Then you can call it from any place like this:
HelperUtils.checkNetworkConnection(this.getContext());
Assuming current class has Context.
You should read books on general OOP concepts where different type of methods are explained.
You can for example create a class - let's call it NetworkUtils. In this class you can create static method boolean isNetworkConnectionAvailable() and return true if is available and false otherwise. In this class you can create another static method void showNoConnectionDialog(Activity activity) - and in this method you create dialog starting with
public static void showNoConnectionDialog(Activity activity) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//setting message, listener etc. and finally
builder.create().show();
}
In your activity, where you want to check and handle network connection you should call:
if (!NetworkUtils.isConnectionAvailable(getApplicationContext())) {
NetworkUtils.showNoConnectionDialog(YourActivityClassName.this)
}
I guess this should work.
I am pulling around 1500 data plots and adding them to overlays for an map view. I want to run this in another thread while the rest of my program finishes starting up. I would like a progress spinner to spin only on the map portion while its loading the data plot points.
I have searched and found what I need, but Im not sure how to implement it and where in my code to put it.
What would I put in the params
Does this go in another class or in my main oncreate method.
When would I call the methods?
private class UpdateFeedTask extends AsyncTask<MyActivity, Void, Void> {
private ProgressDialog mDialog;
protected void onPreExecute() {
Log.d(TAG, " pre execute async");
mDialog = ProgressDialog.show(MyActivity.this,"Please wait...", "Retrieving data ...", true);
}
protected void onProgressUpdate(Void... progress) {
Log.d(TAG, " progress async");
}
#Override
protected Void doInBackground(MyActivity... params) {
// TODO Auto-generated method stub
return null;
}
protected void onPostExecute(Void result) {
Log.d(TAG, " post execute async");
mDialog.dismiss();
}
}
From your question I actually am not a hundred percent sure what you currently understand about AsyncTasks so this may be a little some stuff you already know but bear with me.
"Does this go in another task or in my onCreate method?":
AsyncTask is a class which you are supposed to subclass to do what you need, it is not a piece of code which can inlined in your onCreate. You could make an anonymous AsyncTask class in your onCreate, but usually you want it as either a private internal class or its own class entirely.
As for when you call the methods; you don't they are lifecycle events.
onPreExecute() is called on the UI thread just before starting the background work and is a place to do things such as modify components to show progress, or throw up a dialog.
doInBackground(Params...) is the main method which runs in the background on another thread, do your work here. Do not try to modify UI here
onPostExecute(Result) is when your task has finished and is run on the UI thread again. This is where you should handle your UI update.
You only call execute(Params..), which will start the AsyncTask, passing the objects you put as the params into the doInBackground(Params...) method of the task. So the answer as to what to put as params is whatever you need to have access to in doInBackground(Params...).
That should be a decent overview for your needs but you should really check out the docs.
To start the AsyncTask, you simply go
(new UpdateFeedTask()).execute(activityInstance);
It can go where ever you want it, though where you put it might limit access to the variables and objects you want to interact with. E.g. private internal class will have access to them while an entirely new class might not have as easy of an access.
doInBackground(MyActivity... params)
is where the parameter you passed into the execute() function will go, and you can access it via params[0].
You should not call any methods in the AsyncTask class, besides execute().
1. What would I put in the params
It depends. The First parameter is what the task will take in.
The last generic is what it will return.
2.Does this go in another class or in my main oncreate method. You call the execute method when the you want the task to run. The implementation can be in the Activity class or in a different .java file.
3.When would I call the methods?
You only call the execute method. That will make the task run in the background. Then the task will call onPostExecute when it is done.
Here is an example
private class UpdateFeedTask extends AsyncTask<String, Void, DataReturnType> {
#Override
protected DataReturnType doInBackground(String... params) {
String url = params[0];
//Get data from URL
}
#Override
protected void onPostExecute(ReturnDataType result) {
//Do something with result
}
}
Then call the task using the execute("http://foo.com")
Also add android:configChanges=true to the Activity in the manifest. This will make sure that the activity is not killed when the task is still running in the background. Otherwise the task will finish and report back to a null Activity unless you tell the task to callback the new activity.