notifyDataSetChanged not working with Threads - java

I have a problem to update my UI by use of the notifyDataSetChanged method.
If I do this without using thread it will work.
The following class implements the Runnable interface which gets data through Web API and it updates my db.
After it updates the data I want to update my UI, but it wont work with the thread concept.
public class ExchangeRateUpdateRunnable implements Runnable {
private ExchangeRateDatabase db;
private CustomAdapter adapter;
public ExchangeRateUpdateRunnable(ExchangeRateDatabase db, CustomAdapter adapter) { this.db = db; this.adapter = adapter;}
#Override
public void run() {
refresh();
}
private void refresh() {
List<ExchangeRate> exchangeRates = new ArrayList<>();
String urlString = "https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml";
try {
URL url = new URL(urlString);
URLConnection connection = url.openConnection();
InputStream inputStream = connection.getInputStream();
String encoding = connection.getContentEncoding();
XmlPullParser parser = XmlPullParserFactory.newInstance().newPullParser();
parser.setInput(inputStream, encoding);
int eventType = parser.getEventType();
while (eventType != XmlPullParser.END_DOCUMENT) {
if (eventType == XmlPullParser.START_TAG) {
if ("Cube".equals(parser.getName())) {
if(parser.getAttributeValue(null,"currency") != null) {
String currency = parser.getAttributeValue(null , "currency");
String rate = parser.getAttributeValue(null, "rate");
ExchangeRate exchangeRate = new ExchangeRate(currency, new Double(rate));
exchangeRates.add(exchangeRate);
}
}
}
eventType = parser.next();
}
inputStream.close();
updateDB(exchangeRates);
} catch (Exception e) {
e.printStackTrace();
}
}
private void updateDB ( List<ExchangeRate> exchangeRates) {
db.setExchangeRate(exchangeRates);
adapter.notifyDataSetChanged();
}
}
And this is a method that is implemented on the mainActivity class, which listens to menu items (This means as soon as I click on the item it will call this function). It implements the Thread that runs on the background and updates my data:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.settings:
Intent intent = new Intent(this, CurrencyListActivity.class);
startActivity(intent);
case R.id.refresh:
new Thread(new ExchangeRateUpdateRunnable(db,adapter)).start();
//new RefreshRates(db,adapter).refresh(); //single Threaded
}
return super.onOptionsItemSelected(item);
}
If I put the codeline: adapter.notifyDataSetChanged(); on the method of the mainActivity class it will only work when I click the button twice. But I want to update my UI as soon as I click on the button.
How do I solve this problem ?
Is this a Thread problem or something that Android cant handle by use of Threads?
Thanks in advance.

You won't be able to notifytheDataSetChange from thread, you might need to use the handler.post or runOnUiThread to achieve this.
Example
try {
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("updating...", "");
Notifydatasetchanged()
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}

Related

server connection thread too late than onCreate

