I am writing a small app that capture the audio from the android MIC, performs a FFT on the input and then graphs the chart to the user. I am trying to do the recording and graphing concurrently (obviously with a small delay from being recorded to being graphed). I am attempting to launch two threads, one to read and one to process. However, I am having synchronization issues when I process it seems to only be receiving (or not) zeros. Any advise would be greatly appreciated. :)
public class Plotter extends Activity {
/* plotting objects */
private static GraphicalView mView;
private LineGraph line = new LineGraph();
private boolean recordAudio = true; // record?
private AudioRecord mRecorder = null; // audio object
private Menu mMenu; // app menu
private static final String LOG_TAG = "Frequency Plotter"; // debug tag
private Mfft mfft = null; // FFT class
private static final int BUF_SIZE = 8192; // amount to read in
private Thread listener = null;
private Thread processor = null;
Stack<Float> items = new Stack<Float>();
/* colors for line */
private int[] colors = {Color.BLUE,Color.CYAN,Color.DKGRAY,Color.GRAY,
Color.GREEN,Color.LTGRAY,Color.MAGENTA,Color.RED,Color.WHITE,Color.YELLOW};
private void processAudio(){
ArrayList<Double> real = new ArrayList<Double>();
try{
Random randomGenerator = new Random();
float[] in = new float[2048];
Arrays.fill(in,1);
while(true){
synchronized(items){
while(items.size() < 2048)
items.wait();
items.notifyAll();
for(int i=0; i < 2048; i++){
in[i] = items.pop();
}
}
double[] ret = mfft.fft(2048,44100,in); // get FFT of data
TimeSeries dataset = new TimeSeries( (real.size()+1)/2048 + "" );
XYSeriesRenderer renderer = new XYSeriesRenderer(); // customized renderer
// Customization time
renderer.setColor(colors[randomGenerator.nextInt(10)]);
renderer.setPointStyle(PointStyle.SQUARE);
renderer.setFillPoints(true);
line.addRenderer(renderer); // add custom renderer
for(int i = 0; i < 2048; i++){
real.add(ret[i]);
dataset.add(real.size()-1,ret[i]); // Add it to our graph
}
line.addDataset(dataset); // add data to line
mView.repaint(); // render lines
}
}catch(Exception e){
Log.e(LOG_TAG, e + " ");
}
}
private void writeToBuffer(short[] in) {
synchronized(items){
for(int i = 0; i < BUF_SIZE; i++){ // copy to create float
items.push((float)in[i]);
}
items.notifyAll();
}
}
private void listen(){
final short[] in = new short[BUF_SIZE];
mRecorder = new AudioRecord(
MediaRecorder.AudioSource.MIC, // source
44100, // frequency (HERTZ)
AudioFormat.CHANNEL_IN_MONO, // channel
AudioFormat.ENCODING_PCM_16BIT, // format
BUF_SIZE // size data packet
);
mRecorder.startRecording();
while(recordAudio){
try{
/* read next part */
mRecorder.read(in,0,BUF_SIZE); // read from device
writeToBuffer(in);
}catch(Exception t){
/* something went horribly wrong!!!*/
recordAudio = false;
Log.e(LOG_TAG, "Failure reading" + t.getMessage());
}
}
}
private void startRecording(){
/* create a new thread that will run the recording in the background */
listener = new Thread(
new Runnable(){
public void run(){
listen();
}
});
listener.start();
/* small delay to produce */
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
/* create a thread to process the audio */
processor = new Thread(
new Runnable(){
public void run(){
processAudio();
}
});
processor.start();
}
private void stopRecording(){
recordAudio = false;
mRecorder.stop();
mRecorder.release();
mRecorder = null;
}
/** clear the current chart */
private void clearChart(){
line = new LineGraph();
this.onStart();
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
#Override
protected void onStart() {
super.onStart();
/* instantiate */
mfft = new Mfft(); // instance of the FFT class
mView = line.getView(this); // get the chart view
/* new horizontal layout */
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.addView(mView); // add chart to layout
setContentView(ll); // set layout
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.record:
startRecording();
item.setEnabled(false); // disable start
mMenu.findItem(R.id.stop).setEnabled(true); // enable stop
return true;
case R.id.stop:
stopRecording();
item.setEnabled(false); // disable stop
mMenu.findItem(R.id.clear).setEnabled(true); // enable stop
return true;
case R.id.clear:
clearChart(); // clear chart
item.setEnabled(false); // disable clear
mMenu.findItem(R.id.record).setEnabled(true); // enable stop
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
mMenu = menu;
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.my_menu, menu);
return true;
}
}
Edit: Added full definitions.
several thoughts...
similar example code, Audalyzer
Unfortunately the author has stopped development on this project, but the source tarball is still available online. In particular note: org.hermit.android.io.AudioReader.java . You read the audio and pass it via a Stack object, this author uses short [] arrays. (still that does not seem like it should be your problem source...)
http://code.google.com/p/moonblink/downloads/detail?name=SourceTarball.zip
BUF_SIZE thoughts
Your audio buffer (BUF_SIZE = 8192) feels a bit small. How does that relate to AudioRecord.getMinBufferSize()? I used 2x minBufferSize, and that's without doing any calculations on it (only read/write).
Handler thoughts
I'm still reviewing your code, unclear how your threads communicate. But, your problem sounds like it needs a way for the threads to communicate a Handler.
Below are the links I've been reviewing to grasp how to use Handlers and communicate between threads effectively:
threads - nice overview of handlers (withOUT looper). w/ code example: com.indy.testing.TestMain.java.MyThread.java
http://indyvision.net/2010/02/android-threads-tutorial-part-3/
threads - ok overview of handlers and loopers
http://techtej.blogspot.com/2011/02/android-passing-data-between-main.html
threads w/ 2way comm. w/ code example: sample.thread.messaging.ThreadMessaging.java
http://codinghard.wordpress.com/2009/05/16/android-thread-messaging/
Related
I am using this code to play a sound
final MediaPlayer mp = MediaPlayer.create(this, R.raw.sound);
mp.setOnCompletionListener(new OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
mp.release();
}
});
It works fine on its own, however there was a problem after I added an animation that extends ImageView, which refreshes(by calling handler.postDelayed) the image resource at an interval about 30ms to create animation. The problem is that when the animation starts, it terminates the the playing of the sound. Here is the code for the Runnable that refreshes the ImageView.
private Runnable runnable = new Runnable () {
public void run() {
String name = "frame_" + frameCount;
frameCount ++;
int resId = mContext.getResources().getIdentifier(name, "drawable", mContext.getPackageName());
imageView.setImageResource(resId);
if(frameCount < totalFrameCount) {
mHandler.postDelayed(runnable, interval);
}
}
};
I also tried to use a thread that calls the anmiationView.postInvalidate to do the animation, however it has the same problem. Please help. Thanks
Edit:
It looks like the problem is due to WHEN the animation is called. Previously I called it in the onActivityResult of the activity. Looks like this is not the right place to call. Now I put the animation view in a popupWindow and play it there, it works properly. Not sure exactly why.
in handler's comments :
"A Handler allows you to send and process {#link Message} and Runnable
objects associated with a thread's {#link MessageQueue}. Each Handler
instance is associated with a single thread and that thread's message
queue. When you create a new Handler, it is bound to the thread /
message queue of the thread that is creating it -- from that point on,
it will deliver messages and runnables to that message queue and execute
them as they come out of the message queue."
so, the problem may be caused by both of animation and media playing operations are in
the same message queue own by which thread create the handler (let's say the main thread).
if the animation loops for ever, then the media player will hardly get any chance to run.
you could take it a try with HandlerThread, the thread will contain a new looper for the
handler created from it, all the runnables added to that handler will be running in another
individual thread.
the animation thread and the media play thread should be running in the different threads not
scheduling in the same one.
hope, it helps.
the HandlerThread usage and some discuss looks like this :
How to create a Looper thread, then send it a message immediately?
maybe it is caused by your miss arranged codes, i take it a try on my nexus 4 with android version 4.4.2, even no any cache tech, the animation and music works like a charm...
here is the major codes :
public class MainActivity extends Activity implements View.OnClickListener {
protected static final String TAG = "test002" ;
protected static final int UPDATE_ANI = 0x0701;
protected static final int UPDATE_END = 0x0702;
protected static final int[] ANI_IMG_IDS = {R.raw.img1, R.raw.img2, R.raw.img3, R.raw.img4,
R.raw.img5, R.raw.img6, R.raw.img7};
protected static final int[] BTN_IDS = {R.id.btnStart, R.id.btnStop};
protected android.os.Handler aniHandler = null; // async update
protected boolean isAniRunning = false ;
protected int aniImgIndex = 0 ;
protected ImageView aniImgView = null ;
protected MediaPlayer mediaPly = null ;
// animation timer
class AniUpdateRunnable implements Runnable {
public void run() {
Message msg = null ;
while (!Thread.currentThread().isInterrupted() && isAniRunning) {
msg = new Message();
msg.what = UPDATE_ANI;
aniHandler.sendMessage(msg);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break ;
}
}
msg = new Message() ;
msg.what = UPDATE_END ;
aniHandler.sendMessage(msg) ;
}
}
protected void prepareMediaPlayer(MediaPlayer mp, int resource_id) {
AssetFileDescriptor afd = getResources().openRawResourceFd(resource_id);
try {
mp.reset();
mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getDeclaredLength());
afd.close();
mp.prepare();
} catch (IllegalArgumentException e) {
Log.d(TAG, "IlleagalArgumentException happened - " + e.toString()) ;
} catch(IllegalStateException e) {
Log.d(TAG, "IllegalStateException happened - " + e.toString()) ;
} catch(IOException e) {
Log.d(TAG, "IOException happened - " + e.toString()) ;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// init : buttons onclick callback
{
Button btn;
int i;
for (i = 0; i < BTN_IDS.length; i++) {
btn = (Button) findViewById(BTN_IDS[i]);
btn.setOnClickListener(this);
}
}
// init : update animation handler callback
{
aniHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_ANI:
updateAniImages();
break ;
case UPDATE_END:
updateAniEnd();
break ;
default:
break;
}
}
};
}
// init : prepare image view
{
aniImgView = (ImageView)findViewById(R.id.imgAni) ;
mediaPly = MediaPlayer.create(this, R.raw.happyny) ;
mediaPly.setLooping(true);
}
}
protected void updateAniImages() {
if(aniImgIndex >= ANI_IMG_IDS.length) {
aniImgIndex = 0 ;
}
InputStream is = getResources().openRawResource(ANI_IMG_IDS[aniImgIndex]) ;
Bitmap bmp = (Bitmap) BitmapFactory.decodeStream(is) ;
aniImgView.setImageBitmap(bmp);
aniImgIndex++ ;
}
protected void updateAniEnd() {
aniImgIndex = 0 ;
aniImgView.setImageBitmap(null);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnStart:
isAniRunning = true ;
// no re-enter protectiion, should not be used in real project
new Thread(new AniUpdateRunnable()).start();
mediaPly.start();
break;
case R.id.btnStop:
isAniRunning = false ;
mediaPly.stop();
prepareMediaPlayer(mediaPly, R.raw.happyny);
break;
default:
break;
}
}
}
the major project codes and test apk should be find here :
apk installer
source code
I am trying to make an audio app which allows sound to be projected through the speaker via the mic. I made a toggle button for this app but after click on it, it stays highlighted and I can't adjust the system volume.
So how do i break this loop?
private void setUpButton() {
final ToggleButton tb = (ToggleButton) findViewById(R.id.tb);
tb.setOnClickListener(new View.OnClickListener (){
public void onClick(View view) {
// TODO Auto-generated method stub
boolean on = ((ToggleButton) view).isChecked();
if (on){
boolean is = true;
android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_URGENT_AUDIO);
int buffersize = AudioRecord.getMinBufferSize(20000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT);
AudioRecord arec = new AudioRecord(MediaRecorder.AudioSource.MIC, 20000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, buffersize);
AudioTrack atrack = new AudioTrack(AudioManager.STREAM_MUSIC, 20000, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, buffersize, AudioTrack.MODE_STREAM);
atrack.setPlaybackRate(20000);
byte[] buffer = new byte[buffersize];
arec.startRecording();
atrack.play();
while(is) {
arec.read(buffer, 0, buffersize);
atrack.write(buffer, 0, buffer.length);
}
}
}});
}
The toggle button is staying highlighted because you have your infinite while(is){...} loop running in the main UI thread (as per 323go's comment). The main UI thread becomes busy constantly running the infinite loop and therefore does not have the opportunity to process any more UI actions such as further button state updates etc.
Move your infinite loop into it's own thread with a message handler to handle messages from your main UI thread telling it when to start or stop etc. This will free up the main UI thread to do what it's meant to be doing ... handling the User Interface.
For example:
MainActivity
public class MainActivity {
private AudioScheduler mAudioThread;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAudioThread = new AudioThread();
mAudioThread.start();
final ToggleButton tb = (ToggleButton) findViewById(R.id.tb);
tb.setOnClickListener(new View.OnClickListener (){
public void onClick(View view) {
Message messageToAudioThread = Message.obtain();
boolean on = ((ToggleButton) view).isChecked();
if (on){
messageToAudioThread.what = AudioThread.PLAY;
} else {
messageToAudioThread.what = AudioThread.STOP;
}
mAudioThread.getHandler().sendMessage(messageToAudioThread);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mAudioThread.close();
}
}
AudioThread
public class AudioThread extends Thread {
// Handler message constants
public static final int PLAY = 0;
public static final int STOP = 1;
// Class variables
private boolean mRunning = false;
private boolean mPlayAudio = false;
private static class AudioThreadHandler extends Handler {
private final WeakReference<AudioThread> mThread;
public AudioThreadHandler(AudioThread thread) {
mThread = new WeakReference<AudioThread>(thread);
}
#Override
public void handleMessage(Message msg) {
AudioThread thread = mThread.get();
if (thread != null) {
switch(msg.what) {
case PLAY:
thread.mPlayAudio = true;
break;
case STOP:
thread.mPlayAudio = false;
break;
}
}
}
};
private final AudioThreadHandler mAudioThreadHandler = new AudioThreadHandler(this);
#Override
public void run() {
try {
mRunning = true;
while(mRunning) {
if (mPlayAudio) {
// Your code for handling audio recording/playback
// or whatever goes here
} else {
// If you have code you want to keep executing while
// audio is not active, stick it here
}
}
} catch (Exception e) {
Log.e("AudioThread", "Thread Loop Exception: " + e);
}
}
public void close() {
mRunning = false;
}
public Handler getHandler() {
return mAudioThreadHandler;
}
}
From your description you are wanting to record, manipulate (adjust volume etc), and output audio all in real time. For that level of audio processing you will need to understand and use threads. There are plenty of good examples and tutorials out there if you don't already have a good grasp of threads.
I am having some trouble with controlling a stepper motor using a IOIO board. I started with the HelloIOIOPower example and added my code. The program will run and turn on/off the LED when the toggle button is pressed but if a button is pressed that changes the value of motor_command in the UI thread the motor control signals do not get set and the program appears to hang. It does not crash but the LED control is lost.
I tried using synchronized on the switch statement in the run method to have the entire switch statement evaluated before allowing a change to motor_control in the UI thread.
I believe the correct way to implement this is to bind to the IOIO thread but I have been unsuccessful in getting that to work correctly.
I would like to be able to change the value of "motor_control" to create a control command to the IOIO thread to move the stepper motor. Once the command is executed the IOIO thread sets "motor_control" to zero which is a do noting or hold state.
Running the IOIO in a service would be preferred but is not required.
I plan to have this app running 24/7 with intermittent commands sent to the stepper motor. what is the correct method for controlling a stepper motor via a IOIO when only intermittent commands will be sent to the IOIO?
this is my first attempt at Java/ Android/ IOIO please feel free to point out all errors, I would love to learn where I can improve....
Thanks...
`
package com.medo.testmotor4;
import ioio.lib.api.DigitalOutput;
import ioio.lib.api.IOIO;
import ioio.lib.api.IOIOFactory;
import ioio.lib.api.exception.ConnectionLostException;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ToggleButton;
public class TestMotor4 extends Activity {
/** The text displayed at the top of the page. */
private TextView title_;
/** The toggle button used to control the LED. */
private ToggleButton button_;
Button full;
Button half;
Button step16th;
Button cup;
Button dish;
TextView cup_count;
TextView random_num;
int traycount = 0; // try cup position
int motor_command = 0; //motor command index value for switch statement
/** The thread that interacts with the IOIO. */
private IOIOThread ioio_thread_;
/**
* Called when the activity is first created. Here we normally initialize
* our GUI.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_motor4);
title_ = (TextView) findViewById(R.id.title);
button_ = (ToggleButton) findViewById(R.id.button);
// Assign value to Text Views:
cup_count = (TextView)findViewById(R.id.tv_cup_count);
cup_count.setText(Integer.toString(traycount));
random_num = (TextView)findViewById(R.id.tv_random_number);
random_num.setText("no number");
// Set up buttons
dish = (Button) findViewById(R.id.btn_dish);
dish.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View arg0) {
// call method that moves motor one full step
motor_command = 4;
Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
}
});
full = (Button) findViewById(R.id.btn_fullstep);
full.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View arg0) {
// call method that moves motor one full step
motor_command = 1;
Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
}
});
cup = (Button) findViewById(R.id.btn_cup);
cup.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View arg0) {
// call method that moves motor one full step
traycount ++;
if (traycount % 3 == 0 )
motor_command = 3; // every 3rd tray cup move 54 1/8 steps
// all others move 53 1/8 steps
else motor_command = 2;
Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
cup_count.setText(Integer.toString(traycount));
}
});
half = (Button) findViewById(R.id.btnhalfstep);
half.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View arg0) {
// call method that moves motor one half step
motor_command = 3;
Toast.makeText(TestMotor4.this, "Motor_Command Set to: " + motor_command,Toast.LENGTH_SHORT).show();
}
});
} //onCreate
/**
* Called when the application is resumed (also when first started). Here is
* where we'll create our IOIO thread.
*/
#Override
protected void onResume() {
super.onResume();
ioio_thread_ = new IOIOThread();
ioio_thread_.start();
}
/**
* Called when the application is paused. We want to disconnect with the
* IOIO at this point, as the user is no longer interacting with our
* application.
*/
#Override
protected void onPause() {
super.onPause();
ioio_thread_.abort();
try {
ioio_thread_.join();
} catch (InterruptedException e) {
}
}
/**
* This is the thread that does the IOIO interaction.
*
* It first creates a IOIO instance and wait for a connection to be
* established. Then it starts doing the main work of opening the LED pin
* and constantly updating it to match the toggle button's state.
*
* Whenever a connection drops, it tries to reconnect, unless this is a
* result of abort().
*/
class IOIOThread extends Thread {
private IOIO ioio_;
private boolean abort_ = false;
DigitalOutput led;
private DigitalOutput dir;
private DigitalOutput stp;
private DigitalOutput slp_,rst_, ms3, ms2, ms1,en_;
private DigitalOutput led_;
private DigitalOutput dish;
private static final int STEP_WAIT = 5;
/** Thread body. */
#Override
public void run() {
super.run();
while (true) {
synchronized (this) {
if (abort_) {
break;
}
ioio_ = IOIOFactory.create();
}
try {
setText(R.string. wait_ioio);
ioio_.waitForConnect();
setText(R.string.ioio_connected);
//IOIO pin setup:
//DigitalOutput led = ioio_.openDigitalOutput(0, true);
rst_ = ioio_.openDigitalOutput(13, true); // /RESET
ms3 = ioio_.openDigitalOutput(14, true); // ms3 set step size L L L full step
ms2 = ioio_.openDigitalOutput(15, true); // ms2
ms1 = ioio_.openDigitalOutput(16, true); // ms1 H H H 1/16 step
en_ = ioio_.openDigitalOutput(17, false); // /enable
dir = ioio_.openDigitalOutput(18, false); // DIRECTION - set initial direction of motor rotation
stp = ioio_.openDigitalOutput(19, false); // STEP
slp_ = ioio_.openDigitalOutput(20, true); // /SLEEP -
led = ioio_.openDigitalOutput(IOIO.LED_PIN);
dish = ioio_.openDigitalOutput(9, false); // Dish - linear actuator activate signal
Log.d ("IOIOLooper setup", "Complete");
int test = 0;
Toast.makeText(TestMotor4.this, "Starting while loop",Toast.LENGTH_SHORT).show();
while (true) {
led.write(!button_.isChecked());
switch(motor_command){
case 0: // hold - do nothing for 5 sec.
this.timeout(100);
break;
case 1: //full step on motor
full();
Log.d("STEPPER-FULL","Complete");
motor_command = 0;
break;
case 2: // move motor 1 cup - (53) 1/8 steps
cup(53);
motor_command = 0;
Log.d("STEPPER-CUP - 53","Complete" );
break;
case 3: // move motor 1 cup (54) 1/8 steps
cup(54);
motor_command = 0;
Log.d("STEPPER-CUP - 54","Complete" );
break;
case 4: // dump scoop dish
dish.write(true);
//Toast.makeText(TestMotor4.this, "Dish set HIGH: ",Toast.LENGTH_SHORT).show();
this.timeout(5000);
dish.write(false);
//Toast.makeText(TestMotor4.this, "Dish set LOW " + motor_command,Toast.LENGTH_SHORT).show();
motor_command = 0;
break;
} // switch
sleep(10);
}
} catch (ConnectionLostException e) {
setText(R.string.connectionLost);
} catch (Exception e) {
Log.e("TestMotor4", "Unexpected exception caught", e);
ioio_.disconnect();
break;
} finally {
if (ioio_ != null) {
try {
ioio_.waitForDisconnect();
} catch (InterruptedException e) {
}
}
synchronized (this) {
ioio_ = null;
}
}
} // while(true)
}// Run
/**
* Abort the connection.
*
* This is a little tricky synchronization-wise: we need to be handle
* the case of abortion happening before the IOIO instance is created or
* during its creation.
*/
synchronized public void abort() {
abort_ = true;
if (ioio_ != null) {
ioio_.disconnect();
}
}// abort
/**
* Set the text line on top of the screen.
*
* #param id
* The string ID of the message to present.
*/
private void setText(final int id) {
runOnUiThread(new Runnable() {
#Override
public void run() {
title_.setText(getString(id));
}
});
} //setText
public void full ()throws ConnectionLostException{
// move motor 1 full step (1.8 degrees)
// requires stp to be LOW before entering method - leaves stp LOW on exit of method
try{
en_.write(false); // enable MC outputs
// configure for full step ( L L L)
ms1.write(false);
ms2.write(false);
ms3.write(false);
Log.d("Set FULL", "L L L");
//send step signal with LOW To HIGH transition
stp.write(true);
timeout(STEP_WAIT);
stp.write(false);
this.timeout(STEP_WAIT);
}catch(ConnectionLostException e) {
//enableUi(false);
Toast.makeText(TestMotor4.this, "IOIO Connection Lost ",Toast.LENGTH_LONG).show();
throw e;
}
} // full
public void cup(int num) throws ConnectionLostException{
try{
// set step size to 1/8 step
ms1.write(true);
ms2.write(true);
ms3.write(false);
Log.d("Set 1/8 step size ", "H H L");
// set up wait for signals to stabilize
this.timeout(STEP_WAIT);
//move motor 53 steps
for (int i = 0; i<num; i++){
stp.write(true);
this.timeout(STEP_WAIT);
stp.write(false);
this.timeout(STEP_WAIT);
}
}catch (ConnectionLostException e){
Toast.makeText(TestMotor4.this, "IOIO Connection Lost ",Toast.LENGTH_LONG).show();
throw e;
}
} //cup
private void timeout(int ms) {
try {
sleep(ms);
} catch (InterruptedException e) {
// Do nothing...
}
}
} //IOIOThread
}`
I have a Async Task that does not add the percentage while it is going through the task. It always stays at 0% 0/100
Here is my code
private class getAppInfo extends AsyncTask<String, String, String> {
/** The system calls this to perform work in a worker thread and
* delivers it the parameters given to AsyncTask.execute() */
ProgressDialog dialog;
#Override
protected void onPreExecute() {
if(showLoading == true){
dialog = new ProgressDialog(SelfHelp.this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.setMessage("Loading");
dialog.setIndeterminate(true);
dialog.setCancelable(false);
dialog.setMax(100);
dialog.setProgress(100);
dialog.show();
}
}
#Override
protected String doInBackground(String... urls) {
String xml = null;
int count = 0;
try {
// defaultHttpClient
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(urls[0]);
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
xml = EntityUtils.toString(httpEntity);
while(count != 100){
publishProgress(""+count);
count += 5;
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
Document doc = parser.GetDomElement(xml);
NodeList nl = doc.getElementsByTagName("topic");
getChildElements(nl);
return xml;
}
#Override
protected void onProgressUpdate(String... progress) {
Log.v("count",progress[0]);
dialog.setProgress(Integer.parseInt(progress[0]));
}
/** The system calls this to perform work in the UI thread and delivers
* the result from doInBackground() */
#Override
protected void onPostExecute(String result) {
//dialog.setProgress(100);
menuList.setAdapter(setListItems(menuItems));
menuList.setTextFilterEnabled(true);
if(showLoading == true){
dialog.dismiss();
showLoading = false;
}
}
It does go into onProgressUpdate and the count goes up by 5 but the progress bar does not change. How can I have it increment by 5 and show the progress properly?
Your issue is related to setIndeterminate(true): You should set it to false if you want to have progress update. if you setIndeterminate(true) then the ProgressDialog will work as the classic Windows Hourglass
You can try following code, It is showing progress in % ratio, here is the code,
public class ProgressBarExampleActivity extends Activity
{
ProgressThread progThread;
ProgressDialog progDialog;
Button button1, button2;
int typeBar; // Determines type progress bar: 0 = spinner, 1 = horizontal
int delay = 1000; // Milliseconds of delay in the update loop
int maxBarValue = 30; // Maximum value of horizontal progress bar
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// // Process button to start spinner progress dialog with anonymous inner class
// button1 = (Button) findViewById(R.id.Button01);
// button1.setOnClickListener(new OnClickListener()
// {
// public void onClick(View v)
// {
// typeBar = 0;
// showDialog(typeBar);
// }
// });
// Process button to start horizontal progress bar dialog with anonymous inner class
button2 = (Button) findViewById(R.id.Button02);
button2.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
typeBar = 1;
showDialog(typeBar);
}
});
}
// Method to create a progress bar dialog of either spinner or horizontal type
#Override
protected Dialog onCreateDialog(int id)
{
switch(id)
{
// case 0: // Spinner
// progDialog = new ProgressDialog(this);
// progDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
// progDialog.setMessage("Loading...");
// progThread = new ProgressThread(handler);
// progThread.start();
// return progDialog;
case 1: // Horizontal
progDialog = new ProgressDialog(this);
progDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progDialog.setMax(maxBarValue);
progDialog.setMessage("Dollars in checking account:");
progThread = new ProgressThread(handler);
progThread.start();
return progDialog;
default:
return null;
}
}
// Handler on the main (UI) thread that will receive messages from the
// second thread and update the progress.
final Handler handler = new Handler()
{
public void handleMessage(Message msg)
{
// Get the current value of the variable total from the message data
// and update the progress bar.
int total = msg.getData().getInt("total");
progDialog.setProgress(total);
// if (total >= maxBarValue)
if (total <= 0 )
{
dismissDialog(typeBar);
progThread.setState(ProgressThread.DONE);
}
}
};
// Inner class that performs progress calculations on a second thread. Implement
// the thread by subclassing Thread and overriding its run() method. Also provide
// a setState(state) method to stop the thread gracefully.
private class ProgressThread extends Thread
{
// Class constants defining state of the thread
final static int DONE = 0;
final static int RUNNING = 1;
Handler mHandler;
int mState;
int total;
// Constructor with an argument that specifies Handler on main thread
// to which messages will be sent by this thread.
ProgressThread(Handler h)
{
mHandler = h;
}
// Override the run() method that will be invoked automatically when
// the Thread starts. Do the work required to update the progress bar on this
// thread but send a message to the Handler on the main UI thread to actually
// change the visual representation of the progress. In this example we count
// the index total down to zero, so the horizontal progress bar will start full and
// count down.
#Override
public void run()
{
mState = RUNNING;
total = maxBarValue;
while (mState == RUNNING)
{
// The method Thread.sleep throws an InterruptedException if Thread.interrupt()
// were to be issued while thread is sleeping; the exception must be caught.
try
{
// Control speed of update (but precision of delay not guaranteed)
Thread.sleep(delay);
} catch (InterruptedException e) {
Log.e("ERROR", "Thread was Interrupted");
}
// Send message (with current value of total as data) to Handler on UI thread
// so that it can update the progress bar.
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putInt("total", total);
msg.setData(b);
mHandler.sendMessage(msg);
total--; // Count down
}
}
// Set current state of thread (use state=ProgressThread.DONE to stop thread)
public void setState(int state)
{
mState = state;
}
}
}
See the output,
I will mention another aproach, because I came across this solution when I was looking for some practical way how to communicate from my Service running AsyncTask back to main UI. Lucifer's solution is not modular for Services, if you need to use your Service in more then 1 class (that was my case), you won't be able to access variable handler and as far as I know you can't even send Handler as Intent to Service (you can send it to AsyncTask tho). Solution is broadcasting.
sendBroadcast(new Intent(WORK_DONE));
in AsyncTask and
private BroadcastReceiver receiver = new BroadcastReceiver() {
public void onReceive(Context c, Intent i) { //update your UI here }
}
registerReceiver(receiver, new IntentFilter(WORK_DONE));
in your activity.
I don't like all those inner classes android developers use. I understand it's easier to create inner class and access outer class variables, but once you need to use the class again your doomed and you have to edit the code! I am realy new to Android, maybe I am wrong and you actually don't need to reuse those classes. Never did a bigger project so I have no idea but it just doesn't feel right, since on college, they tried hard to teach us how to programm reusable code.
I have written progress bar when I consume WCF part. I have to know after get response it need to dismiss.
ProgressThread progThread;
ProgressDialog progDialog;
int typeBar; // Determines type progress bar: 0 = spinner, 1 = horizontal
int delay = 40; // manually define thedelay
int maxBarValue = 200; // manually define the maximum value
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case 0:
typeBar = 0;
showDialog(typeBar);
return true;
case 1:
return true;
default:
return super.onOptionsItemSelected(item);
}
}
#Override
protected Dialog onCreateDialog(int id) {
// Spinner
progDialog = new ProgressDialog(this);
progDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progDialog.setMessage("Loading...");
progThread = new ProgressThread(handler);
progThread.start();
return progDialog;
}
final Handler handler = new Handler() {
public void handleMessage(Message msg) {
int total = msg.getData().getInt("total");
progDialog.setProgress(total);
if (total <= 0){
dismissDialog(typeBar);
progThread.setState(ProgressThread.DONE);
}
}
};
This is thread class
private class ProgressThread extends Thread {
final static int DONE = 0;
final static int RUNNING = 1;
Handler mHandler;
int mState;
int total;
ProgressThread(Handler h) {
mHandler = h;
}
#Override
public void run() {
mState = RUNNING;
total = maxBarValue;
while (mState == RUNNING) {
try {
loadDownloadData();
Thread.sleep(delay);
} catch (InterruptedException e) {
Log.e("ERROR", "Thread was Interrupted");
}
Message msg = mHandler.obtainMessage();
Bundle b = new Bundle();
b.putInt("total", total);
msg.setData(b);
mHandler.sendMessage(msg);
total--;
}
}
public void setState(int state) {
mState = state;
}
}
WCF call method is loadDownloadData(); , Where want to call & how we can define the time frame for sleep, after getting response only it need to dismis this progress bar...
Please help me
Thanks in advance.
What exactly should your method do?
At the moment it looks like you are calling some method in loop and after that you sleep a thread. If your loadDownloadData calls the WCF it will be called in each cycle? If it doesn't start asynchronous processing it will wait until the method completes and only after that it will continue to sleeping the thread.
If you want to have progress bar for downloading content over WCF you cannot use such approach. First the whole WCF call must run asynchronously. Next you need to know amount of downloaded data upfront and you need to read data continuously - only that will allow you to control real progress bar. Normal HTTP processing will write all data to the server, pass them to the network, load all data to the buffer on the client and after that your method return the result - from client API such operation is atomic (= you cannot measure progress). This is usually avoided by chunked data where server pushes data in chunks and client is able to read these chunks like a stream. In WCF it is performed by streaming transfer mode.
At the moment you should simply use some unlimited spinner and wait for operation to complete.