My app requires to parse a XML file when the activity is launched. So, I use an Asynctask to parse the XML.
After parsing the XML file, I get the count of number of test-cases in the XML in doInBackground() method, and I use the variable alTestCaseList to keep this count.
Later in onPostExecute() method, I set the ArrayAdapter and register the click listener for the list.
However, when I click any testcase from the list, I'm supposed to parse the test-case entries from the XML again.
So I believe for this I'll have to use an AsyncTask again. So do I start another AsyncTask for onPostExecute() method of first AsyncTask?
Is there any other neat way of doing this?
I tried to put setOnItemClickListener() in onCreate() method, but it resulted in fatal exception with the message: "setOnItemClickListener(android.widget.AdapterView$OnItemClickListener) on a null object reference"........
Kindly give your suggestion.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testcases);
xmlHelp = new XmlHelper();
ConfigParser confParser = new ConfigParser();
confParser.execute();
}
private class ConfigParser extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
alTestCaseList = xmlHelp.getNumberOfNodes();
return null;
}
#Override
protected void onPostExecute(Void v) {
testCasesListView = (ListView) findViewById(R.id.lstTestCases);
arrayAdapter = new ArrayAdapter(TestCasesActivity.this, android.R.layout.simple_list_item_1, alTestCaseList);
testCasesListView.setAdapter(arrayAdapter);
testCasesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3) {
String value = (String) adapter.getItemAtPosition(position);
Log.d("QcTool", "Selected: " + value);
}
});
}
}
Here is my take on the solution. Pass your data as an argument to the task and notify the adapter when you get the list.
See the comments for further explanation. Note that this approach does not handle issues that AsyncTask's typically come with is situations such as configuration changes.
You can then create another ParseXmlTask class which can be called in your OnItemClicked method
private ListView testCasesListView;
private ArrayAdapter arrayAdapter;
private List<String> testCasesList;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testcases);
//Init the list - it's empty but your task will fill it.
testCasesList = new ArrayList<>();
//Init your listView
testCasesListView = (ListView) findViewById(R.id.lstTestCases);
//Add adapter to the listView
arrayAdapter = new ArrayAdapter(TestCasesActivity.this, android.R.layout.simple_list_item_1, alTestCaseList);
//Add your click event
testCasesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3) {
//When clicked, do something awesome
String value = (String) adapter.getItemAtPosition(position);
Log.d("QcTool", "Selected: " + value);
//Create and start parseXmlTask here
}
});
xmlHelp = new XmlHelper();
//Pass in your callback as an argument
ConfigParserTask confParser = new ConfigParserTask(new OnConfigParserTaskCompleted(){
public void onConfigParserTaskCompleted(List<String> result){
//Simply refresh the list
testCasesList.clear();
testCasesList.addAll(result);
//Let the adapter know that the list has changed
//Then update the list view
arrayAdapter.notifyDataSetChanged();
}
});
confParser.execute(xmlHelp);
}
//It's better to pass in the info to the task as arguments than it is to rely on
//field variables
private class ConfigParserTask extends AsyncTask<XmlHelper, Void, List<String>> {
private OnConfigParserTaskCompleted listener;
public ConfigParser(OnConfigParserTaskCompleted listener){
this.listener = listener;
}
#Override
protected Void doInBackground(XmlHelper... params) {
//Do what you need to in the background
//Get your nodes then return it here
List<String> nodes = params[0].getNumberOfNodes();
return nodes;
}
#Override
protected void onPostExecute(List<String> result) {
//pass the result to the callback
listener.onConfigParserTaskCompleted(result);
}
}
//Callback to let your activity/fragment know when
//the task is complete
public interface OnConfigParserTaskCompleted{
public void onConfigParserTaskCompleted(List<String> result);
}
You can do something like this using AsyncTasks
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_testcases);
testCasesListView = (ListView) findViewById(R.id.lstTestCases);
testCasesListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position, long arg3) {
String value = (String) adapter.getItemAtPosition(position);
Log.d("QcTool", "Selected: " + value);
ParserNodeTask nodeTask = new ParserNodeTask();
nodeTask.execute(value);
}
});
xmlHelp = new XmlHelper();
ParserNumberOfNodesTask numberOfNodesTask = new ParserNumberOfNodesTask();
numberOfNodesTask.execute();
}
private class ParserNumberOfNodesTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
alTestCaseList = xmlHelp.getNumberOfNodes();
return null;
}
#Override
protected void onPostExecute(Void v) {
arrayAdapter = new ArrayAdapter(TestCasesActivity.this, android.R.layout.simple_list_item_1, alTestCaseList);
testCasesListView.setAdapter(arrayAdapter);
}
}
private class ParserNodeTask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(String... params) {
String value = params[0];
//TODO : parse the selected node
}
#Override
protected void onPostExecute(Void v) {
//TODO: dunno what you need to do later
}
}
Though AsyncTasks are not ideal for this for many reasons (but easier to implement). You should maybe take a look on to Loaders or Services (i.e https://stackoverflow.com/a/6957909/665823)
Related
I have a main activity with a list view, an onItemClickListener() event is associated to the list view and when I touch an element I start an AsyncTask that does some work. I would like to disable the other touches that the user could make to the other elements of the list during the async task execution and re-enable them when the async task ends.
How could I do?
This is my activity
public class MainActivity extends AppCompatActivity implements AdapterView.OnItemClickListener{
private ListView newList;
private ArrayAdapter<String> newArrayAdapter;
private MyAsyncTask asyncTask;
private ProgressBar progressBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressbar);
newArrayAdapter = new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1);
newList = findViewById(R.id.pairedDevicesListView);
newList.setOnItemClickListener(this);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
newList.setOnItemClickListener(null); //need something like this
asyncTask = new MyAsyncTask(this);
asyncTask.execute();
}
#Override
public void onBackPressed() {
asyncTask.cancel(true);
super.onBackPressed();
}
private class MyAsyncTask extends AsyncTask<Void,Void,Void>{
Context context;
public MyAsyncTask(Context context){
this.context = context.getApplicationContext();
}
#Override
protected Void doInBackground(Void... voids) {
// do some stuff
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
progressBar.setVisibility(View.GONE);
newList.setOnItemClickListener();
//does not work or I don't know what to pass
}
}
}
Try this:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (asyncTask == null || asyncTask.getStatus() == AsyncTask.Status.FINISHED) {
asyncTask = new MyAsyncTask(this);
asyncTask.execute();
}
}
Be careful with onBackPressed, or you may call a function on a null object.
#Override
public void onBackPressed() {
if (asyncTask != null) {
asyncTask.cancel(true);
}
super.onBackPressed();
}
You could write something like this in your onItemClickListener:
newList.setEnabled(false);
And then in your onPostExecute method write:
newList.setEnabled(true);
I made simple Client server to android.
I have problem when I send an object from server to the client.
The object is received ok and when I check the log, it shows me the the object was sent successfully.
The problem occurs when I'm trying to get this object and put it in my ListView adapter.
The adapter works, I checked it with a random ArrayList I created.
My issue is when I'm trying to to put the values of AsyncTask in my adapter.
public class RestaurantListFragment extends Fragment {
private ArrayList<Resturant> res = new ArrayList<>();
private ListAdapter adapter;
private Firebase restRef = new Firebase("https://restmeup.firebaseio.com/restaurants");
private Client mClient;
// private connectTask t = (connectTask)new connectTask().execute();
public RestaurantListFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new connectTask().execute();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// new connectTask(getView()).execute();
final View rootView = inflater.inflate(R.layout.fragment_two, container, false);
ListView restaurantList = (ListView) rootView.findViewById(R.id.list);
adapter = new ListAdapter(getContext(), res, getActivity());
restaurantList.setAdapter(adapter);
// connectTask t = (connectTask)new connectTask().execute();
if (mClient != null) {
mClient.sendMessage("bar");
}
SqlQueriesConverter sql = new SqlQueriesConverter();
sql.getResurantsListQuery("bar");
// sql.getUserFavoritesResturants(accessToken.getUserId());
mClient.sendMessage(sql.getQuery());
// t.setArray(res);
mClient.sendMessage("/quit");
mClient.stopClient();
final EditText searchText = (EditText)rootView.findViewById(R.id.searchListView);
searchText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
System.out.println("Before---------");
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
String text = searchText.getText().toString().toLowerCase(Locale.getDefault());
adapter.filter(text);
adapter.notifyDataSetChanged();
System.out.println("array: " + res.toString());
}
#Override
public void afterTextChanged(Editable s) {
System.out.println("After---------");
}
});
// Inflate the layout for this fragment
return rootView;
}
public class connectTask extends AsyncTask<ArrayList<?>,ArrayList<?>,Client> {
// private Client mClient;
private ArrayList<?> arrayList = new ArrayList<>();
#Override
protected Client doInBackground(ArrayList<?>... message) {
//we create a Client object and
mClient = new Client(new Client.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(ArrayList<?> message) {
//this method calls the onProgressUpdate
// publishProgress(message);
onProgressUpdate(message);
}
});
mClient.run();
return null;
}
// #Override
protected void onProgressUpdate(ArrayList<?>... values) {
super.onProgressUpdate(values);
ArrayList<?> arr2;
if (values[0].get(0) instanceof Resturant){
Log.d("step 1", "1");
if (((ArrayList<?>)values[0]).get(0)instanceof Resturant) {
// arr2 = (ArrayList<Resturant>) values[0];
res = (ArrayList<Resturant>) values[0];
adapter.notifyDataSetChanged();
Log.d("array",res.toString());
}
}
if (values[0].get(0)instanceof Review){
arr2 = (ArrayList<Review>) values[0];
}
if (values[0].get(0)instanceof UserFavorites){
arr2 = (ArrayList<Review>) values[0];
Log.d("step 2", "2");
}
}
}
}
There are two things you need to change to use the AsyncTask as you intend. The first change you need is to return the ArrayList you get from your mClient in the doInBackground method. This is a bit troublesome because it looks like the Client is already running asynchronously since you pass a callback to get the result (this is the Client.OnMessageReceived anonymous class you have there). The second change would be to implement onPostExecute on your AsyncTask since that is where the results from doInBackground are sent. You'd add the result sent from doInBackground to your adapter in there.
In any case, since it looks like Client is already performing the work asynchronously, you shouldn't need to use an AsyncTask at all. Just implement the logic to add the results to your adapter in the Client.OnMessageReceived callback.
Just get the code you already have in onProgressUpdate and throw it inside messageReceived. Something like this:
mClient = new Client(new Client.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(ArrayList<?> values) {
if (values[0].get(0) instanceof Resturant){
Log.d("step 1", "1");
if (((ArrayList<?>)values[0]).get(0)instanceof Resturant) {
res = (ArrayList<Resturant>) values[0];
adapter.notifyDataSetChanged();
Log.d("array",res.toString());
}
}
}
});
I have a ListView which I populate with data from DataStore or from my local database.
I am checking some condition that will determine whether I will fetch data from the DataStore or database. When I fetch from the database the ListView automatically refreshes itself, but when I fetch from the DataStore it does not. I then have to click my TextView, which is below ListView, and when I click it the soft keyboard appears and then my ListView is populated with data from DataStore.
My activity that has the ListView:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xyz);
list_View = (ListView) findViewById(R.id.data_listView);
list_View.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
list_View.setMultiChoiceModeListener(new Selector());
adapter = new MyAdapter(context,Long.valueOf(id),isOnline());
list_View.setAdapter(adapter);
list_View.setSelection(adapter.getCount() - 1);
adapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
list_View.setSelection(adapter.getCount() - 1);
}
LoadDataTask ldt = new LoadDataTask();
ldt.execute("123456789");
}
private void loadDataFromDataStore(final Long contactId){
final ArrayList<Data> data = new ArrayList<>();;
d("loadingdataFromDatasore");
GetDataTask task = new GetDataTask(new ApiTask.ResultListener() {
#Override
public void successHook(Object o) {
if (o instanceof GetDataResponse) {
GetDataResponse res = (GetDataResponse) o;
if (res.getData() != null && res.getData().getItems() != null) {
for (ListDataItem i : res.getData().getItems()) {
Data dp = new Data(i.getPosition(), i.getMessage(), i.getDateCreated(),i.getMessageId(),1);
adapter.addFromOtherThread(dp);
}
}
d("Messages loaded from server: " + adapter.getCount());
}
}
}
public class LoadDataTask extends AsyncTask<String,String,Void> {
#Override
protected Void doInBackground(String... params){
if(isOnline && isFirstTime){
loadDataFromDataStore(Long.valueOf(params[0]));
}else{
//load from database
}
return null;
}
#Override
protected void onPostExecute(Void v){
adapter.notifyDataSetChanged();
}
}
My adapter class that extends BaseAdapter (I have removed unnecessary code for this question):
public class DataAdapter extends BaseAdapter {
private ArrayList<Data>data_list;
public DataAdapter(){
data_list = new ArrayList<>();
}
public void addFromOtherThread(Data object) {
data_list.add(object);
}
What am I missing that is making listview not to automatically refresh itself even after calling notifyDatasetChanged()?
change :
#Override
protected void onPostExecute(Void v){
adapter.notifyDataSetChanged();
}
}
to:
#Override
protected void onPostExecute(Void v){
adapter.notifyDataSetChanged();
list_View.setAdapter(adapter);
}
}
Let me know if more clarification is required by commenting below.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_xyz);
list_View = (ListView) findViewById(R.id.data_listView);
list_View.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
list_View.setMultiChoiceModeListener(new Selector());
adapter = new MyAdapter(context,Long.valueOf(id),isOnline());
list_View.setAdapter(adapter);
list_View.setSelection(adapter.getCount() - 1);
adapter.registerDataSetObserver(new DataSetObserver() {
#Override
public void onChanged() {
super.onChanged();
list_View.setSelection(adapter.getCount() - 1);
}
loadDataFromDataStore("123456789")
}
private void loadDataFromDataStore(final Long contactId){
final ArrayList<Data> data = new ArrayList<>();;
d("loadingdataFromDatasore");
new GetDataTask(new ApiTask.ResultListener() {
#Override
public void successHook(Object o) {
if (o instanceof GetDataResponse) {
GetDataResponse res = (GetDataResponse) o;
if (res.getData() != null && res.getData().getItems() != null) {
for (ListDataItem i : res.getData().getItems()) {
Data dp = new Data(i.getPosition(), i.getMessage(), i.getDateCreated(),i.getMessageId(),1);
adapter.addFromOtherThread(dp);
}
}
d("Messages loaded from server: " + adapter.getCount());
adapter.notifyDatasetChanges();
}
}
}.execute();
}
GetDataTask should work on background internally you don't need to starts a AsyncTask from here.
If you want to use AsyncTask then your AsyncTask should wait for the result from GetDataTask which it is not doing in your code implementation.
I don't know which kind of framework you are using to making api call but your implementation seems to look wrong.
I have write the code on assumption bases if your GetDataTask is a AsyncTask or some background processor it will work perfectly.
I have an implementation of onItemClick() while selecting from a list of items. However inside
onItemClick() I need to perform a check via a library call that is async. Basically within onItemClick() a call to XYZ.checkConnection(booleanCallback); is made which is async.
And then in booleanCallback(int status) I can check for the status. What kind of pattern can I use to handle this scenario?
public class Tester extends Activity {
private ListView list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
new CheckConnection(list.getAdapter().getItem(position))
.execute();
}
});
}
private class CheckConnection extends AsyncTask<Void, Void, Void> {
private Object o;
public CheckConnection(Object o) {
this.o = o;
}
#Override
protected void onPreExecute() {
// Do things like initiate progress bar etc
}
#Override
protected Void doInBackground(Void... params) {
// Do connection check
return null;
}
#Override
protected void onPostExecute(Void result) {
int status = 0;
// determine status
booleanCallBack(status, o);
}
}
private void booleanCallBack(int status, Object o) {
// perform UI related
}
}
you use the delegate approach if you want a modular class
I have essentially given you a starting point,this can be tweaked based on what you want.I didnt exactly get your context.hence the generalised answer
Update1
activity:
public Integer _number = 0;
#Override
public void onCreate(Bundle savedInstanceState) {
if (_number >0)
{
Log.d("onSuccessfulExecute", ""+_number);
}
else
{
Log.d("onSuccessfulExecute", "nope empty songs lists");
}
}
public int onSuccessfulExecute(int numberOfSongList) {
_number = numberOfSongList;
if (numberOfSongList >0)
{
Log.d("onSuccessfulExecute", ""+numberOfSongList);
}
else
{
Log.d("onSuccessfulExecute", "nope empty songs lists");
}
return numberOfSongList;
}
end Update1
UPDATE: AsynchTask has its own external class.
How to pass an value from AsyncTask onPostExecute()... to activity
my code does returning value from onPostExecute() and updating on UI but i am looking for a way to set the activity variable (NumberOfSongList) coming from AsynchTask.
AsyncTask class:
#Override
public void onPostExecute(asynctask.Payload payload)
{
AsyncTemplateActivity app = (AsyncTemplateActivity) payload.data[0];
//the below code DOES UPDATE the UI textView control
int answer = ((Integer) payload.result).intValue();
app.taskStatus.setText("Success: answer = "+answer);
//PROBLEM:
//i am trying to populate the value to an variable but does not seems like the way i am doing:
app.NumberOfSongList = payload.answer;
..............
..............
}
Activity:
public Integer NumberOfSongList;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Several UI Code
new ConnectingTask().execute();
Log.d("onCreate", ""+NumberOfSongList);
}
What about using a setter method? e.g.
private int _number;
public int setNumber(int number) {
_number = number;
}
UPDATE:
Please look at this code. This will do what you're trying to accomplish.
Activity class
public class TestActivity extends Activity {
public int Number;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
Button btnDisplay = (Button) findViewById(R.id.btnDisplay);
btnDisplay.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
Toast toast = Toast.makeText(v.getContext(), "Generated number: " + String.valueOf(Number), Toast.LENGTH_LONG);
toast.show();
}
});
new TestTask(this).execute();
}
}
AsyncTask class
public class TestTask extends AsyncTask<Void, Void, Integer> {
private final Context _context;
private final String TAG = "TestTask";
private final Random _rnd;
public TestTask(Context context){
_context = context;
_rnd = new Random();
}
#Override
protected void onPreExecute() {
//TODO: Do task init.
super.onPreExecute();
}
#Override
protected Integer doInBackground(Void... params) {
//Simulate a long-running procedure.
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Log.e(TAG, e.getMessage());
}
return _rnd.nextInt();
}
#Override
protected void onPostExecute(Integer result) {
TestActivity test = (TestActivity) _context;
test.Number = result;
super.onPostExecute(result);
}
}
Just a word of caution: Be very careful when attempting to hold a reference to an Activity instance in an AsyncTask - I found this out the hard way :). If the user happens to rotate the device while your background task is still running, your activity will be destroyed and recreated thus invalidating the reference being to the Activity.
Create a listener.
Make a new class file. Called it something like MyAsyncListener and make it look like this:
public interface MyAsyncListener() {
onSuccessfulExecute(int numberOfSongList);
}
Make your activity implement MyAsyncListener, ie,
public class myActivity extends Activity implements MyAsyncListener {
Add the listener to the constructor for your AsyncTask and set it to a global var in the Async class. Then call the listener's method in onPostExecute and pass the data.
public class MyCustomAsync extends AsyncTask<Void,Void,Void> {
MyAsyncListener mal;
public MyCustomAsync(MyAsyncListener listener) {
this.mal = listener;
}
#Override
public void onPostExecute(asynctask.Payload payload) {
\\update UI
mal.onSuccessfulExecute(int numberOfSongList);
}
}
Now, whenever your AsyncTask is done, it will call the method onSuccessfulExecute in your Activity class which should look like:
#Override
public void onSuccessfulExecute(int numberOfSongList) {
\\do whatever
}
Good luck.