Android acessing MainActivity from AsyncTask - java

My application crashes after click the button, but the code executes properly.
public void makeLead(View v) throws Exception {
try {
RegisterTimer rt = new RegisterTimer();
rt.ma = this;
rt.execute(null);
} catch (Exception e) {
e.printStackTrace();
}
}
public void log(String msg)
{
final TextView tv = (TextView)findViewById(R.id.editText);
tv.append(msg);
}
private class RegisterTimer extends AsyncTask {
public MainActivity ma;
#Override
protected Object doInBackground(Object[] objects) {
ma.log("ausd");
return null;
}
}
makeLead is onClick event. Method ma.log generates an error but works properly (msg added to textEdit). When I delete ma.log, app doesn't crash. I have no logs in my AndroidStudio so I can't see error message. What's wrong ?

You can not touch the Views in a non UI Thread.
and you are appending text to TextView in a background Thread which is not allowed.
and I hope there is no problem with the initialization of MainActivity inside RegisterTimer as you are not creating the instance of Activity manually. You are in correct way with the initialization rt.ma = this. and why do you need AsyncTask just for changing the text of a TextView?

You cannot update ui from a doInbackground. Initializing ma is not required
Make AsyncTask an inner class of Activity and update ui in onPostExecute
or use interface as a callback to the activity
Edit:
To make it clear
Make asynctaks an inner class of activity. Declare textview as a instance variable. Return result in doInbackground
In Activity
TextView tv;
#Override
public void onCreate(Bundle savedInstancestate)
super.onCreate(savedInstancestate);
setContentView(R.layout.yourlayout);
tv = (TextView)findViewById(R.id.editText);
Then
#Override
protected String doInBackground(Void objects) {
// some background computation
return "ausd";
}
In onpostExecute
#Override
protected void onPostExecute(String result)
{
super.onPostExecute();
tv.append(result);
}
Also you need
private class RegisterTimer extends AsyncTask<Void,Void,String> { // args missing

As described by #Raghunandan you have not initialized ma.
next is you cannot access view in background thread.
if your thread class is inside of MainActivity class then you can use
runOnUiThread(new Runnable() {
#Override
public void run() {
ma.log("ausd");
}
});
inside doInBackground method to update view.

Your method log is public, you don't need to make an object of the MainActivity class to access it, instead you can call it directly. Also you need to add some template after your ASYNC task, if you want to pass some input to your background process, you are using ASYNC task in a wrong way.

Related

Running CountDownTimer inside AsyncTask throws java.lang.RuntimeException - Looper.prepare()

I have a .lrc file and I need to go over every line with a CountDownTimer. I have tried using AsyncTask to do so but I get the error:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
On line new CountDownTimer... I tried doing it with runnable but I still get the same error. My goal is to get it to go over every line in .lrc file which looks like this:
[00:04.15]Help me, it's like the walls are caving in
[00:10.46]Sometimes I feel like giving up
[00:13.63]But I just can't
...
I am not sure how efficient it is to do it the way I am trying to do. I am thinking of going through every line in the doInBackground(). If there is a better way to do it then let me know. But to begin with, why am I getting the EXCEPTION ?
Just to note.. I have simplified the code as much as I could so it would be easier to understand what I am trying to do.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyView myView = new
myView.play();
}
}
public class MyView{
public void play() {
new CustomAsync().execute();
}
}
class CustomAsync extends AsyncTask<Lyric, Void, Void> {
protected Void doInBackground(Lyric... param) {
startLyricCountDownTimer(param);
return null;
}
protected void onPostExecute(Void param) {
//Print Toast or open dialog
}
private void startLyricCountDownTimer(Lyric lyric){
new CountDownTimer(30000, 10) { //This is where it throws the error
public void onTick(long millisUntilFinished) {
//Do the thing
}
public void onFinish() {
}
}.start();
}
}
EDIT
Is it better to go with the AsyncTask and do like Son Truong suggested or to use the following code for each and every lrc line?
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
new CountDownTimer(millisInFuture,countDownInterval) {
#Override
public void onTick(
CountDownTimer uses a Handler to post messages to a Message Queue of a Thread which has a Looper. onTick and onFinish will be called on which thread based on where you create CountDownTimer instance.
In your case because you create CountDownTimer instance in doInBackground method of AsyncTask so these two methods will be call on AsyncTask thread.
In constructor of CountDownTimer, it will create Handler instance as well. The Handler will check whether or not current thread has a Looper, if not it will throw a RuntimeException with message.
Can't create handler inside thread that has not called
Looper.prepare()
Because AsyncTask uses a thread which has no Looper, that why your app crashes.
My suggestion is in doInBackground method you open a connection to .lrc file and read each line, for each line read, use runOnUIThread to send the line to UI thread (then you can process the line read there by display a Toast on screen, etc).
Update: I will demo how to read line by line from a file then display it on a text view each 3 seconds.
First write a class which read from an inputstream line by line
static class ReadLyricTask extends AsyncTask<InputStream, String, Void> {
WeakReference<MainActivity> mMainActivity;
ReadLyricTask(MainActivity activity) {
mMainActivity = new WeakReference<>(activity);
}
#Override
protected Void doInBackground(InputStream... inputStreams) {
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStreams[0]));
String line;
try {
while ((line = reader.readLine()) != null) {
publishProgress(line);
}
} catch (IOException e) {
// Do nothing.
} finally {
try {
inputStreams[0].close();
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onProgressUpdate(String... values) {
MainActivity activity = mMainActivity.get();
if (activity != null) {
activity.displayLyricLineOnTextView(values[0]);
}
}
}
Then just use it in MainActivity
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_LYRIC_TEXT_INTERVAL = 3000; // Change lyric text each 3 seconds.
private int mCurrentInterval = 0;
private TextView mLyricTextView;
private Handler mHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLyricTextView = findViewById(R.id.lyricText);
// I put a file named lyric.lrc in raw folder, for your case just open an input stream from a file.
InputStream inputStream = getResources().openRawResource(R.raw.lyric);
new ReadLyricTask(this).execute(inputStream);
}
private void displayLyricLineOnTextView(final String lyricLine) {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mLyricTextView.setText(lyricLine);
}
}, mCurrentInterval);
mCurrentInterval += UPDATE_LYRIC_TEXT_INTERVAL;
}
}
CountDownTimer runs in separate thread and no need of asynctask to run a timer.Best solution would be create a service and make service to trigger a timer.
As timer run on non ui thread, while updating ui make sure you update from UI thread.You could use UI handler or runOnUithread method to update view.

