I have a button, and when i click to it, i make textView.setText() a very big text, 50000 symbols, and the interface stops for 3 seconds, how can i fix it?
I tried to make it with Handle and thread, but it hasn`t helped.
I tried to make textview.append(), but it also hasn`t helped.
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
textText.append(rowItemHeader.getText().substring((endOfNews - 10000, endOfNews));
}
});
Edit 1 no result
private class MyTask extends AsyncTask <Void, Void, String> {
String str;
TextView txt;
MyTask(String str, TextView txt){
this.str = str;
this.txt = txt;
}
public String doInBackground(Void... args) {
return str.substring(endOfNews - 10000, endOfNews);
}
public void onPostExecute(String myString) {
// do your update here or you will get that error (the original thread one)
txt.append(myString);
}
}
How did you declare the thread ?
Yu've to use something like that :
this.runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
Log.e("TAG synchronize", "synchronize");
}
});
Use AsyncTask, they are meant for exactly what you want. They are a secondary thread that is easy to setup and executes from your main thread.
Use a loop inside the thread that appends portions of the generated text to the TextView using runOnUiThread(new Runnable(...)...) and call Thread.sleep(50) which forces the Thread to sleep for that much milliseconds to prevent the lag.
Use an AyncTask with something like:
private class MyTask extends AsyncTask<Void, Void, String> {
private Context context;
public MyTask(Context context) {
this.context = context;
}
public String doInBackground(Void... args) {
// do your job here
return myString;
}
public void onPostExecute(String myString) {
// do your update here or you will get that error (the original thread one)
((TextView)context.findViewById(R.id.textview)).setText(myString);
}
}
and then in your activity
new MyTask(MyActivity.this).execute();
First, remove the view, then add the text on background thread, then add the view back. Tell me if you need some sample code.
Related
I know how to use AsyncTask to download file, create a zip file or so.. as I call publishProgress() in my loop.
I got stuck when doInBackground() has a single slow line, no loops here, just creating an object where its constructor has slow loops.
I'm not sure about the reasonable way of updating progress in such case.
Here's a sample code:
public class Session {
private QQActivity activity;
public int createdParts;
public DailyClass daily;
private void checkDaily() {
if(!isDailyReady){
new SetAsyncQQDaily().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
class SetAsyncQQDaily extends AsyncTask<Void, Void, String> {
#Override
protected String doInBackground(Void... params) {
String sdq = null;
daily = new DailyClass(Session.this); //Very very Slow!
// Do other network http
sdq = new String(Base64.encode(bos.toByteArray(),Base64.DEFAULT));
// Do some work
return sdq;
}
#Override
protected void onPostExecute(String sdq) {
//Never mind
}
#Override
protected void onPreExecute() {
Toast.makeText(activity,"Preparing the daily. Get ready!",Toast.LENGTH_LONG).show();
}
#Override
protected void onProgressUpdate(Void... values) {
//TODO: Update Value of leftBar
activity.leftBar.setProgress((100*createdParts)/Utils.DAILY_PART_COUNT);
}
}
}
In the slow constructor class, I can set-back an integer of the current progress: createdParts, but cannot call publishProgress.
public class DailyClass implements Serializable {
public DailyClass(Session session){
for(int i=1;i<=partCount;i++ ){ //Very slow loop
session.createdParts = i; //TODO: reflect value to progress bar!?
for(int j=0;j<questionsCount;j++){
objects[i-1][j] = createDefined(i);
}
Log.d("Daily","created part"+i);
}
}
//Bla .. !
}
I also though of passing the object of the AsyncTask to the slow constructor in order to call publishProgress() from there, but cannot. As publishProgress() is accessible only from doInBackground()
What's the best practice?
I am trying to download a file at android using AsyncTask. Because I want to download many files I want to pass the url of the file by arguments. Also I want to change the UI when a user clicks the button that triggers the AsyncTask. As I figured out, apart from the URL I need to pass and the Activity itself so I can have access to it's views so I can change them. And my problem is this how can make the AsyncTask take multiple and different arguments.
Questions:
1.The only way to do this I think is to pass as Object the first argument so anything can pass, right?
public final Example extends AsyncTask<Objects, String, String>
2.When I call the AsyncTask how do I write the function?
Example a=new Example(??????); //I want to pass both a URL and something to give me access to the UI
a.execute(?????);
3. How can I use the "something" stated above argument to change visibility for example at a view?
Thx In advance for your time
Try this..
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)
new Example("URL","something").executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, new String[]{null});
else
new Example("URL","something").execute(new String[]{null});
and Example AsyncTask Class
public class Example extends AsyncTask<String, Void, JSONObject> {
// variables passed in:
String url;
String something;
// constructor
public Example(String url, String something) {
this.url = url;
this.something = something;
}
#Override
protected void onPreExecute() {
}
#Override
protected JSONObject doInBackground(String... args) {
}
#Override
protected void onPostExecute(JSONObject jObjOut) {
}
}
AsyncTask takes 3 generic types:
1st is the argument which you send to execution;
2nd is for publishing updates on UI
3rd is for returning result
If you want to skip one or all of them use Void class:
example: new AsyncTask();
If you want to use your own constructor in AsyncTask you should define it in your own class that extends AsyncTask, something like this:
import android.app.Activity;
import android.os.AsyncTask;
public class Example extends AsyncTask<Object, String, String>{
private Object data;
private String data1;
private String data2;
private Activity activity;
public Example(Object data, String data1, String data2, Activity activity){
this.data = data;
this.data1 = data1;
this.data2 = data2;
this.activity = activity;
}
#Override
protected String doInBackground(Object... arg0) {
//Do here all your needs except UI updating
//transfer data you need for UI updating like this:
publishProgress("UI update data argument");
return null;
}
#Override
protected void onProgressUpdate(String... arg){
super.onProgressUpdate(arg);
//Update your UI using activity
}
}
To execute your Async task :
new Example(data, data1, data2, activity).execute(object);
You can use a parameterized constructor to achieve the same. But be aware that if you have multiple arguments you would end up creating multiple constructors. Since you need to modify multiple activities in response you need to pass a context as well.
query = UtilityClass.getQueries(UtilityClass.CHECKRIDE, paramsList);
new Example(HomeActivity.this, UtilityClass.CHECKRIDE).execute(query);
This is how you can create a query/url and pass it to the AsyncTask.
A Sample AsyncTask :
public class Example extends AsyncTask<String , Void, String> {
public Example(Context contextVar, int calloutTypeVar){
context = contextVar;
CALLOUTTYPE = calloutTypeVar;
}
#Override
protected void onPreExecute() {
progressDialog = ProgressDialog.show(context, "Please wait", "Loading...");
}
#Override
protected String doInBackground(String...sqlQueryList) {
String result = "";
String sqlQuery = sqlQueryList[0];
result = UtilityClass.doHttpCallout(sqlQuery);
return result;
}
// Handle the response from the callout
#Override
protected void onPostExecute(String result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
progressDialog.dismiss();
switch (CALLOUTTYPE) {
case UtilityClass.SCHEDULERIDE:
ResponseHandlerClass.scheduleRide(result, context);
break;
}
}
I have implemented a response handler to handle the response from various callout results.
Hope this helps in handling your response from different urls.
The idea behind using AsyncTask is to avoid penalizing the responsiveness of the UI when performing blocking operations such as the file download of your scenario. When you use AsyncTask, the doInBackground method executes in another thread than the UI and the UI can not be modified from it so there is no point in passing any UI related data to it.
In order to modify your UI upon your background work completion, you can use the onPostExecute method. As #Tamilan suggested, you may pass the Activity object to the constructor of your AsyncTask if you want to use it there.
An example task could be as follows. Notice that you may replace String with URL, or even Object if you still want to pass different object types to the doInBackground method. Also, this example does not cover error handling beyond returning a simple boolean.
private class ExampleTask extends AsyncTask<String, Integer, Boolean>
{
private Activity mActivity;
public ExampleTask(Activity activity)
{
mActivity = activity;
}
#Override
protected Boolean doInBackground(String... params) {
// this is executed in a background thread
boolean success = true;
// do your background operations
publishProgress(50);
// more operations
return Boolean.valueOf(success);
}
#Override
protected void onProgressUpdate(Integer... progress) {
updateProgressbar(progress[0]); // update UI
}
#Override
protected void onPostExecute(Boolean result) {
// this is executed on your UI thread
if (!result)
// show error
else
// modify your UI
}
}
In order to execute this task:
ExampleTask task = new ExampleTask(anActivity);
task.execute(new String[] { "oneUrl", "anotherUrl" });
I have following sample code to understand the TextView update
public class MainActivity extends Activity
{
int i=0;
private ImageButton btnMain;
private TextView txtText;
Context mycont=null;
public void myJob(final String cmd)
{
//txtText.setText(cmd);
runOnUiThread(new Runnable()
{
#Override
public void run() {
txtText.setText(cmd); //---Does not update the TextView here on Main UI
}
});
//----------- Long Work(Take around 15 seconds to complete) ----------
for(i=0;i<=1000000000;i++)
i++;
for(i=0;i<=1000000000;i++)
i++;
//--------------------------------------------------------------------
//---Update the TextView here once above Long work is executed
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mycont=this;
txtText = (TextView) findViewById(R.id.txtText);
txtText.setMovementMethod(new ScrollingMovementMethod());
btnMain = (ImageButton) findViewById(R.id.btnJob);
btnMain.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
myJob("Display this msg");
}
});
}
}
TextView is not updating on time, its waiting for other procedure to execute.
Please point me in right direction. I want to Update the TextView in the beginning of the myJob() function.
Look at this link: How will UI changes be processed in the onClickListener before starting a new activity in Android?
"Changes to UI elements are not executed immediately. They're executed the next time the UI loop runs, which is after you release control."
I would suggest you look into AsyncTasks. They allow you to perform actions before and after doing a timeconsuming job. In your case your AsyncTask would look somewhat like this:
private class LongJob extends AsyncTask<Void, Void, Void> {
TextView textViewToChange;
public LongJob(TextView text){
textViewToChange = text;
}
protected void onPreExecute(){
// Executed on main(UI) thread
textViewToChange.setText("Some random text here");
}
protected Long doInBackground(Void... params) {
// Your long job here, executed on background thread so
// it won't freeze your application.
return null;
}
protected void onPostExecute(Void result) {
// Executed on main(UI) thread
textViewToChange.setText("Text for after your job completed");
}
}
If I understand correctly, you want to update the textview, then run the Long Work. In that case do something like this:
public void myJob(final String cmd)
{
txtText.setText(cmd);
txtText.post(new Runnable() {
#Override
public void run() {
//----------- Long Work(Take around 15 seconds to complete) ----------
for(i=0;i<=1000000000;i++)
i++;
for(i=0;i<=1000000000;i++)
i++;
//--------------------------------------------------------------------
}
});
}
Note: In either case, your long work is running on the UI thread.. because you have never created a background task
Thanks to Amulya and Sander.
Both the solutions worked for me.
But as per my need, i will go for the solution by Amulya which is lightweight.
I was already aware of Android AsyncTasks.
But never thought of using in this way.
Thanks to both of you
I want my AsyncTask class to change the visibility of a TextView from my layout and I get a fatal error. If I want to get the text value of the TextView, it works fine, but when trying to set the visibility it just crashes.
// Main Activity (MainActivity.java)
Context MainContext= getApplicationContext();
new MyAsyncTask((TextView)findViewById(R.id.my_text_view)).execute();
// My Async Task (MyAsyncTask.java)
public class MyAsyncTask extends AsyncTask <String, Void, String> {
private Context context;
private TextView my_text_view;
public MyAsyncTask (TextView my_text_view){
this.context = MainActivity.MainContext;
this.txt_loading = my_text_view;
}
#Override
protected String doInBackground(String... params) {
// TODO Auto-generated method stub
my_text_view.setVisibility(View.VISIBLE); // crashes
Log.i("Value: ", String.valueOf(this.my_text_view.getText())); // O.K.!
return null;
}
#Override
protected void onPostExecute(String result) {
}
#Override
protected void onPreExecute() {
}
#Override
protected void onProgressUpdate(Void... values) {
}
}
How can I make it work? Also, can I get the TextView from the Context? I don't want to send it as a parameter to the constructor.
It crashes because you are manipuling with UI in background Thread. This is not allowed and you can't do it.
my_text_view.setVisibility(View.VISIBLE);
User Interface may be updated / changed only from original / main / UI Thread. AsyncTask provides some methods that actually runs on UI Thread.
These methods are exactly designated for reach your goal: to perform any UI update.
When you want to update UI from AsyncTask, you can use:
onPreExecute()
onProgressUpdate()
onPostExecute()
I am calling a webservice through asynctask, to call the webservice i am calling one method named makeRequest() in doInBackground(), I am getting the response in another methods success(), In success method i am updating the listview
But i am getting error like
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views."
Here Im adding my code.Im calling synctask from activity
new MyTask(this,urlAsString,sp).execute();
here is the Asynctask class
class MyTask extends AsyncTask<Void, Void, Void> {
ProgressDialog progress;
String url;
SharedPreferences sp;
HomepageH2desk c;
public MyTask(HomepageH2desk context,String url,SharedPreferences sp) {
this.c = context;
this.url = url;
this.sp = sp;
progress = new ProgressDialog(this.c);
progress.setMessage("Loading...");
}
public void onPreExecute() {
progress.show();
}
public Void doInBackground(Void... unused) {
c.getTickets(url,sp);
return null;
//progress.setMessage("Loading...");
}
public void onPostExecute(Void unused) {
progress.dismiss();
}
}
Here im getting webservice response
public void success(Object result) {
list = (ArrayList<Map<String, String>>) result;
this.adapter.setList(list);
this.adapter.notifyDataSetChanged();
}
listview is not getting updated and showing error
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Help me to solve this problem...
You should update your List like this.
Activity_name.this.runOnUiThread(new Runnable() {
#Override
public void run() {
list = (ArrayList<Map<String, String>>) result;
this.adapter.setList(list);
this.adapter.notifyDataSetChanged();
}
});
Since this the error that comes up when you do some MainThread task in another thread.....
try This:
runOnUiThread(new Runnable(){
public void run(){
this.adapter.setList(list);
this.adapter.notifyDataSetChanged();
}
});
This code might have some errors But in simple Words.. add the notifyDataSetChanged call into runOnUiThread() method. You will be Done..
OR this can also be DOne ( the perfect way )
add the following in your activity class
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
this.adapter.setList(list);
adapter.setnotifyDataSetChanged();
}
};
Call this handler when and where you want to call the notifydatasetchanged like this
handler.sendEmptyMessage(0);
Thanks
sHaH
call the success method on onPostExecute method