Android thread Loop not work - android

I use Looper.prepare and Looper.loop in Runnable's run function. But the problem is that the thread not loop at all, the Runnable just run one time. In Activity1, I use three Runnable threads, all looping. Two threads get Data and pictures from net constantly through "while" loop(needn't update UI), one thread select data and pic from local sqlite constantly through "Looper". The data is:
protected void onCreate(Bundle savedInstanceState) {
......
new Thread(getMessageTask).start();
getMessageHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
i++;
System.out.println("niuanmata" + i); //one appear the first one
try {
ArrayList<Map<String, String>> listMessages = (ArrayList<Map<String, String>>)msg.obj;
boolean listchange = true;
if (oldMessages.size() != 0) {
if (listMessages.size() == oldMessages.size()) {
for (int i = 0; i < listMessages.size(); i++) {
Map<String, String> oldmessage = (Map<String, String>) oldMessages.get(i);
Map<String, String> newmessage = (Map<String, String>) listMessages.get(i);
if ((oldmessage.get("mID") != newmessage.get("mID")) || (oldmessage.get("mainContent") != newmessage.get("mainContent")) || (oldmessage.get("deadLine") != newmessage.get("deadLine"))) {
break;
}
if (i == (listMessages.size() - 1)) {
listchange = false;
}
}
}
}
if (listchange) {
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this, listMessages, R.layout.layout_invites,
new String[]{"mID", "creater", "mainContent", "deadLine", "mtype", "createrLogo"},
new int[]{R.id.tv_list_type, R.id.tv_list_name, R.id.tv_list_inviteword, R.id.tv_list_invitedate, R.id.tv_list_inviteid, R.id.iv_list_logo});
lvMessage.setAdapter(adapter);
oldMessages = listMessages;
}
} catch (Exception e) {
Toast.makeText(MainActivity.this, "wrong: " + e.toString(), Toast.LENGTH_SHORT).show();
return;
}
}
};
......
lvMessage.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) { //when creater click, update the message; when others click, reset the alarm
Toast.makeText(MainActivity.this, "ok" , Toast.LENGTH_SHORT).show();
}
});
}
.........
Runnable synchroDataTask = new Runnable() {
#Override
public synchronized void run() {
//data syschno
while (IOHelper.loopjudge()) {
{
AccountsDB adb = new AccountsDB(MainActivity.this);
String thelastupdate = adb.getLastUpdate(account.getChatNO());
Calendar calendar = IOHelper.StringToCalendar(thelastupdate);
calendar.add(Calendar.MINUTE, -30);
String accountData = synchroDataWebservice(account.getChatNO(), IOHelper.CalendarToString(calendar)); //get the datas of the account synchroly
AccountBLL.saveDBofWebString(accountData, MainActivity.this, account); //use static method to save the DB string as SQLite data
}
}
.........
#Override
public synchronized void run() {
while (IOHelper.loopjudge()) {
......
}
.......
Runnable getMessageTask = new Runnable() {
#Override
public synchronized void run() {
Looper.prepare();
//while (IOHelper.loopjudge() && (!stopThread)) {
MessageDB messagedb = new MessageDB(MainActivity.this);
List<MessageMain> messages = messagedb.getMessageByChatNO(account.getChatNO());
ArrayList<Map<String, String>> listMessages = setMessaageListToMap(messages);
Message msg = Message.obtain();
msg.obj = listMessages;
getMessageHandler.sendMessageDelayed(msg, 1000);
//}
Looper.loop();
}
};
......
In my limited experience with android, I use while to do the Loop in getMessageTask , because the data and UI's listview need to be updated constantly. But the listview can not be clicked. Then change to Looper, but the the UI's listview can't be updated constantly....

The answer is that I misunderstand the meaning of Looper, think the Looper.prepare() and Looper.loop() as the while() loop, then make the mistake.
Looper.prepare() and Looper.loop() just means that this thread can be looped, but I must write while loop or for loop by myself.

Related

Thread Looping and not Going Outside of Block

I have a method here which loads data from Firestore and populates in My View. Here i Am trying to use a progressbar. I kept the logic for progress inside my Thread but it seems like something is wrong here as it wont come out of loop. This code is working fine in other place.
pStatus is declared as of type int. Please help as m stuck on it since whole day
private void loadDataFromFirebase() {
progressBar.setVisibility(View.VISIBLE);
final Handler handler2 = new Handler();
new Thread(new Runnable() {
#Override
public void run() {
while (pStatus <= 100) {
handler2.post(new Runnable() {
#Override
public void run() {
Log.e("Thread","Running Thread Load Data");
Log.e("Thread",String.valueOf(pStatus));
progressBar.setProgress(pStatus);
if(pStatus==100){
progressBar.setVisibility(View.INVISIBLE);
}
}
});
}
}
}).start();
firebaseFirestore.collection("Merchant").document(id)
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Log.e("Thread","Query");
Map<String, Object> temp = documentSnapshot.getData();
Map<String, Object> temp2 = (Map<String, Object>) temp.get("Items");
Iterator<Map.Entry<String, Object>> it = temp2.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Object> entry = it.next();
System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());
String Item_name = entry.getKey();
Map<String, String> items = new HashMap<>();
items = (Map<String, String>) entry.getValue();
Iterator<Map.Entry<String, String>> it2 = items.entrySet().iterator();
ArrayList<String> list = new ArrayList<>();
String a = "";
String b = "";
String c = "";
String d = "";
while (it2.hasNext()) {
Map.Entry<String, String> entry2 = it2.next();
System.out.println("Key = " + entry2.getKey() +
", Value = " + entry2.getValue());
if (entry2.getKey().equals("Name")) {
a = entry2.getValue();
} else if (entry2.getKey().equals("Metric")) {
b = entry2.getValue();
} else if (entry2.getKey().equals("Price")) {
c = entry2.getValue();
} else if (entry2.getKey().equals("Quantity")) {
d = entry2.getValue();
}
}
list.add(a);
list.add(b);
list.add(c);
list.add(d);
System.out.println(list);
OrderItemModel order = new OrderItemModel(list.get(0), list.get(1),
Integer.parseInt(list.get(2)), Integer.parseInt(list.get(3)));
orderItemModelArrayList.add(order);
}
adapter = new RecyclerViewAdapter(OrderItemList.this, orderItemModelArrayList);
recyclerView.setAdapter(adapter);
pStatus=pStatus+100;
}
});
}
It seems that the thread you handle data is the main thread(UI thread),you can print a log to verify this. But your intention is to put it in the child thread.
Below is my logic:
private static final int HANDLE_DATA = 1;
private Handler uiHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
List<String> data = msg.obj;
// set adapter data and refresh recyclerview
...
progressBar.setViesibility(View.INVISIBLE);
}
}
private HandlerThread thread = new HandlerThread("worker_thread");
private Handler handler = new Handler(thread.getLooper()) {
#Override
public void handleMessage(Message msg) {
if (msg.what == HANDLE_DATA) {
DocumentsSnapShot data = (DocumentsSnapShot)msg.obj;
// handle data
...
Message msg = uiHandler.obtainMessage(0, handleResultData);
uiHandler.postMsg(msg);
}
}
};
public void onCreate() {
...
thread.start();
...
}
private void loadDataFromFirebase() {
progressBar.setVisibility(View.VISIBLE);
firebaseFirestore.collection("Merchant").document(id)
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Message msg = handler.obtainMessage(HANDLE_DATA, documentSnapshot);
handler.postMessage(msg);
}
}
}
..