How do you return a variable from AsyncTask to OnCreate() in Activity

ISSUE/ERROR:
I'm struggling to pass a variable from a doInBackground method into my OnCreate(). I honestly can't believe I'm having so much issues with this.
OBJECTIVE:
Pass a String from AsyncTask method within doInBackground to OnCreate, I want to pass a String to a Textview. And setTextView with the String.
MY UNDERSTANDING:
I have tired creating simple methods within the doInBackground & AsyncTask method and call it in my onCreate(). However the variable is always null. I believe I am miss understanding an aspect of onCreate().
Main Activity: - I want to set variable 'ValueNeeded' in textView
public class OutboxActivity extends ListActivity {
….
…
public void onCreate(Bundle savedInstanceState) {
….
//AsyncTask method
new LoadOutbox().execute();
textView = (TextView) findViewById(R.id.textView6);
textView.setText("ValueNeeded);
Log.d("response", "TOUR NAME: " + ValueNeeded) );
…….
AsyncTask - contains doInBackground
class LoadOutbox extends AsyncTask<String, String, String>
{
/**
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute() {
super.onPreExecute();
….
}
doInBackground - String ValueNeeded is the variable I need passed to onCreate()
protected String doInBackground(String... args)
{
..CODE THAT GETS VALUE IS IN HERE...
//ValueNeeded Is
ValueNeeded = c.getString(TAG_TOUR);
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
You have to do it in onPostExecute, not in doInBackground. Just put into onPostExecute textView.setText("ValueNeeded);
Your problem is not "understanding an aspect of onCreate()" but "understanding an aspect of AsyncTask"
Your onCreate needs to be quick. The point of the AsyncTask is to do stuff in another thread so the onCreate can run.
Implement onPostExecute(...) and have that fill in the result. Your onCreate probably needs to have some sort of "Loading..." message to indicate to the user you're getting the data.
protected String doInBackground(String... args) {
..CODE THAT GETS VALUE IS IN HERE...
//ValueNeeded Is
ValueNeeded = c.getString(TAG_TOUR);
// return your value needed here
return ValueNeeded;
}
protected void onPostExecute(String result) {
super.onPostExecute(result);
// this result parameter has the value what you return in doInBackground
// now it has valueNeeded
// set that value to your textview
textView.setText("ValueNeeded);
}

Android communication between main thread and other thread

I want to change dynamically the text of a textview, but I will need the same logic if I want to make a game thread, so I need to make the communication between the main one and the second one.
I have the files :
MainActivity
public class MainActivity extends ActionBarActivity {
public static Handler mHandler;
Runnable thread = new SampleThread();
TextView txt1 = (TextView) findViewById(R.id.txt1);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
//hiding status bar
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
setContentView(R.layout.activity_main);
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
// i want to change the text of txt1 here
}
};
new Thread(thread).start();
}
}
SampleThread
package com.example.katsar0v.myapplication;
import android.util.Log;
/**
* Created by Katsar0v on 1/21/2015.
*/
public class SampleThread implements Runnable {
#Override
public void run() {
int two = 0;
while(two<10) {
two++;
try {
Thread.sleep(1000);
//instead of logging, i want to send the text to main UI
Log.d("MSG", String.valueOf(two + "sec"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
The problem I see is, how do I change the text with the handler, when my thread is in another file? Or should I make the second class static within the first one (and what should I do when the code gets really long, it can't be all in one file)?
You could implement a custom Interface in order to handle it from your main activity.
On your SampleThread:
public interface TextViewChangeListener
{
public void onTextViewChanged(String newName);
}
TextViewChangeListener mListener;
Then call mListener.onTextViewChanged(String newName) wherever you want to have the new name in your TextView. Remember to initialize mListener with an instance of your MainActivity first, otherwise you will get a null pointer exception. You can do that either in the constructor of SampleThread or by creating a method for the purpose.
In your activity you should implement SampleThread.TextViewChangeListener and override the onTextViewChanged.
#Override
public void onTextViewChanged(String newName)
{
//MyTextView.setText(newName);
}
Edit: untested code:
MainActivity:
public class MainActivity extends ActionBarActivity implements SampleThread.TextViewChangeListener {
#Override
public void onTextViewChanged(Message msg)
{
// process incoming messages here
// i want to change the text of txt1 here
}
public static Handler mHandler;
Runnable thread = new SampleThread(this);
TextView txt1 = (TextView) findViewById(R.id.txt1);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActionBar actionBar = getSupportActionBar();
actionBar.hide();
//hiding status bar
if (Build.VERSION.SDK_INT < 16) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
} else {
View decorView = getWindow().getDecorView();
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);
}
setContentView(R.layout.activity_main);
new Thread(thread).start();
}
}
SampleThread:
package com.example.katsar0v.myapplication;
import android.util.Log;
/**
* Created by Katsar0v on 1/21/2015.
*/
public class SampleThread implements Runnable
{
public interface TextViewChangeListener
{
public void onTextViewChanged(Message msg);
}
public SampleThread(TextViewChangeListener mListener)
{
this.mListener = mListener;
}
TextViewChangeListener mListener;
#Override
public void run() {
int two = 0;
while(two<10) {
two++;
try {
Thread.sleep(1000);
mListener.onTextViewChanged(String.valueOf(two + "sec"));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Let me know if that helped.
You can find some examples in Grafika, which does a lot of work off the UI thread. For example, TextureFromCameraActivity has a pair of handlers, one for the UI thread, one for the renderer thread. In onResume() you can see the main thread passing its handler to the renderer through a constructor, then retrieving the renderer thread's handler with a method call.
ContinuousCaptureActivity has a slightly different approach, using a Handler that also implements a callback interface. The handler object is passed to the CircularEncoder constructor as an interface instance. The public callback methods use the Handler internally.
The only tricky bit is if you're passing a Handler out of the non-UI thread. You either need to do it before the thread starts, or use appropriate thread synchronization operations to avoid data races.
You don't need to have your classes in the same file (and you really shouldn't unless one is nested inside the other). If they're in the same package then the default (package) scope will let them see each other. The first example from Grafika uses nested / private classes, the second example is more spread out.
Of course, if all you're trying to do is submit UI events from a non-UI thread, you can just use Activity.runOnUiThread().

Adding nested views dynamically freezes the App

I am building the app, which generates and adds view dynamically. I don't know in advance what views I need to create, these can be nested layouts or simple labels, etc, depending what comes back from web services.
Everything has been well so far until I started building really complex nested layouts .I have one case where I need to add about 11 levels of Layouts dynamically. When activity starts I display ProgressDialog(ring), while views are being generated. My problem is that with this complex structure ProgressDialog freezes while views are added. This is the code, which creates the view:
private class ViewCreator implements Runnable {
public BackgroundTaskViewCreatedResponse delegate;
private View mCreatedView;
private ComponentDefinition mComponent;
private ViewCreator(ComponentDefinition component){
this.mComponent = component;
}
#Override
public void run() {
try {
if (mComponent != null){
mComponent.setLinkedData(model.getLinkedData());
mCreatedView = componentCreator.createComponent(mComponent);
}
} finally {
if (mCreatedView != null)
delegate.processFinishTask(mCreatedView);
}
}
}
Layout, which has other views in it implements BackgroundTaskViewCreatedResponse, so, when view is ready, it will be added:
#Override
public void processFinishTask(final View createdView) {
//((Activity)view.getContext()).runOnUiThread(new Runnable(){
mView.post(new Runnable(){
#Override
public void run() {
mView.addView(createdView);
}
});
}
As you can see above, I have tried to call runOnUiThread call, but this blocks the UI thread completely while view hierarchy is being generated. At the same time view.post doesn't get called out of the box, so I have made some changes to views as suggested in this SO answer. So, now my views are added, but my ProgressDialog is not running smoothly. It stops in a few occasions and then resumes. I've also tried using Android AsyncTask, but that gives the same effect as runOnUiThread
I am not very experienced with Threads, have been trying to fix this for a few days now. Please help.
You can use AsyncTask to do this/ Here is an example:
private class GenerateViews extends AsyncTask<Void,Void,Void>{
#Override
protected void onPreExecute() {
// SHOW THE SPINNER WHILE GENERATING VIEWS
spinner.setVisibility(View.VISIBLE);
}
#Override
protected Void doInBackground(Void... params) {
//CALL YOUR VIEW GENERATING METHOD HERE
return null;
}
#Override
protected void onPostExecute(Void result){
spinner.setVisibility(View.INVISIBLE);
}
}
You can make this class inside your class, if you want to. And then, you just call
new GenerateCalls.execute();

Non UI thread modifying UI component

I have a thread that needs to be receiving data all the time from the network and I want this data to be displayed to an EditText object.
Obviously, I can't access the UI EditText from within my receiving thread; what I read is that I can use AsyncTask but reading the example in Painless Threading it seems to me that I have to be done with receiving the data before I can be able to post the results to the UI component.
I can't use post or postDelayed as both will be run over the UI thread and I can't block the UI to receive the data; I need to keep receiving the data all the time.
What other options do I have?
Use LocalBroadcastManager, your Activity containing TextView will start listening for broadcast:
public class MyActivity extends Activity {
private TextView mTextView;
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getStringExtra("actionType");
if(action.equals("updateTextView")){
mTextView.setText("whatever you want to set");
}
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Start listening, you can put it on onResume too
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, new IntentFilter(MyActivity.class.getSimpleName()));
mTextView = (TextView) findViewById(R.id.something);
}
}
So whenever your Thread receive something that needs to update the screen, call this:
Intent intent = new Intent(MyActivity.class.getSimpleName());
intent.putExtra("actionType", "updateTextView");
// Once this is called, your broadcast receiver in MyActivity should receive it and start processing
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
Also remember to unregister it in onDestroy or onPause.
*side note: you need to import android support v4 library, and you can pass simple String or object over by intent using Intent.putExtra("","") and Inteng.getExtra("");
Another way is to implement a data listener interface.
public interface DataListener{
void onUpdateData(MyData data);
}
You activities that contain the UI components that need to be updated will implement this interface. It will specify what need to do with updated data.
You may want to keep all instances these data listener interface somewhere in your app.
I assume that you have a different thread to handle network sending/receiving actions. On receiving data, you just call:
dataListenerInstance.onUpdateData(data)
Then it will activate the handler that you have implemented in your activity.
In MainActivity call AsyncTask but make #Override method onPostExecute(..)
public class MainActivity extends ActionBarActivity{
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(Utils.isNetworkAvailable(this)) {
DownloadFileFromURL downloader = new DownloadFileFromURL(){
#Override
protected void onPostExecute(Integer file_content) {
onCompleteLoad();
}
};
downloader.execute(new String[]{file_url, fileName});
...
onCompleteLoad(); - will be call in UI thread of MainActivity. You don't need even implements Interface!
Secon way more suitable for server solutions, but can also be used on the client it is Callable
public class DoGetSize implements Callable<Integer> {
private final String file_url;
private int lenghtOfFile = -1;
public DoGetSize(String file_url) {
this.file_url = file_url;
}
public Integer call() {
try {
URL url = new URL(file_url);
URLConnection connection = url.openConnection();
connection.connect();
lenghtOfFile = connection.getContentLength();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return lenghtOfFile;
}
}
And call this like:
FutureTask<Integer> task = new FutureTask(new DoGetSize(file_url));
ExecutorService es = Executors.newSingleThreadExecutor();
es.submit (task);
try {
Integer result = task.get();
File file = new File(fileName);
if(file.length() != result.intValue()) {
// Do something
...
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
You can send and receive any object in such a way
Full example see on github: https://github.com/app-z/OffLineShop/blob/master/app/src/main/java/net/appz/offlineshop/offlineshop/MainActivity.java
You can do this using a simple delegation.
class NonUIThread {
private NonUIThreadDelegate delegate; //NonUIThreadDelegate can be an interface or an object that has access to your UI thread like an Activity
public void setDelegate(NonUIThreadDelegate delegate) {
this.delegate = delegate;
}
private void doSomthing() {
//do something and at the end:
delegate.someMethodThatUpdatesThatComponent();
}
}
class TheUIThread implements NonUIThreadDelegate /*assuming you've decided to make NonUIThreadDelegate an interface*/ { // the "delegator"
/*
your code
*/
private void initiateNonUIThread() {
NonUIThread nonUIThread;
/*do whatever needed*/
nonUIThread.setDelegate(this);
nonUIThread.start();
}
public void someMethodThatUpdatesThatComponent() { //will be called by the non ui thread
//update the UI
}
}
It's explained a little better (of course using AsincTask) in here: Simple Delegation Pattern in Android

Categories

Resources