I have the following Asyntask in Android Studio.
I'm battling to get the progressbar to work though, the progressbar pops up but then immediately disappears.
I want to know:
Why is it not waiting for the tasks in the DoInBackground to complete before it disappears?
Any advice would be appreciated. I am a beginner so please excuse the silly question if it is indeed a silly question.
The tasks within DoInBackground are calls to a volley requests, which copy data from a remote sql database into the devices sqlite database.
Thanks.
public class AsyncTask extends android.os.AsyncTask<Integer, Integer, String> {
ProgressBar pb;
int status = 0;
public void setProgressBar(ProgressBar progressBar) {
this.pb = progressBar;
}
#Override
protected void onPreExecute() {
Log.d(TAG, "zzzzz2: " + "Pre");
pb.setVisibility(View.VISIBLE);
super.onPreExecute();
}
#Override
protected String doInBackground(Integer[] objects) {
Log.d(TAG, "zzzzz2: " + "DoIn");
// if user doesnt exist in db add to server db
String strUser = mFirebaseUser.getUid();
String strDisplayName = mFirebaseUser.getDisplayName();
String strEmail = mFirebaseUser.getEmail();
clsServerClass.addUserToServer(strUser, strDisplayName, strEmail, context);
// load 12 tables into sqlite database
clsServerClass.copyTblVersions(context);
clsServerClass.copyAnimalClassTable(context);
clsServerClass.copyAnimalGroupTable(context);
clsServerClass.copyAnimalTable(context);
clsServerClass.copyTblSeasons(context);
clsServerClass.copyRegions(context);
clsServerClass.copyCountries(context);
clsServerClass.copyProvinces(context);
clsServerClass.copyHotspots(context);
clsServerClass.copyHabitats(context);
clsServerClass.getMyPlaces(strUser, context);
clsServerClass.getSightingsUser(strUser, context);
Cursor cntry = getCsr("animal_unique_key", "tbl_lookup_animals");
Log.d(TAG, "yyyya: " + cntry.getCount());
JSONArray arrayList1 = createListArray("animal_class_key", 1);
createListSharedPref("List1_name", "List 1: All birds", "List1_where", arrayList1);
JSONArray arrayList2 = createListArray("animal_class_key", 2);
createListSharedPref("List2_name", "List 2: All Mammals", "List2_where", arrayList2);
JSONArray arrayList3 = createListArray("animal_class_key", -99);
createListSharedPref("List3_name", "List 3: All Animals", "List3_where", arrayList3);
JSONArray arrayList4 = createListArray("animal_class_key", 3);
createListSharedPref("List4_name", "List 4: All Reptiles", "List4_where", arrayList4);
JSONArray arrayList5 = createListArray("animal_class_key", 4);
createListSharedPref("List5_name", "List 5: All Amphibians", "List5_where", arrayList5);
return null;
}
#Override
protected void onProgressUpdate(Integer[] values) {
Log.d(TAG, "zzzzz3: " + "Update");
pb.setProgress(values[0]);
super.onProgressUpdate(values);
}
#Override
protected void onPostExecute(String s) {
Log.d(TAG, "zzzzz2: " + "post");
pb.setVisibility(View.GONE);
super.onPostExecute(s);
}
}
Problem lies in onProgressUpdate method.
Docs
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
Since you are not passing any value to your onProgressUpdate, your progress bar disappears immediately.
In doInBackground method after Log.d(TAG, "zzzzz2: " + "DoIn");
Add
publishProgress(5); // Calls onProgressUpdate()
//5 is just an example integer
Now, it should work fine.
To learn more about how to better make use of onProgressUpdate(), consult https://www.tanelikorri.com/tutorial/android/asynctask-tutorial/
Related
For instance, I want to obtain the uri of a Spotify track and put it in another method as a String value, however I'm lost on how I'd go about doing that. I tried experimenting with SharedPreferences to get the value but getString method wasn't working. I was just wondering if there's a simpler way to getting say track.getUri (or any) in another method from the Async/Sync method. Any assistance would be greatly appreciated.
The code so far:
private static final String accessToken = "...";
private static final String id = "01iyCAUm8EvOFqVWYJ3dVX";
public static SharedPreferences.Editor editor;
private static final SpotifyApi spotifyApi = new SpotifyApi.Builder()
.setAccessToken(accessToken)
.build();
private static final GetTrackRequest getTrackRequest = spotifyApi.getTrack(id)
// .market(CountryCode.SE)
.build();
public static void getTrack_Sync() {
try {
final Track track = getTrackRequest.execute();
System.out.println("Name: " + track.getName());
} catch (IOException | SpotifyWebApiException | ParseException e) {
System.out.println("Error: " + e.getMessage());
}
}
#RequiresApi(api = Build.VERSION_CODES.N)
public void getTrack_Async() {
try {
final CompletableFuture<Track> trackFuture = getTrackRequest.executeAsync();
// Thread free to do other tasks...
// Example Only. Never block in production code.
final Track track = trackFuture.join();
String uri = track.getUri();
editor = getSharedPreferences("uri", 0).edit();
editor.putString("uri", uri);
editor.commit();
editor.apply();
System.out.println("Name: " + track.getUri());
} catch (CompletionException e) {
System.out.println("Error: " + e.getCause().getMessage());
} catch (CancellationException e) {
System.out.println("Async operation cancelled.");
}
}
public void go() {
getTrack_Async();
// String value = editor.getString("uri", )
}
To get the track you need some kind of information to start with. e.g. I have the spotify trackId and can find the track (synchronously) like this:
public Track getTrack(String trackId) {
return spotifyApi.getTrack(trackId).build().execute();
}
Now the Track object (specifically com.wrapper.spotify.model_objects.specification.Track) provides a lot of information. e.g. the field uri.
So you could do just:
public void run(String trackId) {
Track track = spotifyApi.getTrack(trackId).build().execute();
String uri = track.uri;
// now call something else with the uri?
}
Does that help? Your question was not entirely clear for me.
I am working on a JavaFx application, there i have a script that extract a zip followed by some other operation like updating files etc.
I want to have a textArea that displays whats going on in background, like "Zip extracting...", "Updating xyz file" etc.
Till now i have tried following way:
MyTask<String> task;
task = new MyTask<String>() {
#Override
protected String call() throws Exception {
File path = new File(exportTo.getAbsolutePath());
updateMessage("Extracting modular app to target directory...");
patcher.unZip(appPath.getAbsolutePath(), path.getAbsolutePath());
if (path.exists()) {
AppInfo info = getAppInfo();
patcher.patchAndroid(info, resourceZip, new File(path.getAbsolutePath() + "/" + appPath.getName().substring(0, appPath.getName().lastIndexOf("."))), this);
showOkAlert("Build completed!");
} else {
showOkAlert("Modular app folder not found");
}
return "";
}
#Override
protected void updateProgress(double workDone, double max) {
patcher.reportLogs(message);
}
private String message;
#Override
public void updateMessage(final String message) {
Platform.runLater(() -> patcher.reportLogs(message));
this.message = message;
//updateProgress(0, 0);
}
};
task.run();
MyTask class
abstract class MyTask<T> extends Task<T> {
abstract public void updateMessage(String message);
}
I have tried using updateProgress method, Platform.runLater() but nothing is working.
All the message i printed in textArea are printed after all operation is done.
Please help.
As javadoc for Task states you need to manually create a Thread to execute your Task:
Thread th = new Thread(task);
th.start();
Currently your task is being run on Application UI thread and blocks UI updates.
I am new to Android.I need help to solve the error below.
Got stuck here.
public class ForecastFragment extends Fragment {
private ArrayAdapter<String> mForecastAdapter;
public ForecastFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
setHasOptionsMenu(true);
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_refresh) {
FetchWeatherTask weatherTask = new FetchWeatherTask();
weatherTask.execute("94043");
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Create some dummy data for the ListView. Here's a sample weekly forecast
String[] data = {
"Mon 6/23 - Sunny - 31/17",
"Tue 6/24 - Foggy - 21/8",
"Wed 6/25 - Cloudy - 22/17",
"Thurs 6/26 - Rainy - 18/11",
"Fri 6/27 - Foggy - 21/10",
"Sat 6/28 - TRAPPED IN WEATHERSTATION - 23/18",
"Sun 6/29 - Sunny - 20/7"
};
List<String> weekForecast = new ArrayList<String>(Arrays.asList(data));
// Now that we have some dummy forecast data, create an ArrayAdapter.
// The ArrayAdapter will take data from a source (like our dummy forecast) and
// use it to populate the ListView it's attached to.
mForecastAdapter =
new ArrayAdapter<String>(
getActivity(), // The current context (this activity)
R.layout.list_item_forecast, // The name of the layout ID.
R.id.list_item_forecast_textview, // The ID of the textview to populate.
weekForecast);
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Get a reference to the ListView, and attach this adapter to it.
ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
return rootView;
}
public class FetchWeatherTask extends AsyncTask<String, Void, String[]> {
private final String LOG_TAG = FetchWeatherTask.class.getSimpleName();
/* The date/time conversion code is going to be moved outside the asynctask later,
* so for convenience we're breaking it out into its own method now.
*/
private String getReadableDateString(long time){
// Because the API returns a unix timestamp (measured in seconds),
// it must be converted to milliseconds in order to be converted to valid date.
SimpleDateFormat shortenedDateFormat = new SimpleDateFormat("EEE MMM dd");
return shortenedDateFormat.format(time);
}
/**
* Prepare the weather high/lows for presentation.
*/
private String formatHighLows(double high, double low) {
// For presentation, assume the user doesn't care about tenths of a degree.
long roundedHigh = Math.round(high);
long roundedLow = Math.round(low);
String highLowStr = roundedHigh + "/" + roundedLow;
return highLowStr;
}
/**
* Take the String representing the complete forecast in JSON Format and
* pull out the data we need to construct the Strings needed for the wireframes.
*
* Fortunately parsing is easy: constructor takes the JSON string and converts it
* into an Object hierarchy for us.
*/
private String[] getWeatherDataFromJson(String forecastJsonStr, int numDays)
throws JSONException {
// These are the names of the JSON objects that need to be extracted.
final String OWM_LIST = "list";
final String OWM_WEATHER = "weather";
final String OWM_TEMPERATURE = "temp";
final String OWM_MAX = "max";
final String OWM_MIN = "min";
final String OWM_DESCRIPTION = "main";
JSONObject forecastJson = new JSONObject(forecastJsonStr);
JSONArray weatherArray = forecastJson.getJSONArray(OWM_LIST);
// OWM returns daily forecasts based upon the local time of the city that is being
// asked for, which means that we need to know the GMT offset to translate this data
// properly.
// Since this data is also sent in-order and the first day is always the
// current day, we're going to take advantage of that to get a nice
// normalized UTC date for all of our weather.
Time dayTime = new Time();
dayTime.setToNow();
// we start at the day returned by local time. Otherwise this is a mess.
int julianStartDay = Time.getJulianDay(System.currentTimeMillis(), dayTime.gmtoff);
// now we work exclusively in UTC
dayTime = new Time();
String[] resultStrs = new String[numDays];
for(int i = 0; i < weatherArray.length(); i++) {
// For now, using the format "Day, description, hi/low"
String day;
String description;
String highAndLow;
// Get the JSON object representing the day
JSONObject dayForecast = weatherArray.getJSONObject(i);
// The date/time is returned as a long. We need to convert that
// into something human-readable, since most people won't read "1400356800" as
// "this saturday".
long dateTime;
// Cheating to convert this to UTC time, which is what we want anyhow
dateTime = dayTime.setJulianDay(julianStartDay+i);
day = getReadableDateString(dateTime);
// description is in a child array called "weather", which is 1 element long.
JSONObject weatherObject = dayForecast.getJSONArray(OWM_WEATHER).getJSONObject(0);
description = weatherObject.getString(OWM_DESCRIPTION);
// Temperatures are in a child object called "temp". Try not to name variables
// "temp" when working with temperature. It confuses everybody.
JSONObject temperatureObject = dayForecast.getJSONObject(OWM_TEMPERATURE);
double high = temperatureObject.getDouble(OWM_MAX);
double low = temperatureObject.getDouble(OWM_MIN);
highAndLow = formatHighLows(high, low);
resultStrs[i] = day + " - " + description + " - " + highAndLow;
}
for (String s : resultStrs) {
Log.v(LOG_TAG, "Forecast entry: " + s);
}
return resultStrs;
}
#Override
protected String[] doInBackground(String... params) {
// If there's no zip code, there's nothing to look up. Verify size of params.
if (params.length == 0) {
return null;
}
// These two need to be declared outside the try/catch
// so that they can be closed in the finally block.
HttpURLConnection urlConnection = null;
BufferedReader reader = null;
// Will contain the raw JSON response as a string.
String forecastJsonStr = null;
String format = "json";
String units = "metric";
int numDays = 7;
try {
// Construct the URL for the OpenWeatherMap query
// Possible parameters are avaiable at OWM's forecast API page, at
// http://openweathermap.org/API#forecast
final String FORECAST_BASE_URL =
"http://api.openweathermap.org/data/2.5/forecast/daily?";
final String QUERY_PARAM = "q";
final String FORMAT_PARAM = "mode";
final String UNITS_PARAM = "units";
final String DAYS_PARAM = "cnt";
final String APPID_PARAM = "02867cfd75153da1eda43a17f213ffc5";
Uri builtUri = Uri.parse(FORECAST_BASE_URL).buildUpon()
.appendQueryParameter(QUERY_PARAM, params[0])
.appendQueryParameter(FORMAT_PARAM, format)
.appendQueryParameter(UNITS_PARAM, units)
.appendQueryParameter(DAYS_PARAM, Integer.toString(numDays))
.appendQueryParameter(APPID_PARAM, BuildConfig.OPEN_WEATHER_MAP_API_KEY)
.build();
URL url = new URL(builtUri.toString());
Log.v(LOG_TAG, "Built URI " + builtUri.toString());
// Create the request to OpenWeatherMap, and open the connection
urlConnection = (HttpURLConnection) url.openConnection();
urlConnection.setRequestMethod("GET");
urlConnection.connect();
// Read the input stream into a String
InputStream inputStream = urlConnection.getInputStream();
StringBuffer buffer = new StringBuffer();
if (inputStream == null) {
// Nothing to do.
return null;
}
reader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = reader.readLine()) != null) {
// Since it's JSON, adding a newline isn't necessary (it won't affect parsing)
// But it does make debugging a *lot* easier if you print out the completed
// buffer for debugging.
buffer.append(line + "\n");
}
if (buffer.length() == 0) {
// Stream was empty. No point in parsing.
return null;
}
forecastJsonStr = buffer.toString();
Log.v(LOG_TAG, "Forecast string: " + forecastJsonStr);
} catch (IOException e) {
Log.e(LOG_TAG, "Error ", e);
// If the code didn't successfully get the weather data, there's no point in attemping
// to parse it.
return null;
} finally {
if (urlConnection != null) {
urlConnection.disconnect();
}
if (reader != null) {
try {
reader.close();
} catch (final IOException e) {
Log.e(LOG_TAG, "Error closing stream", e);
}
}
}
try {
return getWeatherDataFromJson(forecastJsonStr, numDays);
} catch (JSONException e) {
Log.e(LOG_TAG, e.getMessage(), e);
e.printStackTrace();
}
// This will only happen if there was an error getting or parsing the forecast.
return null;
}
}
}
}
}
[{
public final class BuildConfig {
public static final boolean DEBUG = Boolean.parseBoolean("true");
public static final String APPLICATION_ID = "com.example.patels.sunshine";
public static final String BUILD_TYPE = "debug";
public static final String FLAVOR = "";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from build type: debug
public static final String OPEN_WEATHER_MAP_API_KEY = MyOpenWeatherMapApiKey;
}
I have written this code....I got stuck here..Unable to solve the error.
Help me out....Thank you
Under the app folder you can find build.gradle file, in this make this below changes.
Since you are using a String you have to use this syntax:
it.buildConfigField "String" , "OPEN_WEATHER_MAP_API_KEY" , "\"MyOpenWeatherMapApiKey\""
The last parameter has to be a String.
You should create an account here http://openweathermap.org/ and when you register with your email, you get an APIKey for your account. Use this api key and replace the MyOpenWeatherMapApiKey in the following line:
public static final String OPEN_WEATHER_MAP_API_KEY = MyOpenWeatherMapApiKey;
Another alternative would be to write the APIKey in grandle file as proposed already.
I'm using a GridView to display a list of words in a 4-column table. The getView method of my custom adapter checks the width of the word and shrinks it if it doesn't fit. It does this using a recursive check that keeps scaling the text down until it fits.
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + wv.getTextSize()/density + " to " + (wv.getTextSize()/density-1.0f));
wv.setTextSize(wv.getTextSize() / density - 1.0f);
}
}
}).start();
}
Because I'm using a while loop, I am using a new thread to protect against ANR in the unlikely event of an infinite loop. Here's the weird thing: sometimes it works great. And then sometimes I get the following error:
09-26 14:25:31.389 6427-7765/com.myapp.debug E/AndroidRuntime﹕ FATAL EXCEPTION: Thread-7789
Process: com.myapp.debug, PID: 6427
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
I tried putting the setTextSize inside a runOnUiThread statement, but I can't get it to work inside the adapter. Ultimately I just want this to work. I think my options are:
Keep the while loop in the UI thread and somehow safeguard it (how?)
Move the setTextSize call to the UI thread (how?)
Something else?
Thanks for your help!
UPDATE: based on Rustam's answer, I used wv.post to write to the UI. However the need to use the while loop meant that setTextSize had to be done in the same thread as the while condition itself. I switched from evaluating the TextView.getTextSize to the Paint.getTextSize, since I could set the Paint's text size without impacting the UI, and therefore inside the offshoot thread. Jerry-rigged, but it seems to work.
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
final Paint mPaint = new Paint(wv.getPaint());
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && mPaint.measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + mPaint.getTextSize()/density + " to " + (mPaint.getTextSize()/density-1.0f));
mPaint.setTextSize(mPaint.getTextSize() - 1.0f);
}
if (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
wv.post(new Runnable() {
#Override
public void run() {
Logg.d(word + " final size=" + mPaint.getTextSize() / density);
wv.setTextSize(mPaint.getTextSize() / density);
}
});
}
}
}).start();
}
try to update like this:
private void shrinkText(final TextView wv, final String word) {
wv.setTextSize(defaultTextSize);
new Thread(new Runnable() {
#Override
public void run() {
while (wv.getWidth()>0 && wv.getPaint().measureText(word) > wv.getWidth()) {
Logg.d("word too big. Shrink from " + wv.getTextSize()/density + " to " + (wv.getTextSize()/density-1.0f));
wv.post(new Runnable() {
public void run() {
wv.setTextSize(wv.getTextSize() / density - 1.0f);
}
});
}
}).start();
}
I am receiving temperature values from a sensor through BLE and storing it in my db and passing the values as an array through intent to graph activity and diplaying a line graph. I am also posting the values to google app engine. i am doing this all within runOnUiThread() and hence my app crashes when there are too many values.Is it possible to pass values into the graph activity on a new thread? or any other better suggestions?
#Override
public void propertyChange(final PropertyChangeEvent event) {
final String property = event.getPropertyName();
runOnUiThread(new Runnable() {
public void run() {
try {
if (property.equals(PROPERTY_IR_TEMPERATURE)) {
double newIRValue = (Double) event.getNewValue();
// float newIRValue_1 = (Float) event.getNewValue();
TextView textView = (TextView) findViewById(R.id.ir_temperature);
String value = decimal.format(newIRValue);
String formattedText = value + DEGREE_SYM;
float value_chk = (float)(newIRValue);
String formattedText_1 = String.valueOf(value_chk);
String sensorid = test_1;
String sensortype = "temperature";
// write_data(sensorid,sensortype,value);
// Toast.makeText(getBaseContext(), "ARV sensid:"+sensorid+" senstype:"+sensortype, Toast.LENGTH_SHORT).show();
long timemilli = System.currentTimeMillis();
//Log.d("TIMMEE", String.valueOf(timemilli));
String time=String.valueOf(timemilli);
db = new DBHandler(getApplicationContext());
//db.deleteTempReadings();
// inserting data to temp table
Log.i("insert", "Inserting Records...");
);
Temperature(sensorid,formattedText_1));
//Toast.makeText(getBaseContext(), "SENSOR_ID:"+t.getSensorId()+"TEMPERATURE: "+t.getTemperature()+"TIME: "+t.getTimestamp(), Toast.LENGTH_LONG).show();
// reading data from db
Log.i("Reading", "Reading Records...");
List<Temperature> temp = db.getAllTempReadings();
int arraySize=temp.size();
double tempArray[]=new double[arraySize];
int timeArray[]=new int [arraySize];
//Log.d("ans", String.valueOf(temp.size()));
for (Temperature t : temp) {
String log="SENSOR ID: "+t.getSensorId()+"TEMPERATURE: "+t.getTemperature()+"TIME: "+t.getTimestamp();
Log.d("Record"+t, log);
}
// Reading values into the array!
for(int i=0;i<arraySize;i++){
tempArray[i]=Double.parseDouble(temp.get(i).getTemperature());
}
Button btn=(Button)findViewBy Id(R.id.btnGraph);
btn.setOnCLickListener(new View.onClickListener(){
public void onClick(View v){
Intent iIntent =new Intent(getApplicationContext(),GraphActivity.class);
tIntent.putExtra("tempArray",tempArray);
startActivity(tIntent);
}
});
// rest....posting to server code......
textView.setText(formattedText);
}
}
}
}
}
The temperature value you get from the sensor upto this you have to do stuff in one thread and after that you have to start three thread one for the inserting into the DB, the other one for the displaying the line graph and the last and the third one for the posting the data into the google app engine.
I think you are a bit confused here. The idea is to keep slow processing OUT of your UI thread. I suggest you do the writes to the DB and the App engine first in the 'current' (presumably less critical) thread and then when all is done simply update the graph display in the UI thread.
The only reason you need RunOnUiThread is to make changes to the views on the screen.