ResourcesNotFoundException: String resource ID #0x0 (No Response: UI Thread)

I am using OkHttpClient to get the Staff List (in JSON form) as response using AsyncTask Function. After getting the response in AsyncTask funtion, while inserting staff list in database, I want to show the count on the screen. but it gives following error. I tried many solutions, but couldn't resolve.
Following is the synchronizeStaffList Activity that calls SenkronizeEt Funtion using handler.
public class synchronizeStaffList extends AppCompatActivity {
DatabaseHelper gksDatabase;
TextView txtrecordValue;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_synchronize_staff_list);
gksDatabase = new DatabaseHelper(this);
txtrecordValue = (TextView) findViewById(R.id.txtrecordValue);
final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
SenkronizeEt();
}
}, 500);
final Handler handler2 = new Handler();
handler2.postDelayed(new Runnable() {
#Override
public void run() {
//Do something after 100ms
AlertDialog.Builder builder;
builder = new AlertDialog.Builder(synchronizeStaffList.this);
builder.setCancelable(false);
if (progressStaffListInfo == 0) {
builder.setMessage("Sunucu ile iletişim yok");
} else if (progressStaffListInfo == 1) {
builder.setMessage("Sicil/Personel listesi güncellendi");
}
builder.setPositiveButton("Tamam", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// if user pressed "MINIMIZE", cancel this dialog & minimize the application
// while attendance process will keep working in the back
// finish();
startActivity(new Intent(synchronizeStaffList.this, synchronize.class));
finish();
}
});
AlertDialog alert = builder.create();
alert.show();
}
}, 5000);
}
I uses second handler to show an alertbox.
Following the SenkronizeEt Function that call OkHttpHandler AsyncTask function to get the okHttpResponse/result
public int progressStaffListInfo = 0; // 0: List no updated, 1: List updated,
private void SenkronizeEt() {
progressStaffListInfo = 0;
// Updating Staff List in device by calling webservices
HashMap<String, String> Parametre = gksDatabase.GetParameter();
final String urlStaffList = "http://127.0.0.1:8080/JAXRSJsonCRUDExample/rest/employees/" + Parametre.get("DeviceNo");
OkHttpHandler handler = new OkHttpHandler();
String jsonEmployeesList = null;
try {
jsonEmployeesList = handler.execute(urlStaffList).get();
} catch (Exception e) {
//tv.setText("sorry, something went wrong !");
}
if (progressStaffListInfo == 0) {
Toast.makeText(synchronizeStaffList.this, "List Not Updated", Toast.LENGTH_SHORT).show();
} else if (progressStaffListInfo == 1) {
Toast.makeText(synchronizeStaffList.this, "List updated in database", Toast.LENGTH_SHORT).show();
}
}
Here is the OkHttpHandler AsyncTask function that call the URL to get the response and then update it in database.
public class OkHttpHandler extends AsyncTask<String, Integer, String> {
OkHttpClient client = new OkHttpClient();
#Override
protected String doInBackground(String... params) {
Request.Builder builder = new Request.Builder();
builder.url(params[0]);
Request request = builder.build();
try {
Response response = client.newCall(request).execute();
// return response.body().string();
String jsonData = response.body().string();
//JSONObject Jobject = new JSONObject(jsonData);
//JSONArray Jarray = Jobject.getJSONArray("employees");
JSONArray Jarray = new JSONArray(jsonData);
//get the length of the json array
int limit = Jarray.length();
if (limit != 0) {
gksDatabase.PersonsDelete();
progressStaffListInfo = 1; // Staff List updated
}
for (int i = 0; i < limit; i++) {
JSONObject object = Jarray.getJSONObject(i);
//store the data into database
// SId:StaffID SNo:StaffNumber SN:StaffName CNo:CardNumber
gksDatabase.PersonsSave(Integer.parseInt(object.getString("sid")), object.getString("sno"), object.getString("sn"),
object.getString("cno")
);
publishProgress((int) (((i+1) / (float) limit) * 100));
}
return jsonData;
} catch (JSONException e) {
progressStaffListInfo = 0;
} catch (Exception e) {
e.printStackTrace();
progressStaffListInfo = 0;
}
return null;
}
#Override
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
}
private void setProgressPercent(int progValue){
txtrecordValue.setText(progValue);
}
In above AsyncTask function, it gives error when it try to display the count of entries inserted in database.
Actually, not only this. if I set any thing (Like progressbar or count) on different thread, even then it stops till the asyncTask finishes its work. As an example AlertDialog handler also starts when AsyncTask function finishes all its work.
Following are the logs:
E/AndroidRuntime: FATAL EXCEPTION: main android.content.res.Resources$NotFoundException: String resource ID #0x0
at android.content.res.Resources.getText(Resources.java:249)
at android.support.v7.widget.ResourcesWrapper.getText(ResourcesWrapper.java:52)
at android.widget.TextView.setText(TextView.java:3796)
at com.emiturk.gsk.synchronizeStaffList.setProgressPercent(synchronizeStaffList.java:172)
at com.emiturk.gsk.synchronizeStaffList.access$100(synchronizeStaffList.java:25)
at com.emiturk.gsk.synchronizeStaffList$OkHttpHandler.onProgressUpdate(synchronizeStaffList.java:167)
at com.emiturk.gsk.synchronizeStaffList$OkHttpHandler.onProgressUpdate(synchronizeStaffList.java:116)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:647)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4810)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:556)
at dalvik.system.NativeStart.main(Native Method
Kindly help me out.
You cannot set int value on textView so it should be a String object so do it like
private void setProgressPercent(int progValue){
txtrecordValue.setText(""+progValue);
}
""+ will implicitly convert your progValue to String as if first parameter is a string (which it is empty string "") along with + then other will be promoted to string

How to call a asyncTask several times inside a loop- one after another

Actually what i am trying to do is that call an asyncTask several times inside a loop. So, first time the asyncTask will start immediately and from second time onwards, it will check whether the AsyncTask has been finished-if finished than again call it with different values.
Below is my code for the activity:
In onCreate()
btnUpload.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
count_response = 0;
newUploadWithSeparate();
}
});
The newUploadWithSeparate() method:
private void newUploadWithSeparate()
{
responseString_concat = "";
if(filePath.length > 0)
{
for(int i=0;i<filePath.length;i++)
{
count_response = i;
if(i == 0)
{
uploadAsync.execute(filePath[0]);
mHandler = new Handler() {
#Override public void handleMessage(Message msg) {
String s=(String)msg.obj;
Log.d("logIMEI","\n Response from Asynctask: " + s);
str_response_fromAsync = s;
}
};
}
else
{
uploadAsync.getStatus();
while(uploadAsync.getStatus() == AsyncTask.Status.RUNNING) // this while loop is just to keep the loop value waitining for finishing the asyncTask
{
int rx = 0;
}
if(uploadAsync.getStatus() != AsyncTask.Status.RUNNING)
{
if(uploadAsync.getStatus() == AsyncTask.Status.FINISHED)
{
if(str_response_fromAsync != "" || !str_response_fromAsync.equals("") || !str_response_fromAsync.isEmpty())
{
uploadAsync.execute(filePath[i]);
x = i;
mHandler = new Handler() {
#Override public void handleMessage(Message msg)
{
String s=(String)msg.obj;
Log.d("logIMEI","\n Response from Asynctask_" + x + ": " + s);
str_response_fromAsync = s;
}
};
}
}
}
}
}
}
}
And the asyncTask:
private class UploadFileToServer extends AsyncTask<String, Integer, String>
{
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
#Override
protected String doInBackground(String... params)
{
return uploadFile(params[0]);
}
private String uploadFile(String pr)
{
//inside here calling webservice and getting a response string as result.
MyWebsrvcClass mycls = new MyWebsrvcClass();
return responseString_concat = mycls.Call(xxx,yyy) ;
}
#Override
protected void onPostExecute(String result)
{
Log.d("logIMEI" , "\n count_response : "+ count_response + " fileprath_len : " + filePath.length);
Message msg=new Message();
msg.obj=result.toString();
mHandler.sendMessage(msg);
super.onPostExecute(result);
}
}
Now the problem is that its not working as expected. The first time when value of i is equals 0 than the AsyncTask gets called and after that its not getting called anymore.
Plus, when first time AsyncTask is called- its still not directly entering to onPostExecute(). When the loop ends totally and newUploadWithSeparate() method ends then the onPostExecute() is working.
Any solutions for this or any other way to do this job done for using AsyncTask inside loop?
You cannot call execute() on the same object more than once. So create a new instance of UploadFileToServer for each iteration of the loop.

