I recently needed to use AsyncTask in an activity in my android app. So I made a class inside the activity and extended AsyncTask in that class.
But now, whenever I launch that particular activity, my app immediately crashes. I tried putting the whole onCreate() of the activity in a try, catch block, but no exceptions were raised. The app just crashes immediately when I launch that activity, without any explanation in the LogCat.
This started happening after I added the AsyncTask class mentioned above. Also, the AsyncTask part is NOT executed when the activity is launched, it executes on pressing a button. What could be going wrong? I have added the relevant code below:
public class ListViewA extends Activity{
public void onCreate(Bundle savedInstanceState) {try{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView lv= (ListView)findViewById(R.id.listview);
//computation
}
private String[] top() {
new RetreiveFeedTask().execute(ctr,ctz);
return er;
}
public class RetreiveFeedTask extends AsyncTask<Context, Void, String[]> {
String[] q;
protected String[] doInBackground(Context... params) {
//computation
}
protected void onPostExecute() {
er=q;
gff=true;
}
}
EDIT:
I found an error in LogCat which looks important:
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.exampleapp/com.example.exampleapp.ListViewA}: java.lang.NullPointerException
Your edit is interesting and suggests that there is a problem in the instantiation of the Activity, in which case I would look at the variable declarations quite carefully. However, in the absence of any of that code (or more onCreate code), or the surrounding lines from the logcat, it's hard to be more specific.
As #Raghunandan says, the way your AsynTask is constructed is incorrect, it should be something like:
public class RetreiveFeedTask extends AsyncTask<Context, Void, String[]> {
String[] q;
#Override
protected String[] doInBackground(Context... params) {
//computation
return result; // result is of type String[]
}
protected void onPostExecute(String[] result) {
// do something with result ...
er=q;
gff=true;
}
}
A secondary point is your top() function, which returns er. I am guessing that you want it to wait until the AsyncTask is complete and then return er, which has been calculated by the AsyncTask. However, what it will do at present is to set the AsyncTask and then immediately return er: it won't wait for the AsyncTask to complete.
To achieve that, you probably need to split the button press action into two phases:
Create and run the AsyncTask
OnPostExecute in the AsyncTask calls the code which currently uses the er returned by top.
public class ListViewA extends Activity{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView lv= (ListView)findViewById(R.id.listview);
//computation
new RetreiveFeedTask().execute(ctr,ctz);
}
...
public class RetreiveFeedTask extends AsyncTask<Context, Void, String[]>{
#Override
protected String[] doInBackground(Context... fileName) {
//computation
String[] q;
//Know that fileName is an Array
return q
}
protected void onPostExecute(String[] result) {
//do something with the result
}
}
...
}
I have a private class ReadFilesTask extends AsyncTask<String, Void, String> in my program. Under that, I have: protected void onPreExecute(),
protected String doInBackground(String... params), and
protected Void onPostExecute(String result).
The first two have #Override above them. The only one I'm ACTUALLY using though, is doInBackground. I'd first really just try adding #Override to doInBackground(). Next, try changing Context to String.
EDIT:
You are also missing the function to grab an activity. Again, this is simply what I'm doing in my code and a few examples I saw online:
public RetreiveFeedTask(Activity activity) {
this.activity = activity;
}
Something like that.
Related
I have Activity1 and Activity2. Both can call a class named "fetchData.java". Is there any way inside fetchData.java wherein I can get which activity called it?
Here is my fetchData.java:
public class fetchData extends AsyncTask<String,String,String> {
#Override
protected String doInBackground(String... voids) {
//do something
}
#Override
protected void onPostExecute(String aVoid) {
super.onPostExecute(aVoid);
}
}
Activity1 and Activity2 code:
public class Activity1 extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Call fetchData.java
fetchData getValues = new fetchData();
getValues.execute();
}
The only way I can see for you to do this is by passing the Activity in the constructor:
public class FetchData extends AsyncTask<String, String, String> { //rename fetchData to FetchData to follow Java coding practices
private Activity activity;
public FetchData(Activity activity) {
this.activity = activity;
}
#Override
protected String doInBackground(String... strings) { //use the proper variable names
//...
}
#Override
protected void onPostExecute(String string) { //use the proper variable names
//use instanceof to check the identity of the activity instance
if (activity instanceof Activity1) {
//whatever
} else if (activity instanceof Activity2) {
//whatever else
}
//super call isn't needed
}
}
And to instantiate it:
FetchData getValues = new FetchData(this);
getValues.execute();
Take note of my comments. I made a few changes to your code to improve readability and conform better to Java's coding standards.
In other cases, you might be able to read the stacktrace and find the calling class, but since you're using an AsyncTask, which runs on another Thread, the stacktrace only goes back to when the Thread was created, so it wouldn't work here.
Currently im using a simple Fragment with a attached AsyncTask and setRetainInstance(true) to handle runtime configuration changes and a callback interface to the MainActivity straight from the AsyncTask. (following this example). This works fine so far.
But my Problem is that the data onProgressUpdate passes, once the fragment is detached (when switching to the home-screen for example) is lost. My soloution would be to create buffer variables inside the Fragment which store the lost data from the AsyncTask until the fragment is attached again.
public class MyFragment extends SherlockFragment {
static interface TaskCallbacks {
void onPreExecute();
void onProgressUpdate(MyUpdateBundle p);
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mCallbacks;
private WebFetcherTask mTask;
public List<MyUpdateBundle> updateBuffer;
public MyFragment() {
this.updateBuffer = new ArrayList<MyUpdateBundle>();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
if(updateBuffer.size() > 0)
{
for(MyUpdateBundle update : updateBuffer)
mCallbacks.onProgressUpdate(update);
updateBuffer.clear();
}
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
...
private class MyAsyncTask extends AsyncTask<Void, MyUpdateBundle, Void> {
...
#Override
protected void onProgressUpdate(MyUpdateBundle... uB) {
if (mCallbacks != null) {
for(MyUpdateBundle u : uB)
mCallbacks.onProgressUpdate(u);
}
else
{
for(MyUpdateBundle u : uB)
updateBuffer.add(p);
}
}
...
This seems to me the most cleanest soloution, beside saving the data (sinch this is very slow) or using StickyBroadcasts (doesn't seems a clean approach to me). I think that a Service would be a good alternative, but I'm not sure if I would end up in the same problem as here: prevent data from been lost when everthing is unable to recieve. However when I want to re-send the UpdateBundles within the onAtach() methode of the fragment, the buffer is always empty.
I've tryed so far:
volatile statement on the updateBuffer List
Collections.synchronizedList on the updateBuffer List
ensured that the fragment is no create again/twice
put the updateBuffer within the AsyncTask
...
But before I put too much time into this, I would like to know if my approach is even possible and when how.
Thanks in Advance!
Anyway I fixed it. I attached the AsyncTask to the application task and completly removed the Fragment.
It's also a great way to handle any configuration changes or vanished activitys. I don't need to save any data, I just read my buffers and pass them to the callback and have the exact same state as before!
I have an activity with multiple AsyncTask's, but when i press back button, the Activity is reloaded and the AsyncTask's are executed again. what should i do to Back to the previous activity and not reload the activity and asynctask ? please help.
public class LugarActivity extends SherlockActivity {
CargarDatos cargarDatos;
CargarComentarios cargarComentarios;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lugar);
cargarDatos = new CargarDatos();
cargarCometarios = new CargarComentarios();
loadData();
}
public void loadData(){
cargarDatos.execute();
}
public void loadOtherData(){
cargarComentarios.execute();
}
public class CargarDatos extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
#Override
protected void onPostExecute(String html) {
loadOtherData();
}
}
public class CargarComentarios extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
}
}
FIXED!
i fixed the problem with Singleton class:
public class DataManager {
private static DataManager instance = null;
protected static boolean isShowingTheView = false;
protected DataManager() { }
public static synchronized DataManager getInstance() {
if (instance == null) {
instance = new DataManager();
}
return instance;
}
}
in the activity i add this code:
DataManager dataManager = new DataManager();
if(!dataManager.isShowingTheView){
loadData();
dataManager.isShowingTheView = true;
}else{
finish();
}
and finally i override the onDestroy() method
#Override
public void onDestroy(){
dataManager.isShowingTheView = false;
super.onDestroy();
}
Remove loadData() from onCreate and call somewhere else.
Use Fragments
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
A fragment can stay in memory during a configuration change and therefore you can run your asynctask inside itself. You can then query the fragment for any state information you require from your tasks and update your Activity accordingly.
If your Activity is destroyed before the other activity starts, using the back button will call onCreate again, instead of onRestart or onResume.
See here for details.
As Kuffs already mentions, using Fragments is the way to go.
Uglier solution, you could also set a shared preference holding a boolean once your AsyncTask is launched (or on its onPostExecute) so that it won't launch again after checking for that preference on your Activity's onCreate.
I want to implement a splash screen into my app. I already did this. But at the moment it just waits 3 seconds and then calls the MainActivity class. The problem with that is that i have data to load and with the current setup the user have to waits two times. I want a splash screen that loads all the data. I have a MainActivity class where everything happens and I have my SplashScreen class.
The method I want to be run in the background is in my MainClass. So basically I have my splash screen class like that
public class SplashScreenActivity extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
return null;
}
protected void onPostExecute(Void... params) {
// TODO Auto-generated method stub
}
protected void onCancelled() {
// TODO Auto-generated method stub
}
}
I also implemented but not copied the imports and packages, so that shouldn't be a problem. Now, if I understood correctly I need to write the task that should be done into the doInBackground method. So I basically have to call the method from my other activity class, right?
public MainActivity mA = new MainActivity();
and then in the method I would write mA.parseXMLfromURL();
And afterwards I would start an intent of the main class into the onPostExecute-method like this?
protected void onPostExecute(Void... params) {
Intent mainClass = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(mainClass);
}
If more information is needed I will gladly elaborate further.
UPDATES
Well, after your comments I tried it a bit differently.
This is my oncreate method
public void onCreate(Bundle savedInstanceState) {
StrictMode.setThreadPolicy(policy);
super.onCreate(savedInstanceState);
sv = new ScrollView(this);
layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
sv.addView(layout);
setContentView(sv);
new SplashScreenActivity().execute("URL TO FILE");
}
And this is the SplashScreenActivity
public class SplashScreenActivity extends AsyncTask<String, Void, Void> {
public MainActivity mA = new MainActivity();
protected void onPostExecute(Void... params) {
}
protected void onCancelled() {
// TODO Auto-generated method stub
}
#Override
protected Void doInBackground(String... params) {
mA.parseXMLfromURL(params);
return null;
}
}
But this just returns a blank screen. However if I call the parseXMLfromURL in my main activity it works just fine.
#Raghunandan said in comments that I wrongly created the instance of the class. Would be glad if you could elaborate your answer.
UPDATE NUMBER TWO
Current SplashScreen-Code is the following:
package de.activevalue.T1000flies;
import android.app.Activity;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
public class SplashScreenActivity extends Activity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash_screen);
new mTask().execute();
}
private class mTask extends AsyncTask<Void, Void, Void>{
MainActivity mA = new MainActivity();
#Override
protected Void doInBackground(Void... arg0) {
mA.parseXML();
return null;
}
protected void onPostExecute(Void... params){
Intent i = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(i);
finish();
}
}
}
With that code the app just stucks at the Splash-Screen. It seeems like there is a problem with my XML-Parsing. Here is the XML-Parsing-code. Note that it works without any problems, when I just start the main activity wihtout the splash screen
UPDATE NUMBER THREE
I just started to debug by making breakpoints line per line. It jumps out at this line
rankingDate [k] = new TextView(this);
Rest of the code
for(int k = 0; k < metaList.getLength(); k++){
Node metaNode = metaList.item(k);
System.out.println(metaList.getLength());
rankingDate [k] = new TextView(this);
rdate [k] = new TextView(this);
numberOE [k] = new TextView(this);
Element metaElement = (Element) metaNode;
NodeList rankingDateList = metaElement.getElementsByTagName("date");
Element rankingDateElement = (Element) rankingDateList.item(0);
rankingDateList = rankingDateElement.getChildNodes();
rankingDate [k].setText("RankingDate: " + ((Node) rankingDateList.item(0)).getNodeValue());
layout.addView(rankingDate[k]);
xmlSerializer.startTag(null, "date");
xmlSerializer.text(((Node) rankingDateList.item(0)).getNodeValue());
xmlSerializer.endTag(null, "date");
}
The system.out.println gives me 1. And k is 0. So why is it a Null Pointer Exception?
You should create new activity for SplashScreen --> SplashScreenActivity extends Activity, declare in manifest and than set layout ;
public SplashScreenActivity extends Activity{
protected void onCreate(Bundle ...){
super.onCreate(...);
setContentView(...);
new mTask().execute();
}
private class mTask extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
return null;
}
protected void onPostExecute(Void... params) {
Intent mainClass = new Intent(SplashScreenActivity.this, MainActivity.class);
startActivity(mainClass);
finish();
}
}
}
Piggy-backing off nurisezgin's answer explaining how AsyncTasks work in android, you're almost there but need to get some other things out of the way.
First: In Android-world, you never initialize activities by calling their constructor. Activities are handled by the operating system, and you run them using Intents.
That out of the way, you're very close to having your issue solved. You need to take whatever code is in the parseXML function of your MainActivity and put it either into your SplashScreenActivity and call it, or just put it directly in your doInBackground method.
Your doInBackground method should not be calling any outside activities.
An easier way to do your splash screen is to have it appear in front of your main activity that needs to load data using Dialogs. The easiest way to go about this is to override the onPreExecute() method in AsyncTask. The following is a simple example of a splash screen.
MainActivity.java
public class MainActivity extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new SplashScreen(this).execute();
}
}
SplashScreen.java
public class SplashScreen extends AsyncTask<Void, Void, Void>
{
Context ctxt;
Dialog splash;
public SplashScreen(Context ctxt)
{
this.ctxt = ctxt;
}
protected void onPreExecute()
{
splash = new Dialog(ctxt, R.style.full_screen);
splash.show();
}
protected Void doInBackground(Void... ignore)
{
//Fetch stuff
return null;
}
protected void onPostExecute(Void ignore)
{
splash.dismiss();
}
}
In your res/values/styles.xml file, you want to put the following xml for full screen.
<!-- Stuff that's already in here -->
<style name="full_screen" parent="android:Theme.Light">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>
<!-- Stuff that's already in here -->
This will give you a very basic splash screen (i.e., a blank screen that says nothing). If you look into the Dialogs API, you can find other ways to customize it that allow you to use pictures instead of text as well as completely customize the layout of the Dialog. Also look into DialogFragments if you want an even further customization. The benefit of doing your splash screen this way is that you can retrieve all of your information and set it up in the onPostExecute() to your MainActivity without having to worry about transferring the data.
Hope this helps! Good luck.
I tried exactly the same in a project, but my approach was different. Maybe it`could solve your problem as well...
first, my SplashScreen was an overlay in the MainActivity
//main-activtiy xml
<RelativeLayout ...
<RelativeLayout id="overlay" visible="true"... //filled parent and had a centered image
<RelativeLayout id="mainActivity" visible="false"... //the application
Application launched
Start your AsyncTask or Service in your onCreateMethod
if data is loaded, send Notification to your MainActivity and "swap your view" like
public void functionCalledOnDataLoaded(){
//do you init stuff
((RelativeLayout) findViewById(R.id.overlay)).setVisibility(View.INVISIBLE);
((RelativeLayout) findViewById(R.id.mainActivity)).setVisibility(View.VISIBLE);
}
Why don't you only create MainActivity. The Splash is just a frame layout of main.xml, and will be setVisibility(View.GONE) after certain time.
Using this method, you have only 1 activity. Thus, it's easier to handle load data from network without interrupt.
My app loads a lot of stuff on startup and after testing it delays too long at the beginning to not have a splash screen. So, I want to display a splash screen until my app is done loading. I do NOT want to display a screen with a timer for X seconds. I found an example here:
Android SplashScreen
I tried implementing the code in the SO topic above but I just don't understand the code. After integrating it in my code I come up with one error that I commented into the code below. But I don't understand a lot of the code and I have commented in the code below the parts I am confused by.
public class MainMenu extends Activity {
private ProgressDialog pd = null;
private Object data = null; //What is this?
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.mainmenu);
// show the ProgressDialog on this thread
this.pd = ProgressDialog.show(this, "Working...", "Downloading data...", true, false);
// start a new thread that will download all the data
new DownloadTask().execute("Any parameters to download."); //What is DownloadTask()?
}
private class DownloadTask extends AsyncTask<String, Void, Object> {
protected Object doInBackground(String... args) { //Are these parameters correct?
return "replace this with your object"; //What is this?
}
protected void onPostExecute(Object results) {
// pass the resulting data to the main activity
MainMenu.this.data = result; //Error: "result cannot be resolved to a variable"
if(MainMenu.this.pd != null) {
MainMenu.this.pd.dismiss();
}
}
}
}
Let's start with the error:
MainMenu.this.data = result;
Notice the typo? It should be result*s*:
MainMenu.this.data = results;
Addressing the rest of your questions below:
private class DownloadTask extends AsyncTask<String, Void, Object>
The declaration is for an inline class called DownloadTask, and it states that you'll be taking Strings (via String...) as parameters to your doInBackground(String... params).
The second parameter (Void in your case) indicates the datatype used to "publish" the progress via publishProgress(DATATYPE)/onProgressUpdate(DATATYPE... progress). This method is suitable for notifying the user of changes, for example when you've finished downloading a file but still have a few to go.
The last parameter (Object), indicates what type of data you'll be passing on to onPostExecute(DATATYPE), in this example Object. This could either be to update a ListAdapter somewhere, or trigger any other UI change based on the outcome of the actions done in doInBackground.
Show ProgressDialog in onPreexecute and dismiss it in onPostExcute methods
something like this
private class DownloadTask extends AsyncTask<String, Void, Object> {
#Override
protected void onPreExecute() {
mProgressDialog = new ProgressDialog(activity);
mProgressDialog =ProgressDialog.show(activity, "", "Please Wait",true,false);
super.onPreExecute();
}
protected Object doInBackground(String... args) { //Are these parameters correct?
return "replace this with your object"; //What is this?
}
protected void onPostExecute(Object results) {
// pass the resulting data to the main activity
MainMenu.this.data = results; //it should be results
if (mProgressDialog != null || mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
if(MainMenu.this.pd != null) {
MainMenu.this.pd.dismiss();
}
}