I have to refresh my listview every 3 seconds (I'm in ListFragment) so I start a new Thread wich start runOnUiThread to edit UI.
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
new Thread(new Runnable() {
public void run() {
do{
ObjectMapper mapper = new ObjectMapper();
VideoData element = null;
try {
element = mapper.readValue(new URL("http://192.168.4.111:3232/videodata.json").openStream(), VideoData.class);
} catch (Exception e) {
e.printStackTrace();
}
final VideoData newData = element;
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
hd = newData.getChildren().get(0).getChildren().get(id);
vba.notifyDataSetChanged();
}
});
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {}
}while(true);
}
}).start();
setAdapter();
}
I parse data on web, and update the reference.
private void setAdapter()
{
if(id == 0)
hd = hd.getChildren().get(0);
vba = new ValueBaseAdapter(getActivity(), 0, hd.getChildren());
setListAdapter(vba);
}
I saw that when notifyDataSetChanged is called, getView is called (and it's ok) but I have old reference, the one of when I called setAdapter the first time.
Also If I set null hd in run(), doesn't change anything, listview doesn't change.
Where is the error? Thanks.
It seems that notifyDataSetChanged() updates adapter's data only if you work with List
Related
Few days ago, I asked this question for avoiding repetition of reycylerview items whose accepted answer helped me to avoid data repetition.
But now I am facing new problems like: not showing all items sometimes only one like this . The real problem is even after getting all items from server properly data is not shown in recyclerView properly .Data are skipped randomly. I don't understand where the problem is. I even tried to use for loop instead of foreach but result was not different. Can anyone please help to fix this? It has been pain in the neck from last one week.
Code:
private List<TimelineData> timelineDataList=new ArrayList<>() ;
public void onCreateView(){
recyclerview.setLayoutManager(new LinearLayoutManager(ctx));
//Setting Adapter
adapter=new CustomRecyclerViewAdapter(timelineDataList);
recyclerview.setAdapter(adapter);
}
#Override
public void onStart() {
super.onStart();
// Fetching data from server
socket.disconnect();
socket.connect();
//Getting Data from server
JSONObject obj=new JSONObject();
try {
obj.put("timeline_posts","all");
socket.emit("data",obj);
} catch (JSONException e) {
e.printStackTrace();
}
}
void addTimelineData(String type,String time,String img_link){
boolean isRepeated = false;
for(TimelineData data : timelineDataList){
if(data.getTime().equals(time)){
isRepeated = true;
}
}
if(!isRepeated){
timelineDataList.add(new TimelineData(type,time,img_link));
}
adapter.notifyDataSetChanged();
}
private Emitter.Listener handlePosts = new Emitter.Listener(){
#Override
public void call(final Object... args){
try {
JSONArray jsonArray=(JSONArray)args[0];
timelineDataList.clear(); //clear data before inserting new one
for(int i=0;i<jsonArray.length();i++){
try {
JSONObject ob=jsonArray.getJSONObject(i);
post_type=ob.getString("post_type");
post_time=ob.getString("time");
post_link=ob.getString("img_link");
addTimelineData(post_type,post_time,post_link);
} catch (JSONException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
Log.e("error",e.toString());
}
}
};
Adapter Code:
#Override
public void onBindViewHolder( CustomRecyclerViewHolder holder, int position) {
//Fetching TimelineData
TimelineData timelineData=totalList.get(position);
///Here I'm getting and converting array of image links which are there in jsonObject to arraylist
//Getting Imglink
Gson gson=new Gson();
Type type = new TypeToken<ArrayList<String>>() {}.getType();
ArrayList<String> arrayList = gson.fromJson(timelineData.getImg_link(), type);
//Setting ViewPager
CustomPagerAdapter adp=new CustomPagerAdapter(arrayList);
pager.setAdapter(new PagerAdapter() {
#Override
public int getCount() {
return 0;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return false;
}
});
holder.pager.setCurrentItem(position, false);
holder.pager.clearAnimation();
adp.notifyDataSetChanged();
holder.pager.setAdapter(adp);
holder.pager.setOffscreenPageLimit(1);
}
You are using notifyDataSetChnaged() very quickly, change notify method by this. So that you notify only selected item which you inserted.
Recommended method : You will put below code in your adapter and call this method from for loop where you were setting notifyDataSetChnaged here String s will be replaced by your model class. By this way you just notify only one element when inserting one element. This will also create some inserting animation automatically.
public void insertItemInList(String s) {
if (list == null) list = new ArrayList<>();
list.add(s);
notifyItemInserted(list.size() - 1);
}
Or
You can call notify outside for loop when your work is done like this.
for(int i=0;i<jsonArray.length();i++) {
try {
JSONObject ob=jsonArray.getJSONObject(i);
post_type=ob.getString("post_type");
post_time=ob.getString("time");
post_link=ob.getString("img_link");
addTimelineData(post_type,post_time,post_link);
} catch (JSONException e) {
e.printStackTrace();
}
}
adapter.notifyDataSetChanged();
Issue is that you are notifying adapter rapidly, it can also lead to UI inconsistency.
Let me know if this resolves your issue.
You are modifying the data list in the non-UI thread which could cause problems in the RecyclerView. Instead, you should collect all the data in tempTimelineDataList at once and update the adapted timelineDataList in the UI thread.
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.
I load my articles from my wordpress webpage. And want to display them in my app.
In order to be allowed to wait for a internet response I had to create a Thread.
Using this method:
private void loadArticles(final String url) {
Thread thread = new Thread(new Runnable(){
#Override
public void run() {
try {
//Get my data and run createArticles();
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
Then in the createArticles function I try to use my function addViewToParent(...) and I get an error that says:
Only the original thread that created a view hierarchy can touch its
views.
How can i work around this problem?
EDIT: I use these networking functions if it matters...
//Connect to the url
URL pageURL = new URL(url);
HttpURLConnection connection = (HttpURLConnection) pageURL.openConnection();
BufferedReader in = new BufferedReader(
new InputStreamReader(connection.getInputStream())
);
If you want to modify your Views, e.g. show your article, you have to run the code on the UI/main thread. To achieve this, you could call run runOnUiThread() in your Activity:
private void loadArticles(final String url) {
Thread thread = new Thread(new Runnable(){
#Override
public void run() {
try {
//Get my data ...
runOnUiThread(new Runnable() {
public void run() {
// modify Views here
createArticles();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
You need to use function runOnUiThread. This is function for user interface thread.
private void loadArticles(final String url) {
//Get my data ...
runOnUiThread(new Runnable() {
public void run() {
// modify Views here
createArticles();
}
});
});
Just use a Handler instead, so you'll have access to the UI Thread:
new Handler().post(new Runnable(){
#Override
public void run() {
try {
//Get my data and run createArticles();
} catch (Exception e) {
e.printStackTrace();
}
}
});
I use a worker thread to read text from a url. My thread is as follow. In the first time running, I am sure thread running is finished as I can check sdcard_readstr is null.
In the second time running, when I call thread_download.start();, then the program crashed.
What could be wrong? Thanks
public class DownloadingThread extends AbstractDataDownloading {
#Override
public void doRun() {
// TODO Auto-generated method stub
try {
// Create a URL for the desired page
URL url = new URL(SDcard_DetailView.textfileurl);
// Read all the text returned by the server
BufferedReader in = new BufferedReader(new
InputStreamReader(url.openStream()));
do{
sdcard_readstr = in.readLine();
}while(sdcard_readstr!=null);
in.close();
} catch (MalformedURLException e) {
} catch (IOException e) {
}
}
}
public abstract class AbstractDataDownloading extends Thread{
private final Set<ThreadCompleteListener> listeners
= new CopyOnWriteArraySet<ThreadCompleteListener>();
public final void addListener(final ThreadCompleteListener listener) {
listeners.add(listener);
}
public final void removeListener(final ThreadCompleteListener listener) {
listeners.remove(listener);
}
private final void notifyListeners() {
for (ThreadCompleteListener listener : listeners) {
listener.notifyOfThreadComplete(this);
}
}
#Override
public final void run() {
try {
doRun();
} finally {
notifyListeners();
}
}
public abstract void doRun();
}
EDIT1:
In my thread complete notification, I use runOnUiThreadto use the UI components.
Is that causing problem?
public void notifyOfThreadComplete(Thread thread) {
// TODO Auto-generated method stub
if(downloadingStopbuttonispressed == false){//background process completed
textfileurl = null;
this.runOnUiThread(new Runnable() {
public void run() {
Wifibutton = (Button) findViewById(R.id.Wifiscanning);
Wifibutton.setText("Load another day's data");
final MenuItem refreshItem = optionsMenu.findItem(R.id.airport_menuRefresh);
refreshItem.setActionView(null);
}
});
}
}
I called thread start in onResume() as
#Override
protected void onResume() {
super.onResume();
if(textfileurl != null){
Wifibutton.setText("Stop Data Loading");
buttonStatus = "loading";
setRefreshActionButtonState(true);
thread_download.start();
}
}
EDIT2:
My LogCat image is attached.
My solution is here . I can't reuse the same instance of the Thread object in the second time. I need to create a new instance to call the Thread in the second time. So Thread is suitable for single time running process, for multiple time running process I should use AsyncTask. Even AsyncTack is only for one time execution and for multiple time execution, we should use as new MyAsyncTask().execute(""); I don't understand why people downvote with no reason given. I couldn't find the link in my first search.
I never got this working in a straightforward manner. Sorry if I'm being a little vague. I'll try to elaborate on what I'm trying to do. I am trying to build a listview that grabs its data from a webservice. Once I initialize a listview, I want to keep polling the webserver periodically and update the contents of the listview. For this I am doing something like this:
public class SampleAutoUpdateList extends Activity {
//Autoupdate handler
private Handler handler = new Handler();
private Runnable updater = new Runnable() {
public void run() {
/*
* Update the list
*/
try {
Log.i("UPDATE", "Handler called");
searchAdapter = getFeed(URL);
searchAdapter.notifyDataSetChanged();
handler.postDelayed(this, Configuration.REFRESH_INTERVAL);
} catch(Exception e) {
Log.e("UPDATE ERROR", e.getMessage());
}
}
};
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.linearmode);
this.context = this;
searchAdapter = getFeed(URL);
LinearLayout l2 = (LinearLayout) findViewById(R.id.secondaryLayout);
ListView list = new ListView(context);
l2.addView(list);
// display UI
UpdateDisplay(list);
updater.run();
}
private SearchAdapter getFeed(String URL) {
try
{
SearchHandler handler = new SearchHandler();
URL url = new URL(URL);
String data = convertStreamToString(url.openStream());
data = data.substring(data.indexOf('['), data.length()-1);
handler.parseJSON(data);
return handler.getFeed();
}
catch (Exception ee)
{
// if we have a problem, simply return null
Log.e("getFeed", ee.getMessage());
return null;
}
}
private void UpdateDisplay(View searchView) {
// TODO Auto-generated method stub
// TODO Auto-generated method stub
searchList = (ListView) searchView;
myProgressDialog = ProgressDialog.show(this,
"Please wait...", "Loading search....", true);
new Thread() {
public void run() {
try{
Thread.sleep(2000);
} catch (Exception e) { }
runOnUiThread(new Runnable() {
#Override
public void run() {
if (searchAdapter == null)
{
Log.e("ERROR", "No Feed Available");
return;
}
searchAdapter.setContext(context);
searchList.setAdapter(searchAdapter);
searchList.setSelection(0);
}
});
// Dismiss the Dialog
myProgressDialog.dismiss();
}
}.start();
}
}
And the SearchHandler class is simple:
public class SearchHandler extends DefaultHandler {
SearchAdapter _adapter;
SearchItem _item;
public SearchHandler()
{
}
public SearchAdapter getFeed()
{
return _adapter;
}
public void parseJSON(String data) {
// TODO Auto-generated method stub
_adapter = new SearchAdapter();
JSONArray parseArray;
try {
parseArray = new JSONArray(data);
for (int i=0; i < parseArray.length(); i++) {
SearchItem item = new SearchItem();
JSONObject jsonUser = parseArray.getJSONObject(i);
item.set_from(jsonUser.getString ("from"));
item.set_msg(jsonUser.getString("msg"));
}
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
No matter what I do, the handler gets called and the new items are fetched, but the list is never refreshed... Any ideas on what could be going wrong?
Well, it is a little bit difficult to follow your code, since you only have a fragment of it, and few of the really relevant bits. For example, based on your available code, your list should be forever empty, since you never associate the searchAdapter with a ListView...at least in the code you have shown.
That being said, the following lines seem particularly odd:
searchAdapter = getFeed(URL);
searchAdapter.notifyDataSetChanged();
I am going to assume that getFeed() (not shown) creates a new ListAdapter of some sort. If getFeed() is creating a new ListAdapter, there is no need to call notifyDataSetChanged() on it, as its data set hasn't changed -- it's brand new. Moreover, unless you are associating this new ListAdapter to your ListView, the new ListAdapter will have no effect.
If I'm barking up the wrong tree, consider adding lines to your sample showing the implementation of getFeed() and where you are using searchAdapter.