Why does this Thread freeze the UI - android

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

Related

Thread dosen't interrupted

I'm work on crate server and android client
but thread doesn't interrupted by android client
My android Client has request for the RoomList from server
and Server receive, Client Accept
and fill in my ListView used the Adapter
here's problem if Click backButton,
than RoomListAcitivity was close and thread was stop
but thread dosen't stop just alive in my app
first Enter has work on sucessfully
but Press BackButton on and re-Enter this Activity
MyApp just White, No Action
how can i stop this thread?
(and sorry for my English skill...)
i tried .interrupt() method , and handler.removeMessages(0)
but failed thread keep alive
upload this full java code just in case...
ListView roomList;
RoomAdapter roomAdapter;
Socketservice ss;
String msg,rtitle;
String msgs[];
String list[];
Thread listthread,EnterRoomThread,removeV;
boolean staterun = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_room);
roomList = findViewById(R.id.roomList);
roomAdapter = new RoomAdapter();
listthread = new Thread() {
public void run(){
ss.out.println("163|");
ss.out.println("100|");
try {
while (staterun == true) {
msg = ss.in.readLine();
handler.post(new Runnable() {
public void run() {
msgs = msg.split("\\|");
String protocol = msgs[0];
switch (protocol) {
case "163":
list = msgs[1].split(",");
for (int i = 0; i < list.length; i++) {
String list1[] = list[i].split("-");
String listT = list1[0];
int listC = Integer.parseInt(list1[1]);
int listI = Integer.parseInt(list1[2]);
roomAdapter.CreateRoom(listI, listT, listC);
}
roomList.setAdapter(roomAdapter);
msg = "";
msgs = null;
break;
case "200":
Intent i = new Intent(getApplicationContext(), GameWaitingActivity.class);
i.putExtra("tname", rtitle);
staterun = !staterun;
Toast.makeText(getApplicationContext(),""+listthread.isAlive(),Toast.LENGTH_SHORT).show();
startActivity(i);
finish();
break;
case "201":
Toast.makeText(getApplicationContext(), "방이 꽉 찼습니다.", Toast.LENGTH_SHORT).show();
break;
}
}
});
}
} catch (IOException e) {
}
}
};
listthread.start();
roomList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Room item = (Room) roomList.getItemAtPosition(position);
rtitle=item.getTitle();
EnterRoomThread = new Thread() {
public void run() {
ss.out.println("200|" + rtitle);
EnterRoomThread.interrupt();
}
};
EnterRoomThread.start();
}
});
}
Handler handler = new Handler();
#Override
public void onBackPressed() {
removeV = new Thread() {
public void run(){
ss.out.println("101|");
removeV.interrupt();
}
};
removeV.start();
handler.removeMessages(0);
staterun = false;
listthread.interrupt();
Toast.makeText(getApplicationContext(),""+listthread.isAlive(),Toast.LENGTH_SHORT).show();
finish();
}
}
Go ahead with this, write this inside run() method
//use this boolean value to keep track.
boolean shouldRunFlag = true;
while (shouldRunFlag) {
try {
Thread.sleep(10);
//Do your work............
} catch (Exception e) {
e.printStackTrace();
Log.v(TAG, "Interrupted Exception caught");
// change the flag, so that while loop can be broken.
shouldRunFlag = false;
Log.v(TAG, "run: breaking the loop");
break;
}
}
and this is how you interrupt and clean the thread
private void interrupt(Thread currentThread) {
Log.i(TAG, "interrupt");
if (currentThread != null) {
Thread dummyThread = currentThread;
dummyThread.interrupt();
currentThread = null;
}
}

Changing image src over time

