android - set intent extra from inside runnable - java

I'm still new to android development. I've been at this problem for some time but am still unable to figure out what to do on my own. In an Activity I set up a series of Runnables containing CountDownTimers. One executes after the next, but depending on which CountDownTimer is active, I need to pass a different Intent.extra to a fragment. I've tried setting my extra from inside Runnable, inside Run, and inside of the CountDownTimer onTick, and onFinish.
I fear I have way too much going on in my original Activity to post it, but here is the problem in essence.
public class MatchUpActivity extends Activity implements OpponentFragment.OnFragmentInteractionListener{
List mTotalDrafts;
Bundle mBundle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_match_up);
mBundle = new Bundle();
mDraftUsernames = extras.getStringArrayList("DRAFT_LIST");
for (int i = 0; i < totalDrafts; i++) {
Handler delayhandler = new Handler();
delayhandler.postDelayed(new Runnable() {
//bundle.put("extra", totalDrafts.get(0))
public void run() {
//bundle.put("extra", totalDrafts.get(0))
getTimer();
}
}, mTodaysDraftTime + (i * singleDraftDuration) - Calendar.getInstance().getTimeInMillis());
}
}
CountDownTimer
private void getTimer() {
new CountDownTimer(singleDraftDuration, 1000) {
public void onTick(long millisUntilFinished) {
//bundle.put("extra", totalDrafts.get(0))
}
public void onFinish() {
//bundle.put("extra", totalDrafts.get(0))
list.remove(0)
}
}.start();
}
}
I am able to remove items from my list in onFinish, but after I do so I need to send the next element in the list as an extra.
I hope this is enough code to get my question across. I tried to change some things from my original code for simplicity. If there is something I am missing or a better way to do this, please, anybody let me know.

Define the Bundle as global variable in your Activity and not in a Method implementation.

Related

Stop the onPostExecute of an Async Task in java

I have the following code. My problem is, that I can't get the JSON.execute() to stop/cancel. I spend quite some time looking up possible answers but I wasn't able to find anything that really worked (e.g. JSON.cancel(true)). As soon as I turn the trackerswitch on, the AsnycTask starts running every 3 seconds just like it's supposed to. Is there a way to easily stop the AsyncTask from executing as soon as the trackerswitch is turned off?
private boolean tracking = false;
private Switch trackerswitch;
private final Timer timer= new Timer();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.table_layout);
final Handler handler=new Handler();
final int delay = 4000;
trackerswitch=findViewById(R.id.trackerswitch);
trackerswitch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NetworkAccess JSON = new NetworkAccess();
if(trackerswitch.isChecked()){
trackerswitch.setText("Tracking...");
tracking=true;
handler.postDelayed(new Runnable() {
#Override
public void run() {
NetworkAccess JSON = new NetworkAccess();
JSON.execute();
handler.postDelayed(this, delay);
}
},delay);
}
else{
tracking=false;
trackerswitch.setText("Start Tracking");
}
}
});
}
}
This is what's called in the network class:
public class NetworkAccess extends AsyncTask<Void, Void, Void> {
public ArrayList<String> alldata = new ArrayList<>();
public ArrayList<String> locationlist = new ArrayList<>();
int stride;
String data;
#Override
protected Void doInBackground(Void... voids) {//4B4ADC
SOME CODE WHICH ISN'T IMPORTANT FOR MY PROBLEM
alldata.addAll(elementlist);
locationlist.addAll(loctrack);
}
}
catch(IOException | JSONException e){
MainActivity.field1.setText(e.getClass().getCanonicalName());
}
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity.field1.setText(String.format("%20s %20s", alldata.get(0), alldata.get(1)));
COUPLE MORE OF THESE SETTEXT COMMANDS TO FILL A TABLE WITH DATA
}
}
Thanks for your help!
handler.postDelayed() adds objects of the Runnable you provide to the message queue, to be run at the specified interval. You need to remove all the queued objects from the message queue in order to cancel the execution. Calling JSON.cancel(true) does not affect other objects that are already added to the queue.
You'll have to retain a reference to your Runnable implementation and then call handler.removeCallbacks(r) to prevent further executions. Instead of using an anonymous class in handler.postDelayed().
This documentation page sheds more light on the matter.
Also refer this page for what happens when you call cancel(true) on an AsyncTask.

Moving logic from MainActivity to another class in Android

