I currently have a helper class to perform rudimentary AsyncTasks such as the following. I call the function from an activity as and when needed. The code seems to work fine and I haven't encountered any problems. But, I was wondering if this is a good coding practice or if there were any ramifications that I am unaware of. Any feedback would be gladly accepted and appreciated.
public class OtherUtils {
public static void updatePromptsOption(final boolean showPrompt, final Context context) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
Editor preferenceEditor = PreferenceManager.getDefaultSharedPreferences(context).edit();
preferenceEditor.putBoolean(Constants.SHOW_PROMPT, showPrompt).commit();
return null;
}
}.execute();
}
}
I don't see anything wrong with doing things that way. Being a static function, you're not hiding implicit this references that could bite you later. Seems like a reasonable convenience function to me.
Related
I struggled with some issues about design complex tasks with fragments
use. Fragments and asynchronous approach are quite new for me, so I think it will be better to describe my app.
Application
App works with GitHub API and has two screens: list of repositories and details about selected one. For retrieving data from json I use Retrofit and store it in SQLite. Little remark As I understood Retrofit can be used asynchronously but in case of additional work with DB it is better to use asynchronous approach for operations under DB also. In my case I'm checking internet connection: in case of absence I load data from DB. Otherwise I upgrade DB and then use it. Now I want to add fragments for different screen density support( usual master - detail workflow).
And my questions are
Where is the better place to run async tasks? Is it a right solution to make it in activity and then pass result to fragments?
What is the better solution for asynchronous processing? As I understood from my search about that, AsyncTask is deprecated but the easiest solution.
AsyncTask is a pain in the rear. Many beginners still seem to use it, but imho it's not worth learning it. Sooner or later you'll be bugged out by it because AsyncTask is error prone, has lots of caveats and tons of boilerplate code.
Retrofit does make it's calls asynchronously automatically, so you've got that covered already. Retrofit also plays very nice with RxJava which is I guess considered the way of doing asynchronous things on Android these days.
RxJava has a steeper learning curve initially than other patterns, but it's worth learning. If you got your database stuff working already, it won't be much work making ti asynchronous with Rx.
As for
Is it a right solution to make it in activity and then pass result to
fragments?
If you don't follow an MVP design approach, which is okay, in my opinion it's absolutely okay to do 'business logic' stuff in the Fragment and not let the Fragment call the Activity, then let the Activity get back to the Fragment. Whichever is easier for you and suits your app.
You can place your background thread anywhere, but make sure it is cancelled if the class is garbage collected, and don't keep references (Context, callbacks) in your thread.
public class MyActivity extends Activity {
private static interface OnDownloadThreadCompleteListener {
public void onDone(String data);
}
private static class DownloaderThread extends AsyncTask<Void, Void, String> {
private OnDownloadThreadCompleteListener mListener;
public DownloaderThread(OnDownloadThreadCompleteListener listener) {
mListener = listener;
}
#Override
protected String doInbackground(Void... args) {
// Do your network request here
return result;
}
#Override
public void onPostExecute(String data) {
if (mListener != null && !isCancelled()) {
mListener.onDone(data);
}
mListener = null;
}
#Override
public void onCancelled() {
mListener = null;
}
}
private DownloaderThread mThread;
private OnDownloadThreadCompleteListener mListener;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
mListener = new OnDownloadThreadCompleteListener() {
#Override
public void onDone(String data) {
Fragment fragment = getFragmentManager().findFragmentByTag("fragment_git");
fragment.show(data);
}
}
mThread = new DownloaderThread(mListener);
findViewById(R.id.btn_download).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mThread.execute(null, null, null);
}
}
}
#Override
public void onPause() {
super.onPause();
if (mThread != null) {
mThread.cancel(true);
}
mThread = null;
mListener = null
}
}
I am currently starting to develop Android applications, and I must say that it all came out very very simple and straightforward.
I have a small question about the AsyncTask. Maybe I've been doing something wrong, but here's the situation.
I have a small app that needs to load a list's content from the web.
I developed everything based on fake requests, and it all came out awesome. Then I updated the code with actual requests and got the 'Network on main thread error'. So I decided to switch to AsyncTask.
I was wondering if I could let AsyncTask just do the asynchronous work, and handle the result somewhere else (where I actually have the GUI connections and everything). I thought that in terms of readability and logic it makes more sense to have all the code that handles the interface in the Activity, but how could I let the Activity know when a task was completed?
I wrote these simple classes and interfaces (and it works) but I wanted to know from you if this is a good thing or there are better methods to do that.
So, here's the code:
public interface AsyncDelegate {
public void executionFinished(LazyLoaderWithDelegate lazyLoaderWithDelegate);
}
This is a simple interface. The purpose is to have the Activity implement this and handle the 'executionFinished' method. Something like a listener.
import android.os.AsyncTask;
public class LazyLoaderWithDelegate<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{
AsyncDelegate delegate;
Result result;
public LazyLoaderWithDelegate(AsyncDelegate delegate){
this.delegate = delegate;
}
#Override
protected Result doInBackground(Object... params) {
//This will be Overridden again from the subclasses anyway.
return null;
}
#Override
protected void onPostExecute(Result r){
this.result = r;
delegate.executionFinished(this);
}
public Result getResult(){
return result;
}
}
This class basically gives a skeleton structure to notify the delegate when the task is finished.
That's all. Here's an example of using this classes:
public class LazyVideoLoader extends LazyLoaderWithDelegate<Void, Void, List<List<Video>>>{
public LazyVideoLoader(AsyncDelegate delegate) {
super(delegate);
}
#Override
protected List<Video> doInBackground(Void ...params) {
return ServerInterface.getVideos();
}
}
public class MainActivity extends Activity implements AsyncDelegate {
private LazyVideoLoader videoLoader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
* Set up the lazy loaders
*/
videoLoader = new LazyVideoLoader(this);
videoLoader.execute();
}
#Override
public void executionFinished(LazyLoaderWithDelegate task) {
if(task == videoLoader){
List<Video> result = videoLoader.getResult();
//Do whatever you need...
}
}
Everything you run on onPostExecute is in the UI Thread. Also you can run a code on UI Thread once a certain part of the work is done simply on onProgressUpdate by calling publishProgress on doInBackground.
Refer this for more information. It has everything you need to know about AsyncTask.
If I understand this correct you have a seperate class, which runs an AsyncTask. If the task is completed the as callback used interface informs the Activity. This is good if you think in components to make the code more modular.
The most common practice is to use an AsyncTask as an inner class in an Activity. If you just wanna download a picture or something similar with relative small size this is the prefered way. Because you can access all fields in your inner class, which makes things easier than passing them around in constructors/interfaces.
Don't use an AsyncTask in an extra Class just for readability. If you have to do some fair calculation/modification on the results with different methods your way is ok.
I've already developed many Android apps that make web service requests, always with the following approach:
In every activity that need to make a web service request, I define an inner AsyncTask that shows a ProgressDialog in onPreExecute(), makes the web service call in doInBackground, and dismisses the progressDialog and updates the results in the UI from onPostExecute().
My concern is: Is there a better (shorter) way to do it? Does it make sense to repeat all that code in every activity? I've been googling a lot, but I've found nothing.
My question is: Couldn't I define a Callback interface? for example this one:
public interface RequestCallback {
public void onSuccess(Whatever whatever);
public void onError(ErrorCode errorCode, String message);
}
... and then define an external class, for example AsyncRequest, that wraps the AsyncTask definition and the ProgressDialog show() and dismiss() statements. So, all activities would just need to instantiate that class, and pass in the following parameters:
1) The method of the web service to run
2) A Bundle with all the parameters of that method of the web service
3) A RequestCallback instance (that could be an anonymous inline instance, where I could update the UI from onSuccess())
4) The context of the Activity (necessary to show the ProgressDialog(), so I would still need a way to prevent configuration change exceptions and so...),
Do you find this a good design? It could save hundreds of lines of code...
Your approach is what I did on my project. And it saved a lot of code as you said, I don't have any complaint about it. But here is some issues that I want to tell you:
You should create new instance of AsyncTask every time you do a background thread to avoid to pile callback.
For the progress dialog, I use it as Singleton, because you don't show many dialogs at the same time. The dialog will be showed when you call the background job, and will be dismiss in the callback. Here is what I did:
private void showProgressDialog(String strMess){
if(null == progressDialog){
progressDialog = new ProgressDialog(MainActivity.this);
}
if(!progressDialog.isShowing()){
progressDialog.setMessage(strMess);
progressDialog.show();
}
}
private void hideProgressDialog(){
if(null != progressDialog && progressDialog.isShowing()){
progressDialog.dismiss();
}
}
void someMethod(){
showProgressDialog("Loading...");
doBackgroundJob(param, new RequestCallBack() {
public void onRequestCompleted(String message, boolean isSuccess) {
hideProgressDialog();
if(isSuccess){
}else{
//do something on error
}
}
});
}
It is an optional, I defined an interface to notify instead of specific class, for each response I use one class, so in base class, I don't care what the response is. Here is it:
public interface OnRequestCompleted<TResponse> {
void requestCompleted(TResponse response);
}
public abstract class BaseRequest<TResponse> implements IRequest{
protected OnRequestCompleted<TResponse> delegate;
protected Class<TResponse> responseClass;
#Override
public void send() {
new HttpTask().execute();
}
private class HttpTask extends AsyncTask<Void, Void, String> {
//...
#Override
protected void onPostExecute(String result) {
if (null != response && null != delegate) {
delegate.requestCompleted(response);
}
}
}
// the response example
public class GroupResponse {
public static class Clip {
public int clipId;
public String detail;
}
public static class Movie {
public int movieId;
public String detail;
}
}
In the subclass of BaseRequest, I will tell it exactly what the response class is (Movie, Clip...)
Hope this help.
If you use it already and it works for you, then yes it makes sense to make it generic and save the time (and bugs) of reimplementing the same thing dozens of times. If you ever find yourself copy-pasting large sections of code with few to no differences you should turn it into a library function or class of some sort. Otherwise if you find a problem later you'll have to fix it in a dozen places. It doesn't even matter if you think of a better way to do things later- its still easier to change it in one place than a dozen.
The only real issue I'd have with your solution is I wouldn't add the progress bar to it- I'd handle it in the calling code and the onSuccess/onError implementations. That way you could also reuse it for a background call that doesn't need to put up a UI. I try to keep my UI decisions as far away from data grabbing code as possible, MVC patterns are good.
I'm currently doing something like this in the AsyncTask's onPostExecute method, where NewTask is not the current task that's executing:
private class OlderTask extends AsyncTask<String, Void, Integer> {
//other functions (not important)
#Override
protected void onPostExecute(Integer result) {
new NewTask().execute(null, null);
}
}
I'm wondering if this is a bad idea. Will doing so cause GC for the OlderTask to wait for the NewTask? Are there any other possible problems with using such an approach?
And if this is a problem, how can I rectify it?
Unless NewTask is inner non static class in OlderTask it will not prevent GC from collecting OlderTask unless you store reference to it in some other way.
But even if GC will wait until NewTask is done it should not be a big deal unless you save lot of data in OlderTask or create lots of copies of OlderTask.
So if your design requires doing that, it's ok. But it surely cleaner not to have chained tasks.
I use a callback method, So when result comes to onPostExecute I call another AsynkTask from UI, I think it is good idea, Let me know what do you think.
public class PatientSearchController extends AsyncTask < String, Void, String > {
private PatientSearchResultHandler handler = null;
public void onResultHandler(PatientSearchResultHandler handler) {
this.handler = handler;
}
#Override
protected String doInBackground(String...params) {
}
#Override
protected void onPostExecute(String result) {
this.handler.onResultSuccessHandler(result);
}
}
I've been using my Activity class to access my DB which made my program freeze sometimes.
So I decided to use AsyncTask instead to handle the DB.
My problem is I don't know how to instantiate my SQLite DB "TheDB" from AsyncTask's class
public class myClass extends AsyncTask<Void, Void, Void>{
private TheDB db;
any method() {
this.db = new TheDB(this); //<-- Error here
}
this worked fine on the Activity class, but it I dont know how to use it here
TheDB's constructor is TheDB(Context context) but this class is not a "context" so how can i use my DB here?
please provide examples if you can
and please do not give me links to google's references, am a newbie and i find them hard to follow
you need to pass the application context here
this.db = new TheDB(getApplicationContext());
import android.content.Context;
public class SuperTask extends AsyncTask<Void, Void, Void> {
private final Context mContext;
public SuperTask(Context context) {
super();
this.mContext = context
}
protected Void doInBackground(Void... params) {
// using this.mContext
}
}
public class MainActivity extends Activity {
// and run from Activity
public void onButtonClick(View view) {
new SuperTask(this.getApplicationContext()).execute();
}
}
There are two ways that i see:
Pass a context object to your AsyncTask constructor, then instantiate database like this this.db = new TheDB(context);
Or you probably can pass the actual database object to the constructor, but the first approach seems better.
An important part of learning to program is learning to read and understand documentation. As documentation goes, the Android docs are pretty detailed, so its really worth your time to understand how they work.
As you can see in the AsyncTask docs, there is no onCreate or onExecute method in an AsyncTask.
The docs clearly walk you through the 4 main functions of an AsyncTask, onPreExecute(), doInBackground(Params...), onProgressUpdate(Progress...), onPostExecute(Result).
The likely choices for your instance are onPreExecute() or doInBackground(Params...). The difference is whether or not you want the initializition to occur on the UI thread. If you don't, then do it in doInBackground(Params...).