Im pretty new to android and I'm trying to make an imagebutton in android studio that changes images at set inervals. I tried to just use wait and put it into a different thread but that doesn't seem to work. All I want if for the image to change while also still allowing for a mp3 to be played whilst doing so.
Runnable r = new Runnable() {
#Override
public void run()
{
synchronized (this)
{
try
{
wait(1000);
ank.setImageResource(R.drawable.shank2);
wait(1000);
ank.setImageResource(R.drawable.shank3);
wait(1000);
ank.setImageResource(R.drawable.shank4);
wait(1000);
ank.setImageResource(R.drawable.shank5);
}
catch (Exception e) {}
}
}
};
Thread myThread = new Thread(r);
myThread.start();
Simple way:
public void changeImages() {
Handler uiHandler = new Handler();
uiHandler.postDelayed(new Runnable {
#Override
public void run() {
ank.setImageResource(R.drawable.shank1);
}
}, 1000);
uiHandler.postDelayed(new Runnable {
#Override
public void run() {
ank.setImageResource(R.drawable.shank2);
}
}, 2000);
...
}
Better way:
public void changeImages() {
int[] images = new int[] { R.drawable.shank1, R.drawable.shank2 ... }
long delay = 1000;
Handler uiHandler = new Handler();
for (int imageRes : images) {
uiHandler.postDelayed(new Runnable {
#Override
public void run() {
ank.setImageResource(imageRes);
}
}, delay);
delay += 1000;
}
}
The best way
final Handler uiHandler = new Handler();
final Queue<Integer> queue = new LinkedBlockingQueue<>();
void changeImages() {
queue.add(R.drawable.shank1);
queue.add(R.drawable.shank2);
...
loopImages();
}
void loopImages() {
if (!queue.isEmpty()) {
uiHandler.postDelayed(new Runnable() {
#Override
public void run() {
ank.setImageResource(queue.poll());
}
}, 1000);
}
}

RealTime Graph Entry

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();
}

Using handler to change ui, still "only the original thread that created a view hierarchy can touch its views." error

