Current scenario: Example app which stores images from several URLs in the SD cache and displays them in a ListView.
Task: instead of take hard-coded URLs inside a private method in the MainActivity retrieve them from JSON data placed in a URL resource.
I'm retrieving the JSON and parsing the data well, but I'm having difficulties on how to send this parsed data to the MyImageLoaderAdapter because the returned list seems to come later..
File: MainActivity.java
public class MainActivity extends Activity {
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
try{
SimpleAsyncTask mTask = new SimpleAsyncTask();
mTask.execute(resource);
ArrayList list = mTask.list;
String[] strArray = new String[ list.size() ];
int length = strArray.length; // lenght = 0
mStrings = new String[ list.size() ];
int length = strArray.length;
for( int j = 0; j < length; j++ ) {
mStrings[j] = list.get(j).toString();
}
}catch (Exception e){}
// Create custom adapter for listview
adapter=new MyImageLoadAdapter(this, mStrings);
...
}
private String[] mStrings={
"http://resourse1.com",
"http://resourseN.com",
};
}
File: SimpleAsyncTask.java
public class SimpleAsyncTask extends AsyncTask<String, String, String>{
ArrayList list = new ArrayList();
protected String doInBackground(String... uri) {
//working code
}
#Override
protected void onPostExecute(String response) {
super.onPostExecute(response);
...
return list //expected value;
}
}
You could do it like this:
public class MainActivity extends Activity implements OnTaskCompleteListener{
private ArrayList list;
...
#Override
public void onCreate(Bundle savedInstanceState) {
...
try{
SimpleAsyncTask mTask = new SimpleAsyncTask();
mTask.execute(resource);
}catch (Exception e){}
...
}
private String[] mStrings={
"http://resourse1.com",
"http://resourseN.com",
};
#Override
private void onTaskComplete(ArrayList taskList){
list = taskList;
//String[] strArray = new String[ list.size() ];
//int length = strArray.length; // lenght = 0
//mStrings = new String[ list.size() ];
//int length = strArray.length;
//for( int j = 0; j < length; j++ ) {
// mStrings[j] = list.get(j).toString();
//}
//Instead of the above code you can also use this
String[] array = list.toArray(new String[list.size()]);
// Create custom adapter for listview
adapter=new MyImageLoadAdapter(this, array);
}
}
Now change your Asynctask as follows:
public class SimpleAsyncTask extends AsyncTask<String, String, String>{
private OnTaskCompleteListener listener;
public SimpleAsyncTask(OnTaskCompleteListener listener) {
this.listener = listener;
}
protected String doInBackground(String... uri) {
//working code
}
#Override
protected void onPostExecute(String response) {
super.onPostExecute(response);
...//Convert the response to list and call your listener
listener.onTaskComplete(list);
// return list //expected value;// no need of it now.
}
}
Create an interface in your package.
public interface OnTaskCompleteListener {
void onTaskComplete(ArrayList list);
}
Here you are implementing an interface in your activity, passing it to the async task while creating it, once the onpostexecute is called in the async task you are calling the method implemented in the activity using the passed interface object.
Hope this helps.
Related
I'm new to android studio and Java. My app uses jsoup to pass the contents of a website into an array where each element gets displayed on swipable flashcards (like tinder)
I've got a problem where my app crashes when I try to pass the result of the variable 'words' from onPostExecute() (line 123) to String num on line 49. I want to take the output of the function in onPostExcecute and set it as String num but I'm not sure how to do it.
public class AppHome extends AppCompatActivity implements PopupMenu.OnMenuItemClickListener {
TextView texx;
private ArrayList<String> al;
private ArrayAdapter<String> arrayAdapter;
private int i;
String words;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_home);
texx= findViewById(R.id.text1);
new doit().execute();
String num = words;
String str[] = num.split(",");
final ArrayList al = new ArrayList<String>(Arrays.asList(str));
arrayAdapter = new ArrayAdapter<>(this, R.layout.item, R.id.helloText, al );
SwipeFlingAdapterView flingContainer = (SwipeFlingAdapterView) findViewById(R.id.frame);
registerForContextMenu(flingContainer);
flingContainer.setAdapter(arrayAdapter);
flingContainer.setFlingListener(new SwipeFlingAdapterView.onFlingListener() {
#Override
public void removeFirstObjectInAdapter() {
// this is the simplest way to delete an object from the Adapter (/AdapterView)
Log.d("LIST", "removed object!");
al.remove(0);
arrayAdapter.notifyDataSetChanged();
}
#Override
public void onLeftCardExit(Object dataObject) {
//Do something on the left!
//You also have access to the original object.
//If you want to use it just cast it (String) dataObject
Toast.makeText(AppHome.this, "left", Toast.LENGTH_SHORT).show();
}
#Override
public void onRightCardExit(Object dataObject) {
Toast.makeText(AppHome.this, "right", Toast.LENGTH_SHORT).show();
}
#Override
public void onAdapterAboutToEmpty(int itemsInAdapter) {
// Ask for more data here
al.add("XML ".concat(String.valueOf(i)));
arrayAdapter.notifyDataSetChanged();
Log.d("LIST", "notified");
i++;
}
#Override
public void onScroll(float scrollProgressPercent) {
}
});
}
public class doit extends AsyncTask<Void,Void,Void> {
//String words;
#Override
protected Void doInBackground(Void... voids) {
try {
Document doc = Jsoup.connect("https://screenscrape4top40.000webhostapp.com/").get();
words=doc.text();
}catch(Exception e){e.printStackTrace();}
return null;
}
#Override
public void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
texx.setText(words);
//String str = (words);
//List<String> elephantList = Arrays.asList(str.split(","));
//texx.setText(elephantList.toString());
// texx.setText(elephantList);
}
}
}
public class doit extends AsyncTask<Void, Void, String> {
#Override
protected Void doInBackground(Void... voids) {
String words = "";
try {
Document doc = Jsoup.connect("https://screenscrape4top40.000webhostapp.com/").get();
words = doc.text();
} catch(Exception e) {
e.printStackTrace();
}
return words;
}
#Override
public void onPostExecute(String words) {
super.onPostExecute(aVoid);
texx.setText(words);
//String str = (words);
//List<String> elephantList = Arrays.asList(str.split(","));
//texx.setText(elephantList.toString());
// texx.setText(elephantList);
}
}
It should be fine now.
The problem is, you are not returning anything from the doInBackground method and hence you are not getting anything in the onPostExecute function.
You might consider checking the documentation for AsyncTask here.
In doInBackgroud() return the string(make sure it never beacome null) you want to use it on PostExecute()
also changed the data type of Parameter in onPostExecute method
onPostExecute(String Strs)
Then pass it to supper.OnPostExecute().
then u can use it.
My applications hang for a bit when I populate data from realm database to my listview.
So I planned to do it using Asynchronously so meanwhile data is collected I display a Loading dialogue here is the Code.
Already referred to this question by not able to implement in my case.
private class YourAsyncTask extends AsyncTask<String, String, RealmResults> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
// start loading animation maybe?
progressDialog = ProgressDialog.show(DictionarySscWords.this,
"ProgressDialog",
"Loading all words!");
}
#Override
protected RealmResults doInBackground(String... params) {
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);
realm = realm.getDefaultInstance();
RealmQuery<Word> query = realm.where(Word.class);
for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
} else {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
.or();
}
}
sscresult = query.findAll(); //error 1
return sscresult;
}
#Override
protected void onPostExecute(RealmResults r) {
progressDialog.dismiss();
list.setAdapter(new MyAdapter(sscresult)); //error 2
realm.close();
}
}
ok so there are two problems if anyone can be solved my application would be error-free
if I try to run list.setAdapter(new MyAdapter(sscresult)); in background process the error is:-
this can run only in UI thread
if try to run in postExecute error is :-
Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
I am not able to solve this issue please help
You can have your query evaluated on a background thread using asynchronous query API in Realm.
private OrderedRealmCollectionChangeListener<RealmResults<User> callback = new OrderedRealmCollectionChangeListener<>() {
#Override
public void onChange(RealmResults<User> results, OrderedCollectionChangeSet changeSet) {
if (changeSet == null) {
// The first time async returns with an null changeSet.
} else {
// Called on every future update.
}
}
};
private RealmResults<User> result;
public void onStart() {
result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}
But if you give the RealmResults to a RealmRecyclerViewAdapter, then this is all automatic.
P.S. not closing Realm instance in doInBackground() is like, S-class horrible mistake. Please close your Realm instance on non-looping background threads.
Specifically the following:
// private class YourAsyncTask extends AsyncTask<String, String, RealmResults> {
//
// ProgressDialog progressDialog;
// #Override
// protected void onPreExecute() {
// // start loading animation maybe?
// progressDialog = ProgressDialog.show(DictionarySscWords.this,
// "ProgressDialog",
// "Loading all words!");
// }
//
// #Override
// protected RealmResults doInBackground(String... params) {
// RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
// Realm.setDefaultConfiguration(realmConfig);
// realm = realm.getDefaultInstance();
// RealmQuery<Word> query = realm.where(Word.class);
//
// for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
// if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
//
// query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
// } else {
// query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
// .or();
//
// }
//
// }
// sscresult = query.findAll(); //error 1
// return sscresult;
//
// }
//
// #Override
// protected void onPostExecute(RealmResults r) {
// progressDialog.dismiss();
// list.setAdapter(new MyAdapter(sscresult)); //error 2
// realm.close();
// }
//}
and
public class MyActivity extends AppCompatActivity {
private RealmResults<Word> words;
private Realm realm;
private WordAdapter wordAdapter;
#BindView(R.id.recycler_view)
RecyclerView recyclerView;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.my_activity);
ButterKnife.bind(this);
realm = Realm.getDefaultInstance();
words_for_ssc = ...
RealmQuery<Word> query = realm.where(Word.class);
String[] array = words_for_ssc[Integer.parseInt(params[0])];
for (int i = 0; i < array.length; i++) {
query = query.equalTo("word", array[i]);
if (i != array.length - 1) {
query = query.or();
}
}
words = query.findAllSortedAsync("word");
wordAdapter = new WordAdapter(words);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(wordAdapter);
}
#Override
public void onDestroy() {
super.onDestroy();
realm.close();
realm = null;
}
}
public class WordAdapter extends RealmRecyclerViewAdapter<Word, WordViewHolder> {
public class WordAdapter(OrderedRealmCollection<Word> words) {
super(words, true);
}
#Override
public WordViewHolder onCreateViewHolder(...) {
...
}
#Override
public void onBindViewHolder(WordViewHolder holder, int position) {
holder.bind(getData().get(position));
}
public static class WordViewHolder extends RecyclerView.ViewHolder {
public WordViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
public void bind(Word word) {
...
}
}
}
I think a cleaner solution to your problem without changing much of the code can be written below. In this case, everything that realm does happen on the background thread inside doInBackground. The realm instance is also closed on the thread it was created.
Now what I did basically is that I extracted a deep copy of the list of Words from RealmResult from realm.copyFromRealm(sscresult) which is completely detached from realm and can be moved around and modified inside any thread. All these objects are now free from realm and can be used in onPostExecute without any worries. The only thing you need to modify is the MyAdapter constructor which doesn't take a RealmResult but instead a List of Words which is exactly what you need and can be iterated the same way as RealmResult was.
The only downside of this approach is that the list of Words will not get synced automatically since they're detached and their value won't change automatically if they get altered inside Realm from somewhere else. But I'm pretty sure though that it won't bother you.
I'm also going to attach an official reference for realm.copyFromRealm() which is here.
private class YourAsyncTask extends AsyncTask<String, String, List<Word>> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
// start loading animation maybe?
progressDialog = ProgressDialog.show(DictionarySscWords.this,
"ProgressDialog",
"Loading all words!");
}
#Override
protected List<Word> doInBackground(String... params) {
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);
try(realm = realm.getDefaultInstance()) {
RealmQuery<Word> query = realm.where(Word.class);
for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
} else {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
.or();
}
}
// Here's the sort. Use findAllSorted instead.
// You can change Sort.ASCENDING to Sort.DESCENDING to reverse
// the order.
sscresult = query.findAllSorted("word", Sort.ASCENDING);
// This is where the magic happens. realm.copyFromRealm() takes
// a RealmResult and essentially returns a deep copy of the
// list that it contains. The elements of this list is however
// completely detached from realm and is not monitored by realm
// for changes. Thus this list of values is free to move around
// inside any thread.
ArrayList<Word> safeWords = realm.copyFromRealm(sscresult);
realm.close();
return safeWords;
}
}
#Override
protected void onPostExecute(List<Word> words) {
progressDialog.dismiss();
// Please note here MyAdaptor constructor will now take the
// list of words directly and not RealmResults so you slightly
// modify the MyAdapter constructor.
list.setAdapter(new MyAdapter(words));
}
}
Hope it helps!
I am trying to populate fragment listview using async task,
But listvew is not populating. I am getting data in logs, And there is no error and exception in logs.
I am following this example
dynamic listview adding "Load more items" at the end of scroll
Here is code:-
public class HindiFragment extends Fragment {
// XML node keys
static final String KEY_SONG = "song"; // parent node
static final String KEY_ID = "id";
static final String KEY_TITLE = "title";
static final String KEY_ARTIST = "artist";
static final String KEY_DURATION = "duration";
static final String KEY_THUMB_URL = "thumb_url";
static final String KEY_VIDEO_URL = "video";
static final String KEY_UPLOAD_BY = "upload_by";
Context abc=null;
static int startIndex = 0;
private WeakReference<MyAsyncTask> asyncTaskWeakRef;
ListView list;
static LazyAdapter adapter;
JSONObject json;
static int offset = 10;
static ArrayList<HashMap<String, String>> songsList = new ArrayList<HashMap<String, String>>();
public static Context hindiFragment=null;
private static int catId=0;
static View rootView ;
public HindiFragment(){}
public HindiFragment(int position) {
// TODO Auto-generated constructor stub
catId=position;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
//StrictMode.setThreadPolicy(policy);
rootView = inflater.inflate(R.layout.main, container, false);
// TextView tvLabel = (TextView)rootView.findViewById(R.id.txtLabel);
// tvLabel.setText("Hello"); try
hindiFragment=rootView.getContext();
try{
//Toast.makeText(hindiFragment, "catid is "+catId,
// Toast.LENGTH_LONG).show();
UserFunctions userFunction = new UserFunctions();
json=userFunction.getAndroidVersion();
JSONArray android_version_array = json.getJSONArray("version");
TextView tv = (TextView) rootView.findViewById(R.id.android_version);
//getting android version
for (int i = 0; i < android_version_array.length(); i++) {
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
JSONObject myObj = android_version_array.getJSONObject(i);
if(! myObj.getString("version").equalsIgnoreCase(String.valueOf(getString(R.string.android_version)))){
tv.setText( Html.fromHtml(myObj.getString("text")));
tv.setMovementMethod(LinkMovementMethod.getInstance());
}else
{
tv.setVisibility(View.GONE);
}
// adding each child node to HashMap key => value
}
startNewAsyncTask(this.getActivity());
return rootView;
}catch(Exception e){
e.printStackTrace();
}
return rootView;
}
public static void loadMore(int startIndex,int page,Activity myActivity){
try{
UserFunctions userFunction = new UserFunctions();
Log.e("page ",""+page);
int status=0;
JSONObject json;
json = userFunction.getChannelData(String.valueOf(catId),page);
if(json.has("video")){
JSONArray deletedtrs_array = json.getJSONArray("video");
for (int i = 0; i < deletedtrs_array.length(); i++) {
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
JSONObject myObj = deletedtrs_array.getJSONObject(i);
// adding each child node to HashMap key => value
map.put(KEY_ID, myObj.getString("uid"));
map.put(KEY_TITLE, myObj.getString("uid"));
map.put(KEY_ARTIST,myObj.getString("video"));
map.put(KEY_DURATION, myObj.getString("duration"));
map.put(KEY_THUMB_URL,myObj.getString("thumb_url"));
map.put(KEY_VIDEO_URL, myObj.getString("url"));
map.put(KEY_UPLOAD_BY,"By: "+ myObj.getString("upload_by"));
// adding HashList to ArrayList
//if(!songsList.contains(map))
{
songsList.add(map);
status=1;
}
}
}
//if(status==1)
{
//]list=(ListView)rootView.findViewById(R.id.list);
// Getting adapter by passing xml data ArrayList
// adapter=new LazyAdapter(myActivity, songsList);
// list.setAdapter(adapter);
// adapter.notifyDataSetChanged();
}
}catch(Exception e){
e.printStackTrace();
}
}
private void startNewAsyncTask(Activity act) {
MyAsyncTask asyncTask = new MyAsyncTask(act);
this.asyncTaskWeakRef = new WeakReference<MyAsyncTask >(asyncTask );
asyncTask.execute();
}
private static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<HindiFragment> fragmentWeakRef;
HindiFragment uindiFragment;
Activity myActivity;
private MyAsyncTask (Activity activity) {
this.fragmentWeakRef = new WeakReference<HindiFragment>(uindiFragment);
myActivity=activity;
}
#Override
protected Void doInBackground(Void... params) {
//Toast.makeText(hindiFragment, "helllo",
// Toast.LENGTH_LONG).show();
//TODO: your background code
Log.e("Now in background",offset+"");
loadMore(startIndex, offset,myActivity);
return null;
}
#Override
protected void onPostExecute(Void response) {
super.onPostExecute(response);
ListView list=(ListView)rootView.findViewById(R.id.list);
// Getting adapter by passing xml data ArrayList
adapter=new LazyAdapter(myActivity, songsList);
list.setAdapter(adapter);
adapter.notifyDataSetChanged();
Log.e("Ended here11 ","now endeded11");
if (this.fragmentWeakRef.get() != null) {
//TODO: treat the result
adapter.notifyDataSetChanged();
}
}
}
}
You are over-using weak reference. My guess is your async task or fragment is being garbage collected since you use them cyclically: you can verify this by seeing if weakReference.get() returns null.
In fact, you don't need WeakReference at all: just implement appropriate cancel logic that you can call from onPause onStop or onDestroy depending on how you want to handle repeated calls.
Also you shouldn't pass the context to your Async class: instead create an observer such as:
public interface Callback {
public void onAsyncDone(Arraylist<DataItem> listItems);
}
and if not cancelled,
#Override public void onPostExecute(Void v) {
if (!isCancelled()) {
Callback c = callbackReference.get();
if (c != null) c.onAsyncDone(getListItems());
}
}
where getListItems() returns whatever work is done in doInBackground. Here I use a weak reference for the callback (which you implement in your activity) since you seem to want to use one. But again, as long as you release the reference to the context if cancelled, you don't actually need one. The reason the activity is wrapped as the Callback is that the async task should only have one job: process the items and pass them along. Let your activity or controller change the UI with the new items.
I suspect the loadMore() method does not load up any data to object songsList into the adapter. Simply trace the reason why it is not. There's no way I can debug this without using debugger, the code flow is rather complicated with unusually tight references and, more so, with static access.
The suspected code for review:
protected void onPostExecute(Void response) {
super.onPostExecute(response);
ListView list=(ListView)rootView.findViewById(R.id.list);
// Getting adapter by passing xml data ArrayList
adapter=new LazyAdapter(myActivity, songsList);
list.setAdapter(adapter);
adapter.notifyDataSetChanged();
...
}
Note: You can put some good logging in LazyAdapter code and see if data is populated there.
Im trying to return an ArrayList from the AsyncTask class back to the MainAcitivity class and then use this arraylist to fill the gridview in MainActivity.
The parseURL takes a String paramater to parse the url. And parseURL is executed when the user clicks the button. The code i have compiles and run but the gridview is not populated after triggering the button event and pressing the button twice crashes the app.
EDIT: After adding loop callback, it stops crashing but it still wont populate the gridview. The ArrayList object that i want to populate the gridview is in this format 10,John,Smith
Here is my code for MainActivity (using Stanislav Bodnar suggestion)
private GridView grid1;
private ArrayAdapter<String> adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initalized the grid and adapter
grid1 = (GridView)findViewById(R.id.gridView1);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
grid1.setAdapter(adapter);
}
public void onButtonClick(View v) {
EditText textInput = (EditText) findViewById(R.id.editText1);
String code = textInput.getText().toString();
new parseURL() {
#Override
protected void onPostExecute(List<String> list) {
//use the list from parseURL to fill grid view
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
gridView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}.execute(code);
ParseURL class
public class parseURL extends AsyncTask<String, Void, List<String>> {
protected List<String> doInBackground(String... params) {
List<String> str = new ArrayList<String>();
try {
Document doc = Jsoup.connect("http://www.mywebsite.com/id/" + params).get();
Elements row1 = doc.select("table");
Elements row2 = doc.select("td");
Elements row3 = doc.select("td");
for (int i = 0; i < row1.size(); i++) {
str.add(row1.get(i).text() + "," + row2.get(i).text() + "," + row2.get(i).text());
}
return str;
} catch (Exception e) {
return new ArrayList<String>();
}
}
You can add loading callback.
public void onButtonClick(View v) {
EditText textInput = (EditText) findViewById(R.id.editText1);
String code = textInput.getText().toString();
new parseURL() {
#Override
protected void onPostExecute(List<String> list) {
//use the list from parseURL to fill grid view
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
gridView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
}.execute(code);
}
Asynctask:
public class parseURL extends AsyncTask<String, Void, List<String>> {
protected List<String> doInBackground(String... params) {
List<String> str = new ArrayList<String>();
try {
Document doc = Jsoup.connect("http://www.mywebsite.com/id/" + params).get();
Elements row1 = doc.select("table");
Elements row2 = doc.select("td");
Elements row3 = doc.select("td");
for (int i = 0; i < row1.size(); i++) {
str.add(row1.get(i).text() + "," + row2.get(i).text() + "," + row2.get(i).text());
}
return str;
} catch (Exception e) {
return new ArrayList<String>();
}
}
}
Supplement
If array list that returns by onPostExecute is not empty your grid will be populated in the next way each cell will have string 10,John,Smith. Please check that method doInBackground does not catch some exception and fills array list correctly.
Next if you want to do a table view where 1 row will contain 3 columns 10 | John | Smith then parse data into object structure:
class Person {
private int id;
private String firstName;
private String lastName;
}
Then change method doInBackground to return array list of Person objects.
Create custom adapter (extend BaseAdapter) where init view using Person object.
View will be as LinearLayout with horizontal orientation which will contain 3 TextView with Layout Weight (android:layout_weight="0.3" - set in each TextView, you can change this value). Then use ListView instead of GridView. Each row of list view will contain 1 Person.
The AsyncTask is by definition asynchronous. Your code handles it like it was synchronous.
Move
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
gridView.setAdapter(adapter);
adapter.notifyDataSetChanged();
into the setList method.
Also, passing around the MainActivity feels a bit shaky. What happens if the Activity is destroyed during the async-task?
Instead, make the AsyncTask a private inner class or an anonymous class in MainActivity
And of course you will need to initialize your list before you populate it.
Don't do gridview.setAdapter() twice. There are a few problems with that code. As P-a Bäckström wrote:
Also, passing around the MainActivity feels a bit shaky. What happens if the Activity is destroyed during the async-task? Instead, make the AsyncTask a private inner class or an anonymous class in MainActivity
You need to fix that too. Now comes the updating part:
Declare a global Handler like this:
private final Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
if(msg.what == 1) {
List<String> list = (List) msg.obj;
adapter.insert(list);
adapter.notifyDataSetChanged();
}
}
};
Then in onPostExecute() send your data to the UI thread using the handler and update the gridview:
protected void onPostExecute(List<String> list) {
handler.obtainMessage(1, list);
}
try this:
Mainactivity.class
private GridView grid1;
private ArrayAdapter<String> adapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//initalized the grid and adapter
grid1 = (GridView)findViewById(R.id.gridView1);
adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1);
grid1.setAdapter(adapter);
}
public void onButtonClick(View v) {
EditText textInput = (EditText) findViewById(R.id.editText1);
String code = textInput.getText().toString();
new parseURL(this).execute(code);
}
public void onBackgroundTaskCompleted(List<String> result) {
// TODO Auto-generated method stub
Log.i(TAG, "onBackgroundTaskCompleted List: "+result);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list);
gridView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
Asynctask:
public class parseURL extends AsyncTask<String, Void, List<String>> {
MainActivity caller;
public Scheduler(MainActivity mainActivity) {
// TODO Auto-generated constructor stub
this.caller = mainActivity;
}
protected void onPostExecute(List<String> result) {
caller.onBackgroundTaskCompleted(result);
}
protected List<String> doInBackground(String... params) {
List<String> str = new ArrayList<String>();
try {
Document doc = Jsoup.connect("http://www.mywebsite.com/id/" + params).get();
Elements row1 = doc.select("table");
Elements row2 = doc.select("td");
Elements row3 = doc.select("td");
for (int i = 0; i < row1.size(); i++) {
str.add(row1.get(i).text() + "," + row2.get(i).text() + "," + row2.get(i).text());
}
return str;
} catch (Exception e) {
return new ArrayList<String>();
}
}
}
I'm newbie in Java/ADT and I'm trying to get an array from "Activity A" to "Activity B". The app takes information from a webpage, and then saves it in a pair of arrays and show the information. I want to click a "go to graph" button (calls to viewallday()) to redirect to Activity B who will show a graphic with all this information.
The problem is that they're a self refresh array (1sec refresh) and don't want to loose this feature when the app It's on graphic mode (Activity B). Any ideas about how to do that?
Thank all of you in advance, I'm learning a lot from this site.
UPDATE: I'm trying to do this with a Singleton pattern. But LogCat says:
02-26 22:21:59.300: E/AndroidRuntime(2677): FATAL EXCEPTION: main
02-26 22:21:59.300: E/AndroidRuntime(2677): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.Chispa/com.example.Chispa.Activity_allday}: android.os.NetworkOnMainThreadException
02-26 22:21:59.300: E/AndroidRuntime(2677): Caused by: android.os.NetworkOnMainThreadException
UPDATE 2: Finally got it!! Here's the code I used:
Here's the code for Activity A:
public class MainActivity extends Activity {
private TextView tvmax, tvmid, tvmin, tvactualval,tvvaloractual,tvdate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvdate=(TextView)findViewById(R.id.tvdate);
tvvaloractual=(TextView)findViewById(R.id.tvvaloractual);
tvmax=(TextView)findViewById(R.id.tvmaximo);
tvmid=(TextView)findViewById(R.id.tvmedio);
tvmin=(TextView)findViewById(R.id.tvminimo);
new BackGroundTask().execute();
callAsynchronousTask();
}
public void callAsynchronousTask() {
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
BackGroundTask performBackgroundTask = new BackGroundTask();
// PerformBackgroundTask this class is the class that extends AsynchTask
performBackgroundTask.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
});
}
};
timer.schedule(doAsynchronousTask, 0, 1000); //execute in every 1000 ms
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public class Pair
{
public String[] bar;
public String[] values;
}
public void viewallday(View view) {
Intent intent = new Intent(MainActivity.this, Activity_allday.class);
startActivity(intent);
}
class BackGroundTask extends AsyncTask<Void, Void, Pair> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
public String[] getValuesGraph(Document doc) {
int cont=24,var=7;
String bar[] = new String[cont];
/*
* Getting elements from the graphic in an array from 0-23. 0 it's 1:00am, 23 it's 00:00am
*/
for (cont=0; cont < 24; cont++){
String onMouseOver = doc.select("a").get(var+cont).attr("onMouseOver");
bar[cont] = onMouseOver.split("'")[9];
}
return bar;
}
public String[] getValuesFooter(Document doc) {
String values[] = new String[7];
/*
* Getting elements from the graphic footer
*/
String delimiters= "[ /]+";
Elements elements = doc.select("td.cabeceraRutaTexto");
elements.size(); // 6
/* Getting text from table */
values[0] = elements.get(0).text(); // TITLE
values[1] = elements.get(1).text(); // TEXT MAX VALUE
values[2] = elements.get(2).text(); // TEXT MIDDLE VALUE
values[3] = elements.get(3).text(); // TEXTO MIN VALUE
/* Getting numbers from table */
values[4] = elements.get(4).text().split(delimiters)[0]; // NUMBER MAX VALUE
values[5] = elements.get(5).text().split(delimiters)[0]; // NUMBER MIDDLE VALUE
values[6] = elements.get(6).text().split(delimiters)[0]; // NUMBER MIN VALUE
return values;
}
public Document getUrl(){
try {
URL url= new URL("http://www.endesaonline.com/canal/precios/Canal_Preciosdelpool.asp?FECHA=20140226");
/*URL url= new URL("http://www.endesaonline.com/canal/precios/Canal_Preciosdelpool.asp?lang=es&frameId=4064&segmento=1&promocion=");*/
Document doc = Jsoup.connect(url.toString()).get();
return doc;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
public Pair doInBackground(Void... params) {
Pair p = new Pair();
GlobalVariables gs = (GlobalVariables) getApplication();
gs.setBar(getValuesGraph(getUrl()));
p.bar = getValuesGraph(getUrl());
p.values = getValuesFooter(getUrl());
return p;
}
public String ActualHourValue() {
Format formatter = new SimpleDateFormat("H");
String onlyhour = formatter.format(new Date());
return onlyhour;
}
public void ShowDateHour(){
Calendar c = Calendar.getInstance();
SimpleDateFormat df3 = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss a");
String formattedDate3 = df3.format(c.getTime());
tvdate.setText("Fecha y hora actuales : "+formattedDate3);
}
#Override
protected void onPostExecute(Pair p) {
int hour = Integer.parseInt(ActualHourValue());
tvvaloractual.setText(p.bar[hour]+" €/MWh");
tvmax.setText(p.values[4]+" €/MWh");
tvmid.setText(p.values[5]+" €/MWh");
tvmin.setText(p.values[6]+" €/MWh");
ShowDateHour();
/*super.onPostExecute(p.values);*/
}
}
}
And here's the code for Activity B:
public class Activity_allday extends MainActivity {
private TextView tvall;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.all_day_price);
TextView tvall=(TextView)findViewById(R.id.tvall);
GlobalVariables gs = (GlobalVariables) getApplication();
String[] s = gs.getBar();
tvall.setText(s[0]);
}
}
And here's a GlobalVariable class who captures the array I want to send to Activity B:
public class Activity_allday extends MainActivity {
private TextView tvall;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.all_day_price);
TextView tvall=(TextView)findViewById(R.id.tvall);
GlobalVariables gs = (GlobalVariables) getApplication();
String[] s = gs.getBar();
tvall.setText(s[0]);
}
}
That's all! hope It'll help to future users.
Thanks all for your help.
Here is something you could try:
Inside your AsyncTask, define an interface and a method inside it that will pass back the data to the calling activity and inside that method, call the next activity and set the data as an extra.
This is the simplest way.
In your AsyncTask, inside onPostExecute(result), use a try block to call the method which belongs to the above mentioned interface which must be implemented by the calling activity.
HomeActivity.java
/public class SampleActivity extends Activity implements SampleAsyncTask.OnUpdateListener{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
executeAsync();
}
public void executeAsync(){
new SampleAsyncTask(this).execute("someFlagToCheck");
}
#Override
public void onDataProcessed(String result){
Intent intent = new Intent(this, SecondActivity.class);
intent.putExtra("data", result);
startActivity(intent);
}
}
SampleAsyncTask.java
public class SampleAsyncTask extends AsyncTask<Void, Void, String>{
Context context;
//constructor
SampleAsyncTask(Context context){
this.context = context;
}
#Override
public void doInBackground(String... params){
//do something depending on the arguments in params
return "data";
}
#Override
public void onPostExecute(String result){
try{
((OnUpdateListener) context).onDataProcessed(result);
}catch(Exception e){
e.printStackTrace();
}
}
public interface OnUpdateListener{
public void onDataProcessed(String data);
}
}
Follow this example. The calling activity implements the AsyncTask's interface and overrides its method which will be called when the async task is done with the result.
I hope this helped.
SOLUTION:
Here's the code for Activity A:
public class MainActivity extends Activity {
private TextView tvmax, tvmid, tvmin, tvactualval,tvvaloractual,tvdate;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvdate=(TextView)findViewById(R.id.tvdate);
tvvaloractual=(TextView)findViewById(R.id.tvvaloractual);
tvmax=(TextView)findViewById(R.id.tvmaximo);
tvmid=(TextView)findViewById(R.id.tvmedio);
tvmin=(TextView)findViewById(R.id.tvminimo);
new BackGroundTask().execute();
callAsynchronousTask();
}
public void callAsynchronousTask() {
final Handler handler = new Handler();
Timer timer = new Timer();
TimerTask doAsynchronousTask = new TimerTask() {
#Override
public void run() {
handler.post(new Runnable() {
public void run() {
try {
BackGroundTask performBackgroundTask = new BackGroundTask();
// PerformBackgroundTask this class is the class that extends AsynchTask
performBackgroundTask.execute();
} catch (Exception e) {
// TODO Auto-generated catch block
}
}
});
}
};
timer.schedule(doAsynchronousTask, 0, 1000); //execute in every 1000 ms
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public class Pair
{
public String[] bar;
public String[] values;
}
public void viewallday(View view) {
Intent intent = new Intent(MainActivity.this, Activity_allday.class);
startActivity(intent);
}
class BackGroundTask extends AsyncTask<Void, Void, Pair> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
public String[] getValuesGraph(Document doc) {
int cont=24,var=7;
String bar[] = new String[cont];
/*
* Getting elements from the graphic in an array from 0-23. 0 it's 1:00am, 23 it's 00:00am
*/
for (cont=0; cont < 24; cont++){
String onMouseOver = doc.select("a").get(var+cont).attr("onMouseOver");
bar[cont] = onMouseOver.split("'")[9];
}
return bar;
}
public String[] getValuesFooter(Document doc) {
String values[] = new String[7];
/*
* Getting elements from the graphic footer
*/
String delimiters= "[ /]+";
Elements elements = doc.select("td.cabeceraRutaTexto");
elements.size(); // 6
/* Getting text from table */
values[0] = elements.get(0).text(); // TITLE
values[1] = elements.get(1).text(); // TEXT MAX VALUE
values[2] = elements.get(2).text(); // TEXT MIDDLE VALUE
values[3] = elements.get(3).text(); // TEXTO MIN VALUE
/* Getting numbers from table */
values[4] = elements.get(4).text().split(delimiters)[0]; // NUMBER MAX VALUE
values[5] = elements.get(5).text().split(delimiters)[0]; // NUMBER MIDDLE VALUE
values[6] = elements.get(6).text().split(delimiters)[0]; // NUMBER MIN VALUE
return values;
}
public Document getUrl(){
try {
URL url= new URL("http://www.endesaonline.com/canal/precios/Canal_Preciosdelpool.asp?FECHA=20140226");
/*URL url= new URL("http://www.endesaonline.com/canal/precios/Canal_Preciosdelpool.asp?lang=es&frameId=4064&segmento=1&promocion=");*/
Document doc = Jsoup.connect(url.toString()).get();
return doc;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
#Override
public Pair doInBackground(Void... params) {
Pair p = new Pair();
GlobalVariables gs = (GlobalVariables) getApplication();
gs.setBar(getValuesGraph(getUrl()));
p.bar = getValuesGraph(getUrl());
p.values = getValuesFooter(getUrl());
return p;
}
public String ActualHourValue() {
Format formatter = new SimpleDateFormat("H");
String onlyhour = formatter.format(new Date());
return onlyhour;
}
public void ShowDateHour(){
Calendar c = Calendar.getInstance();
SimpleDateFormat df3 = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss a");
String formattedDate3 = df3.format(c.getTime());
tvdate.setText("Fecha y hora actuales : "+formattedDate3);
}
#Override
protected void onPostExecute(Pair p) {
int hour = Integer.parseInt(ActualHourValue());
tvvaloractual.setText(p.bar[hour]+" €/MWh");
tvmax.setText(p.values[4]+" €/MWh");
tvmid.setText(p.values[5]+" €/MWh");
tvmin.setText(p.values[6]+" €/MWh");
ShowDateHour();
/*super.onPostExecute(p.values);*/
}
}
}
Activity B:
public class Activity_allday extends MainActivity {
private TextView tvall;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.all_day_price);
TextView tvall=(TextView)findViewById(R.id.tvall);
GlobalVariables gs = (GlobalVariables) getApplication();
String[] s = gs.getBar();
tvall.setText(s[0]);
}
}
Global variable class who captures the array I want to send to Activity B:
public class Activity_allday extends MainActivity {
private TextView tvall;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.all_day_price);
TextView tvall=(TextView)findViewById(R.id.tvall);
GlobalVariables gs = (GlobalVariables) getApplication();
String[] s = gs.getBar();
tvall.setText(s[0]);
}
}
Thanks all for your help!!