Access Activity Method From AsyncTask Callback Interface - java

I have several activities and one asyncTask that uses the same interface for callback methods as below
The interface used by all
public interface AsyncTaskCallback {
void onCookie(CookieManager cookieManager);
void onResponse(String response);
void onProgress(String... values);
void onError(Exception e);
}
AsyncTask1 is called from all activities as follows
public void exec_taskt() {
alertDialog.SetText("Sending Request...");
AsyncTask1 task1 = new AsyncTask1("https://stackoverflow.com");
task1.setCookieManager(cookiejar);
task1.setCallback(this);
task1.execute();
}
Each Activity also implements the interface
#Override
public void onCookie(CookieManager cookieManager) {
cookiejar = cookieManager;
}
#Override
public void onResponse(String response) {
try {
PostProc(response);
}catch (Exception e){ // ERROR HERE
e.printStackTrace();
}
}
#Override
public void onProgress(String... values) {
alertDialog.SetText(values[0]);
}
#Override
public void onError(Exception e) {
///SAME ERROR HERE TOO
//Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
}
private void PostProc(String response) {
//the response string is parsed and displayed in a recyclerview in this method
//this method is slightly different for each activity
}
AsyncTask1
public class AsyncTask1 extends AsyncTask<String, String, String> {
private String address = "";
private CookieManager mCookieManager;
private Exception mException;
private AsyncTaskCallback mCallback;
public AsyncTask1 (String page) {
this.address = page;
}
public void setCookieManager(CookieManager cm) {
this.mCookieManager = cm;
}
public void setCallback(AsyncTaskCallback cb) {
this.mCallback = (AsyncTaskCallback) cb;
}
#Override
protected String doInBackground(String... strings) {
try{
//all code here is executed without error
//code skipped for simplicity
// basically just loads the given url and then...
publishProgress("Page Loaded");
mCallback.onCookie(mCookieManager);
mCallback.onResponse(response);
return response;
} catch (Exception e) {
publishProgress("Error");
e.printStackTrace();
mCallback.onError(e);
return "";
}
#Override
protected void onProgressUpdate(String... values) {
Log.d(tag, TextUtils.join(",", values));
mCallback.onProgress(values);
super.onProgressUpdate(values);
}
}
The errors I get are marked
ERROR HERE
in the code above. and the message is as follows
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
My main suspect is the PostProc() method in the activity that does play with the UI component. But doesnt implementing the interface in the activity mean that part of the code runs in the main UI thread? I know you can pass the activity instance to the asyncTask and call the PostProc() method from postexecute of the asyncTask but I would like to explore an alternative way to it, since the same asynctask is accessed by other activities as well. the returns are the same for all activities. Only the PostProc Method is different.
Thanks in advance

Instead of using callback interface you can use a android.os.Handler() which is associated with the Looper.getMainLooper() to update the views like:-
AsyncTask1 task1 = new AsyncTask1(android.os.Handler(Looper.getMainLooper(), msg -> {
// handle message sent from asyncTask
return false;
}), url);
Then in task1 send the message using using the handler
public AsyncTask1 (Handler handler, String page) {
this.address = page;
this.handler = handler;
}
then ,
Message message = new Message();
message.obj = uiData;
handler.sendMessage(message);
The handler onHandleMessage will always execute on the main looper !

Related

Null textview when it's call on a handler

I'm trying to do a setText() on a Textview (already instantiate in the onCreate()) called by a Handler and using the ruiOnUiTread() but I have a nullPointerException on the Textview.
Where can the problem come from?
I saw in the debug that the instance of the activity was not the same between the instantiation and the setText() while I do not change activity but impossible to instantiate it in the same place as the setText().
private TextView ambianceTextView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ambianceTextView = findViewById(R.id.valeur_ambiance);
StarterKillerPidroid.getInstance().startApp();
}
private final Runnable retrieveData = new Runnable() {
public void run() {
try {
setText();
} catch (Exception e) {
e.printStackTrace();
}
handlerRecup.postDelayed(retrieveData, 1000);
}
};
public void setText(){
runOnUiThread(new Runnable() {
#Override
public void run() {
ambianceTextView.setText("test");
}
});
}
public void doAfterLogin() {
handlerRecup.postDelayed(retrieveData, 10000);
}
the runnable is started by a function called by a callback of an http request with Volley
public class StarterKillerPidroid {
void startApp() {
//Sending a request
PostmanPidroid.getInstance().login();
}
public void ackLogin(Boolean isValid) {
if (isValid) {
ActivityMain.getInstance().doAfterLogin();
} else {
PostmanPidroid.getInstance().login();
}
}
}
The class Postman :
public class Postman {
public void login(){
// Parameters
String email = "test#tes";
String password = "test";
// Encoding the request with parameters
JsonObjectRequest request = EncoderDecoderPidroid.getInstance()
.encodeRequestLogin(email, password);
// Sending the request
sendRequest(request);
}
void sendRequest(StringRequest message){
// Creating the queu if it's not create
if (queue == null) {
queue = Volley.newRequestQueue(context);
}
// Adding the request to the queue
queue.add(message);
}
}
When a success response is received, this callback is called :
private Response.Listener<JSONObject> callbackLogin =
new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
...
StarterKillerPidroid.getInstance().ackLogin(true);
}
};
Basically, this kind of problem is due to the instance. It may be possible that your textview instance is not initialized. One more thing using handler directly for updating UI thread is not a good idea. Instead of directly updating Ui with handler you should use FunctionalInterface for doing this.
FunctionalInterface is a good approach for such cases.
A functional interface is an interface that contains only one abstract method. They can have only one functionality to exhibit. From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. ... Runnable, ActionListener, Comparable are some of the examples of functional interfaces.
Java has a predefined FunctionalInterface Callable. It goes something like this
public static void doDid(final Callable<Void> callable) {
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
try {
callable.call();
handler.postDelayed(this, every * repeattime);
} catch (Exception e) {
e.printStackTrace();
}
}
}, every * tempvalue);
}
and use this for updating UI in this way
doDid(new Callable<Void>() {
#Override
public Void call() {
textView.setText("Your text");
return null;
}
});
There is one open-source library available for Android which works like a charm is such cases called Predictor. You can download it from here and import in your project. You can also contribute in this project for saving many developers life.
Do you wanna see how predictor can do this?
Predictor.every(3).second().doDid(new Job<Void>() {
#Override
public Void run() {
textView.setText("Your text");
return null;
}
});
What can you do with predictor?
Predictor gives you several ways of handling multithreading some of them are as follows:
Predictor.every(3).second().doDid(something());
Predictor.every(5).minutes().doDid(something());
Predictor.every().hour().doDid(something());
Predictor.every().week().doDid(something());
Predictor.every().month().doDid(something());
and many more...
Try this way:
private final Runnable retrieveData = new Runnable() {
public void run() {
try {
ambianceTextView = (TextView) findViewById(R.id.valeur_ambiance);
setText();
} catch (Exception e) {
e.printStackTrace();
}
handlerRecup.postDelayed(retrieveData, 1000);
}
};