I'm making app which get server data from mvc spring server.
If client login success in main Activity, app try to get data from server, and use list view show data in next activity.
It works clear in first login, but when I turn back on main Activity, and try login again, next activity doesn't show anything.
I used logs to find problem and I found that when client login again, next activity's onCreate() and onResume() works too fast. My app uses thread and get data from server, logs says after onCreate and onResume works and my thread get data from server.
So this is my problem
1 App uses thread to get data from server
2 First try works but after thread is too late than onResume and onCreate in activity
3 should I have to make thread more fast? or use flags or something make onCreate and onResume works after thread works end? or does my code have problems?
This is my activity which show data from server
public class ServerListActivity extends AppCompatActivity {
public static ArrayList<ServerListItem> serverListItemArrayList;
public static ArrayList<ServerListItem> scrollEventServerListItemList;
public TextView serverListInfoTextView;
private ProgressBar progressBar;
private boolean lockListView;
private boolean isThisLastItemVisibleFlag;
private ServerListViewAdapter serverListViewAdapter;
private int currentPageNum;
public Handler msgHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
lockListView = false; // scroll event
currentPageNum = 0; // scroll event
isThisLastItemVisibleFlag = false; // scroll event
scrollEventServerListItemList = new ArrayList<>();
serverListItemArrayList = new ArrayList<>();
msgHandler = new Handler();
super.onCreate(savedInstanceState);
ServerListManager serverListManager = new ServerListManager(msgHandler);
serverListManager.getServerList();
setContentView(R.layout.activity_server_list);
ImageButton turnBackBtn = findViewById(R.id.turn_back_btn);
serverListInfoTextView = findViewById(R.id.server_list_Info);
ListView serverListView = findViewById(R.id.server_list_view);
progressBar = findViewById(R.id.progressBar);
serverListViewAdapter = new ServerListViewAdapter(scrollEventServerListItemList, R.layout.server_list_item, getApplicationContext());
serverListView.setAdapter(serverListViewAdapter);
progressBar.setVisibility(View.GONE);
serverListView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL);
System.out.println(serverListItemArrayList.size());
setServerListInfo(serverListItemArrayList);
and this is my thread
public class ServerListManager {
private final static int SERVER_PROBLEM = 666;
private Handler handler;
public ServerListManager(Handler handler) {
this.handler = handler;
}
public void getServerList() {
new Thread(new Runnable() {
#Override
public void run() {
HttpURLConnection connection = null;
StringBuilder stringBuffer = new StringBuilder();
try {
URL url = new URL("my ip and blah balh");
connection = (HttpURLConnection) url.openConnection();
} catch (IOException e) {
e.printStackTrace();
}
if (connection != null) {
connection.setConnectTimeout(5000);
connection.setUseCaches(false);
try {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8"));
String line = bufferedReader.readLine();
if (!"".equals(line)) {
stringBuffer.append(line);
Log.i("ServerListManager", stringBuffer.toString());
}
setServerListItem(stringBuffer.toString());
} catch (IOException e) {
e.printStackTrace();
}
}
}
}).start();
}
private void setServerListItem(String dataFromServer) {
Log.i("ServerListManager", "setServerListItem() works ");
ArrayList<ServerListItem> serverListItems = new ArrayList<>();
JsonParser jsonParser = new JsonParser();
JsonObject jsonObjectFirst = (JsonObject) jsonParser.parse(dataFromServer);
if (!String.valueOf(jsonObjectFirst.get("status")).equals("\"200\"")) {
Message message = new Message();
message.what = SERVER_PROBLEM;
handler.sendMessage(message);
} else {
String serverListJsonVersion = String.valueOf(jsonObjectFirst.get("serverModelList"));
JsonArray jsonArray = (JsonArray) jsonParser.parse(serverListJsonVersion);
Gson gson = new Gson();
for (JsonElement jsonElement : jsonArray) {
ServerListItem serverListItem = gson.fromJson(jsonElement, ServerListItem.class);
serverListItems.add(serverListItem);
}
ServerListActivity.serverListItemArrayList = serverListItems;
}
}
You can try using AsyncTask
private class AsyncCaller extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//this method will be running on UI thread
//can call progress bar here to show loading process
}
#Override
protected Void doInBackground(Void... params) {
//this method will be running on background thread
//so don't update UI from here
//do your long running http tasks here,
//you don't want to pass argument and you
//can access the parent class' variable url over here
// call your server data here
serverListManager.getServerList();
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
//this method will be running on UI thread
//Show the result obtained from doInBackground
//this executed after doInBackground so after you get data from doInBackground
//you can set your adapter here
}
}
Then you can execute AsyncTask like this
new AsyncCaller().execute();

Listview jump to top when adding new item

