I am creating a small game over wifi-direct through sockets on Android. On the client receiving thread I am listening for Messages from the other device:
class ReceivingThread implements Runnable {
#Override
public void run() {
BufferedReader input;
try {
input = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
while (!Thread.currentThread().isInterrupted()) {
final String messageStr = input.readLine();
if (messageStr != null) {
activity.runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(activity, messageStr, Toast.LENGTH_LONG).show();
activity.handleMessage(messageStr);
}
});
} else {
break;
}
}
input.close();
} catch (IOException e) {
}
}
}
And the toast shows the message just fine, but the app crashes on activity.handleMessage(). This is what the method does (cut it down just to one case, just for the point of the method):
public void handleMessage(String msg){
switch(msg){
case "1":
Button temp = (Button) findViewById(btn1);
if(isOwner) {
temp.setText("O");
} else {
temp.setText("X");
}
temp.setEnabled(false);
break;
case "You Lose":
TextView textBox = (TextView) findViewById(R.id.textBox);
textBox.setText("You Lose!");
}
}
And the message I get is:
java Looper.prepare() wasn't called on this thread.
Instead of activity.handleMessage(messageStr); you need to send it as a message to the handler, so handleMessage will execute in the loop.
Bundle data = new Bundle();
data.putString("MESSAGE_KEY", messageStr);
Message msg = new Message();
msg.setData(data);
activity.sendMessage(msg);
So you will receive in handleMessage of the Handler the Message, to extract the String you do message.getData().getString("MESSAGE_KEY");
Create UI thread inside handler and try to perform the UI operation.
new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message message) {
// This is where you do your work in the UI thread.
// Your worker tells you in the message what to do.
}
};
Related
So I am working on an app that is involving sockets. Multiple strings is sent through the socket, the receiving end receives it, and updates the RecyclerView correspondingly. It's working fine, that is until the app crashes / restarts. I do not know what happens, but there's this app called XRecorder on Play Store and everytime I use the screenshot function- my app restarts. Now when I send a message through the socket, it receives the message but it doesn't update the RecyclerView? The Toast appears that's why I know it is received. And it updates as well when I call removeItem()
Here's my code :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
serverAsync.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
private class ServerAsync extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
try {
serverSocket = new ServerSocket(serverPort);
while (true) {
Socket client = serverSocket.accept();
isConnected = client.isConnected();
if (isConnected) {
try {
handler.post(new Runnable() {
#Override
public void run() {
updateStatusUI();
}
});
Entry entry = new Entry();
String inputLine;
BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
while ((inputLine = in.readLine()) != null) {
StringBuilder sb = new StringBuilder();
String message = inputLine;
sb.append(message);
entry.add(sb.toString());
}
//READ NEW ENTRIES
handler.post(new Runnable() {
#Override
public void run() {
splitOrder(entry);
saveArrayList();
}
});
in.close();
handler.post(new Runnable() {
#Override
public void run() {
entryAdapter.notifyDataSetChanged();
Toast.makeText(getApplicationContext(), "In has closed :" + client.getRemoteSocketAddress(), Toast.LENGTH_SHORT).show();
updateStatusUI();
}
});
} catch (Exception e) {
Log.e("TEST", e.getMessage(), e);
}
} else {
Toast.makeText(getApplicationContext(), "Client :" + client.getRemoteSocketAddress()+" has DISCONNECTED", Toast.LENGTH_SHORT).show();
handler.post(new Runnable() {
#Override
public void run() {
updateStatusUI();
}
});
}
}
} catch (Exception e) {
Log.e("UHE", e.getMessage(), e);
}
return null;
}
}
public void removeItem(int position) {
entryAdapter.notifyDataSetChanged();
}
I've tried resetting the adapter for the RecyclerView, but it just wouldn't work. I also noticed that everytime I the app crashes and restart, I am getting the java.net.BindException: bind failed: EADDRINUSE (Address already in use)
Pointing to the creation of my serverSocket (serverSocket = new ServerSocket(serverPort)
Could this be the reason why the RecyclerView won't update? The ArrayList updates just fine with the incoming messages, but not the view.
I'm sending a message to a Handler which is associated
to it's own Thread.
In the handleMessage method I try to update the UI with the content of
the message using runOnUiThread.
This works fine if take the message obj parameter from handleMessage
and assign it to a new final variable.
But if I don't use this assignment and take msg.obj directly in the runnable
the obj variable is null even though the msg reference is having the same id
when checking the msg reference passed to handleMessage before calling runOnUiThread.
Why is that happening?
This is working:
bt01.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Message msg = new Message();
msg.obj = new Data("Hi im the message");
mHandler.sendMessage(msg);
}
});
class LooperThread extends Thread {
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(final Message msg) {
final Object messageString = msg.obj;
runOnUiThread(new Runnable() {
#Override
public void run() {
tv01.setText(((Data)messageString).getMessage());
}
});
}
};
Looper.loop();
}
}
This is not working:
public void handleMessage(final Message msg) {
runOnUiThread(new Runnable() {
#Override
public void run() {
tv01.setText(((Data)msg.obj).getMessage());
}
});
}
My guess is that the operating system is reusing these Message objects, rather than creating new ones. By the time your Runnable starts running on the UI thread, the Message has been returned to the pool and its fields have been set to null. The existence of obtainMessage() supports this hypothesis.
I create a thread to listening the input stream of the serial port.
And when there is input data coming, I want to send a message to UI thread.
Before doing my task, I try to send message from the thread with interval 5000 ms, and send a message to UI thread and Toast it.
From the log, I know that the thread is created and running succefully, but the number is not toasted in the UI thread.
I think it may be due to fail to send a message from the thread to UI thread, or fail to handle the message in the MessageQueue of UI thread.
What's wrong with my code?
Many thanks!
MainActivity.java:
public class MainActivity extends Activity {
...
#Override
protected void onCreate(Bundle savedInstancesState) {
...
setMessageProcessor();
SerialPortListener.start();
}
private void setMessageProcessor() {
new Handler(Looper.getMainLooper()) {
public void handleMessage(Message msg) {
Toast.makeText(MainActivity.this,
"Message: " + msg.obj.toString(),
Toast.LENGTH_SHORT).show();
}
};
}
}
SerialPortListener.java:
public class SerialPortListener {
...
private static int testNumber = 1;
public static void start() {
...
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Message msg = new Message();
msg.obj = testNumber++;
new Handler(Looper.getMainLooper()).sendMessage(msg);
Log.i("TEST", "TEST);
try {
Thread.sleep(5000);
} catche(Exception e) {
...
}
}
}
});
thread.start();
}
}
It's because you are creating a second Handler bound to the main thread from within your "reader thread". Don't use an anonymous object for the handler in the main thread, make it a member field. Then in your reader thread, send the Message to that specific Handler:
private void setMessageProcessor() {
mHandler = new Handler() {
...
}
}
public class SerialPortListener {
...
private static int testNumber = 1;
public static void start() {
...
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
Message msg = mHandler.obtainMessage();
msg.obj = testNumber++;
msg.sendToTarget();
Log.i("TEST", "TEST);
...
}
}
}
I know how to receive packets but I want my app to always check to packets that received
public void controlListener(){
control.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch(receiveData().charAt(0)){
case '1':{
Log.d("","1");
setVisibility(View.INVISIBLE);
control.setVisibility(View.VISIBLE);
break;
}
case '3':{
Log.d("","3");
setVisibility(View.VISIBLE);
control.setVisibility(View.INVISIBLE);
Thread thread = new Thread()
{
#Override
public void run() {
try {
Log.d("", "here");
runOnUiThread(new Runnable() {
public void run() {
switch(receiveData().charAt(0)){
case '1':{
Log.d("", "1");
setVisibility(View.INVISIBLE);
control.setVisibility(View.VISIBLE);
break;
}
case '3':{
Log.d("", "3");
setVisibility(View.VISIBLE);
control.setVisibility(View.INVISIBLE);
break;
}
case '4':{
Log.d("", "4");
setVisibility(View.INVISIBLE);
control.setVisibility(View.VISIBLE);
break;
}
default:{
Log.d("","not expecting this");
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
};
thread.start();
break;
}
case '4':{
Log.d("","4");
setVisibility(View.INVISIBLE);
control.setVisibility(View.VISIBLE);
break;
}
default:{
Log.d("","not expecting this");
}
}
}
});
}
What I wanted to do once I receive a 3 too keep receiving until i receive either a 1 or a 4. and allow the user to use the buttons while its a 3. If a 1 or 4 make all of the buttons invisible till a receive a 3.
So as you said you have to use a worker thread to permanently execute
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
and also in that thread you need a Handler, which should be created on the UI thread and a WeakReference to your Activity to prevent memory leaks and have ability to update UI. So when you receive new package you can do like this
handler.post(new Runnable() {
public void run() {
// do something with received string and activity
}
}
I have a class that implements two interfaces : OnClickListener and Runnable
When the user presses a button I will go in a Switch like this:
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
pd = ProgressDialog.show(this, "Please Wait",
"Generating", false);
Thread t = new Thread(this);
t.start();
break;
}
}
This should start my Run-method, which has an else-statement like this:
else {
Message msg = handler.obtainMessage();
msg.obj = "1";
handler.sendMessage(msg);
}
And my handler method:
private Handler handler = new Handler() {
#SuppressWarnings("unchecked")
#Override
public void handleMessage(Message m) {
pd.dismiss();
if(m.toString().equals("1")) {
Toast.makeText(getApplicationContext(), "Do a scan first", Toast.LENGTH_SHORT).show();
}
}
};
The code runs without any exception, but the Toast is not showed, it should be! I am sure that people will say that I should use an AsyncTask for this, but thats not the answer that I am looking for.
What am I doing wrong here?
You have to retrive the object inside the message:
private Handler handler = new Handler() {
#SuppressWarnings("unchecked")
#Override
public void handleMessage(Message m) {
pd.dismiss();
String message = (String)m.object;
if(message.equals("1")) {
Toast.makeText(getApplicationContext(), "Do a scan first", Toast.LENGTH_SHORT).show();
}
}
};