I want to access and populate a ListView from seprate thread... But its object has not its scope in new thread... What is the solution for it?
ListView FilesListView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FilesListView= (ListView)findViewById(R.id.remoteFilesListView);
new Thread ( new Runnable() {
#Override
public void run() {
// fetch data from server…
String xmlFormServer = Worker.getXmlresponse();
Log.d("Response from Serever", xmlFormServer;
// FilesListView object of Listview is not accessable in this thread to populate data…
}
}).start();
}
You cannot populate populate ListView/RecyclerView or any other view or component on another thread.
That has to be on the MainThread(UIThread).
Checkout this link for more info about UIThread in android and how its works.
What is the Android UiThread (UI thread)
However, you can put the logic or IO operation on different thread asynchronously and then you can put populate the result on UI thread.
One approach would be to use AsyncTask.
https://developer.android.com/reference/android/os/AsyncTask.html
Something like this..
new AsyncTask<Void, Void, String >() {
// Runs on Worker thread
#Override
protected String doInBackground(Void... params) {
String xmlFormServer = Worker.getXmlresponse();
return xmlFormServer;
}
// Runs on Ui thread
#Override
protected void onPostExecute(String data) {
if(data != null){
adapter.setDate(data);
adapter.notifyDataSetChanged();
}
}
}.execute();
You should to implement adapter for your ListView
Try to follow pattern below
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FilesListView = (ListView) findViewById(R.id.remoteFilesListView);
adapter = new SimpleAdapter(/*...*/); // implement your custom adapter or use native
// 1. set adapter into FileListView
FilesListView.setAdapter(adapter);
new AsyncTask<Void, Void, List>() {
#Override
protected List doInBackground(Void... params) {
String xmlFormServer = Worker.getXmlresponse();
Log.d("Response from Serever", xmlFormServer;
// 2. READ YOUR DATA HERE
List result = null; //
// 3. send result to ui thread
return result;
}
#Override
protected void onPostExecute(List list) {
// the method executes on ui thread
// 4. put your data into adapter
// you should implement the method or create native adapter
adapter.setDate(list);
// 5. refresh list - only UI THREAD can do this
adapter.notifyDataSetChanged();
super.onPostExecute(list);
}
}.execute();
}
Related
Let's say I need to fetch data from a db asynchronously and then pass it to a recyclerview's adapter. Using AsyncTask, I can do the following:
void getItemsFromDb() {
new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... voids) {
this.items = getFromDb(); // items is the adapter's data set
return null;
}
#Override
protected void onPostExecute(Void unused) {
super.onPostExecute(unused);
this.adapter.notifyDataSetChanged();
}
}.execute();
If I were to be doing this using WorkRequest, the fetching would look like this:
class MyWork extends Worker {
#NonNull
#Override
public Result doWork() {
getItemsFromDb() // <---- how do I pass this to my adapter?
return Result.success();
}
}
WorkRequest myWorkRequest = OneTimeWorkRequest.from(MyWork.class);
How would I get the items from a db and pass them to the adapter? And how do I call notifyDataSetChanged in the UI thread after the background work has completed?
How can I add elements to an Array or ArrayList from the AsyncTask class(Inner) , from where I am receiving data from server .
I tried adding elements from doInBackgroud() and printing them in onPostExecute() ,it works fine then .
But the Array and List are both empty when I am trying to access them outside the AsyncTask class.
Code :-
public String[][] mov_details ;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.vpmovies);
mov_details=new String[NUM_OF_MOVIES][9];
new MyHttpMovies().execute();
Toast.makeText(this,mov_details[0][2],Toast.LENGTH_LONG).show(); //prints blank
doInBackgroud() and onPostExecute() -
for(int i=0; i < jsonArray.length(); i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
//parsing some json
mov_details[i][0] = (jsonObject.optString("name").toString());
mov_details[i][1] = (jsonObject.optString("lang").toString());
mov_details[i][2] = (jsonObject.optString("genre").toString());
.
.
.
.
return mov_details;
}
protected void onPostExecute(String[][] strings) {
super.onPostExecute(strings);
Toast.makeText(VPmovies.this,strings[0][1],Toast.LENGTH_LONG).show();
// prints successfully
}
public class MainActivity extends AppCompatActivity {
ArrayList<String> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public class fetchData extends AsyncTask<Void,Void,Void> {
#Override
protected Void onPreExecute() {
super.onPreExecute();
list = new ArrayList<String>();
}
#Override
protected Void onPostExecute(Void... params) {
...
list.add("bla bla");
...
return null;
}
}
}
Use publishProgress("data") inside your doInBackgroung and pass data from AsyncTask to your Activity/Fragment. Keep in mind doInBackgroung runs in separate thread you can not directly update your main GUI from doInBackgroung, if you will call some methods directly from doInBackgroung than your calling method should run in main thread.
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
// send data to your required location its recommended to use interfaces for communication
}
Solved by simply using the get() method on the AsyncTask instance that would return the output/result of the AsyncTask backgroud thread.
mov_details = new MyHttpMovies().execute().get(); //returns 2d array
and instead of using mov_details variable inside doInBackground() , I used some other 2d array variable local to the just AsyncTask.
I've got an Activity where before showing the Text/EditText fields, I want to make a call to the server to get the details and then setText of the fields based on the data gotten back from the server.
Below is what I'm doing but the fields don't seem to have the data fetched from the server. I think because I am calling an AsyncTask which gets run in the background and in the mean time the fields are shown to the user.
Question
How does android deal with this? What pattern should I be using?
This activity gets called from MainActivity.java like so:
Intent act = new Intent(getApplicationContext(), MySecondActivity.class);
create.putExtra("theId", "138");
startActivity(create);
in MySecondActivity.java i do the following:
public class MySecondActivity extends SherlockActivity {
private EditText fieldOne;
private EditText fieldTwo;
private MyObj obj = new MyObj();
private int id;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.shared_activity);
fieldOne = (EditText)findViewById(R.id.field_one);
fieldTwo = (EditText)findViewById(R.id.field_two);
id = Integer.parseInt(getIntent().getStringExtra("theId"));
new FetchDetail().execute();
//If I put the below two lines inside the AsyncTask then I get an error:
//"Only the original thread that created a view hierarchy can touch its views."
fieldOne.setText(obj.getOne()); //
fieldTwo.setText(obj.getTwo()); //
}
class FetchDetail extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... strings) {
final RestAdapter restAdapter = new
RestAdapter.Builder().setServer("http://10.0.2.2:8080").build();
final MyTaskService apiManager = restAdapter.create(MyTaskService.class);
final MyObj obj = apiManager.getDetails(id);
return null;
}
}
}
If I put the below two lines inside the AsyncTask then I get an error
Have these in onPostExcute
fieldOne.setText(obj.getOne());
fieldTwo.setText(obj.getTwo());
Do your background computation in doInbackground. Return result in doInbackground. The result of doInbackground computation is a pram to onPostExecute.
So you can update ui in onPostExecute which is invoked on the ui thread
Example:
protected String doInBackground(String... params)
{
// background computation
return "hello"; // return string
}
#Override
protected void onPostExecute(String result) // string
{
super.onPostExecute(result);
fieldOne.setText(result); // hello is set to field One
}
For more info read the topic under The4Steps in the docs
http://developer.android.com/reference/android/os/AsyncTask.html
AsyncTask has 3 methods to override:
1: onPreExecute
Executes on UI thread. So do what you want to do on UI before service call here(Ex: show a progress dialog).
2: doInBackground
Executes in background so perform long running task like fetching data from server.
3: onPostExecute
Executes on UI thread and gets called once doInBackground is completed you can process the result here and update the UI
Ex:
public class RestServiceTask extends AsyncTask<String, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected String doInBackground(String... params) {
}
#Override
protected void onPostExecute(String result) {
}
}
I'm using a ListView on my Activity and it takes a while to load from a SQLite DB, so I wanted to show a ProgressDialog to the user to let them know something is loading. I tried to run the task on a separate thread but I'm getting a CalledFromWrongThreadException. Here's my main Activity code:
#Override
public void onCreate(Bundle savedInstanceState)
{
try
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.open_issues);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
//Set Window title.
final TextView title = (TextView) findViewById(R.id.customTitle);
if (title != null)
title.setText("Open Issues");
//Call Async Task to run in the background.
new LoadIssuesTask().execute();
}
catch (Exception e)
{
Errors.LogError(e);
}
}
And the LoadIssuesTask code:
private class LoadIssuesTask extends AsyncTask<Void, Void, Cursor> {
ProgressDialog pdDialog = null;
protected void onPreExecute()
{
try
{
pdDialog = new ProgressDialog(OpenIssues.this);
pdDialog.setMessage("Loading Issues and Activities, please wait...");
pdDialog.show();
}
catch (Exception e)
{
Errors.LogError(e);
}
}
#Override
protected Cursor doInBackground(Void... params) {
LoadIssues();
return null;
}
#Override
protected void onPostExecute(Cursor c) {
pdDialog.dismiss();
pdDialog = null;
}
}
And the LoadIssues code:
private void LoadIssues(){
//Set listview of Issues.
ListView lvIssues = (ListView)findViewById(R.id.lvIssues);
lvIssues.setOnItemClickListener(viewIssuesListener);
IssueCreator = new IssueInfoCreator(this, Integer.parseInt(AppPreferences.mDBVersion));
IssueCreator.open();
lvIssues.setAdapter(new IssueInfoAdapter(this, IssueCreator.queryAll()));
IssueCreator.close();
}
Constructor for IssueInfoAdapter:
public IssueInfoAdapter(Context c, List<IssueInfo> list){
mListIssueInfo = list;
//create layout inflater.
mInflater = LayoutInflater.from(c);
}
It's throwing the error on the .setAdapter method inside LoadIssues().
ERROR:
03-12 10:41:23.174: E/AndroidRuntime(11379): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
You're trying to access the views in the doInBackground method that doesn't run on the main UI thread. You'll have to set your adapter in the method onPostExecute that runs on the UI thread:
#Override
protected void onPostExecute(List<IsueInfo> items) {
pdDialog.dismiss();
ListView lvIssues = (ListView)findViewById(R.id.lvIssues);
lvIssues.setOnItemClickListener(viewIssuesListener);
lvIssues.setAdapter(new IssueInfoAdapter(this, items));
}
and in your doInBackground method:
#Override
protected List<IssueInfo> doInBackground(Void... params) {
IssueCreator = new IssueInfoCreator(this, Integer.parseInt(AppPreferences.mDBVersion));
IssueCreator.open();
IssueCreator.close();
return IssueCreator.queryAll();
}
Also your AsyncTask should be:
private class LoadIssuesTask extends AsyncTask<Void, Void, List<IssueInfo>>
In private void LoadIssues method call handler.setMessage(0) and create a private Handler instance to call setAdapter method
Use Handler instead of Asynctask.
my issue is the same as this
Instance variable of Activity not being set in onPostExecute of AsyncTask or how to return data from AsyncTask to main UI thread
but i want to send the data back to the same calling activity. Doesnt startActivity for intents always restart the activity
On option is to use listeners, where you create an interface that your activity implents, something like:
public interface AsyncListener {
public void doStuff( MyObject obj );
}
That way, if you're subclassing AsyncTask, it is easy to add this listener, then in onPostExecute(), you could do something like:
protected void onPostExecute( MyObject obj ) {
asyncListener.doStuff(obj);
}
This depends on your class structure, but if your AsyncTask is a class within your Activity then you can reference methods of that activity.
What you would do is in your onPostExecute method call a function of your Activity that passes some data that was retrieved in the AsyncTask to the activity where you can then use it..
The code would look like this
class YourActivity extends Activity {
private static final int DIALOG_LOADING = 1;
public void onCreate(Bundle savedState) {
setContentView(R.layout.yourlayout);
showDialog(DIALOG_LOADING);
new LongRunningTask1().execute(1,2,3);
}
protected Dialog onCreateDialog(int dialogId) {
switch(dialogId) {
case DIALOG_LOADING:
ProgressDialog pDialog = new ProgressDialog(this);
pDialog.setTitle("Loading Data");
pDialog.setMessage("Loading Data, please wait...");
return pDialog;
default:
return super.onCreateDialog(dialogId);
}
}
private void onBackgroundTaskDataObtained(List<String> results) {
dismissDialog(DIALOG_LOADING);
//do stuff with the results here..
}
private class LongRunningTask extends AsyncTask<Long, Integer, List<String>> {
#Override
protected void onPreExecute() {
//do pre execute stuff
}
#Override
protected List<String> doInBackground(Long... params) {
List<String> myData = new ArrayList<String>();
for (int i = 0; i < params.length; i++) {
try {
Thread.sleep(params[i] * 1000);
myData.add("Some Data" + i);
} catch(InterruptedException ex) {
}
}
return myData;
}
#Override
protected void onPostExecute(List<String> result) {
YourActivity.this.onBackgroundTaskDataObtained(result);
}
}
}
So the typical flow is like this, set the view of the current page, and then show a progress dialog. Right after that start the async task (or whenever, it doesn't matter really).
After your async task is complete, call a function of the activity and pass it the data.
Don't use shared data within the async task or you risk issues with threading.. Instead once you are done with it pass it to the activity. If you want to update the view progressively while doing work you can use on onProgressUpdate