Android robust threading Architecture? Asynctask vs thread/handler vs service/thread - java

Let's say you're designing the threading architecture for a an app -> the primary purpose is that your app will have a lot of tasks that need something done on the background thread, and sometimes a result task on UI thread, or something not (though more times, the result needs to be run on UI thread). For simplicity, let's say the tasks will be stuff like: download a file and display a pop-up, log a user in and go to a different page, process an image and store the result in a database (popular tasks that a lot of apps do)
I've researched a lot about the nuances, but would really like a deep-dive explanation/knowledge on what kind of architecture is better, and what are the considerations.
here are the three models in consideration:
AsyncTask model: each operation (like downloading a file and displaying a pop-up) is an AsyncTask, or some derivative of a parent class that abstracts out the common functionalities.
Thread/handler model: i always create a new Handler(Looper.getMainLooper()); and each time i need to do a task, i use a thread factory to spin off the task, with the handler on UI thread (or whatever custom handler).
Service/Thread model: i use a general Service class that is in charge of operations based on some operation code. there's a bunch of ServiceTask derivative objects that do certain things, but the Service class communicates with each ServiceTask when tasks are started/done.
I'm slightly leaning towards going the whole service/threading model, just because i've read some really awkward nuances with AsyncTask/Threads:
AsyncTask has a private static handler, and if the classloader calls it at the wrong time (such as including a library that uses it before your application does) then all of your onPostExecute will happen at the wrong time since your handler was not the main handler
it's easy to forget to check a bunch of things in the onPostExecute such as if there was a config change, or your activity was destroyed, or application was backgrounded/paused when the onPostExecute is called (leading to crashes)
AsyncTask changed its serial/parallel execution behavior on different APIs
If you went with the Thread/Handler model, on older devices, thread priority is actually incredibly low. i've heard something like there was a priority scale of 1-15 such that your threads automatically get a low priority and if the system was low on resources, your threads would stop running (whereas since services are running independently of your activity the thread priority there is higher?)
What is the best way to design a robust threading architecture that doesn't easily lead to crashes/unexpected behavior while also maintaining good performance ??
Please also let me know in the comments if this question is too vague and if you need actual code (i'm afraid to post code because it would super overbloat the question length more than it already is).

I don't think you will find a one-size fits all approach here.
Downloading a file? Use DownloadManager
Logging a user in and go to next screen? Probably an AsyncTask would be best.
Process an image and store it? A Service might be a good choice here since you don't want the action to be attached to any particular Activity.
Handlers are more tricky, if they are attached to a Looper running on a background thread you need to call quit() on the Looper when you are done with it. Handlers are good when you need to delay an action, postDelayed() is great for that. They are also good when you need to communicate back to the UI thread from a background thread.
But yes you are correct that each one has pitfalls as you mentioned. Android is a complex beast and it seems they could have a done a better job preventing developers from shooting themselves in the foot, especially in regards to AsyncTask being called after an Activity is destroyed!

I was using Java's old school approach by creating a class (I called it ThreadRunner) derived from Java's Thread. A constructor looked like:
public ThreadRunner (Object [] params, AbstractCallback callBack) {...}
AbstractCallback was a class that was implemnting a single 'onCall' method and was mostly used to notify a calling party about an event such as "execution of a task is completed".
I've used it to get content from Internet and run other time consuming operations. It didn't cause any problems and worked as expected.
However, I've heard many times that AsyncTask is an Android-ish way of doing things like that. I don't know why and do not have any intention to change, since I'm preaching "don't fix it if it's not broken" approach.
I've seen also comments that you'll need to write less code with AsyncTask, but in my approach with traditional Java's Threat the amount of coding was small as well, so I queses it's just a matter of your personal preferences and experience.
In regard of your 3-rd approach - I think you should use it when write a service that runs all the time, listens on requests and never stops. When you just need to execute a single task asynchronously Java Threads or AsyncTask should be used.

I think AsyncTask is a good tool for listed purposes. But it needs to wrap AsyncTask for an easy using. My variant of such wrapping (with a progress indicator) is a following:
Main class AsyncActivity for extending it in application activities:
public abstract class AsyncActivity extends Activity{
// Поле нужно обязательно объявить как статическое!
private static AsyncConnect asyncConnect = null;
protected void runBackgroundTask(String progressInscription, RequestTask task){
asyncConnect = new AsyncConnect(this, responseListener, progressInscription, task);
asyncConnect.execute();
}
protected abstract void onBackgroundTaskEnd(boolean result);
#Override
protected void onResume(){
super.onResume();
// Перерегистрируем текущий контекст этой формы
// для корректной работы слушателя ответа с сервера
responseListener.registerCurrentContext( this );
if (asyncConnect != null){
asyncConnect.onResume(this);
}
}
#Override
protected void onPause(){
super.onPause();
if (asyncConnect != null){
asyncConnect.onPause();
}
}
/**
* Чтобы диалоги не вызывались из устаревшего контекста
* и по этой причине не исчезали при повороте экрана,
* слушателя ответа с сервера необходимо сделать статическим полем класса,
* в котором должен быть зарегистрирован текущий контекст
*/
private static final OnServerResponseListener responseListener = new OnServerResponseListener(){
private AsyncActivity context = null;
#Override
public void registerCurrentContext(AsyncActivity context){this.context = context; }
#Override
public void onResponse(boolean result){
// Если никакой контекст не был зарегистрирован, ничего не делаем
if (context == null) return;
// Освождаем статическое поле для сборщика мусора (но делать это не обязательно!)
asyncConnect = null;
// Вызываем колбэк о завершении фоновой задачи
context.onBackgroundTaskEnd(result);
}
};
}
Additional class and a pair of interfaces:
public class AsyncConnect {
private final Activity context;
private final RequestTask task;
private final String progressInscription;
private final OnServerResponseListener responseListener;
private boolean isDone = false;
private ProgressDialog progressDialog;
public AsyncConnect(Activity context, OnServerResponseListener responseListener,
String progressInscription, RequestTask task){
this.context = context;
this.task = task;
this.progressInscription = progressInscription;
this.responseListener = responseListener;
progressDialog = null;
isDone = false;
}
public void execute(){
if (isDone) return;
new ConnectTask().execute();
}
public void onPause(){
if (isDone) return;
if (progressDialog != null){
if (progressDialog.isShowing()){
progressDialog.dismiss();
progressDialog = null;
}
}
}
public void onResume(Activity context){
if (isDone) return;
progressDialog = ProgressDialog.show( context, null, (CharSequence)progressInscription,
true, false);
}
private class ConnectTask extends AsyncTask<Object, Void, Boolean> {
#Override
protected void onPreExecute( ) {
super.onPreExecute();
progressDialog = ProgressDialog.show( context, null,
(CharSequence)progressInscription, true, false);
}
#Override
protected Boolean doInBackground(Object... messages) {
return task.call();
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
if (progressDialog != null){
if (progressDialog.isShowing()){
progressDialog.dismiss();
progressDialog = null;
}
}
// Делаем невозможным повторное использование этого объекта
isDone = true;
responseListener.onResponse(result);
}
}
}
public interface OnServerResponseListener {
public void registerCurrentContext(AsyncActivity context);
public void onResponse(boolean result);
}
public interface RequestTask {
public boolean call();
}
For using AsyncActivity we only need to call runBackgroundTask and implement onBackgroundTaskEnd in the target activity. It's possible to create different kinds of AsyncTask wrappings based on this idea.

You may also check out Needle; it's an open-source, simple but powerful multithreading library for Android. With it you can say things like:
Needle.onMainThread().execute(new Runnable() {
#Override
public void run() {
// e.g. change one of the views
}
});
or
Needle.onBackgroundThread().execute(new UiRelatedTask<Integer>() {
#Override
protected Integer doWork() {
int result = 1+2;
return result;
}
#Override
protected void thenDoUiRelatedWork(Integer result) {
mSomeTextView.setText("result: " + result);
}
});
very simple API
fixed thread pool size
customizable thread pool size
supports UI interaction ("do work and then use result on UI thread")
android 1.5+
behaves the same on all platform versions
Check it out on GitHub: https://github.com/ZsoltSafrany/needle

Related

Best Approach for Readable Ordered AsyncTask Code

I'm going round in circles trying different techniques but every time hitting a different snag.
Ideally I want my code to look something like;
public class MyActivity extends AppCompatActivity {
SomeMethod();
new SomeAsyncTask().execute();
SomeOtherMethod();
new SomeOtherAsyncTask().execute();
}
But I need each method to be executed in order (and to wait for the previous to complete). Let's imagine the first AsyncTask authenticates and stores an authentication token in a static somewhere- this is then needed for the calls which follow.
My AsyncTasks have to be Async as they are communicating with an API.
I know I could use the onPostExecute() methods of the AsyncTasks but I don't like the mess this creates (having to jump around the code).
I know I could also create an interface and pass a class in to my AsyncTask but this doesn't help a great deal either (code still jumps around).
I thought I had come up with the perfect solution, calling SomeAsyncTask.execute().get() to wait until the task completes before continuing with the next line of code BUT I've hit a few issues with that today too.
Are there any other techniques I might be able to use to achieve clean code with a mix of foreground and background thread activity?
EDIT At the moment I am considering whether I can make all of my method non-async but then call them from an async- more like;
public class MyActivity extends AppCompatActivity {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground( final Void ... params ) {
SomeMethod();
SomeMethod2();
SomeOtherMethod();
SomeOtherMethod2();
return null;
}
#Override
protected void onPostExecute( final Void result ) {
// any ui stuff
}
}.execute();
}
EDIT2 Current favoured solution is;
Define a "PostExecute" interface in my AsyncTask;
public class GetMerchantDataAsync extends AsyncTask<Void, Void, Void> {
private Context mContext;
private PostExecute mDelegate;
public interface PostExecute {
void Callback();
}
public GetMerchantDataAsync(Context context, PostExecute delegate) {
mContext = context;
mDelegate = delegate;
}
#Override
protected Void doInBackground(Void... params) {
}
#Override
protected void onPostExecute(Void v){
mDelegate.Callback();
}
}
Then define an instance of the interface and pass it on the constructor;
public class StartActivity extends Activity {
private final Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new GetMerchantDataAsync(context, getMerchantDataPostExecute).execute();
}
private GetMerchantDataAsync.PostExecute getMerchantDataPostExecute = new GetMerchantDataAsync.PostExecute() {
#Override
public void Callback() {
DoSomethingElse();
}
};
}
It's a little bit messy (not a single block of code) but it almost reads as a single block and hopefully with sensible/consistent naming will be easily readable.
There are many ways to skin this cat and a few nice async await libraries you can use, similar to Promise.
Or you can also simply do onPostExecute call backs inline if that fits your needs and call the next step onPostExecute, assuming life cycle is still valid.
However, if you want some nice clean code, you should consider Kotlin and Coroutines.
Example with Timeout, cancellation and error handling:
Notice the withTimeout and the withContext calls. Those allow you to await on the contents within before moving on. Also as an added bonus, the method here is a suspendable coroutine which means the caller can wait on it as well. You resume the caller with c.resume(returnValueType).
If you find this too complicated, then I would stick with your onPostExecute, but what most developers forget on AsyncTasks are.
AsyncTasks should be canceled if you exit the Activity typically
AsyncTasks can complete out of order if you are allowing threadpool
management
Object locks must be wrapped to ensure concurrent
modification of variables does not become a problem.
Callbacks should handle errors and not just the positive route
Timeouts have to be managed outside of the Async task adding much more bloat.
So you see, simply doing a myAsyncTask.execute{onPostExecute(value){//dostuff}}
may look like simple fast code, it is definitely prone to errors if you don't handle all the niche cases that can occur.
The Coroutines have provided nice wrappers around all of this in easy to read brackets.
private suspend fun updateConfigurationWithChanges() : Boolean = suspendCoroutine { c ->
A35Log.v(mClassTag, "updateConfigurationWithChanges")
setIsActionInProgress(true)
mSaveOrUpdateJob = launch(UI) {
try{
withTimeout(TIMEOUT_FOR_DB_INTERACTION_MS){
showProgressDialog(mClassTag, "Saving", false)
mSelectedConfiguration!!.setLastSavedDateStr(DateTimeHelper.getNowTimeStamp())
val updatedRecordCount = withContext(DefaultDispatcher){ SSDBHelper.updateConfiguration(mSelectedConfiguration!!) }
if(updatedRecordCount > 0){
showFancyToast("Successfully updated", true, FancyToast.SUCCESS)
c.resume(true)
}else{
showFancyToast("Error while updating, please try again or press back", true, FancyToast.SUCCESS)
c.resume(false)
}
}
}catch(ex: JobCancellationException){
showFancyToast("Save canceled", true, FancyToast.ERROR, "Save canceled: ${ex.message}")
c.resume(false)
}catch (ex: TimeoutCancellationException) {
showFancyToast("Timed out updating, please try again or press back", true, FancyToast.ERROR, "Timed out updating database: ${ex.message}")
c.resume(false)
}catch(ex: Exception){
showFancyToast("Error updating database, please try again or press back", true, FancyToast.ERROR, "Error updating database: ${ex.message}")
c.resume(false)
}
}
}
Of course it is still good practice to cancel if the user leaves your screen if it doesn't hurt anything.
override fun onPause() {
super.onPause()
if(mSaveOrUpdateJob != null && mSaveOrUpdateJob!!.isActive) {
A35Log.v(mClassTag, "canceling saveOrUpdate job")
mSaveOrUpdateJob?.cancel()
}
}
But at the end of the day, do what's best for your situation, if managing the asyncTask and onPostExecute works for your needs and you have all your i's dotted and your t's crossed you should be fine going that route as well.
Also just for completeness if you are wondering how to call and await this above method it would look like this.
fun myButtonClick(){
launch(UI) {
if(mIsNewConfiguration){
//save first if new
if(withContext(DefaultDispatcher){ isNewOrUnchangedName() }) {
if (withContext(DefaultDispatcher) { saveNewConfigurationToDatabase() }) {
refreshCurrentNamesList()
launchConnectAndSendToDeviceFlow()
}
}
}else {
//update first if changes made
if(withContext(DefaultDispatcher){ isNewOrUnchangedName() }) {
if(DeviceAndConfigurationHelper.hasConfigurationModelChanged(mOriginalCopyConfiguration!!, mSelectedConfiguration!!)){
if(withContext(DefaultDispatcher) { updateConfigurationWithChanges() }){
refreshCurrentNamesList()
launchConnectAndSendToDeviceFlow()
}
}else{
launchConnectAndSendToDeviceFlow()
}
}
}
}
}
Happy Coding.
I can think of two approaches, depending on your constraints you can pick any of them, one using custom async tasks:
Create a custom AsyncTask which will be inherited by all your async tasks (or it can be done inline too):
public abstract class BaseAsyncTask<T, U, V> extends AsyncTask<T, U, V> {
public interface Callback<X> {
void onComplete(X param);
}
private Callback mainCallback;
public void execute(Callback<V> callback, T... params) {
mainCallback = callback;
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
#Override
protected void onPostExecute(V v) {
super.onPostExecute(v);
if (mainCallback != null) {
mainCallback.onComplete(v);
}
}
}
This can be used like this for your case:
public static class TestThinghy {
BaseAsyncTask<String, String, String> task = new BaseAsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... strings) {
return null;
}
};
void firstFunOnMainThread() {
//....do whatever...///
}
void runEverything() {
firstFunOnMainThread();
new BaseAsyncTask<String, String, String>() {
#Override
protected String doInBackground(String... strings) {
return "testing thinghy";
}
}.execute(new Callback<String>() {
#Override
public void onComplete(String param) {
secondRunOnMainThread();
//you can start new async here, or better start it from a different methond
}
}, "the other string param");
}
void secondRunOnMainThread() {
///...whatever here
}
}
The other approach is using RxJava, it's a powerful approach that gives you a ton of ways to chain tasks like this and decide how to run them, for this approach I would let you to do some research.

Best practices for start() and stop() in Android MVP?

I have some questions regarding the Presenter’s start(), stop() method. What would you normally put into these methods to prevent memory leaks or any potential problem.
For example, I have an Activity that host a VideoView. The videoPath passed to the Activity will be passed to the Presenter to a VideoUtility to trim the original video into a shorter one before getting passed back to the Activity to be played with the VideoView.
Here’s the confusion: I don’t know where is the appropriate place to call the trimVideo() method as it essentially only need to happen once (unlike in the Android Architect Blueprint, the task is updated with latest data, and thus it’s put in the onResume()).
Please see the code snippet below:
VideoEditorContract:
public interface VideoEditorContract {
interface View extends BaseView<Presenter> {
void playTrimVideo(String trimmedVideoPath);
}
interface Presenter extends BasePresenter {
}
}
VideoEditorActivityBase:
public class VideoEditorActivityBase extends AppCompatActivity implements VideoEditorContract.View {
private VideoEditorContract.Presenter mPresenter;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_video_editor);
String videoPath = getIntent().getStringExtra(RequestCode.EXTRA_VIDEO_PATH);
mPresenter = new VideoEditorPresenter(videoPath, this);
}
#Override
public void onResume(){
super.onResume();
mPresenter.start();
}
#Override
public void playTrimVideo(String trimmedVideoPath) {
final VideoView vv = findViewById(R.id.act_video_editor_videoView);
vv.setVideoPath(trimmedVideoPath);
vv.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
vv.start();
}
});
}
#Override
public void setPresenter(VideoEditorContract.Presenter presenter) {
//do nothing as this activity has already init a presenter
}
}
VideoEditorPresenter:
public class VideoEditorPresenter implements VideoEditorContract.Presenter {
private final VideoEditorContract.View mVideoEditorView;
#NonNull
private String mVideoPath;
public VideoEditorPresenter(#NonNull String videoPath, #NonNull VideoEditorContract.View videoEditorView) {
mVideoPath = checkNotNull(videoPath);
mVideoEditorView = checkNotNull(videoEditorView, "videoEditorView cannot be null!");
mVideoEditorView.setPresenter(this);
//trimVideo(); //should I do it here since this task is only need to be done once
}
#Override
public void start() {
//trimVideo(); //I can do it here but then I need to introduce a control variable; not sure if this is the best practice
}
private void trimVideo() {
//trim video stuff
}
// Currently it doesn't have a stop() method. But if it have one,
// what should I put in it? Releasing and clean up the
// VideoUtility I suppose?
}
I got the answer from Francesco Cervone in Medium about this matter (his article is also an excellent resource on MVP, btw. Very well in tune with the Android Architect Blueprint). I leave the relevant bit here for future reader.
Hi, thank you.
Well, I think that the video should be trimmed in the Presenter#start(). Then, after the video has been trimmed, the presenter should call view.playTrimmedVideo(). You shouldn’t do anything in the presenter constructor.
I suppose the video “editing” is something expensive, so you should do that in a separate thread (using for example an async task). You need to implement the Presenter#stop() method because you have to cancel ongoing operations if there are any, unless you retain the presenter.
You said that the trimVideo should be called just once. You could cache/persist in some way the result of trimVideo so that if the video has been already trimmed, you use it.
I hope I answered your question.
"Could you elaborate more on why shouldn’t we put anything in the Presenter’s constructor? I’ve seen the Presenter’s bare minimal constructor in a couple of places but I don’t understand the reason behind it."
First, it’s a responsibility problem: you are going to create an instance of Presenter, and I don’t think that the video editing is something that belongs to the construction of that object.
Second, you don’t know when the presenter is being instantiated, so you shouldn’t execute expensive tasks in the constructor. If you use some dependency injection framework, the construction of the Presenter would be managed by the framework itself and it needs to be efficient. The construction of other objects could depend on the presenter one.