i am making application that have scroll listener that add data from multiple URL
but when i scroll the list jump to first position and than load the URL i know that the adapter in my a sync task get executed every time i load new URL task when i load new item but i don't know how to fix it
public class jsontask extends AsyncTask<String, String, List<newsmodel>> {
#Override
protected List<newsmodel> doInBackground(String... params) {
BufferedReader reader = null;
HttpURLConnection connection = null;
try {
URL url = new URL(params[0]);
connection = (HttpURLConnection) url.openConnection();
if (connectiondetector.isConnected()) {
connection.addRequestProperty("Cache-Control", "max-age=0");
} else {
moviemodelList.addAll((List<newsmodel>) cacheThis.readObject(
technology.this, fileName));
}
connection.connect();
InputStream stream = connection.getInputStream();
reader = new BufferedReader(new InputStreamReader(stream));
StringBuffer buffer = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
String finaljson = buffer.toString();
JSONObject parentobject = new JSONObject(finaljson);
JSONArray parentarray = parentobject.getJSONArray("articles");
for (int i = 0; i < parentarray.length(); i++) {
JSONObject finalobject = parentarray.getJSONObject(i);
newsmodel newsmodel = new newsmodel();
newsmodel.setAuthor(finalobject.getString("author"));
if (finalobject.isNull("author")) {
}
newsmodel.setDescription(finalobject.getString("description"));
newsmodel.setTitle(finalobject.getString("title"));
newsmodel.setImage(finalobject.getString("urlToImage"));
newsmodel.setUrl(finalobject.getString("url"));
newsmodel.setPublishedAt(finalobject.getString("publishedAt"));
cacheThis.writeObject(technology.this, fileName, moviemodelList);
moviemodelList.add(newsmodel);
}
return moviemodelList;
} catch (MalformedURLException e) {
e.printStackTrace();
return moviemodelList;
} catch (IOException e) {
e.printStackTrace();
return moviemodelList;
} catch (JSONException e) {
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if (connection != null) {
}
try {
if (reader != null) {
reader.read();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return moviemodelList;
}
#Override
protected void onPostExecute(List<newsmodel> result) {
super.onPostExecute(result);
newsadapter adapter = new newsadapter(getApplicationContext(), R.layout.row, result);
lvnews.setAdapter(adapter);
}
}
public class newsadapter extends ArrayAdapter {
private List<newsmodel> moviemodelList;
private int resource;
private LayoutInflater inflater;
public newsadapter(Context context, int resource, List<newsmodel> objects) {
super(context, resource, objects);
moviemodelList = objects;
this.resource = resource;
inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
}`
and this is how i excute my a sync task
protected void onStart() {
super.onStart();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
moviemodelList.clear();
new jsontask().execute("https://newsapi.org/v1/articles?source=engadget&sortBy=top&apiKey=ade8f00a634b4825a028837ec107afae");
lvnews.setOnScrollListener(new EndlessScrollListener() {
#Override
public boolean onLoadMore(int page, int totalItemsCount) {
new jsontask().execute("url1");
new jsontask().execute("url2");
new jsontask().execute("url3");
new jsontask().execute("url4");
new jsontask().execute("url5");
new jsontask().execute("url6");
return false;
}
});
}
Remove these lines from onPostExecute
newsadapter adapter = new newsadapter(getApplicationContext(), R.layout.row, result);
lvnews.setAdapter(adapter);
You are setting adapter eveytime after executing doInBackground. You need to do it only once. You can do it in onCreate
The basic methods used in an android AsyncTask class are defined below
:
doInBackground() : This method contains the code which needs to be executed in background. In this method we can send results multiple
times to the UI thread by publishProgress() method. To notify that the
background processing has been completed we just need to use the
return statements
onPreExecute() : This method contains the code which is executed before the background processing starts
onPostExecute() : This method is called after doInBackground method completes processing. Result from doInBackground is passed to
this method
onProgressUpdate() : This method receives progress updates from doInBackground method, which is published via publishProgress method,
and this method can use this progress update to update the UI thread
Please refer this link to know how Async task
After getting first url response call for second new jsontask().execute("url2"); then call for third, fourth and so on.
Make a common code to reuse it again and again.
try this link : http://www.devexchanges.info/2015/03/android-listview-dynamically-load-more.html

How to get Android TextToSpeech to read from a ListView

I have a ListView that pulls data from a ContentProvider via a CursorLoader.
I want to have a button that reads out the data in the ListView when pressed. The tricky part is that the data in the ListView is continually updating (data from the ContentProvider changes periodically every few seconds) and the data for each row might update while the audio is being read.
How do I make it such that the latest data is read each time it is updated?
Try the following code:
private boolean ttsEnabled= true;
private Thread ttsThread = null;
private ListView lastState = null;
public void enableTTS() {
ttsEnabled = true;
ttsThread = new Thread(ttsRunnable);
ttsThread.start();
}
public void disableTTS() {
ttsEnabled = false;
try {
ttsThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private Runnable ttsRunnable = new Runnable() {
#Override
public void run() {
while (ttsEnabled) {
if (lastState == null || !lastState.equals(yourListView)) {
// List view updated, tts here
lastState = yourListView;
}
}
}
};

How to implement FragmentActivity as Fragment?

I'm new at making apps for Android and I'm now making my first app and I'm having issues.
I've successfully made myself a navigation drawer following this tutorial:
http://www.androidhive.info/2013/11/android-sliding-menu-using-navigation-drawer/
I have found this example which does exactly what I need - parsing XML and showing it as a list with images opening a more detailed view:
http://techiedreams.com/android-rss-reader-part-two-offline-reading-swipe-through-detail-views/
I have huge problems implementing the last example into my app (consisting of the first link) as the example uses a FragmentActivity while my app creates new Fragments from my MainActivity (I know FragmentActivity and Fragments are different).
How MainActivity creates new fragments:
private void displayView(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new NewsFragment();
break; }
I need to make a Fragment consisting of what is inside of SplashActivity.
What would I need to do to implement SplashActivity into my MainActivity (and thus create a new Fragment of it)? Would I need to convert the FragmentActivity to a Fragment, or would I need to find a whole new solution?
If you would need and want to try it out yourself everything is available from the links above. As I'm a total beginner I really hope I can use the example above as it suits my app perfect.
SplashActivity:
public class SplashActivity extends Activity {
private String RSSFEEDURL = "http://www.nordichardware.se/feed/rss.html";
RSSFeed feed;
String fileName;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
fileName = "TDRSSFeed.td";
File feedFile = getBaseContext().getFileStreamPath(fileName);
ConnectivityManager conMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
if (conMgr.getActiveNetworkInfo() == null) {
// No connectivity. Check if feed File exists
if (!feedFile.exists()) {
// No connectivity & Feed file doesn't exist: Show alert to exit
// & check for connectivity
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(
"Unable to reach server, \nPlease check your connectivity.")
.setTitle("TD RSS Reader")
.setCancelable(false)
.setPositiveButton("Exit",
new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog,
int id) {
finish();
}
});
AlertDialog alert = builder.create();
alert.show();
} else {
// No connectivty and file exists: Read feed from the File
Toast toast = Toast.makeText(this,
"No connectivity! Reading last update...",
Toast.LENGTH_LONG);
toast.show();
feed = ReadFeed(fileName);
startLisActivity(feed);
}
} else {
// Connected - Start parsing
new AsyncLoadXMLFeed().execute();
}
}
private void startLisActivity(RSSFeed feed) {
Bundle bundle = new Bundle();
bundle.putSerializable("feed", feed);
// launch List activity
Intent intent = new Intent(SplashActivity.this, ListActivity.class);
intent.putExtras(bundle);
startActivity(intent);
// kill this activity
finish();
}
private class AsyncLoadXMLFeed extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Obtain feed
DOMParser myParser = new DOMParser();
feed = myParser.parseXml(RSSFEEDURL);
if (feed != null && feed.getItemCount() > 0)
WriteFeed(feed);
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
startLisActivity(feed);
}
}
// Method to write the feed to the File
private void WriteFeed(RSSFeed data) {
FileOutputStream fOut = null;
ObjectOutputStream osw = null;
try {
fOut = openFileOutput(fileName, MODE_PRIVATE);
osw = new ObjectOutputStream(fOut);
osw.writeObject(data);
osw.flush();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
fOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// Method to read the feed from the File
private RSSFeed ReadFeed(String fName) {
FileInputStream fIn = null;
ObjectInputStream isr = null;
RSSFeed _feed = null;
File feedFile = getBaseContext().getFileStreamPath(fileName);
if (!feedFile.exists())
return null;
try {
fIn = openFileInput(fName);
isr = new ObjectInputStream(fIn);
_feed = (RSSFeed) isr.readObject();
}
catch (Exception e) {
e.printStackTrace();
}
finally {
try {
fIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return _feed;
}
}
If you want to create a fragment you have to extend the fragment class.
public class SplashActivity extends Fragment{
//your fragment code.
}
//Also you need to actually use the fragment to do this you can create an intent and start the intent or you can try
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);

Trying to circumvent NetworkOnMainThreadException on AsyncTask because of ProgressDialog

I have the following problem. I am using the DropBox SDK to upload a file to dropbox which works fine. When the file is being uploaded (inside an AsyncTask) a ProgressDialog is being shown with a cancel button, still fine here. What is not working fine is, when the cancel button gets pressed a NetworkOnMainThreadException is being raised. I am new to Android programming but I am suspecting it has something to do with the ProgressDialog which is in the constructor. Since it is not in the "doInBackground" part.
Tried to fix it with implementing the OnDismissListener and doing the abortion onDismiss but still no luck. I am getting the error when "mRequest.abort()" is called.
Thanks in advance for any answers!
So here is my code
public class DropBoxUpload extends AsyncTask<Void, Long, Boolean> implements OnDismissListener {
private DropboxAPI<?> mApi;
private String mPath;
private File mFile;
private long mFileLen;
private UploadRequest mRequest;
private Context mContext;
private final ProgressDialog mDialog;
private String mErrorMsg;
public DropBoxUpload(Context context, DropboxAPI<?> api, String dropboxPath, File file) {
// We set the context this way so we don't accidentally leak activities
mContext = context.getApplicationContext();
mFileLen = file.length();
mApi = api;
mPath = dropboxPath;
mFile = file;
mDialog = new ProgressDialog(context);
mDialog.setMax(100);
mDialog.setMessage("Uploading " + file.getName());
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.setProgress(0);
mDialog.setButton("Cancel", new OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// This will cancel the putFile operation
mDialog.dismiss();
}
});
mDialog.setCancelable(true);
mDialog.setOnDismissListener(this);
mDialog.show();
}
#Override
protected Boolean doInBackground(Void... params) {
try {
// By creating a request, we get a handle to the putFile operation,
// so we can cancel it later if we want to
FileInputStream fis = new FileInputStream(mFile);
String path = mPath + mFile.getName();
mRequest = mApi.putFileOverwriteRequest(path, fis, mFile.length(),
new ProgressListener() {
#Override
public long progressInterval() {
// Update the progress bar every half-second or so
return 500;
}
#Override
public void onProgress(long bytes, long total) {
publishProgress(bytes);
}
});
if (mRequest != null) {
mRequest.upload();
return true;
}
} catch (DropboxUnlinkedException e) {
// This session wasn't authenticated properly or user unlinked
mErrorMsg = "This app wasn't authenticated properly.";
} catch (DropboxFileSizeException e) {
// File size too big to upload via the API
mErrorMsg = "This file is too big to upload";
} catch (DropboxPartialFileException e) {
// We canceled the operation
mErrorMsg = "Upload canceled";
} catch (DropboxServerException e) {
// Server-side exception. These are examples of what could happen,
// but we don't do anything special with them here.
if (e.error == DropboxServerException._401_UNAUTHORIZED) {
// Unauthorized, so we should unlink them. You may want to
// automatically log the user out in this case.
} else if (e.error == DropboxServerException._403_FORBIDDEN) {
// Not allowed to access this
} else if (e.error == DropboxServerException._404_NOT_FOUND) {
// path not found (or if it was the thumbnail, can't be
// thumbnailed)
} else if (e.error == DropboxServerException._507_INSUFFICIENT_STORAGE) {
// user is over quota
} else {
// Something else
}
// This gets the Dropbox error, translated into the user's language
mErrorMsg = e.body.userError;
if (mErrorMsg == null) {
mErrorMsg = e.body.error;
}
} catch (DropboxIOException e) {
// Happens all the time, probably want to retry automatically.
mErrorMsg = "Network error. Try again.";
} catch (DropboxParseException e) {
// Probably due to Dropbox server restarting, should retry
mErrorMsg = "Dropbox error. Try again.";
} catch (DropboxException e) {
// Unknown error
mErrorMsg = "Unknown error. Try again.";
} catch (FileNotFoundException e) {
}
return false;
}
#Override
protected void onProgressUpdate(Long... progress) {
int percent = (int)(100.0*(double)progress[0]/mFileLen + 0.5);
mDialog.setProgress(percent);
}
#Override
protected void onPostExecute(Boolean result) {
mDialog.dismiss();
}
#Override
public void onDismiss(DialogInterface arg0) {
// TODO Auto-generated method stub
mRequest.abort();
}
}
You cannot access the mRequest object from the main UI thread as this is what is responsible for the network operation. That is why you get a NetworkOnMainThreadException when you call mRequest.abort().
You should modify your code such that you use AsyncTask.cancel on dialog dismissal and check for isCancelled periodically in your doInBackground and call mRequest.abort() when the task is cancelled.
you should call the cancel method to stop your uploading process.
ast.cancel(true);
where ast is your asynctask object

Categories

Resources