Please understand that I am using Google Translator because I do not understand English well.
I am currently working on an application that draws an EMG sensor value sent by Arduino via Bluetooth communication and a smartphone draws a graph based on that value.
Currently the application configuration has one activity and one fragment.
The Bluetooth function is in the activity and the graph is in the fragment.
I want to have the graph drawn only when the value comes in via Bluetooth communication. What should I do?
The code now depends on the fact that the fragment receives a value, even though the fragment is requesting a value.
Fragment
private void feedMultiple() {
if (thread != null)
thread.interrupt();
final Runnable runnable = new Runnable() {
#Override
public void run() {
addEntry();
}
};
thread = new Thread(new Runnable() {
#Override
public void run() {
while (loop) {
// Don't generate garbage runnables inside the loop
getActivity().runOnUiThread(runnable);
try {
Thread.sleep(5);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
}
}
});
thread.start();
}
Activity
void beginListenForData() {
final Handler handler = new Handler();
readBufferPosition = 0;
readBuffer = new byte[1024];
mWorkerThread = new Thread(new Runnable()
{
#Override
public void run() {
while(!Thread.currentThread().isInterrupted()) {
try {
int byteAvailable = mInputStream.available();
if(byteAvailable > 0) {
byte[] packetBytes = new byte[byteAvailable];
mInputStream.read(packetBytes);
for(int i=0; i<byteAvailable; i++) {
byte b = packetBytes[i];
if(b == mCharDelimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes,"UTF-8");
readBufferPosition = 0;
handler.post(new Runnable(){
#Override
public void run() {
//raw = data.split("#");
bundle.putString("yValue1", data);
}
});
}
else {
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "데이터 수신 중 오류가 발생 했습니다.", Toast.LENGTH_LONG).show();
finish();
}
}
}
});
mWorkerThread.start();
}
void beginListenForData() {
//final Handler handler = new Handler();
final Handler handler = new Handler(Looper.getMainLooper());
final byte delimiter = 10; //This is the ASCII code for a newline character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[2048];
workerThread = new Thread(new Runnable() {
public void run() {
while (!Thread.currentThread().isInterrupted() && !stopWorker) {
try {
int bytesAvailable = mmInputStream.available();
if (bytesAvailable > 0) {
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for (int i = 0; i < bytesAvailable; i++) {
byte b = packetBytes[i];
if (b == delimiter) {
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable() {
public void run() {
//myLabel.setText(data);
dataArray = new String []{data};
//Log.d("dataArray", data);
}
});
} else {
readBuffer[readBufferPosition++] = b;
}
}
}
} catch (IOException ex) {
stopWorker = true;
}
}
}
});
public class Bluetooth_dataDisplay extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
String MAC = getIntent().getStringExtra("MAC");
mAdapter = BluetoothAdapter.getDefaultAdapter();
BluetoothDevice bluetoothDevice = mAdapter.getRemoteDevice(MAC);
// Initiate a connection request in a separate thread
ConnectingThread t = new ConnectingThread(bluetoothDevice);
t.start();
recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
adapter = new RecyclerAdapter(dataArray);
recyclerView.setAdapter(adapter);
}
Begindata() is call in the connected thread. I trying to get the dataarray back to the Bluetooth_dataDisplay extends AppCompatActivity and call it in the oncreate. How do I senddataarray back to main activity? Please help any expert. I had look at post that talked about thread data send back to main UI. But I am very new to it, so it quite confusing. :( Help please.
Replace handler call under your beginListenForData() function
handler.post(new Runnable() {
public void run() {
//myLabel.setText(data);
dataArray = new String[]{data};
//Log.d("dataArray", data);
}
});
with runOnUiThread()
runOnUiThread(new Runnable() {
#Override
public void run() {
//myLabel.setText(data);
dataArray = new String[]{data};
//Log.d("dataArray", data);
}
});
Everything under runOnUiThread() runs on UI/Main thread.
In my code I have to send a message as long as my ToggleButton is checked. To prevent the UI Thread from freezing, I put the action in a seperate Thread.
My Problem is, that it still freezes, but I don't know why
This is the relevant code:
private ToggleButton.OnClickListener lightMirrorOnClickListener = new ToggleButton.OnClickListener() {
#Override
public void onClick(View v) {
if (lightMirrorBtn.isChecked()) {
lightThread = new LightThread();
lightThread.start();
} else if(!lightMirrorBtn.isChecked()) {
lightThread.interrupt();
}
}
};
class LightThread extends Thread {
Handler lightHandler = new Handler();
Runnable light = new Runnable() {
public void run() {
while (lightMirrorBtn.isChecked()) {
lightTxMsg.frameFormat = ConstantList.STANDARD_FRAME;
lightTxMsg.frameType = ConstantList.DATA_FRAME;
lightTxMsg.dataLength = (byte) 8;
lightTxMsg.messageID = 0x3C1;
int[] messageArray = AMBI_LIGHT;
for (int i = 0; i < lightTxMsg.dataLength; i++) {
lightTxMsg.data[i] = messageArray[i];
}
returnCode = demoController.transmitMessage(lightTxMsg,
ConstantList.BINARY_FORMAT);
}
}
};
public void run() {
while (!isInterrupted()) {
try {
Thread.sleep(60);
lightHandler.post(light);
} catch (InterruptedException e) {
break;
}
}
}
}
EDIT:
This was the solution for the problem:
private ToggleButton.OnCheckedChangeListener lightMirrorOnClickListener = new ToggleButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (isChecked == true) {
new Thread(new Runnable() {
public void run() {
lightTxMsg.frameFormat = ConstantList.STANDARD_FRAME;
lightTxMsg.frameType = ConstantList.DATA_FRAME;
lightTxMsg.dataLength = (byte) 8;
lightTxMsg.messageID = 0x3C1;
int[] messageArray = AMBI_LIGHT_ON;
for (int i = 0; i < lightTxMsg.dataLength; i++) {
lightTxMsg.data[i] = messageArray[i];
}
returnCode = demoController.transmitMessage(lightTxMsg,
ConstantList.BINARY_FORMAT);
}
}).start();
} else if (!isChecked) {
new Thread(new Runnable() {
public void run() {
lightTxMsg.frameFormat = ConstantList.STANDARD_FRAME;
lightTxMsg.frameType = ConstantList.DATA_FRAME;
lightTxMsg.dataLength = (byte) 8;
lightTxMsg.messageID = 0x3C1;
int[] messageArray = AMBI_LIGHT_OFF;
for (int i = 0; i < lightTxMsg.dataLength; i++) {
lightTxMsg.data[i] = messageArray[i];
}
returnCode = demoController.transmitMessage(lightTxMsg,
ConstantList.BINARY_FORMAT);
}
}).start();
}
}
};
Handler lightHandler = new Handler();
When you create your handler your thread has not yet started. It is just being created. So, according to the Handler's default constructor documentation this handler is associated "with the Looper for the current thread" ... which is currently the main(UI) thread. So you post your messages on the main thread.
You don't need a Handler to post your runnable on. You can either:
Create a Thread and specify it's actions in the run() method
or
Pass a Runnable to your thread that will be executed in your thread using the Thread(Runnable) constructor
Here are the basic articles about Threads:
Processes and threads
Keeping your app responsive
Specifying the Code to Run on a Thread
My code have more than one thread and Runnable. My problem is i change the value of a certain variable in the thread that the Runnable calling .
After the calling i make a check on that variable value but the value was not retrieved yet.
How can i retrieve the value after the processing? Here is the Runnable and the Thread code:
final Runnable r = new Runnable()
{
public void run()
{
if(flag==true)
onSwipe();
if(SwipeAgain==true)
handler.postDelayed(this, 1000);
}
};
private void onSwipe() {
new Thread() {
public void run() {
String data = null;
decryption_data = null;
encryption_data = null;
SwipeAgain=false;
handler.post(clear_encryption);
try {
data = sreader.ReadCard(15000);
} catch (Exception ex) {
if (ex instanceof TimeoutException) {
return;
} else
CloseSinWave();
}
if (data == null) {
SwipeAgain=true;
encryption_data = sreader.GetErrorString();
if (encryption_data.equalsIgnoreCase("cancel all"))
return;
handler.post(display_encryptiondata);
} else {
encryption_data = "\n" + data;
handler.post(display_encryptiondata);
}.start();
}
SwipeAgain is the value i want after processing
You have to use Callable, Runnable interface do not pass values to the parent method.
See this example.
You may require to use Generic Objects
Use a MONITOR final Object to wait and notify it when processing is done.
private final MONITOR Object[] = new Object[0];
private AtomicBoolean ready = new AtomicBoolean(false);
final Runnable r = new Runnable() {
public void run()
{
if(flag==true){
ready.set(false);
onSwipe();
synchronized(MONITOR){
if(!ready.get()){
try{
MONITOR.wait(); //will block until it get notified
}catch(InteruptedException e){}
}
}
}
if(SwipeAgain==true)
handler.postDelayed(this, 1000);
}
};
private void onSwipe() {
new Thread() {
public void run() {
try{
String data = null;
decryption_data = null;
encryption_data = null;
SwipeAgain=false;
handler.post(clear_encryption);
try {
data = sreader.ReadCard(15000);
} catch (Exception ex) {
if (ex instanceof TimeoutException) {
return;
} else
CloseSinWave();
}
if (data == null) {
SwipeAgain=true;
encryption_data = sreader.GetErrorString();
if (encryption_data.equalsIgnoreCase("cancel all"))
return;
handler.post(display_encryptiondata);
} else {
encryption_data = "\n" + data;
handler.post(display_encryptiondata);
}finally{
synchronized(MONITOR){
ready.set(true);
MONITOR.notifyAll(); //notify (and so unblock r.run())
}
}
}.start();
}
I have trouble using thread.join in my code below. It should wait for the thread to finish before executing the codes after it, right? It was behaving differently on different occasions.
I have three cases to check if my code goes well
App is used for the first time - works as expected but the loading page don't appear while downloading
App is used the second time (db is up to date) - works okay
App is used the third time (db is outdated, must update) - won't update, screen blacks out, then crashes
I think I have problems with this code on onCreate method:
dropOldSchedule();
dropThread.join();
triggerDownload();
Based on the logs, the code works until before this part... What can be the problem?
MainActivity.java
public class MainActivity extends Activity {
final static int INDEX_ACCTTYPE = 0;
final static int INDEX_ECN = 1;
final static int INDEX_TLN = 2;
final static int INDEX_SIN = 3;
final static int INDEX_MOBILE = 4;
final static int INDEX_CITY = 5;
final static int INDEX_START_DATE = 6;
final static int INDEX_START_TIME = 7;
final static int INDEX_END_DATE = 8;
final static int INDEX_END_TIME = 9;
final static int INDEX_REASON = 10;
final static int INDEX_DETAILS = 11;
DatabaseHandler db;
String str;
ProgressDialog pd;
TextView homeText1, homeText2, homeText3, homeText4;
final private String csvFile = "http://www.meralco.com.ph/pdf/pms/pms_test.csv";
final private String uploadDateFile = "http://www.meralco.com.ph/pdf/pms/UploadDate_test.txt";
Thread dropThread = new Thread(new Runnable() {
public void run() {
db = new DatabaseHandler(MainActivity.this);
db.dropOldSchedule();
runOnUiThread(new Runnable() {
public void run() {
while (!pd.isShowing());
db.close();
pd.dismiss();
}
});
}
});
Thread getUploadDateThread = new Thread(new Runnable() {
public void run() {
try {
URL myURL = new URL(uploadDateFile);
BufferedReader so = new BufferedReader(new InputStreamReader(myURL.openStream()));
while (true) {
String output = so.readLine();
if (output != null) {
str = output;
}
else {
break;
}
}
so.close();
}
catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
runOnUiThread(new Runnable() {
public void run() {
while (!pd.isShowing());
pd.dismiss();
}
});
}
});
Thread downloadThread = new Thread(new Runnable() {
public void run() {
db = new DatabaseHandler(MainActivity.this);
db.beginTransaction();
try {
URL url = new URL(csvFile);
Log.i("dl", "start");
InputStream input = url.openStream();
CSVReader reader = new CSVReader(new InputStreamReader(input));
Log.i("dl", "after reading");
String [] sched;
while ((sched = reader.readNext()) != null) {
if(sched[INDEX_CITY].equals("")) sched[INDEX_CITY]="OTHERS";
try {
db.addRow(sched[INDEX_SIN], sched[INDEX_CITY],
sched[INDEX_START_DATE], sched[INDEX_START_TIME],
sched[INDEX_END_DATE], sched[INDEX_END_TIME],
sched[INDEX_DETAILS], sched[INDEX_REASON]);
} catch (IndexOutOfBoundsException e) {
db.addRow(sched[INDEX_SIN], sched[INDEX_CITY],
sched[INDEX_START_DATE], sched[INDEX_START_TIME],
sched[INDEX_END_DATE], sched[INDEX_END_TIME],
"", sched[INDEX_REASON]);
//e.printStackTrace();
}
}
input.close();
Log.i("dl", "finished");
} catch (MalformedURLException e) {
e.printStackTrace();
db.endTransaction();
} catch (IOException e) {
e.printStackTrace();
db.endTransaction();
}
Log.d("Count", ""+db.count());
db.setTransactionSuccessful();
db.endTransaction();
writeUploadDateInTextFile();
}
});
#SuppressWarnings("unqualified-field-access")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pms_main);
Button home = (Button) findViewById(R.id.home);
home.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MeralcoSuite_TabletActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
finish();
}
});
homeText1 = (TextView) findViewById(R.id.home_text1);
homeText2 = (TextView) findViewById(R.id.home_text2);
homeText3 = (TextView) findViewById(R.id.home_text3);
homeText4 = (TextView) findViewById(R.id.home_text4);
homeText1.setVisibility(View.INVISIBLE);
homeText2.setVisibility(View.INVISIBLE);
homeText3.setVisibility(View.INVISIBLE);
homeText4.setVisibility(View.INVISIBLE);
getUploadDate();
try {
getUploadDateThread.join(); //wait for upload date
Log.d("getUploadDate","thread died, upload date=" + str);
if(dbExists()){
db = new DatabaseHandler(MainActivity.this);
Log.d("Count", "" + db.count());
db.close();
if(!uploadDateEqualsDateInFile()){
dropOldSchedule();
dropThread.join();
triggerDownload();
}
showDisclaimer();
Log.i("oncreate", "finished!");
return;
}
triggerDownload();
showDisclaimer();
Log.i("oncreate", "finished!");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void dropOldSchedule(){
if(pd!=null && pd.isShowing())
pd.setTitle("Getting upload date...");
else
pd = ProgressDialog.show(this, "Getting upload date",
"This may take a few minutes...", true, false);
dropThread.start();
}
public void triggerDownload() {
if (!checkInternet()) {
showAlert("An internet connection is required to perform an update, please check that you are connected to the internet");
return;
}
if(pd!=null && pd.isShowing())
pd.setTitle("Getting upload date...");
else
pd = ProgressDialog.show(this, "Getting upload date",
"This may take a few minutes...", true, false);
downloadThread.start();
}
public void getUploadDate() {
Log.d("getUploadDate", "getting upload date of schedule");
if(pd!=null && pd.isShowing())
pd.setTitle("Getting upload date...");
else
pd = ProgressDialog.show(this, "Getting upload date",
"This may take a few minutes...", true, false);
getUploadDateThread.start();
}
public void writeUploadDateInTextFile() {
Log.d("writeUploadDateTextFile", "writing:"+str);
try {
OutputStreamWriter out = new OutputStreamWriter(openFileOutput(
"update.txt", 0));
out.write(str);
out.close();
} catch (java.io.IOException e) {
e.printStackTrace();
}
}
public void showDisclaimer() {
Log.d("ShowDisclaimer", "showing disclaimer");
homeText3
.setText("..." + str
+ "...");
homeText1.setVisibility(View.VISIBLE);
homeText2.setVisibility(View.VISIBLE);
homeText3.setVisibility(View.VISIBLE);
homeText4.setVisibility(View.VISIBLE);
Log.d("showDisclaimer", "finished showing disclaimer");
}
public boolean uploadDateEqualsDateInFile() {
Log.d("uploadDateEqualsDateInFile","comparing schedule upload dates");
try {
String recordedDate = "";
InputStream instream = openFileInput("update.txt");
if (instream != null) { // if file the available for reading
Log.d("uploadDateEqualsDateInFile","update.txt found!");
InputStreamReader inputreader = new InputStreamReader(instream);
BufferedReader buffreader = new BufferedReader(inputreader);
String line = null;
while ((line = buffreader.readLine()) != null) {
recordedDate = line;
Log.d("uploadDateEqualsDateInFile","recorded:"+recordedDate);
}
Log.d("uploadDateEqualsDateInFile","last upload date: " + str + ", recorded:" +recordedDate);
if(str.equals(recordedDate)) return true;
return false;
}
Log.d("uploadDateEqualsDateInFile","update.txt is null!");
return false;
} catch (FileNotFoundException e) {
e.printStackTrace();
return false;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
public boolean checkInternet() {
ConnectivityManager cm = (ConnectivityManager) this
.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo infos[] = cm.getAllNetworkInfo();
for (NetworkInfo info : infos)
if (info.getState() == NetworkInfo.State.CONNECTED
|| info.getState() == NetworkInfo.State.CONNECTING) {
return true;
}
return false;
}
public boolean dbExists() {
File database=getApplicationContext().getDatabasePath(DatabaseHandler.DATABASE_NAME);
if (!database.exists()) {
Log.i("Database", "Not Found");
return false;
}
Log.i("Database", "Found");
return true;
}
#Override
protected void onDestroy() {
super.onDestroy();
if (db != null) {
db.close();
}
}
#Override
protected void onPause() {
super.onPause();
if (db != null) {
db.close();
}
}
}
Sorry but I couldn't find mistakes or problems in your code. But I would strongly recommend you to use AsyncTask for doing something in different thread. AsyncTask is very easy to use and I would say that it is one of the biggest advantages of java. I really miss it in obj-c.
http://labs.makemachine.net/2010/05/android-asynctask-example/
http://marakana.com/s/video_tutorial_android_application_development_asynctask_preferences_and_options_menu,257/index.html
check those links hope that will help you.
It was already mentioned that AsyncTask is the better alternative. However, it may be the case, that your call to join will throw InterruptedException. Try to use it like this:
while(getUploadDateThread.isRunning()){
try{
getUploadDateThread.join();
} catch (InterruptedException ie){}
}
// code after join
I think the problem that your facing is that you are blocking the UI thread when you call join in the onCreate() method. You should move this code into another thread which should execute in the background and once its done you can update the UI.
Here is a sample code:
final Thread t1 = new Thread();
final Thread t2 = new Thread();
t1.start();
t2.start();
new Thread(new Runnable() {
#Override
public void run() {
// Perform all your thread joins here.
try {
t1.join();
t2.join();
} catch (Exception e) {
// TODO: handle exception
}
// This thread wont move forward unless all your threads
// mentioned above are executed or timed out.
// ------ Update UI using this method
runOnUiThread(new Runnable() {
#Override
public void run() {
// Update UI code goes here
}
});
}
}).start();