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.
Related
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();
}
}
};
I've got following piece of code:
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
TextureView textureView = (TextureView) findViewById(R.id.texture);
if (textureView != null) {
try {
Log.i("MainActivity", "Read start");
String str = readQr(textureView.getBitmap());
if (str != null)
Log.i("MainActivity", str);
else Log.i("MainActivity", "No QR Code found.");
} catch (NotFoundException ex) {
Log.i("MainActivity", "No QR Code found.");
} catch (ChecksumException | FormatException e) {
e.printStackTrace();
}
}
}
}, 0, 250);
It should check for a QR code in the TextureView four times a second. The calculation takes a bit less than one second. The problem is, that the timer opens only one thread, which blocks itself => The task gets run only around one time a second.
According to this comment scheduleAtFixedRate should do the trick... But it does not, it behaves exactly like schedule in my situation.
What would be the best option to achieve what I want?
EDIT: I replaced the Timer by a ScheduledExecutorService, as suggested in the comments, but it still results in the exactly same behaviour.
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10); // I know it's too much, I just tried using 10 Threads because 4 did still not work.
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
final TextureView textureView = (TextureView) findViewById(R.id.texture);
if (textureView != null) {
try {
Log.i("MainActivity", "Read start");
String str = readQr(textureView.getBitmap());
if (str != null)
Log.i("MainActivity", str);
else Log.i("MainActivity", "No QR Code found.");
Log.i("MainActivity", "Read end");
} catch (NotFoundException ex) {
Log.i("MainActivity", "No QR Code found.");
Log.i("MainActivity", "Read end");
} catch (ChecksumException | FormatException e) {
e.printStackTrace();
Log.i("MainActivity", "Read end");
}
}
}
}, 0, 250, TimeUnit.MILLISECONDS);
A possible solution would be for the timer task to launch a new thread to perform the check, so as long as the new thread launch doesnt take more than 0.25 seconds it would be able to keep up.
The problem is that the actual check is going to overlap, meaning that you may have 3-4 tasks all finding the QR code at the same time. So is that what you want as it may have concurrency issues further down the line.
You want to check the QR code in the background thread and update the ui, then you should use background thread and Handler for updating the UI. It can be done in number of ways. this is one way.
public class FifthActivity extends AppCompatActivity {
Handler uiHandler;
Handler backgroundHandler;
TextView textureView;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.collapsing_layout);
textureView = (TextView) findViewById(R.id.textView_fifth);
uiHandler=new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
textureView.setText(msg.getData().getString("msg"));
}
};
HandlerThread thread=new HandlerThread("BackgroundTask");
thread.start();
backgroundHandler=new Handler(thread.getLooper());
backgroundHandler.post(new Runnable() {
#Override
public void run() {
Log.i("FifthActivity", "Read start");
String str = ""+ new Random().nextInt(10);
if (str != null) {
Log.i("FifthActivity", str);
Message message=uiHandler.obtainMessage();
Bundle bundle=new Bundle();
bundle.putString("msg",str);
message.setData(bundle);
uiHandler.sendMessage(message);
// textureView.setText(str);
}
else Log.i("FifthActivity", "No QR Code found.");
backgroundHandler.postDelayed(this,250);
}
});
}
}
You can also use asynctask or Observer pattern Mechanism also
Im writing a script which consists of a thread with a wait. If the thread get executed, it doesn't update the screen. In the script i change a picture to another picture, but it doesn't show it until I press the button again. So the main question is: After setting an different Image Resource, i need to update the screen.
Here is the code:
public void Start(View view) {
Runnable r = new Runnable() {
#Override
public void run() {
int i =0;
while(i<12) {
synchronized (this) {
try {
Setfoto(i);
Thread.sleep(1000);
Log.i(TAG, "Begonnen met loop");
Log.i(TAG, "i = " + i);
i = i +1;
} catch (Exception e) {}
}
}
}
};
Thread buckysThread = new Thread(r);
buckysThread.start();
}
public void Setfoto(int nummer) {
if (nummer == 1) {
een.setImageResource(R.drawable.eerster);
Log.i(TAG, "Foto 1 wordt rood");
} if(nummer ==2) {
twee.setImageResource(R.drawable.eerster);
Log.i(TAG, "Foto 2 wordt rood");
} if(nummer ==3) {
drie.setImageResource(R.drawable.tweer);
Log.i(TAG, "Foto 3 wordt rood");
}
}
You'll need to have some sort of way to run code on the UI thread, which is responsible for updating the contents of the screen and manipulating views.
Activity.runOnUiThread(Runnable) can be called. Any code placed in there will be run on the UI thread.
You can use runOnUiThread, AsyncTask or Thread-Handler to implement your task
Activity.runOnUiThread
runOnUiThread(new Runnable() {
public void run() {
//Do something on UiThread
}
});
Thread-Handler
Communicating with the UI Thread
AsyncTask
AsyncTask Tutorial
I am making an app to do something in webview automatically.
I want to make a pause between two lines inside (for-loop) until page finished loading without using Thread.sleep because it freezing my application.
this is my code:
webview.loadUrl("http://**********");
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
for(int i=1;i<10;i++){
evaluateJavascript( "document.getElementById('select').value=" + i)
evaluateJavascript("document.getElementById('Search').click();")
//wait until finished loading
while( isloading() ){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
evaluateJavascript( "document.getElementById('any_select').value=5")
.
.
.
.
}
public boolean isloading(){
boolean isloading;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
webview.evaluateJavascript("(function() { return document.getElementById(\"Loading\").style.display; })();", new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
if(s.equals("none")){
isloading=false;
}else{
isloading=true;
}
}
});
}
if(isloading=true)return true;
if(isloading=false)return false;
}
If you don't want to use Thread.sleep then the alternative is to use AsyncTask in your application.
You can do your loading task in doInBackground() method of AsyncTask and call it using new AsyncTaskClass.execute();
You can go through it from here : http://developer.android.com/reference/android/os/AsyncTask.html
You can do something similar (instead of AsyncTask):
Edit:
Timer timer = new Timer();
while( isloading() ){
try {
timer.schedule( new TimerTask(){
public void run() {
//
}
}, delay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
If you want to achieve, what i understand, that once your second line is called you want to stop there and when after 1000 ms you want to continue. You can do one thing, copy all the code after your second line and put that in run method of below code:
new Handler().postDelayed(new Runnable()
{
#Override
public void run() {
}
}, 1000);
It will execute you code after 1000ms
If you want to execute sequentially actions happening on UIThread and in backgrounds threads you should be looking for the Bolt Library by Parse/Facebook.
It apply Javascript promises to Android application.
You can go through it from here : https://github.com/BoltsFramework/Bolts-Android
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.