I have a simple logic on my app that looks for a certain pitch.
The problem is that the logic is in the OnCreate method of the app (it has to detect the pitch the moment the application is running).
It is a little bit ugly as I plan to add some more logic as the application starts.
Does anyone have any advice of how to move that code to a different class so that it could be invoked from there?
The class still has to access views in the main activity.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
Float value = pitchInHz;
Toast.makeText(getApplicationContext(),value.tostring(), Toast.LENGTH_SHORT).show();
}
});
}
}));
foo = new Thread(dispatcher,"Audio Dispatcher");
foo.start();
}
Basically, you have two options to make your code cleaner.
Move all the code in onCreate() (except first two lines) into another method, let's say lookForPitch(). Then you can call it right in onCreate().
If you plan to create more methods that focus on audio processing, you can create separate class, for example AudioUtils.java. This util class should contain public static methods, that you can invoke from any place in your code. In case of onCreate() you may call it like this: AudioUtils.lookForPitch(). Also if you want to handle Views, that are accessible only in your Activity, you can pass them as argument. So your method in AudioUtils can look like this:
public static void lookForPitch(TextView myTextView) {
// your code goes here
}
Just make it a method
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myLongAndSweetMethod();
}
private void myLongAndSweetMethod(){
AudioDispatcher dispatcher = AudioDispatcherFactory.fromDefaultMicrophone(22050,1024,0);
dispatcher.addAudioProcessor(new PitchProcessor(PitchEstimationAlgorithm.FFT_YIN, 22050, 1024, new PitchDetectionHandler() {
#Override
public void handlePitch(PitchDetectionResult pitchDetectionResult,
AudioEvent audioEvent) {
final float pitchInHz = pitchDetectionResult.getPitch();
runOnUiThread(new Runnable() {
#Override
public void run() {
Float value = pitchInHz;
Toast.makeText(getApplicationContext(),value.tostring(), Toast.LENGTH_SHORT).show();
}
});
}
}));
foo = new Thread(dispatcher,"Audio Dispatcher");
foo.start();
}
Then using the Code folding of Android Studio to hide it.
If you want to improve the readability of your code I can recommend the book "Clean Code: A Handbook of Agile Software Craftmanship" by Robert C. Martin (aka Uncle Bob).
This book is really great! It helped me to a lot to make my code more clean and easy to read. It is a book you should have read if you want to be(come) a professional software developer.

The method Sleep(int) is undefined for the type new Runnable(){} in Android

I have used code from Android recipe - create loading screen However, I am having problems using this.sleep() as I receive the error "The method Sleep(int) is undefined for the type new Runnable(){}" Other questions have stated that there is a problem with naming the class such as "Thread". This does not seem to be the case for my code though.
Code
public class LoadingScreenActivity extends Activity {
//Introduce an delay
private final int WAIT_TIME = 2500;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
System.out.println("LoadingScreenActivity screen started");
setContentView(R.layout.loading_screen);
findViewById(R.id.mainSpinner1).setVisibility(View.VISIBLE);
new Handler().postDelayed(new Runnable(){
#Override
public void run() {
//Simulating a long running task
this.Sleep(1000);
System.out.println("Going to Profile Data");
/* Create an Intent that will start the ProfileData-Activity. */
Intent mainIntent = new Intent(LoadingScreenActivity.this,ProfileData.class);
LoadingScreenActivity.this.startActivity(mainIntent);
LoadingScreenActivity.this.finish();
}
}, WAIT_TIME);
}
}
What you're probably looking for is
Thread.sleep(1000);
Runnable is an Interface and does not have any implemented methods of it's own, only public void run(), which you have to override and implement.
That code is simply wrong. It should be Thread.sleep(1000), but even so, it's not correct, and it will produce an "Application not responding" error, because it's trying to pause the UI thread.
Please take some time and read a Java and Android book. You won't get anywhere following half-baked implementations from the Internet.

android - Showing variable changes

Ok - I know there has got to be a simple solution to this but for the life of me I can't figure it out.
Programming a very basic android activity to simply iterate through 0-99. I have a textview that I want to display the count. What happens is that it simply stays blank until the end and then shows the ending count (99).
Not sure if a textview is the right way to display or what the answer is. Any help is greatly appreciated. Thanks in advance
Try using code like this in onCreate (where number is defined as a field):
textView.post(new Runnable() {
#Override
public void run() {
number++;
textView.setText("counting: " + number);
if (number < 100) {
textView.postDelayed(this, 50);
}
}
});
Edit: code was edited as View classes have post and postDelayed, which propagates call to Handler instance they have internally.
You need to read a bit about Handler class.
Warning: this code leaks Activity for the time of approximatelly 5 seconds and should not be used directly in production code. You need to remove Runnable from the message queue at the appropriate time (maybe in onDestroy, but it depends on your needs).
View.removeCallbacks for anti-memory-leak.
My guess is that your onCreate() has code like this:
for (int i=0;i<100;i++) {
tv.setText(String.valueOf(i));
Thread.sleep(100); // or something to delay for a bit
}
That will give you the output that you are describing.
As with many GUI frameworks, Android's UI is event-driven. Calling setText() does not update the screen. Rather, it puts a message on a queue, asking for the screen to be updated. That queue is processed by the main application thread... the same thread that is calling onCreate() in the first place. Hence, what you are doing is queuing up 100 setText() calls, none of which will be processed until your loop is complete. Applying the 100 of them takes very little time, giving the visual result of only seeing the last change.
User a timer scheduled at a fixed rate. Increment a counter every second. Set the text on the UI thread. cancel the timer when required.
public class MainActivity extends Activity {
TextView _tv;
Timer _t;
int _count=0;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
_tv = (TextView) findViewById( R.id.textView1 );
_t = new Timer();
_tv.setText(""+_count);
_t.scheduleAtFixedRate( new TimerTask() {
#Override
public void run() {
_count++;
runOnUiThread(new Runnable() //run on ui thread
{
public void run()
{
_tv.setText(""+_count);
if(_count==99)
{
_t.cancel();
}
}
});
}
}, 1000, 1000 );
}
#Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
_t.cancel();
}
}
Use a countdown timer, in below code, onTick() will get called every second, here you can display/update your number each second.
set interval according to your need. Its in mili seconds.
public class TimerActivity extends Activity {
private final long startTime = 100 * 1000;
private final long interval = 1 * 1000;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_timer);
countDownTimer = new MyCountDownTimer(startTime, interval);
countDownTimer.start();
public class MyCountDownTimer extends CountDownTimer {
public MyCountDownTimer(long startTime, long interval) {
super(startTime, interval);
}
#Override
public void onFinish() {
text.setText("Time's up!");
}
#Override
public void onTick(long millisUntilFinished) {
text.setText(100 - millisUntilFinished/1000);
}
}
}

