Update ListView in fragment from locking mechanism locks UI thread - java

I have a ListView that I want to update with messages coming from a Bluetooth socket. The ListView is in a fragment, this does not matter too much.
The problem comes when I want to listen to incoming messages from the socket (which is a locking mechanism on a separate thread) and update the ListView with the received message.FChat.java
public class FChat extends Fragment {
ArrayList<String> listItems=new ArrayList<String>();
ArrayAdapter<String> itemsAdapter;
....
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//setup list view
ListView messageContainer = (ListView) thisView.findViewById(R.id.btMessagesContainer);
itemsAdapter = new ArrayAdapter<String>(thisView.getContext(), android.R.layout.simple_list_item_1, listItems);
currentAct = getActivity();
Thread test = new Thread() {
public void run() {
try {
currentAct.runOnUiThread(new Runnable() {
#Override
public void run() {
listItems.add("other:" + String.valueOf(times));
try {
String reply = bluetoothConnector.readSingleMessage();
listItems.add("other:" + reply);
itemsAdapter.notifyDataSetChanged();
}
catch (IOException e) {
e.printStackTrace();
}
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
test.start();
}
}
So, this feels like it's blocking the UI thread completely, so I guess that runOnUiThread it's blocking the UI thread.
If I take out the blocking part
String reply = bluetoothConnector.readSingleMessage(); and replace it with String reply = "test" it works fine, the UI is updated and seems to work great.
So, my question is, how can I read data from a socket and update the ListView with its contents?
Thank you

Obviously it blocks UI thread.
How your code looks like in pseudo:
Thread {
//there is separate thread
UiThread{
//there is UI thread
blockingOperation()
}
}
In other words your current thread is almost useless since you do blocking operation in UI thread.
And for sure it works with
String reply = "test"
Because that is not blocking operation.
So to solute problem just move
String reply = bluetoothConnector.readSingleMessage();
inside separate thread:
Thread test = new Thread() {
public void run() {
try {
final String reply = bluetoothConnector.readSingleMessage();
currentAct.runOnUiThread(new Runnable() {
#Override
public void run() {
listItems.add("other:" + String.valueOf(times));
listItems.add("other:" + reply);
itemsAdapter.notifyDataSetChanged();
}
});
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};

Related

How to run socket code in another threads?

In my application I have adapter and in this adapter I should call socket.
I want call socket in another thread and not call in MainThread.
I write socket code, but I don't know how can I call this in another thread.
My socket code :
mSocket.on("finish", new Emitter.Listener() {
#Override
public void call(final Object... args) {
try {
Constants.currentActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("socketLogs", args[0] + "");
try {
startTimer();
final FinishResponse finishResponse = new Gson().fromJson(args[0].toString(), FinishResponse.class);
countDownerLayout.setVisibility(View.GONE);
winnerLay.setVisibility(View.VISIBLE);
bidCount.setVisibility(View.GONE);
offerCount.setVisibility(View.GONE);
price.setVisibility(View.GONE);
timeView.setVisibility(View.GONE);
userPic.setVisibility(View.GONE);
winnerLay.setBackgroundColor(ContextCompat.getColor(context, R.color.white));
//if (finishResponse.getRes().getWinnerAvatar() != null && !finishResponse.getRes().getWinnerAvatar().equals("")) {
Glide.with(context)
.load(Constants.SERVER + finishResponse.getRes().getWinnerAvatar())
.placeholder(R.mipmap.ic_launcher)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(User);
/* } else {
User.setImageDrawable(ContextCompat.getDrawable(context, R.mipmap.ic_launcher_round));
}*/
edtUserName.setText(finishResponse.getRes().getWinnerName());
edtUserName.setTextColor(ContextCompat.getColor(context, R.color.black));
txtStartPrice.setText("Sell");
txtStartPrice.setBackgroundColor(ContextCompat.getColor(context, R.color.TextColorGreen));
txtStartPrice.setTextColor(ContextCompat.getColor(context, R.color.white));
try {
String[] splitDate = finishResponse.getRes().getEnd().split(" ");
String[] dateSpliet = splitDate[0].split("-");
TimeUtils timeUtils = new TimeUtils(Integer.parseInt(dateSpliet[0]), Integer.parseInt(dateSpliet[1]), Integer.parseInt(dateSpliet[2]));
txtPrice.setText(splitDate[1] + " " + timeUtils.getIranianDate());
} catch (Exception e) {
}
} catch (JsonSyntaxException e) {
}
}
});
} catch (Exception e) {
}
}
});
I write above code in getView method from Adapter.
But I want write above code in another thread , and not run in MainThread.
How can I do it?
Just create new Thread for this
Runnable runnable = new Runnable() {
#Override
public void run() {
// do stuff
}
};
new Thread(runnable).start();
I would give some recommendations concerning your code. First, it is bad to call Socket in adapter in getView. You should Call it in fragment or Activity (if you don't use MVP or some other architecture).
Next big problem is that you have current activity saved in constants
Constants.currentActivity
It will cause memory leaks, you activity will live even if you close it, and your app will have many problems with such approach.

My handler doesn't instantly get notification when a message sent to it

I use two search thread and a filter thread to search for the file I want.
After the FileFilter found the matched file, the passed-in parameter 'handler' will obtainMessage to notify m_handler in MainActivity to update the UI.
It was fine before they work without thread pool (Excecutor). After I have my threads activated by Executor in Controller.java, my file view UI doesn't instantly update when message sent From FileFilter.
After viewing the log, the hanlder message notifications don't show until the two search threads end, and that cause my UI to get stuck till threads end. It seems that the notifications get stuck because of the try-catch block in Controller.java cuz the executor was waiting for the result of the two seach threads. After leaving the startSearching() function in Controller.java, the notifications to the target m_handler in MainActivity then can be informed to update. That is, UI delayed till the end.
My way I deliver the variable of my handler: MainActivity -> Controller -> FileFilter
How can I solve this problem?
MainActivity Class
public class MainActivity extends Activity {
Handler m_handler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("myLog", "UI handler got informed to update.");
switch (msg.what){
case 1:
File receivedFile = (File) msg.obj;
addFilesToAdapter(receivedFile);
}
}
};
private Controller m_controller = new Controller();
#Override
protected void onCreate(Bundle savedInstanceState) {
m_btn_search.setOnClickListener(new Button.OnClickListener() {
#Override
public void onClick(View view) {
List<String> strListInputText = detectEditTextInputStatus();
m_controller.startSearching(m_handler, strListInputText);
}
});
}
}
Controller Class
public class Controller {
public void startSearching(Handler handler, List<String> strListInputText) {
CallBack forSearchAndFilter = new SharedFiles();
ExecutorService executor = Executors.newCachedThreadPool();
//my 1st search thread
FileSearcher searchCallable = new FileSearcher(forSearchAndFilter);
Future<Boolean> result = executor.submit(searchCallable);
//my 2ed search thread
FileSearcher SecSearchCallable = new FileSearcher(forSearchAndFilter);
Future<Boolean> result2 = executor.submit(SecSearchCallable);
executor.shutdown();
//my filter thread
if (strListInputText != null) { //has input
Runnable filterRunnable = new FileFilter(forSearchAndFilter, handler, strListInputText);
Thread filterThread = new Thread(filterRunnable);
filterThread.start();
}
//this try catch block cause my UI to get stuck
try {
Log.d("myLog", "result: " + result.get());
Log.d("myLog", "result2: " + result2.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
forSearchAndFilter.setFinishedPutFile(true); //inform filter thread that the two search threads have finished
Log.d("myLog", "All thread tasks done");
}
}
FileFilter Class
public class FileFilter implements Runnable {
private Handler m_handler;
public FileFilter(CallBack callBackToTakeFile, Handler handler, List<String> strListinputText) {
this.m_callBackToTakeFile = callBackToTakeFile;
this.m_handler = handler;
}
#Override
public void run() {
if (matchedFile != null) {
m_handler.obtainMessage(1, matchedFile).sendToTarget(); //Send matched file to UI
}
}
}

The content of the adapter has changed but ListView did not receive a notification.(Using background Service)

I my activity i am using a background service from were i get data from json every 7 second.and update my list view on UI thread.some time when there is large number of date is coming and list view is scrolling its crashing. with this java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.
Updating service
android.os.Handler handler_service = new android.os.Handler() {
public void handleMessage(android.os.Message msg) {
runOnUiThread(new Runnable() {
#Override
public synchronized void run() {
if (jon_list_Adapter == null) {
try {
jon_list_Adapter = new Different_Job_List_Adapter(
getApplicationContext(),
Different_Job_List.getInstance(),
Driver_Request_JobActivity.this);
listdriver_invites.setAdapter(jon_list_Adapter);
jon_list_Adapter.notifyDataSetChanged();
// notifyDataSetChanged();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
} else {
try {
jon_list_Adapter.notifyDataSetChanged();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
});
};
};
You need to call notifyDataSetChanged() in the adapter, so set up a method called refreshData in the adapter like this:
public void refreshData(ArrayList<yourList> data) {
mValues.clear();
mValues.addAll(data);
notifyDataSetChanged();
}
and call from your activity:
json_list_Adapter.refreshData(newData)
with the new data as an argument.

Changing background image at certain time interval

I was trying to develop a android application in which the background changes every 5 seconds I wrote code something like this :
public class BgView extends Activity implements Runnable {
ImageView img;
int [] setImg = {R.drawable.one,R.drawable.two};
RelativeLayout layout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bg_view);
layout = (RelativeLayout) findViewById(R.id.first);
Thread t = new Thread(new BigView);
t.start();
}
#Override
public void run() {
for (int i = 0; i < 2; ++i) {
layout.setBackgroundResource(setImg[i]);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
It compiled fine but when i tried to run this in emulator it stopped after 5 seconds and in logcat i saw an exception which says that i cannot touch Activity class view from other function or something like that.
so i put the whole for loop inside the onCreate() method and now i don't get any exception but all i see is a blank screen for 5 seconds after that i get the last image
I know this question has been repeated but i just don't want to copy paste the code...i want to know why this happened and how can we make this working with least changes
edit :
i get following exception :
E/AndroidRuntime(904): android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
And i want to know why putting whole for loop inside onCreate() method didn't work
Only UI thread can/should update the UI views, In your code you are trying to update the views directly from the new thread.
You should create a handler in your activity and then invoke that from your thread at required interval. Then handler should then update the UI. Try the code(I have not compiled the code, just typed it so ther might be typos/errors)
public class BgView extends Activity implements Runnable {
ImageView img;
final int [] setImg = {R.drawable.one,R.drawable.two};
RelativeLayout layout;
interface MyHandler {
void letMeKnow(int i);
}
private MyHandler handler = new MyHandler(){
#Override
void letMeKnow(int i){
layout.setBackgroundResource(setImg[i]);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bg_view);
layout = (RelativeLayout) findViewById(R.id.first);
Thread t = new Thread(new BigView);
t.start();
}
#Override
public void run() {
for (int i = 0; i < 2; ++i) {
handler.letMeKnow(i);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} `
Put this line inside ui thread like that as you can't modify in the ui thread from another thread
runOnUiThread(new Runnable() {
#Override
public void run()
{
layout.setBackgroundResource(setImg[i]);
}
});
And I think that the right way to do it is using alarm . Refer to this link Android alarms
Hope this helps.
Your run method should be re-written like below:
public void run() {
for (int i = 0; i < 2; ++i) {
runOnUiThread(new Runnable() {
public void run()
{
layout.setBackgroundResource(setImg[i]);
}
});
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

Updating Multiple Textbox Java GUI

I have an activity or form in which there is one text box called time here. As suggested by experts from this forum I am using runnable to update the TextBox while receiving the data from wifi.
My doubt is what to do when I want to update multiple TextBox's. Should I use multiple blocks of runnables like
time1.post(new Runnable() {
#Override
public void run() {
time2.setText(s1);
}
});
time2.post(new Runnable() {
#Override
public void run() {
time2.setText(s2);
}
});
time3.post(new Runnable() {
#Override
public void run() {
time3.setText(s2);
}
});
Or some other technique is there to update multiple TextBoxes? My present code is like below.
package com.example.cdttiming;
public class MainActivity extends Activity
{
EditText time;
String s;
Button button;
byte[] buffer = new byte[65535];
InetAddress ia = null;
byte[] bmessage = new byte[1500];
DatagramPacket dp = new DatagramPacket(bmessage, bmessage.length);
MulticastSocket ms = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
time = (EditText) findViewById(R.id.et_time);
try
{
WifiManager wm = (WifiManager)getSystemService(Context.WIFI_SERVICE);
WifiManager.MulticastLock multicastLock = wm.createMulticastLock("multicastLock");
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
ia = InetAddress.getByName("226.1.1.1");
try {
ms = new MulticastSocket(4321);
} catch (IOException e) {
e.printStackTrace();
}
try {
ms.joinGroup(ia);
} catch (IOException e) {
e.printStackTrace();
}
ms.setReuseAddress(true);
}
catch (UnknownHostException e) {
time.setText(e.getMessage());
}
catch (IOException e) {
time.setText(e.getMessage());
}
}
public void startProgress(View view) {
Runnable runnable = new Runnable() {
#Override
public void run() {
while(true) {
try {
// String str="This is test string";
ms.receive(dp);
s = new String(dp.getData(),0,dp.getLength());
char retval[] = s.toCharArray();
}
catch (UnknownHostException e) {
time.setText(e.getMessage());
}
catch (IOException e) {
time.setText(e.getMessage());
}
****////// My doubt is here if i have multple strings of data and multiple
/// multiple textboxes to update then what to do ???****
time.post(new Runnable() {
#Override
public void run() {
time.setText(s);
}
});
} // while
}
};
new Thread(runnable).start();
}
#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;
}
}
Your code is a bit confusing. In one case inside your primary while loop you are capturing the data, assigning it to a String variable s then using the text widget post() function and a Runnable to set the EditText widget to that value. But inside that same while loop you have exception handlers that simply set the same EditText widget directly. Your code also looks like you could potentially lose messages if the while loop resets the value of s before the timer loop has a chance to fire the set text call.
It appears you are trying to create some form of real-time system and need the primary while loop to continually process, and display data as it becomes available. Now you have 3 different consumers (text widgets), but you didn't mention if you also have 3 different sources of messages or is there still only one main processing loop and some form of selector will decide which text widget gets the message?
Were I building something along these lines, I would probably use a messaging system and follow the producer-consumer model. When text was received, I would have the primary processing loop push a simple 2-field message onto a queue that contained a reference to the text widget and a reference to the data string. Because Strings are immutable in Java, once the message object had its own copy of the text, any changes to s would not affect the message.
Then, I would have a second thread running in the background that consumes the message queue. It would pull the message off the queue, construct a post call to the target text widget with the message data, fire it off, then go back to get the next message.
By going this route you separate the data processing thread from the UI update processing and would not need to worry about how many text widgets or other widgets you need updated. If you ever need to add others you only need to worry about the new widgets being known to the code that creates the update messages. The thread doing the widget update doesn't know how many widgets you have, it simply uses the one referenced in the update message object that the message creator said to use.
I suggest you only create one runnable and post it once on main thread like this :
time1.post(new Runnable() {
#Override
public void run() {
time2.setText(s1);
time2.setText(s2);
time3.setText(s3);
}
});
The need to create a runnable and to post it on the main thread handler of a view is only about running a piece of code on the UI thread. No matter where you get the main thread handler reference from.
you could also have created your how handler on main thread :
protected void onCreate(Bundle savedInstanceState)
{
this.uiThrdHandler = new Handler();
}
then post a runnable using it :
this.uiThrdHandler.post(new Runnable(){
...
});
Of course there is no need to create another handler but it's for demonstration purpose.
The Activity object has an utility method for that purpose : runOnUiThread
Using it, it would be :
MainActivity.this.runOnUiThread (new Runnable() {
#Override
public void run() {
time2.setText(s1);
time2.setText(s2);
time3.setText(s3);
}
});
But again, the result is the same.

Categories