Best way to execute HTTP GET in onCreate()

I'm coding an Android client that connects to a REST API. Since Android 3.0 it is not allowed to perform blocking network operations on the main thread, so I'm looking for the best way to implement this.
I have managed my objective using AsyncTask but it seems a rather dirty implementation so I'd like to ask for advice.
The data returned from the async task is used to update the UI.
MainActivity.java:
public class MainActivity extends ActionBarActivity{
ListView establishment_list;
CustomListAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
establishment_list = (ListView) findViewById(R.id.establishment_list);
adapter = new CustomListAdapter( this, R.layout.establishment_row_layout, new String[]{"Loading data..."} );
establishment_list.setAdapter(adapter);
View header = (View) getLayoutInflater().inflate(R.layout.establishment_headerview, null);
establishment_list.addHeaderView(header);
// GET establishments from REST API
new NonBloquingGET().execute();
}
/* DIRTY IMPLEMENTATION */
private class NonBloquingGET extends AsyncTask<List<Establishment>, Void, List<Establishment>> {
List<Establishment> establishments;
#Override
protected List<Establishment> doInBackground(List<Establishment>... params) {
/* This is a synchronous call using the Retrofit library*/
establishments = Client.getInstance().getEstablishments();
return establishments;
}
#Override
protected void onPostExecute(List<Establishment> result) {
Log.d("ASYNC TASK", result.toString());
List<String> data = new ArrayList<>();
for (Establishment e : result){
data.add(e.getName());
}
adapter.updateDataSet( data.toArray(new String[data.size()]) );
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
}
Is there a better way to do it as I'm planning to add more Activities and adding an AsyncTask class for each seems awkward?
The first thing you can start with is to make your inner AsyncTask class implementation static, otherwise it will hold an implicit reference to the host class (Activity), this means it won't be garbage collected until task is alive (let's imagine execution of NonBloquingGET takes a lot of time (you are sitting with dial-up speed somewhere on a desert island) and you rotate the screen, activities will be kept in memory). The second step you can think of is to implement a pair of IntentService and BroadcastReceiver. After you understand the key concepts behind it, you can take a look on 3rd party libraries, for example Retrofit for network communications, RxJava for events and Otto for event bus.
Using AsyncTask is actually a good thing as the execution of that is not synchronized with the main activity and so when you want to do some background work and need the main UI to be active at the same time always use AsyncTask.
The drawback of doing network operations on the main thread is that it'll make the UI unresponsive for the time being which will look too laggy and is not a good habit.
static {
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
With this code you can use network operations on your main thread.

How to open a url with an IP in the background without any browser?

I have this code for android to open a url, but I can't make it to run in the background. Is it possible?
Can someone help me with this? Thanks.
public void goToSu (View view) {
goToUrl("http://192.168.2.66/index.html?o0=0");
}
private void goToUrl (String url) {
Uri uriUrl = Uri.parse(url);
Intent background = new Intent(Intent.ACTION_VIEW, uriUrl);
startActivity(background);
}
You cannot make network operations in your main UI, because that would block your program execution with a very disgusting experience for your users. For this, you have to separate network operations inside a Thread, or even better, an AsyncTask if you're just starting with the thread world.
This is the structure you would use:
final class MyNetworkOperation extends AsyncTask<URL, Integer, Void> {
#Override
protected void onPreExecute(final Void param) {
...
}
#Override
protected Void doInBackground(final URL... args) {
...
return null;
}
#Override
protected void onPostExecute(final Void param) {
...
}
}
Even the method names are very self explanatory. When you define your AsyncTask, and call .execute(url_object) on it, the first called method will be .onPreExecute(), there you may initialize variables and prepare everything for the network operation you want to do. The hard part of your network operation should be done inside doInBackground(). There you connect, do the data transfer and disconnect from the host. Finally, onPostExecute() is called: Once you're done, you can process here your results (transfer it to the main Activity, show a Dialog, etc.).
For more on AsyncTasks (and know what does those parameters mean) I strongly recommend reading the reference.
A very good example might be found here.

How can I tell if my context is still valid?

I'm working with a fairly common situation right now - download some data over the web, then update a view to display it. Clearly, I want to do the web download in the background, and then update the view on the main UI thread. Now looking at my code, I'm a little worried about my Activity and its UI elements being killed off before I update them. Here's the essence of what I have in mind:
Thread update = new Thread() {
public void run() {
final Data newData = requestData();
if (newData != null) {
post(new Runnable() {
public void run() {
Toast.makeText(MyClass.this, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
}
});
}
}
};
update.start();
It seems possible that while I'm downloading data, the activity may be destroyed. What happens then? Will my thread continue to execute? Will I end up trying to access dead objects?
Usually I do this by AsycTask, but the work seemed simple enough this time to just inline the threads-launching-threads stuff. Will I make things any better by using an AsyncTask instead?
If your Context is an Activity, you can check if it is finishing or has finished with the isFinishing() method:
if ( context instanceof Activity ) {
Activity activity = (Activity)context;
if ( activity.isFinishing() ) {
return;
}
}
Toast.makeText(context, "I'll do things here that depend on my context and views being valid", Toast.LENGTH_SHORT).show();
What you really want to use is an AsyncTaskLoader. These are my new favorite classes in the Android API. I use them all the time and they were made to solve problems just like this. You won't have to worry about when to stop your download or anything like that. All the threading logic is taken care of for you, including telling the thread to stop if the activity has been closed. Just say what it is you want to do in the loadInBackground() method. Note that if you are developing for an API lower than 3.0, you can still access all the loaders via the Android Support Package.
If you use anonymous classes, they will have an internal reference to the outer class, so it's not like it becomes inaccessible all of a sudden because other references have been cleared. AsyncTask actually doesn't change anything, it uses similar mechanics for notifying about results.
You can use loaders, they are designed to be in sync with the activity lifecycle. They are available only since Android 3.0, but you can use support package to work with them on any device with 1.6 or later.
There is even a simpler solution, you can just use a boolean field which indicates whether activity has gone away. You should set this field in onPause() (or whenever you think you won't need the notifications anymore) and check for it when you show toast. You won't even have to use synchronization, since this field is confined to the main thread, so it's absolutely safe. By the way, if you change this field somewhere else than in onDestroy(), don't forget to add a statement which resets your field back in the counterpart method.
public class MyActivity extends Activity {
private boolean activityDestroyed = false;
#Override
protected void onDestroy() {
activityDestroyed = true;
}
private void updateData() {
new Thread() {
#Override
public void run() {
final Data newData = requestData();
if (newData == null) return;
runOnUiThread(new Runnable() {
public void run() {
if (activityDestroyed) return;
Toast.makeText(MyActivity.this, "Blah",
Toast.LENGTH_SHORT).show();
}
});
}
}.start();
}
}
I usually use Weak Reference to avoid leaking context in views
Weak Reference for Context
private var mContext: WeakReference<Context?>? = null
Assign Context
mContext = WeakReference(appContext)
Get Context
mContext .get()
Validate Context
if (mContext?.get() is Activity &&
(mContext?.get() as Activity).isFinishing){
return
}
Kurtis is right. However, if you REALLY want to keep it simple, you can try this:
class MyActivity extends Activity {
static MyActivity context;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
MyActivity.context = this;
}
#Override
public void onDestroy() {
super.onDestroy();
MyActivity.context = null;
}
}
And then you just use MyActivity.context in your class (and check for null there). If you want the toast to not even show up when your app is in the background, use onPause/onResume instead.
Again, this is the quick and lazy approach. AsyncTask or AsyncTaskLoader is how you should be doing things.

Categories