findViewById(R.id.progressbar) and static hell:

clipProgress resets a progress bar to zero:
public class Whatever extends Activity implements ListenerA, ListenerB {
private Handler mHandler = new Handler();
ProgressBar myProgressBar;
int myProgress = 0;
public void clipProgress() {
myProgressBar = (ProgressBar)findViewById(R.id.progressbar);
myProgressBar.setProgress(myProgress);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
clipProgress();
public void onClick(View view) {
Duration.duration = 0;
startRecording();
new Thread(new Runnable(){
#Override
public void run() {
while(myProgress<100){
try{
myHandle.sendMessage(myHandle.obtainMessage());
Thread.sleep(50);
}catch(Throwable t){
}
}
}
}).start();
}
Handler myHandle = new Handler(){
#Override
public void handleMessage(Message msg) {
myProgress++;
myProgressBar.setProgress(myProgress);
}
};
});
}
I have two different methods, below, where I suppose I could attempt to reset the progress bar to zero. But they both reside in a different class entirely:
#Override protected void onPostExecute(Void result) {
Whatever.clipProgress(); //...which throws all kinds of red lines if I use this statement.
}
and
public void stopThis(){
thing.stop();
try {
Whatever.clipProgress(); //...which throws all kinds of red lines if I use this statement.
dos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Eclipse wants to apply "static" to various initializations in the Whatever class. But those fixes just leave findViewById(R.id.progressbar) redlined.
I just want to reset the progress bar (which was started from within class 1) from class 2. It's too much to ask for.
UPDATE:
Whatever is the starting point, the Android Activity. Its handler, which I should have included in the above snip, right underneath onCreate is included in the snip below. So more completely, and invoked from a button, is thus:
public void onClick(View view) {
new Thread(new Runnable(){
#Override
public void run() {
while(myProgress<100){
try{
myHandle.sendMessage(myHandle.obtainMessage());
Thread.sleep(87);
}catch(Throwable t){
}
}
}
}).start();
}
Handler myHandle = new Handler(){
#Override
public void handleMessage(Message msg) {
myProgress++;
myProgressBar.setProgress(myProgress);
}
};
Once that button is pressed a listener is produced:
myTask = new Task();
myTask.addTaskListener(this);
myTask.execute(this);
It is inside the Task class:
public class Task extends AsyncTask<Whatever, ArrayList<Float>, Void>{
...
public void addTaskListener(TaskListener rl){
this.rl = rl;
}
...
that those two functions reside, one of which I'm hoping to use to stop the progress bar.
A task is designed elsewhere to shut down at exactly a constant number of seconds, and so I can hardcode 50 into that progress bar value on the sleep(). I just want to be able to shut the bar down back to zero earlier if I stop the task earlier.
Eclipse warns you because you are accessing the clipProgress method in a static way (by calling Whatever.clipProgress()).
Also, you should only assign your progress bar once, so move
myProgressBar = (ProgressBar)findViewById(R.id.progressbar);
into your onCreate method.
You should probably pass the ProgressBar into the other class that needs to update it, and then do something like
myProgressBar.post(new Runnable() {
public void run() {
myProgressBar.setProgress(myProgress);
}
});
this post method updates the ProgressBar on the UI thread, which is the only safe way to do it. Make sure you are only doing any of this if the activity with the ProgressBar is currently active.
You should also question whether you need to update the progress bar in this other class at all. I only see you updating the myProgress variable inside your handler, where you set the progress on the bar immediately after. Could you explain a bit more about the relationship between this other class and the activity with your progress bar?
This has more to do with Java programming and instance / static methods than with Android:
When you call Whatever.clipProgress() you're attempting to make a call to a static method call to clipProgress on the Whatever class, which is why Eclipse wants to add the static modifier.
However, findViewById() is an instance method (read the link above) on Activity, which is presumably some ancestor of your Whatever class. Static methods can't call instance methods statically, so even if you let Eclipse make clipProgress static, you need to call clipProgress on an instance of your activity.
Your other class needs to have a reference to the instance of the class on which you want to call clipProgress(). You should either pass in that reference to the other class, or as another poster noted, pass in a reference to the ProgressBar itself.
Then you can call e.g. mWhatever.clipProgress() or progressBar.setProgress(0).

Categories