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
}
}
Related
I'm new on Android and working an big app which has sending data to API and saving it on SQlite. All of this process is on one class file . But it leaves me on an error. Sometimes the device hanged. other scenario is the data is incomplete . I have read about Intent Service and Services and I want to learn about the two, but I'm wondering how to get all of my data from UI and put it on services. May I know How?
It depends on the nature of the application. If this should happen in response to a user input...you could well use an AsyncTask. Otherwise, a background service could also do the job.
What you should NEVER do is run a network operation and/or database access on the main UI thread.
Services can receive data via intents. The way to send these intents depend on the type of service (Started, Bound or both). There are plenty of resources out there you can read...here's one from Android documentation...
https://developer.android.com/guide/components/services
An Example of an AsyncTask
The example below shows an implementation of AsyncTask that fetches a user's details from a network resource...
public class FetchUserTask extends AsyncTask<String,Void, UserDTO> {
private FetchUserTaskListener listener;
#Override
protected UserDTO doInBackground(String...params){
if(params == null || params.length == 0)
return null;
String userID = params[0];
UserDataProvider provider = new UserDataProvider(userID);
try {
return provider.get(userID);
}
catch(Exception ex){
//log the error
return null;
}
}
#Override
protected void onPostExecute(UserDTO user){
if(listener != null)
listener.onCompleted(user);
}
public void setListener(FetchUserTaskListener listener){
this.listener = listener;
}
public interface FetchUserTaskListener{
void onCompleted(boolean success);
}
}
How'd you use this AsyncTask?
For example, in an Activity, you would use it as below...
public class UserDetailsActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
//instantiate activity...
super.onCreate(savedInstanceState);
setContentView(R.layout.whatever_layout);
fetchUser(userId);
}
private void fetchUser(String userID){
FetchUserTask task = new FetchUserTask();
task.setListener(new FetchUserTaskListener<UserDTO>() {
#Override
public void onCompleted(UserDTO user) {
//CAUTION: make sure the activity hasn't been stopped before
//accessing any UI elements and/or context
}
}
task.execute(userID);
}
}
Note
You can (and will need to) make the example(s) above a bit more sophisticated. For example you can have the FetchUserTaskListener's onCompleted method return also an error message if an error occurred.
You will also need to check whether the activity has been paused or stopped before you access any context-bound data otherwise you might get an ILlegalStateException.
Make use of SQLiteOpenHelper class and it has methods to be overridden in your own class by extending SQLiteOpenHelper. Create Add, Update, Delete, Get methods as per your requirement in this class and keep this class as Singleton pattern. User Asynctasks to call those methids and you are done.
Hope that helps you visualise things in better way.
The suggested way to implement ViewModel is to expose the changing data by using LiveData objects to activities, fragments and views. There are cases, when LiveData is not an ideal answer or no answer at all.
The natural alternative would be, to apply the observer pattern to the ViewModel, make it an observable. When registering observers to the ViewModel, the ViewModel will hold callback references to notify the observers.
The documentation says, a ViewModel must not hold references to activities, fragments or views. The only answer to the question "why" I found is, that this may cause memory leaks. Then how about cleaning up the references to avoid memory leaks?
For views this is a difficulty. There is no defined moment, when the view goes away. But activities and fragments have a defined lifecycle. So there are places to unregister as observers.
What do you think? Is it valid to register activities as observers to ViewModels if you take care to always unregister them? Did you hit upon any valid information about this question?
I set a small reward for the best answer. It's not because I think it a recommended solution (as it does not work with views). I just want to know and extend my options.
public class ExampleViewModel extends ViewModel {
public interface OnEndListener {
public void onEnd();
}
private List<OnEndListener> onEndListeners = new ArrayList<>();
public void setOnEndListener(OnEndListener onEndListener) {
onEndListeners.add(onEndListener);
}
public void removeOnEndListener(OnEndListener onEndListener) {
onEndListeners.remove(onEndListener);
}
public void somethingHappens() {
for (OnEndListener onEndListener: new ArrayList<OnEndListener>(onEndListeners) ) {
onEndListener.onEnd();
}
}
}
public class ExampleActivity extends AppCompatActivity {
ExampleViewModel exampleViewModel;
ExampleViewModel.OnEndListener onEndListener;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
onEndListener = new ExampleViewModel.OnEndListener() {
#Override
public void onEnd() {
finish();
}
};
exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
exampleViewModel.setOnEndListener(onEndListener);
}
#Override
protected void onDestroy() {
super.onDestroy();
exampleViewModel.removeOnEndListener(onEndListener);
}
}
To ask "am I allowed..." is not really a useful question, IMO. The docs are clear that what you are suggesting is discouraged and why. That said, I expect that your code would probably work as expected and is therefore "allowed" (i.e. not prevented by a technical constraint).
One possible gotcha scenario: InstanceA of ExampleActivity is started and kicks off some long-running task on the ExampleViewModel. Then, before the task completes, the device is rotated and InstanceA is destroyed because of the configuration change. Then, in between the time when InstanceA is destroyed and a new InstanceB is created, the long-running task completes and your view model calls onEndListener.onEnd(). Except: Oh no! The onEndListener is null because it was cleared when InstanceA was destroyed and hasn't yet been set by InstanceB: NullPointerException
ViewModel was designed (in part) precisely to handle edge cases like the gotcha scenario above. So instead of working against the intended use of the ViewModel, why not just use the tools it offers along with LiveData to accomplish the same thing? (And with less code, I might add.)
public class ExampleActivity extends AppCompatActivity {
ExampleViewModel exampleViewModel;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleViewModel = ViewModelProviders.of(this).get(ExampleViewModel.class);
exampleViewModel.getOnEndLive().observe(this, new Observer<Boolean>() {
#Override
public void onChanged(#Nullable Boolean onEnd) {
if (onEnd != null && onEnd) {
finish();
}
}
});
}
}
public class ExampleViewModel extends ViewModel {
private MutableLiveData<Boolean> onEndLive = new MutableLiveData<>();
public MutableLiveData<Boolean> getOnEndLive() {
return onEndLive;
}
public void somethingHappens() {
onEndLive.setValue(true);
}
}
Think of the LiveData in this case not as actual "data" per se, but as a signal that you can pass from your ViewModel to your Activity. I use this pattern all the time.
I am working on an Android app. The code I attach is creating a recyclerview. The very first thing we do is to create an asynctask that would fetch data on an SQLite database and load it into the adapter->recylcerview. While the background task is working, a progressdialog is shown to the user.
public class HomeActivity extends AppCompatActivity
{
private RecyclerView recycler;
private RecyclerViewAdapter adapter;
private SwipeRefreshLayout swipeRefresh;
private progressDialog progressDialog;
// ... some code here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// ... some code here
createRecyclerView();
loadRecyclerView();
// ... some code here
}
private void loadRecyclerView()
{
new LoadingBackgroundTask().execute();
}
private void createRecyclerView()
{
Context context = getApplicationContext();
recycler = (RecyclerView) findViewById(R.id.recycle_view_home);
recycler.setHasFixedSize(true);
RecyclerView.LayoutManager lManager = new LinearLayoutManager(context);
recycler.setLayoutManager(lManager);
adapter = new RecyclerViewAdapter();
recycler.setAdapter(adapter);
recycler.setItemAnimator(new DefaultItemAnimator());
}
private class LoadingBackgroundTask extends AsyncTask<Void, Void, List<items>> {
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog = ProgressDialog.show(HomeActivity.this, getString(R.string.dialog_load_list),getString(R.string.dialog_please_wait), false, false);
}
#Override
protected List doInBackground(Void... params) {
List<items> lists;
//Data Source Class ( SQLite)
ListDS listDS = new ListDS(getApplicationContext());
list = listDS.getList();
return list;
}
#Override
protected void onPostExecute(List result) {
super.onPostExecute(result);
//it inserts de list on recyclerview performing animation
adapter.animate(result);
progressDialog.dissmiss();
swipeRefresh.setRefreshing(false);
recycler.scrollToPosition(0);
}
}
}
So far, so good. However, as you probably know this code has some well-known issues; for example if I rotate the screen while asynctask is doing its magic, it will crash the app.
I've tried an alternative I've seen Googling, rxandroid.
(Sorry if I typed something wrong, I am doing it by memory)
public class HomeActivity extends AppCompatActivity
{
private Subscriber suscriptor;
private progressDialog progressDialog;
//some code ....
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
suscriptor = new Subscriber() {
#Override
public void onCompleted() {
progressDialog.dismiss();
Log.d("SUSCRIPTOR","ON COMPLETE");
}
#Override
public void onError(Throwable e) {
Log.d("SUSCRIPTOR","ON ERROR");
}
#Override
public void onNext(Object o) {
adapter.animate((List<items>)o);
}
};
Observable.create(
new Observable.OnSubscribe<List<items>>() {
#Override
public void call(Subscriber<? super List<items>> sub) {
progressDialog = ProgressDialog.show(HomeActivity.this, getString(R.string.dialog_load_list),getString(R.string.dialog_please_wait), false, false);
List<items> lists;
//Data Source Class ( SQLite)
ListDS listDS = new ListDS(getApplicationContext());
list = listDS.getList();
sub.onNext(list);
sub.onCompleted();
}
#Override
protected void finalize() throws Throwable {
super.finalize();
Log.d("OBSERAVBLE","FINALIZED");
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.newThread())
.cache()
.subscribe(suscriptor);
}
#Override
public void onDestroy()
{
if(suscriptor!=null)
{
if(!suscriptor.isUnsubscribed())
{
suscriptor.unsubscribe();
}
}
super.onDestroy();
}
}
Now the app is not crashing anymore when I rotate the screen. However, the observable keeps working on the background until it finishes but as I unsubscribe to avoid crashing, I don't receive the results properly. Moreover, progressbar disappears even though the observable keeps working as I mentioned.
Looking for a solution, I found there is a pattern called "Ted Mosby" which seems to solve my problem. Although it looks promising, I think is too much coding for something I felt it is not worth it and that rxandroid may have a workaround.
So, my question is how can I get what I want without getting immersed in an architectural coding mess too big for my purpose? Could you give an example if you guys have solved this? Do you think I am wrong and I should implement TedMosby pattern?
Mosby is a Model-View-Presenter (MVP) library. So the pattern you named "ted mosby pattern" is actually MVP.
But you didn't have understood what MVP is all about. It's not about retaining async running taks, even thought this could be achieved with Mosby. MVP is about separation of concerns. View is just displaying UI elements, Presenter is controlling the View, i.e. the presenter tells the view: now display the progress diaolog, now hide the progress dialog, etc. In other words, the presenter controls the state of the view. The Model could be an async task or a RxJava Observable. The Presenter than gets the result back and tell the view to display it. You code is decoupled into 3 layers Model (also called business logic) and Presenter and View. The advantage is that you can change the view (i.e. replace progress dialog with an progressbar widget) without touching any code that loads data (Presenter and business logic). Additionally with MVP your code becomes testable.
So what you should compare is: Should I use AsyncTask or RxJava for loading data. With Mosby you would execute your http request in the presenter. While orientation changes are done the presenter doesn't get destroyed (and hence the background task doesn't get canceled).
However, MVP is not the solution for everything. If you have to ensure that a single http call is executed correctly (i.e. sign up for a community) you should think about using an android service.
What you can do is whatever you do in activity like your AsycTask and RecyclerView, put it inside a fragment and, setRetainInstance(true) in onCreateView() method of fragment and load that fragment in your activity.
setRetainInstance(true) won't let your fragment instance destroy when screen is rotated.
Your Observable should handle himself the fact on unsubscription. There are two mechanism for this:
check subscriber.isUnsubscribed. You can do it between or after "heavy" steps
add unsubscription callback. You can use it to stop long running operations, release resources etc
Take a look at this code:
Observable.create(
new Observable.OnSubscribe<List<items>>() {
#Override
public void call(Subscriber<? super List<items>> sub) {
sub.add(Subscriptions.create(new Action0() {
#Override
public void call() {
cancelLongRunningOperationIfItStillRunning();
}
}));
if (!sub.isUnsubscribed()) {
//start long running operation here
}
}
})
.doOnSubscribe(new Action0() {
#Override
public void call() {
}
})
You shouldn't reference to your activity/context/progress dialog etc inside Observable. Instead use doOnSubscribe if you want to do some side effects.
I am trying to refactor my fragment-heavy code with safer async tasks as I had noticed some occasional crashes. Now I am slightly better educated after some searches I've noticed my main problem is accessing activity or fragment variables from within the async task itself.
I am separating each async task into single classes, which implement interface callbacks to the fragment or activity which called them. For example:
public class LoginFragment extends Fragment implements RequestForgotPassword {
...
//On Forgot Password click
new AsyncForgotPassword(LoginFragment.this, mEmail).execute();
...
#Override
public void onForgotPasswordResult(Result result)
{
if(isAdded())
{
if (result == Result.SUCCESS)
Toast.makeText(getActivity(), "An email has been sent to your account to assist in recovering your password", Toast.LENGTH_LONG).show();
else
Toast.makeText(getActivity(), "We don't have any record of that registered email, sorry! Please try again", Toast.LENGTH_LONG).show();
}
}
...
}
OnTaskCompleted.java
public interface OnTaskCompleted {
enum Result{SUCCESS, FAILURE};
}
RequestForgotPassword.java
public interface RequestForgotPassword extends OnTaskCompleted {
void onForgotPasswordResult(Result result);
}
AsyncForgotPassword.java
public class AsyncForgotPassword extends AsyncTask<Void, Void, JSONObject> {
private RequestForgotPassword mRequest;
private String mEmail;
public AsyncForgotPassword(RequestForgotPassword request, String email)
{
mRequest = request;
mEmail = email;
}
#Override
protected JSONObject doInBackground(Void... params) {
ServerFunctions serverFunctions = new ServerFunctions();
return serverFunctions.forgotPassword(mEmail);
}
#Override
protected void onPostExecute(JSONObject obj) {
try {
JSONObject json1 = obj.getJSONObject("response");
if (json1.getString("success").matches("1")) {
mRequest.onForgotPasswordResult(OnTaskCompleted.Result.SUCCESS);
} else {
mRequest.onForgotPasswordResult(OnTaskCompleted.Result.FAILURE);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
So basically I think it would be good if most async tasks will be written like this. Many will feature progress dialogs and could be long running, or feature meaningful interactions with the calling fragment etc. and I've come across some very interesting discussions here, here, here and here which have led me to approach this set-up.
My understanding of Dianne's code in that first link's answer (written way back in 2010) was that it ensures the activity will never be null when accessed from an async task. My app has only a couple of activities and many fragments so I can't really see how this approach would work as nicely today. I have had problems swapping fragments with async tasks etc. in the past and want to make sure that I won't get any more null Context related problems.
So if I check isAdded() within a fragment after an async task has complete, is getActivity() guaranteed to be non-null? Should I call executePendingTransactions() just beforehand everytime just to make sure? Is this all overkill?
Thankyou for any feedback!
from the doc:
public final boolean isAdded ()
Return true if the fragment is currently added to its activity.
So the activity could not be null.
edit:
To achieve a long running task I recommend you to use an IntentService (if the task do complex local operations, otherwise an AsyncTask is ok), save data in DB (or in other forms, for example in SharedPreferences if it is a small chunk of informations) and propagate the logic using a LocalBroadcast. You could also manage a check in onCreate() callback of the fragment so if it was detached when the task was expired, it could retrieve the data persisted and manage them properly; after that, you could flush the data stored previously.
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.