Quick summary: I'm making an application that parses a binary file, stores vertices and their properties, and displays them in openGL. I'm trying to implement a ProgressDialog while it parses, but I'm having considerable trouble. I've tried implementing this in many places, but this is my current setup:
public class PointViewer extends Activity{
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
filePath = extras.getString("file_path");
mGLView = new GLSurfaceView(this);
theRenderer = new OpenGLRenderer();
mGLView.setRenderer(theRenderer);
//Parse the file and set the arrays
theRenderer.setLAS(filePath);
setContentView(mGLView);
}
...
}
The Rendering class...
public class OpenGLRenderer extends Activity implements GLSurfaceView.Renderer {
...
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void setLAS (String fileName){
new ProgressTask(this).execute();
}
...
/*
* The class for the progress dialog
*/
private class ProgressTask extends AsyncTask<String, Void, Boolean> {
private ProgressDialog dialog;
private Context context;
//private List<Message> messages;
public ProgressTask(Context ctx) {
context = ctx;
dialog = new ProgressDialog(context);
}
/** progress dialog to show user that the backup is processing. */
protected void onPreExecute() {
this.dialog.setMessage("Progress start");
this.dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
this.dialog.show();
}
#Override
protected void onPostExecute(final Boolean success) {
if (dialog.isShowing()) {
dialog.dismiss();
}
if (success) {
Toast.makeText(context, "OK", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(context, "Error", Toast.LENGTH_LONG).show();
}
}
protected Boolean doInBackground(final String... args) {
try{
ptCloud = new PointCloud(args[0]);
...
dialog.setProgress(percentParsed);
return true;
} catch (Exception e){
Log.e("tag", "error", e);
return false;
}
}
}
When I call dialog = new ProgressDialog(context); It errors on a null pointer exception, I'm assuming because there is a context issue... Does anyone have any ideas?
First, you shouldn't create OpenGLRenderer yourself, because its an Activity and is supposed to be managed by system. When you create OpenGLRenderer yourself, then this activity has its context incorrectly initialized. And when your create ProgressDialog with invalid context, you receive NullPointerException.
But even if you start OpenGlRenderer correctly, your app will still crash at line:
dialog.setProgress(percentParsed);
You should use publishProgress instead and update ProgressDialog in AsyncTask's onProgressUpdate function. That's because you can't update UI from non-ui thread.
maybe try replaceing "this" with "OpenGLRenderer.this" inside your setLAS() method. It doesn't seem like it from the code you've posted but sometimes if you are making that call from a different object type it will try to pass in an OnClickListener (or whatever object your calling from) instead of an activity, thus the object has no context. Like I said doesn't seem like that is the case, but worth a shot.
Also where are you calling setLAS() from? perhaps post that section of your code too.
Edit:
Try modifying your setLAS() method to have a Context parameter and pass it in from your first activity, and just pass it along from the second activity to the async task instead of using the context from the second activity.
Related
I'm making a url request using AsyncTask, and want to display some sort of progress spinner while the request is processed. The AsyncTask class is located within another Activity VerificationActivity which extends Activity. The app crashes on this line: dialog = new ProgressDialog(context); Probably the problem is with the context of the app, I tried VerificationActivity.this and creating a local instance of VerificationActivity and passing it as a context.
SmsTask smsTask = new SmsTask(VerificationActivity.this);
/*
* some code
*/
class SmsTask extends AsyncTask<String,Void, Boolean> {
final AtomicBoolean b = new AtomicBoolean(false);
private Context mContext;
ProgressDialog pd;
SmsTask(Context context){
this.mContext = context;
}
#Override
protected void onPreExecute(){
super.onPreExecute();
pd = new ProgressDialog(mContext);
pd.setProgressStyle(AlertDialog.THEME_HOLO_DARK);
pd.show(mContext,"Please wait","Sending...");
}
protected Boolean doInBackground(String... params) {
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(params[0]);
try {
httpclient.execute(httppost);
b.set(true);
} catch (IOException e) {
e.printStackTrace();
}
return b.get();
}
#Override
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
// result holds what you return from doInBackground
if(pd.isShowing()){
pd.dismiss();
}
Log.i("result from async: ",""+result);
}
}
This is the error message I'm getting:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources$Theme android.content.Context.getTheme()' on a null object reference
UPDATE:
I fixed the context issue based on the answers here, and ran a test, simulated some heavy work inside the doInBackground method using the Thread.sleep() method, but the progress spinner still doesnt show. Any ideas?
UPDATE2:
I managed to show the progress dialog as i need, but now im not able to dismiss it. I assume it is because I cant dismiss UI elements, do i need to run the progress dialog inside runOnUIThread?
I assume you pass the activity as context and by the time the task is going to be executed, the activity might be gone and the you'll get NullPointerException, so 2 options you have;
Check for the context- of it's null you might not need the loading dialog at all
Use the Application context
Hi please see below the updated AsynTas.
class SmsTask extends AsyncTask<String,Void, Boolean> {
final AtomicBoolean b = new AtomicBoolean(false);
private Context mContext;
private ProgressDialog dialog;
private SmsTask(Context context){
this.mContext = context;
}
#Override
protected void onPreExecute(){
super.onPreExecute();
Log.i("executing Pre - execute","");
dialog = new ProgressDialog(mContext );
dialog.setMessage("Loading...");
dialog.show();
}
protected Boolean doInBackground(String... params) {
//.... dosing stuff
return b.get();
}
#Override
protected void onPostExecute(Boolean result) {
if(dialog.isShowing()){
dialog.dismiss();
}
super.onPostExecute(result);
}
}
private Context context;
you have defined context here, but you didn't initialize it. You can initialize it by a constructor
public SmsTask(Context con){
context = con;
}
and when you call your SmsTask pass context to it.
You need to get the application context -
dialog = new ProgressDialog(getContext());
I am trying to unzip a folder using Android's AsyncTask. The class (called Decompress) is an inner class of Unzip where Unzip itself is a non-Activity class. The pseudo-code is:
public class Unzip {
private String index;
private String unzipDest; //destination file for storing folder.
private Activity activity;
private boolean result; //result of decompress.
public void unzip(String loc) {
Decompress workThread = new Decompress(loc, activity);
workThread.execute();
if(unzip operation was successful) {
display(index);
}
//Class Decompress:
class Decompress extends AsyncTask<Void, Integer, Boolean> {
private ProgressDialog pd = null;
private Context mContext;
private String loc;
private int nEntries;
private int entriesUnzipped;
public Decompress(String location, Context c) {
loc = location;
mContext = c;
nEntries = 0;
entriesUnzipped = 0;
Log.v(this.toString(), "Exiting decompress constructor.");
}
#Override
protected void onPreExecute() {
Log.v(this.toString(), "Inside onPreExecute.");
pd = new ProgressDialog(mContext);
pd.setTitle("Unzipping folder.");
pd.setMessage("Unzip in progress.");
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
Log.v(this.toString(), "Showing dialog and exiting.");
pd.show();
}
#Override
protected Boolean doInBackground(Void... params) {
//unzip operation goes here.
unzipDest = something; //unzip destination is set here.
if(unzip operation is successful) {
result = true;
index = url pointing to location of unzipped folder.
} else {
result = false;
}
}
#Override
protected void onPostExecute(Boolean result) {
if(result) {
if(pd != null) {
pd.setTitle("Success");
pd.setMessage("folder is now ready for use.");
pd.show();
pd.dismiss();
pd = null;
Log.v(this.toString(), "Unzipped.");
index = unzipDest + "/someURL";
Log.v(this.toString(), "index present in: " + index);
}
} else {
pd = ProgressDialog.show(mContext, "Failure", "Cannot unzip.");
pd.dismiss();
}
}
}
Problems I am facing:
1. The value of unzipDest and index, updated in doInBackground, remain null to Unzip and all its objects. How can I ensure that the values remain updated?
2. I know that doInBackground occurs in a thread separate from the main UI thread. Does that mean that any values updated in the new thread will be lost once that thread returns?
How can I ensure that the values remain updated?
They will be updated since they are member variables. However, since AsyncTask is asynchrounous, they might not be updated yet when you check them. You can use an interface to create a callback when these values are updated. This SO answer covers how to do this
Does that mean that any values updated in the new thread will be lost once that thread returns?
No they shouldn't be "lost". They probably just haven't been changed in the AsyncTask when you check them.
Since this isn't your actual code I can't see when you are trying to access them but you can use the interface method or call the functions that need these values in onPostExecute(). You also can do a null check before trying to access them. It just depends on the functionality and flow that you need as to which is the best way. Hope that helps.
Edit
In the answer I linked to, you tell the Activity that you will use that interface and override its method(s) with implements AsyncResponse in your Activity declaration after creating the separate interface class
public class MainActivity implements AsyncResponse{
then, in your Activity still, you override the method you declared in that class (void processFinish(String output);)
#Override
void processFinish(String output){ // using same params as onPostExecute()
//this you will received result fired from async class of onPostExecute(result) method.
}
then this is called in onPostExecute() when the listener sees that it is done with delegate.processFinish(result); delegate is an instance of AsyncResponse (your interface class)
public class AasyncTask extends AsyncTask{
public AsyncResponse delegate=null;
#Override
protected void onPostExecute(String result) {
delegate.processFinish(result);
}
Interface example taken from linked answer above and adjusted/commented for clarity. So be sure to upvote that answer if it helps anyone.
I want download details from web and update the UI within the doInBackground(),
For that I think I must get reference to activity within that method .How can I do it or is there another way to do that? What must be the something parameter? Or can’t update UI real-time?
public class DownloadActivity extends ListActivity {
public class DownloadItems extends AsyncTask<Something,Integer,Long> {
#Override
protected Long doInBackground(DownloadActivity... params) {
Toast.makeText(params[0], getIntent().getExtras().get("location").toString(), Toast.LENGTH_SHORT).show();
return null;
}
}
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
new DownloadItems().execute(Something);
}
}
You can either use a Handler or update your UI in onPostExecute(), which I recommend. Let your Async take care of its background logic and update the UI when that work is finished.
The best way is to simply move anything which affects UI into onPostExecute() because it's there to allow you to update the UI, it's the point of it.
There are other ways but when using AsyncTask there's really no reason not to use this.
public class DownloadActivity extends ListActivity {
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
new DownloadItems(this).execute();
}
public class DownloadItems extends AsyncTask<Something,Integer,Long> {
private Context context;
public DownloadItems(Context c){
context = c;
}
#Override
protected Long doInBackground(DownloadActivity... params) {
// Do something
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
Toast.makeText(context, context.getIntent().getExtras().get("location").toString(), Toast.LENGTH_SHORT).show();
}
}
}
You can create a constructor for passing or adding Context as a parameter.
public class DownloadItems extends AsyncTask<Something,Integer,Long> {
Context context;
public DownloadItems(Context cntx){
context = cntx;
}
#Override
protected Long doInBackground(DownloadActivity... params) {
//Toast.makeText(params[0], getIntent().getExtras().get("location").toString(), Toast.LENGTH_SHORT).show();
Toast.makeText(context, "String test", Toast.LENGTH_SHORT).show();
return null;
}
}
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
new DownloadItems(this).execute(Something);
}
By passing the context of the activity you can make any operation that are context related.
You can't execute UI operations in doInBackground(), you must do them in onPostExecute(). In DownloadActivity, you will create an instance of DownloadItems, and pass it the url where you want to download your stuff :
For example :
public class DownloadActivity extends ListActivity {
private void someMethod() {
DownloadItems yourTask = new DownloadItems(getApplicationContext());
yourTask.execute(yourUrl);
}
In the AsyncTask, you will do your download operations in doInBackground() and return the result so it can be handled by onPostExecute() :
public class DownloadItems extends AsyncTask<Something,Integer,Long> {
Context mContext;
public DownloadItems(Context context){
mContext = context;
}
#Override
protected String doInBackground(String... params) {
String theResult;
// download operations using url stored in params[0], and where you set theResult variable (for example...)
return theResult;
}
In onPostExecute(), you deal with the result, for example in your code above, you can call the Toast :
#Override
protected void onPostExecute(String result) {
Toast.makeText("YOUR TAG", result, Toast.LENGTH_SHORT).show();
}
You can call this in doInBackground:
runOnUiThread(new Runnable() {
public void run() {
//Your code
}
});
But isn't right... Please read the AsyncTask for more details, or use the onPostExecute to update UI...
I hate inner class.
I've a main activity who launches a 'short-life' AsyncTask.
AsyncTask is in a separate file, is not an inner class of main activity
I need async task updates a textView from main Activity.
I know i can update a TextView from onProgressUpdate, if AsyncTask is a inner class
But how from an external, indipendent, async task ?
UPDATE: This looks like working :
In acitivty i call the task
backgroundTask = new BackgroundTask(this);
backgroundTask.execute();
In the constructor i've
public BackgroundTask(Activity myContext)
{
debug = (TextView) myContext.findViewById(R.id.debugText);
}
where debug was a private field of AsyncTask.
So onProgressUpdate I can
debug.append(text);
Thanks for all of you suggestions
AsyncTask is always separate class from Activity, but I suspect you mean it is in different file than your activity class file, so you cannot benefit from being activity's inner class. Simply pass Activity context as argument to your Async Task (i.e. to its constructor)
class MyAsyncTask extends AsyncTask<URL, Integer, Long> {
WeakReference<Activity> mWeakActivity;
public MyAsyncTask(Activity activity) {
mWeakActivity = new WeakReference<Activity>(activity);
}
...
and use when you need it (remember to NOT use in during doInBackground()) i.e. so when you would normally call
int id = findViewById(...)
in AsyncTask you call i.e.
Activity activity = mWeakActivity.get();
if (activity != null) {
int id = activity.findViewById(...);
}
Note that our Activity can be gone while doInBackground() is in progress (so the reference returned can become null), but by using WeakReference we do not prevent GC from collecting it (and leaking memory) and as Activity is gone, it's usually pointless to even try to update it state (still, depending on your logic you may want to do something like changing internal state or update DB, but touching UI must be skipped).
Using Interface
1) Create one Interface
public interface OnDataSendToActivity {
public void sendData(String str);
}
2) Implements it in your Activity
public class MainActivity extends Activity implements OnDataSendToActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
new AsyncTest(this).execute(new String[]{"AnyData"}); // start your task
}
#Override
public void sendData(String str) {
// TODO Auto-generated method stub
}
}
3) Create constructor in AsyncTask(Activity activity){}
Register your Interface in AsyncTask file
and call interface method as below.
public class AsyncTest extends AsyncTask<String, Integer, String> {
OnDataSendToActivity dataSendToActivity;
public AsyncTest(Activity activity){
dataSendToActivity = (OnDataSendToActivity)activity;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
dataSendToActivity.sendData(result);
}
}
Here, your OnPostExecute will call after all task done by AsyncTask and will get "result"
as a parameter, returned by doInBackground(){ return "";}.
While "dataSendToActivity.sendData(result);" it will call activity's overrided method "public void sendData(String str) {}".
An edge case to remember: Be sure to pass this, i.e. you current activity's context to AsyncTask and not create another instance of your activity, otherwise your Activity will be destroyed and new one is created.
Make an static function in your activity class passing context in it to update your text view and then call this function in your AsynkTask class to update.
In Activity class:
public static void updateTextView(){
//your code here
}
In AynckTask class call this function.
Just pass the context (activity or whatever) to your AsyncTask in a constructor and then in onSuccess or onProgressUpdate call whatever you need on the context.
I wrote a small extension to AsyncTask for this kind of scenario. It allows you to keep your AsyncTask in a separate class, but also gives you convenient access to the Tasks's completion:
public abstract class ListenableAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{
#Override
protected final void onPostExecute(Result result) {
notifyListenerOnPostExecute(result);
}
private AsyncTaskListener<Result> mListener;
public interface AsyncTaskListener<Result>{
public void onPostExecute(Result result);
}
public void listenWith(AsyncTaskListener<Result> l){
mListener = l;
}
private void notifyListenerOnPostExecute(Result result){
if(mListener != null)
mListener.onPostExecute(result);
}
}
So first you extend ListenableAsyncTask instead of AsyncTask. Then in your UI code, make a concrete instance and set listenWith(...).
The Question has already been answered, still im posting how it should be done i guess..
Mainactivity class
public class MainActivity extends Activity implements OnClickListener
{
TextView Ctemp;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Ctemp = (TextView) findViewById(R.id.Ctemp);
doConv = (Button) findViewById(R.id.doConv);
doConv.setOnClickListener(this);
}
#Override
public void onClick(View arg0) // The conversion to do
{
new asyncConvert(this).execute();
}
}
now in the async class
public class asyncConvert extends AsyncTask<Void, Void, String>
{
SoapPrimitive response = null;
Context context;
public asyncConvert(Context callerclass)
{
contextGUI = callerclass;
}
.
.
.
.
protected void onPostExecute(String result)
{
((MainActivity) contextGUI).Ctemp.setText(result); // changing TextView
}
}
/**
* Background Async Task to Load all product by making HTTP Request
* */
public static class updateTExtviewAsyncTask extends AsyncTask<String, String, String> {
Context context;
ProgressDialog pDialog;
String id, name;
String state_id;
//--- Constructor for getting network id from asking method
public updateTExtviewAsyncTask(Context context,String id,String city)
{
context = context;
state_id = id;
city_name = city;
}
/* *
* Before starting background thread Show Progress Dialog
* */
#Override
protected void onPreExecute()
{
super.onPreExecute();
pDialog = ProgressDialog.show(context, "","Please wait...", true, true);
pDialog.show();
}
/**
* getting All products from url
* */
protected String doInBackground(String... args)
{
return null;
}
/**
* After completing background task Dismiss the progress dialog
* **/
protected void onPostExecute(String file_url) {
YourClass.UpdateTextViewData("Textview data");
}
}
// place this code inside your activity class and also declare updating textview static
public static void UpdateTextViewData(String tvData)
{
tv.setText(tvData);
}
I've developed an Android 2.2 application and I get only one time this error:
java.lang.IllegalArgumentException: View not attached to window manager
This error occur when I dismiss ProgressDialog. This dialog is on an AsyncTask. Here is my code:
private class LoadGatesAsyncTask extends AsyncTask<Void, Void, Boolean>
{
private Context mContext;
private ArrayList<Gate> mGatesList;
private ProgressDialog mLoadingDialog;
public LoadGatesAsyncTask(Context context)
{
this.mContext = context;
mLoadingDialog = new ProgressDialog(mContext);
mLoadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
mLoadingDialog.setMessage(getString(R.string.msg_loading_gates));
mLoadingDialog.setCancelable(false);
mLoadingDialog.show();
}
#SuppressWarnings("unchecked")
#Override
protected Boolean doInBackground(Void... params)
{
Boolean result = false;
try
{
[ ... ]
}
catch (Exception ex)
{
ex.printStackTrace();
}
return result;
}
#Override
protected void onPostExecute(Boolean result)
{
super.onPostExecute(result);
if (result)
{
[ ... ]
mLoadingDialog.dismiss();
}
else
{
mLoadingDialog.dismiss();
Toast toast = Toast.makeText(mContext,
getString(R.string.msg_error_loading_gates),
Toast.LENGTH_SHORT);
toast.setGravity(Gravity.CENTER,
toast.getXOffset() / 2,
toast.getYOffset() / 2);
toast.show();
}
}
}
How can avoid this error programmatically? Is there any check that I could do to avoid dismiss it if it isn't attached to window manager?
are you instantiating LoadGatesAsyncTask in OnCreate?
If so move the loadingDialog instantiation to OnPreExecute rather than in the Constructor.
You could add the check:
if(mLoadingDialog.isShowing()){
mLoadingDialog.dismiss();
}
You are initializing this ProgressDialog in constructor, not in onPreExecute() and that is wrong because you are dismissing the ProgressDialog in onPostExecute, you need to do it on the same UI thread.
If you initialize the object - AsynTask, you get your constructor called. So your ProgressDialog will be shown, but you haven't called .execute() on the AsyncTask yet! So when you're trying to dismiss it, the compilator is finding itself on a different Thread.
EDIT: Clarifying misunderstood statement.
I think the real problem is behind the attached Activity behind the progressDialog,
it's changing to a new reference (instance),
So that the progressDialog.OwnerActivity=null (usually when in rotation onCreate is called)
it's solved by attaching the new recreated Activity to the owner : progressDialog.OwnerActivity = myActivity; (it will be passed a local parameter in your class, in the constructor pass it,
like :( here C# android, similar to java.. same idea)
class ProgressDialogAsync: AsyncTask //extends asynctask
{
//used parameters
Activity Activity;
string Title;
string Message;
ProgressDialog AsyncDialog;
public ProgressDialogAsync(Activity Activity, string Title, string Message)
{
this.Title = Title;
this.Message = Message;
this.Activity = Activity;
}
protected override void OnPreExecute()
{
AsyncDialog = new ProgressDialog(Activity);
}
protected override Java.Lang.Object DoInBackground(params Java.Lang.Object[] #params) {
//do background operation
}
protected override void OnPostExecute(Java.Lang.Object result)
{
//do something
AsyncDialog.OwnerActivity = Activity;
AsyncDialog.Dismiss();
base.OnPostExecute(result);
}