I have two threads, two handlers. From thread i check a random number and send result to handle to update ui. But i am getting the error "only the original thread that created a view hierarchy can touch its views.". I searched some articles, they tell to use handler. I am doing that, yet can not avoid the errors.
After some checking, I found that it crashes when A sends the result. In case of B, it works
Also, can i use one handler for two thread?
public class MainActivity extends Activity implements View.OnClickListener{
Button start;
TextView status_B, status_A;
Boolean isRunning;
Thread Athread, Bthread;
int a, b;
Handler a_Handler, b_Handler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Initialize variables
start = (Button) findViewById(R.id.button);
start.setOnClickListener(this);
status_B = (TextView) findViewById(R.id.textView);
status_A = (TextView) findViewById(R.id.textView1);
a_Handler = new Handler() {
public void handleMessage(Message msg)
{
int number = msg.getData().getInt("number");
String winner = msg.getData().getString("winner");
start.setEnabled(true);
isRunning = false;
status_A.setText(winner + number);
}
};
b_Handler = new Handler() {
public void handleMessage(Message msg)
{
int number = msg.getData().getInt("number");
String winner = msg.getData().getString("winner");
start.setEnabled(true);
isRunning = false;
status_B.setText(winner + number);
}
};
}
#Override
protected void onStart() {
super.onStart();
isRunning = false;
}
#Override
public void onClick(View v) {
start.setEnabled(false);
status_B.setText("Guessing...");
if (!isRunning)
{
Athread = new Thread(new Runnable() {
#Override
public void run() {
while (isRunning)
{
try
{
Athread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
a = (int) (Math.random()*1000);
System.out.println("a "+ a);
if(a%7 == 0) {
isRunning = false;
Bundle bundle = new Bundle();
bundle.putString("winner", "A");
bundle.putInt("number", a);
Message msg = a_Handler.obtainMessage();
msg.setData(bundle);
a_Handler.handleMessage(msg);
}
}
}
});
Bthread = new Thread(new Runnable() {
#Override
public void run() {
while(isRunning)
{
try
{
Bthread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
b = (int) (Math.random()*1000);
System.out.println("b "+ b);
if(b%7 == 0) {
isRunning = false;
Bundle bundle = new Bundle();
bundle.putString("winner", "B");
bundle.putInt("number", b);
Message msg = b_Handler.obtainMessage();
msg.setData(bundle);
b_Handler.sendMessage(msg);
}
}
}
});
isRunning = true;
Athread.start();
Bthread.start();
}
}
}
You need put your code to modify views on UI thread:
a_Handler = new Handler() {
public void handleMessage(Message msg)
{
final int number = msg.getData().getInt("number");
final String winner = msg.getData().getString("winner");
runOnUiThread(new Runnable() {
#Override
public void run() {
start.setEnabled(true);
status_A.setText(winner + number);
}
});
isRunning = false;
}
};
b_Handler = new Handler() {
public void handleMessage(Message msg)
{
final int number = msg.getData().getInt("number");
final String winner = msg.getData().getString("winner");
runOnUiThread(new Runnable() {
#Override
public void run() {
start.setEnabled(true);
status_B.setText(winner + number);
}
});
isRunning = false;
}
};

Androidish way to organize multithreading (code review)?

I need to download thousands of objects. I need to be able to pause and resume downloads and to display the progress. I'm not good in multithreading, so I've compiled my code from different sources (I've simplified maths and UI but left logic intact):
public class DownloadActivity extends Activity {
private static final int MSG_FINISH = 1;
private static final int MSG_PROGRESS = 2;
private long total;
private ProgressBar progress;
private static DownloadThread thread;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.download);
progress = (ProgressBar) findViewById(R.id.progress);
total = 10000;
progress.setMax((int) total);
if (thread == null)
thread = new DownloadThread(progressHandler, 15000, 25000);
else
thread.setHandler(progressHandler);
((Button) findViewById(R.id.start_button)).setEnabled(thread.paused());
((Button) findViewById(R.id.pause_button)).setEnabled(! thread.paused());
((Button) findViewById(R.id.start_button)).setOnClickListener(startOnClickListener);
((Button) findViewById(R.id.pause_button)).setOnClickListener(pauseOnClickListener);
}
#Override
public void onBackPressed()
{
thread.pause();
thread = null;
super.onBackPressed();
}
private OnClickListener startOnClickListener = new OnClickListener() {
public void onClick(View v) {
((Button) findViewById(R.id.start_button)).setEnabled(false);
thread.unpause();
((Button) findViewById(R.id.pause_button)).setEnabled(true);
}
};
private OnClickListener pauseOnClickListener = new OnClickListener() {
public void onClick(View v) {
((Button) findViewById(R.id.pause_button)).setEnabled(false);
thread.pause();
((Button) findViewById(R.id.start_button)).setEnabled(true);
}
};
final Handler progressHandler = new Handler() {
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MSG_PROGRESS:
if (progress != null)
{
long current = msg.getData().getLong("current");
progress.setProgress((int) current);
}
break;
case MSG_FINISH:
Button pause = ((Button) findViewById(R.id.pause_button));
if (pause != null)
pause.setEnabled(false);
break;
}
}
};
private class DownloadThread extends Thread
{
Handler handler;
long current;
long x;
long x2;
LinkedList<Long> pendingList;
Thread threadA;
Thread threadB;
Thread threadC;
Thread threadD;
boolean paused = true;
DownloadThread(Handler h, long x1, long x2)
{
current = 0;
this.x = x1;
this.x2 = x2;
pendingList = new LinkedList<Long>();
handler = h;
threadA = new Thread(this);
threadA.start();
threadB = new Thread(this);
threadB.start();
threadC = new Thread(this);
threadC.start();
threadD = new Thread(this);
threadD.start();
}
public void run()
{
while (! isInterrupted())
{
synchronized (this)
{
if (paused)
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
continue;
}
}
Long l;
synchronized (pendingList)
{
if (pendingList.size() == 0)
{
x++;
if (x > x2)
{
continue;
}
l = new Long(x);
pendingList.add(l);
synchronized (this)
{
notifyAll();
}
continue;
}
else
{
l = pendingList.poll();
if (l == null)
{
synchronized (this)
{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
continue;
}
}
}
Object d = DownloadFactory.download(l);
if (d != null)
{
synchronized (DownloadActivity.this)
{
current++;
sendProgress();
}
}
else
{
synchronized (pendingList)
{
pendingList.add(l);
}
}
}
}
public void interrupt()
{
threadA.interrupt();
threadB.interrupt();
threadC.interrupt();
threadD.interrupt();
}
public synchronized boolean paused()
{
return paused;
}
public synchronized void pause()
{
paused = true;
}
public synchronized void unpause()
{
sendProgress();
paused = false;
notifyAll();
}
public synchronized void setHandler(Handler h)
{
handler = h;
sendProgress();
}
private void sendProgress()
{
Message msg = handler.obtainMessage(MSG_PROGRESS);
Bundle b = new Bundle();
b.putLong("current", current);
msg.setData(b);
handler.sendMessage(msg);
if (current == total)
handler.sendEmptyMessage(MSG_FINISH);
}
}
}
This code works fine and does everything I want but I understand that it is ugly and not correct (at least in putting nesting threads). So what is the nice and androidish way to accomplish the same task?
Ok I am not a concurrency professional but using a Thread which contains 4 other threads, too, sounds pretty evil :)
If you just want to make a download manager this is way beyond necessary work. If you use API 9 or above, check the DownloadManager.
Otherwise I recommend to use a simple Manager class that contains a queue and handles the add/remove and start/restart/stop of the downloads. If the queue has an element, I would start a AsyncTask which downloads the element and removes it from the queue if successfully downloaded. If not it tries to resume.

Categories

Resources