Execute Async Tasks one after another

I know that there are already some discussions and I searched on the internet a lot for a solution, I have 3 get calls (volley) I want to execute one after another because the next get does need variables which were set on the get before this but it doesn't seem to work here. When I debug the whole process it worked fine of course but when running the app normally it doesn't get any data which I would have gotten when debugging it.
Now I tried to set static boolean variables to make this whole thing work but theres so far no success..
public class AsyncToken extends AsyncTask<String , Void, Void> {
private PacketBuilder pb = new PacketBuilder();
private Context context;
AsyncUser u = new AsyncUser(context);
#Override
protected Void doInBackground(String... params) {
while( task1finished == false )
{
try
{
Log.d("App", "Waiting for GetTask");
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
Log.d("App", "GetTask finished");
// Do what ever you like
// ...
pb.Get("token", "", params[0], context);
return null;
}
protected void onPostExecute(String ... params)
{
task2finished = true;
}
public AsyncToken(Context context)
{
this.context = context;
}
}
EDIT, the code:
When async task is completed onPostExecute() is called, so start another async task with computed values there. You can have global variables what should be computed or you can pass them through parameters.
//start first async task
new TaskOne().execute();
class TaskOne extends AsyncTask<Void,Void,String>{
String tmp;
#Override
protected String doInBackground(Void... records) {
tmp = computeNeededValue();
return tmp;
}
#Override
protected void onPostExecute(String result) {
new TaskTwo().execute(tmp);
}
)
class TaskTwo extends AsyncTask<String,Void,Void>{
#Override
protected Void doInBackground(String... records) {
//use computed value from first task here
}
#Override
protected void onPostExecute(Void result) {
//start another async task
}
)

Send a value from a listener interface to onPostExecute in AsyncTask class

I'm actually trying to build a WebSocket client application on Android with the nv-websocket-client.
But I'm stuck with interfaces in my doInBackground function.
I'm trying to use a AsyncTask for the WebSocket connetion, but I'm stuck when I have to pass to onPostExecute the message from onTextMessage :/
private class Reseau extends AsyncTask<Void, Void, String> {
// This is run in a background thread
#Override
protected String doInBackground(Void... params) {
socket.addListener(new WebSocketAdapter() {
#Override
public void onTextMessage(WebSocket websocket, String message) throws Exception {
// Here I want to return the String message to onPostExecute
// How to do it ? return message do not work because onTextMessage is void
}
});
try { socket = new WebSocketFactory()
.setConnectionTimeout(5000)
.createSocket(adresse[0] + adresse[1])
.connect();
}
catch (IOException e) { e.printStackTrace(); } catch (WebSocketException e) { e.printStackTrace(); }
return "I want to pass message from onTextMessage to onPostExecute";
}
// This runs in UI when background thread finishes
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
zone.append(result);
}
}
I can use a RunOnUIThread or enable StrictMode but this is not the good method to do that job.
Thank you for your help !

How to make an Android class wait until another class completes its task?

I'm writing an Android messaging application, and 1 class is calling another class, and I wish for the calling class to wait for the callee class to complete before carrying on.
Caller Class (MessageManagement) code snippet is as follows:
private static Messenger myMessenger;
try {
Message msg = Message.obtain();
msg.arg1 = constructedMessage.length();
msg.arg2 = -1;
msg.obj = constructedMessage;
Log.d(TAG, "Calling myMessenger.send()");
myMessenger.send(msg);
Log.d(TAG, "Sent");
} catch (Exception e) {
e.printStackTrace();
}
// Wait here until myMessenger completes its task
doOtherStuff();
Right now, doOtherStuff() starts and finishes before myMessenger starts. I need myMessenger to complete before doOtherStuff() starts.
I've read about wait() and notify() but I'm not sure how to implement it here, or whether it's the right choice.
Some background about the flow of the program. It's basically a messaging app that I inherited, so I'm not exactly sure of its framework. From what I can tell tracing the flow of the code:
When an SMS message is received, the SMS receiver BroadcastReceiver(SmsReceiver) handles it, getting the sender address and message body, then calling a SMS handler service(HandleSmsService), which then calls the caller class in a runnable with the following code:
HandleSmsService
public class HandleSmsService extends Service {
private String message;
private MessageManagement messageManager;
private Handler timeoutHandler = new Handler();
#Override
public void onStart(Intent intent, intent startid) {
message = intent.getExtras().getString("message");
messageManager = new MessageManagement(this);
timeoutHandler.postDelayed(runnable, 10);
}
private Runnable runnable = new Runnable() {
#Override
public void run() {
try {
messageManager.handleMessage(message);
stopSelf();
} catch (Exception e) {
e.printStackTrace();
}
}
};
MessageManagement is my caller class, and MessageManagement.handleMessage() is the top most code snippet presented earlier.
The MessageManagement.handleMessage() apparently calls another Handler in the callee class when it calls myMessenger.send(msg). This Handler code is as follows:
private Handler smsHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// do some stuff
}
};
I'm assuming the posted code is running on the MainThread and the reason why you are using a handler is that something asynchronous is done on another thread when receiving that message.
In that case, you can't use wait on the thread, as it will lock up the UI and probably cause an application not responding error.
Without changing too much of your code, one way to do it is to nest a listener in your constructedMessage for e.g.
public class DoStuffRequest {
private OnFinishListener mOnFinishListener;
private boolean isCanceled;
private String mMessage;
public interface OnFinishListener {
public void onFinish();
}
public DoStuffRequest(String message) {
mMessage = message;
}
public OnFinishListener getOnFinishListener() {
return mOnFinishListener;
}
public void setOnFinishListener(OnFinishListener onFinishListener) {
mOnFinishListener = onFinishListener;
}
public void cancel() {
isCanceled = true;
}
public void notifyFinish() {
if (!isCanceled && mOnFinishListener != null) {
mOnFinishListener.onFinish();
}
}
public String getMessage() {
return mMessage;
}
}
then use some along the line of this to get the ball rolling:
private static Messenger myMessenger;
private DoStuffRequest mRequest;
...
private void send(String message) {
mRequest = new DoStuffRequest(message);
mRequest.setOnFinishListener(new ConstructedMessage.OnFinishListener() {
#Override
public void onFinish() {
doOtherStuff();
}
});
try {
Message msg = Message.obtain();
msg.arg1 = constructedMessage.length();
msg.arg2 = -1;
msg.obj = constructedMessage;
Log.d(TAG, "Calling myMessenger.send()");
myMessenger.send(msg);
Log.d(TAG, "Sent");
} catch (Exception e) {
e.printStackTrace();
}
}
private void doThisIfYouWantToCancel() {
if (mRequest != null) {
mRequest.cancel();
}
}
your Handler/Service code can now call constructedMessage.finish() when the async stuff is done. Depending on what doOtherStuff() does (e.g. when manipulating the UI), you might want to do this on the MainThread (the code i've written above is NOT thread safe and i assume you are calling the listener on the MainThread).
Also remember to call constructedMessage.cancel() in case you do not want to get notified any more (e.g. you are leaving the activity/fragment).
this is just one way to do it, depending on your needs, some other methods might be a better choice.
I guess it should look something like this:
try {
Message msg = Message.obtain(handler, new Runnable() {
#Override
public void run() {
doOtherStuff();
}
});
msg.arg1 = constructedMessage.length();
msg.arg2 = -1;
msg.obj = constructedMessage;
Log.d(TAG, "Calling myMessenger.send()");
msg.sendToTarget();
Log.d(TAG, "Sent");
} catch (Exception e) {
e.printStackTrace();
}
The other way to do this using native means:
private static Messenger myMessenger = new Messenger(new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
// do something what you need
if (msg.getTarget() != null) {
msg.sendToTarget();
}
return false;
}
}));
try {
final Message msg = Message.obtain();
msg.setTarget(new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
doOtherStuff();
return false;
}
}));
msg.arg1 = constructedMessage.length();
msg.arg2 = -1;
msg.obj = constructedMessage;
Log.d(TAG, "Calling myMessenger.send()");
myMessenger.send(msg);
Log.d(TAG, "Sent");
} catch (final Exception e) {
e.printStackTrace();
}