Thread not moving to notify after wait is being called

I have this below piece of code, I want to my thread to wait untill either of the callback functions are called.
Issue my code hits the line where i am making the synchObj wait but after that it just vanishes it doesn't move anywhere.
If after wait its not gonna move ahead how the notify will be called?
iGPlaceApi.getStreams(new Callback<mGooglePlacesApiResponse>() {
#Override
public void failure(RetrofitError retrofitError) {
String serverResponse = retrofitError.toString();
synchronized (synchObj) {
synchObj.notify();
//synchObj.notifyAll();
}
}
#Override
public void success(mGooglePlacesApiResponse googlePlacesObj, Response arg1){
nearbyPlaces = new String[googlePlacesObj.results.size()][4];
for (int i = 0; i < googlePlacesObj.results.size(); i++) {
mGooglePlaces.place place = googlePlacesObj.results.get(i);
nearbyPlaces[i][0] = place.icon;
nearbyPlaces[i][1] = place.name;
nearbyPlaces[i][2] = String.valueOf(place.geometry.location.lat);
nearbyPlaces[i][3] = String.valueOf(place.geometry.location.lng);
}
synchronized (synchObj) {
synchObj.notify();
//synchObj.notifyAll();
}
}
});
synchronized (synchObj) {
synchObj.wait();
}
Handler handler=new Handler();
Runnable thr = new Runnable() {
public void run() {
iGPlaceApi.getStreams(new Callback<mGooglePlacesApiResponse>() {
#Override
public void failure(RetrofitError retrofitError) {
String serverResponse = retrofitError.toString();
synchronized (synchObj) {
synchObj.notifyAll();
}
}
#Override
public void success(mGooglePlacesApiResponse googlePlacesObj, Response arg1) {
nearbyPlaces = new String[googlePlacesObj.results.size()][4];
for (int i = 0; i < googlePlacesObj.results.size(); i++) {
mGooglePlaces.place place = googlePlacesObj.results.get(i);
nearbyPlaces[i][0] = place.icon;
nearbyPlaces[i][1] = place.name;
nearbyPlaces[i][2] = String.valueOf(place.geometry.location.lat);
nearbyPlaces[i][3] = String.valueOf(place.geometry.location.lng);
}
synchronized (synchObj) {
synchObj.notifyAll();
}
}
});
}
};
handler.post(thr);
synchronized (synchObj) {
synchObj.wait();
}
You can't do that. The callbacks are going to be called on the same thread that is calling the getStreams method.
The callbacks cannot be called until your calling method returns. You probably need to call getStreams in yet another thread.
You are doing it wrong. Handler task will be executed on the same thread so in your case it will never be executed. If you want to wait for your task to be done you should use Thread like this:
// defined somewhere
boolean done = false;
Thread thr=new Thread(Runnable() {
public void run() {
iGPlaceApi.getStreams(new Callback<mGooglePlacesApiResponse>() {
#Override
public void failure(RetrofitError retrofitError) {
String serverResponse = retrofitError.toString();
synchronized (synchObj) {
done = true;
synchObj.notifyAll();
}
}
#Override
public void success(mGooglePlacesApiResponse googlePlacesObj, Response arg1) {
nearbyPlaces = new String[googlePlacesObj.results.size()][4];
for (int i = 0; i < googlePlacesObj.results.size(); i++) {
mGooglePlaces.place place = googlePlacesObj.results.get(i);
nearbyPlaces[i][0] = place.icon;
nearbyPlaces[i][1] = place.name;
nearbyPlaces[i][2] = String.valueOf(place.geometry.location.lat);
nearbyPlaces[i][3] = String.valueOf(place.geometry.location.lng);
}
synchronized (synchObj) {
done = true;
synchObj.notifyAll();
}
}
});
}
});
thr.start();
synchronized (synchObj) {
while (!done) {
synchObj.wait();
}
}
Note that instead of just runnable I'm using Thread, and done variable which indicates task status which is neccesary since wait call can spontaneously wake up and you have to call it again if the task has not been finished yet.

