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
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);
In the OnCreate method, I have invoked 3 AsyncTask which basically fills data for the 3 Spinners. What I need is that I should have the Login button disabled till all 3 tasks finish. How can I achieve that ?
new SpinnerDataFetcher("GetFreeDrivers1",(Spinner)findViewById(R.id.Spinner_1)).execute();
new SpinnerDataFetcher("GetFreeDrivers2",(Spinner)findViewById(R.id.Spinner_2)).execute();
new SpinnerDataFetcher("GetFreeDrivers3",(Spinner)findViewById(R.id.Spinner_3)).execute();
Just increment a number that corresponds on how many AsyncTask are done.
int s = 0;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new SpinnerDataFetcher(){
#Override
protected void onPostExecute(....) {
super.onPostExecute(...);
s++;
check();
}
}.execute();
new SpinnerDataFetcher(){
#Override
protected void onPostExecute(....) {
super.onPostExecute(...);
s++;
check();
}
}.execute();
new SpinnerDataFetcher(){
#Override
protected void onPostExecute(....) {
super.onPostExecute(...);
s++;
check();
}
}.execute();
}
public void check(){
if(s >=3){
s= 0;
// enable button here
}
}
Initialize your AsyncTask instance with a reference to the Activity/Fragment that creates it. Then signal back in onPostExecute when its done
e.g.
class Spinner1DataFetcher extends AsyncTask<...> {
public Spinner1DataFetch(YourActivityOrFragment activity) {
_activity = activity;
}
protected void onPostExecute(...) {
_activity.spinner_1_is_done();
}
}
For that you have to Disable Button before calling new Spinner1DateFetcher and call Second from Spinner1DateFetcher method onPostExecute and same as Third Spinner method and in Third Spinner onPostExecute set Button to Enable..
For Disable Button use
Button.setEnabled(false);
and For Enable Button use
Button.setEnabled(true);
Edit :
for the parameter check you have to add Constuctor and check the condition like below way.
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
public MyAsyncTask(boolean showLoading) {
super();
// do stuff
}
// doInBackground() et al.
}
There are multiple ways how you can achieve this.
The straightforward way to implement this is create a counter which will trigger UI update.
final InterfaceTrigger trigger = new InterfaceTrigger();
new AsyncTask<>() {
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
trigger.finishJob();
if (trigger.isTimeToUpdateUi()) {
// TODO update your UI
}
}
};
public class InterfaceTrigger {
private static final int THRESHOLD = 3;
private int counter;
public synchronized void finishJob() {
counter++;
}
public synchronized boolean isTimeToUpdateUi() {
return counter == THRESHOLD;
}
}
Another way is to use CyclicBarier and ExcutorService mechanism.
I don't want to implement an interface directly in asynctask class like this:
new AsyncTask<Void, Void, Void>() {
#Override
protected void onPreExecute() {
super.onPreExecute();
swipeRefreshLayout.setRefreshing(true);
}
#Override
protected Void doInBackground(Void... params) {
retrofitCallBackUtil.getLastTenMessageCallBack(AppConfig.USER_ID, userId, offsetNumber, service, new RetrofitResponseCallBack() {
#Override
public void onSuccess(ArrayList<Message> messages) {
messageAdapter.insertToTheFirst(messages);
}
#Override
public void onFailure() {
}
});
offsetNumber += 5;
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
messageAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
}.execute();
I just to implement it by main class and put this into this parameter like this:
retrofitCallBackUtil.getLastTenMessageCallBack(AppConfig.USER_ID, userId, offsetNumber, service, this);
but I cannot do it inside asynctask method. So anyone can give me some solution?
I assume you have these code in MainActivity (or something similar).
Let's implement the interface you need to pass into getLastTenMessageCallBack in this Activity by implements RetrofitResponseCallBack {...}
Now, in your Asyntask doInBackground method, call
retrofitCallBackUtil.getLastTenMessageCallBack(AppConfig.USER_ID, userId, offsetNumber, service, MainActivity.this)
Voila, you don't have to create anonymous class inside your Asyntask anymore.
Note that, you have to move and place the fields/params properly, just the matter of being right place, no big deal :D
Create a class separately for AsyncTask like this. This AsyncTask will process the data and publish the data who implements the interface defined inside the AsyncTask . In this case interface defined is DataDownloadListener
public class GetFoldersAsync extends AsyncTask<Integer,Boolean,Boolean> {
Context context;
ProgressDialog dialog;
ArrayList mFolderDataLevel;
public GetFoldersAsync(Context context){
this.context=context;
}
public static interface DataDownloadListener {
public void dataDownloadedSuccessfully(ArrayList data);
public void dataDownloadFailed();
}
DataDownloadListener dataDownloadListener;
public void setDataDownloadListener(DataDownloadListener dataDownloadListener) {
this.dataDownloadListener = dataDownloadListener;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
dialog = ProgressDialog.show(context, "Please wait", "Processing.........");
}
#Override
protected Boolean doInBackground(Integer... params) {
Log.v(Constants.BLL_LOG, "ExplorerDBOperation doInBackground ");
mFolderDataLevel=new ArrayList();
// Process to populate mFolderDataLevel
return true;
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if(dialog!=null && dialog.isShowing())
{dialog.dismiss();}
Log.v(Constants.BLL_LOG, "ExplorerDBOperation onPostExecute ");
if(mFolderDataLevel!=null && mFolderDataLevel.size()>0){
dataDownloadListener.dataDownloadedSuccessfully(mFolderDataLevel);
}else{
dataDownloadListener.dataDownloadFailed();
}
}
}
Now in the caller Activity, call this method when the AsyncTask needs to process and get back data to same activity.
public class HomeActivity extends AppCompatActivity {
//Other necessary methods goes here .....
//call this method for processing AsyncTask and to get back data
public void getFolderData(int parentID, int callerID){
Log.v(Constants.BLL_LOG,"parentID="+parentID+" , callerID="+callerID);
mCallerID=callerID;
GetFoldersAsync getFolders = new GetFoldersAsync(this);
getFolders.setDataDownloadListener(new GetFoldersAsync.DataDownloadListener()
{
#SuppressWarnings("unchecked")
#Override
public void dataDownloadedSuccessfully(ArrayList data) {
Log.v(Constants.BLL_LOG,"getFolderData dataProcessSuccessfully");
// Success data with populated ArrayList to process further
}
#Override
public void dataDownloadFailed() {
//Failure
Log.v(Constants.BLL_LOG,"getFolderData dataProcessFailed()");
}
});
getFolders.execute(callerID);
}
}
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)
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.