Android Message.sendToTarget() working without getTarget() set

I have the following piece of code to work with but I just did not understand how this works, to start off refer the following link
http://developer.android.com/reference/android/os/Message.html#sendToTarget%28%29
It clearly states that sendToTarget() sends message to the specified Handler by getTarget() or it throws a NPE. In the following code, I dont see an implementation of getTarget() but its working fine, please explain how this works, look for queueThumbnail() function here..specified in comments exactly where Im not understanding how it works
public class ThumbnailDownloader<Token> extends HandlerThread {
private static final String TAG = "ThumbNailDownloader";
private static final int MESSAGE_DOWNLOAD = 0;
Handler mHandler;
Map <Token, String> requestMap = Collections.synchronizedMap(new HashMap<Token,String>());
public ThumbnailDownloader(Handler responseHandler)
{
super(TAG);
}
#SuppressLint("HandlerLeak")
#Override
protected void onLooperPrepared()
{
mHandler = new Handler(){
#Override
public void handleMessage(Message msg)
{
if(msg.what==MESSAGE_DOWNLOAD)
{
#SuppressWarnings("unchecked")
Token token = (Token)msg.obj;
Log.i(TAG, "Got a request for Url:"+requestMap.get(token));
handleRequest(token);
}
}
};
}
public void handleRequest(final Token token)
{
try
{
final String url = requestMap.get(token);
if(url == null) return;
byte[] bitmapBytes = new FlickrFetchr().getUrlBytes(url);
final Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);
Log.i(TAG, "Bitmap Created");
mResponseHandler.post(new Runnable(){
public void run()
{
if(requestMap.get(token)!=url) return;
requestMap.remove(token);
mListener.onThumbnailDownloaded(token, bitmap);
}
});
}
catch(IOException ioe)
{
Log.e(TAG, "Error Downloading Image"+ioe);
}
}
public void queueThumbnail(Token token, String url)
{
Log.i(TAG, "Got an URL: "+url);
requestMap.put(token, url);
mHandler.obtainMessage(MESSAGE_DOWNLOAD, token).sendToTarget();
//some other class calls this function
//here the target is not set, but I see the request is handled well
//how is this possible? getTarget() is not set anywhere is here
}
just look at here https://android.googlesource.com/platform/frameworks/base.git/+/6083d81ce4d67ec632962270fda64ebb9db0d5b1/core/java/android/os/Message.java, and you will know everything, btw if you are extending HandlerThread you are probably on the wrong path

Categories

Resources