Handler will not bind to main thread

So my code seems to run just fine until it hits this line
adapter.notifyDataSetChanged();
The error that pops up in the logcat is CalledFromWrongThreadException. The debug also shows the handler being run in the Background thread. How do I get the handler to bind to the main thread, and not the background one? I thought I just had to create the handler in the main thread, but I guess I am wrong, quite possible I am new to andriod. How do I fix this?
//Imports are included
public class DirectoryActivity extends ListActivity {
private ProgressDialog ProgressDialog = null;
private ArrayList<DirectoryListing> listing = null;
private DirectoryAdapter adapter;
private Runnable viewOrders;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.directory);
final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (listing != null && listing.size() > 0) {
adapter.notifyDataSetChanged();
for (int i = 0; i < listing.size(); i++)
adapter.add(listing.get(i));
Log.e("log_tag", "\nStill running\n");
}
ProgressDialog.dismiss();
adapter.notifyDataSetChanged();
}
};
listing = new ArrayList<DirectoryListing>();
adapter = new DirectoryAdapter(this, R.layout.rows, listing);
setListAdapter(adapter);
ProgressDialog = ProgressDialog.show(DirectoryActivity.this, "Please wait...", "Retrieving data ...", true);
viewOrders = new Runnable() {
#Override
public void run() {
listing = PreparePage.getArrayList();
handler.handleMessage(null);
}
};
Thread thread = new Thread(null, viewOrders, "Background");
thread.start();
}
private static class PreparePage {
protected static ArrayList<DirectoryListing> getArrayList() {
ArrayList<DirectoryListing> listings = new ArrayList<DirectoryListing>();
JSONObject information = GetPageData.getJSONFromURL(url);
Iterator key = information.keys();
while (key.hasNext()) {
String id = (String) key.next();
JSONObject info = null;
try {
info = information.getJSONObject(id);
} catch (JSONException e) {
e.printStackTrace();
}
String name = "", title = "", photo = "";
try {
name = info.get("firstName") + " " + info.get("lastName");
title = info.getJSONObject("position").getString("name");
photo = info.optString("photoPath", "none");
} catch (JSONException e) {
e.printStackTrace();
}
listings.add(new DirectoryListing(name, title, photo));
}
return listings;
}
}
}
Try calling handler.sendEmptyMessage(0); instead of handler.handleMessage(null);
I don't know why this would cause the errors you are seeing, but this is how I have it set up when I use handler and thread instead of AsyncTask. And I have have never seen that error doing it this way.
#Nguyen is right though AsyncTask is the preferred way to handle these types of things now. And it actually makes it much easier to do.
AsyncTask docs
AsyncTask Example
In my experience, you should create your own class that extends AsyncTask class to do something at background. This is a simpler and more effectively than using thread + handler.

Categories

Resources