Thread Issue : NullPointerException - java

I have an issue when I am trying to set text from a last item of a listView into text View which is in the MainActivity. It crashes during running time on “NullPointerException” because I think that that when I’m calling it in MainActivity, It didn’t finish to download on the ListView so when the MainActivity first launched, the listView did not finish his work and the functions getCount(),getItem() and my function getLastElement() are still null.
The issue is that I am not very good with dealing with Thread (Wait(), notify(),..)
Can you please help me with all this?
Here is my code and my LogCat :
public class MainActivity extends Activity implements OnClickListener{
private static Context mContext;
public Button mExit, mHistory, mRating;
public TextView mSignal;
HistoryAdapt myAdapter;
HistoryItems m_myLastItem;
ArrayList<HistoryItems> m_myListItem;
Runnable m_run;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainActivity.mContext=getApplicationContext();
mExit=(Button)findViewById(R.id.ExitButton);
mExit.setOnClickListener(this);
mHistory=(Button)findViewById(R.id.HistoryButton);
mHistory.setOnClickListener(this);
mRating=(Button)findViewById(R.id.RateButton);
mRating.setOnClickListener(this);
mSignal=(TextView)findViewById(R.id.SignalOfTheDayTV);
//### SET LAST ELEMENT INTO TEXTVIEW
m_myListItem = new ArrayList<HistoryItems>();
myAdapter= new HistoryAdapt(mContext, m_myListItem);
new Thread(
new Runnable(){
#Override
public void run(){
try{
///Need to wait until the data to be downloaded inside HistoryAdapt so it can show the last element from the ListView here
HistoryParser parser = new HistoryParser();
parser.parse(getInputStream(HistoryAct.RSS_LINK));
} catch (XmlPullParserException e) {
Log.w(e.getMessage(), e);
} catch (IOException e) {
Log.w(e.getMessage(), e);
} finally {
//notify that the data finished to download
MainActivity.this.runOnUiThread(
new Runnable(){
public void run(){
m_myLastItem = myAdapter.getLastElement();
//set last signal into TextView
mSignal.setText(m_myLastItem.getTitle());
}
}
);
}
}
}
).run();
//#RATER
// AppRater.app_launched(this);
// AppRater.showRateDialog(this, null);
//Get a Tracker (should auto-report)
((AppManager) getApplication()).getTracker(AppManager.TrackerName.APP_TRACKER);
}//oncreate
private InputStream getInputStream(String link) {
try {
URL url = new URL(link);
return url.openConnection().getInputStream();
} catch (IOException e) {
Log.w(Constants.DATA, "Exception while retrieving the input stream", e);
return null;
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public static Context getAppContext(){
return MainActivity.mContext;
}
public void ExitState(){
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("You're about to quit Signals4Trading");
builder.setIcon(R.drawable.five);
//builder.setMessage("Your device has been registered successfully. You'll receive signals very soon.");
builder.setMessage("Are you sure you want to quit?");
builder.setCancelable(false);//can't click on the background of the activity
builder.setPositiveButton("Yes",new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(),"See you soon", Toast.LENGTH_LONG).show();
finish();
}//OnClickListener PositiveButton
});//anonymous class PositiveButton
builder.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(),"Enjoy your visit", Toast.LENGTH_LONG).show();
}
});
AlertDialog alertDialog = builder.create();
alertDialog.show();
}//ExitState
public void goToHistoryActivity(){
Intent intent = new Intent(MainActivity.this, HistoryAct.class );
startActivity(intent);
}
public void rateApp(){
Intent intent = new Intent(MainActivity.this, Rate.class);
startActivity(intent);
}
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()){
case R.id.ExitButton:
ExitState();
break;
case R.id.HistoryButton:
goToHistoryActivity();
break;
case R.id.RateButton:
rateApp();
break;
}
}
#Override
protected void onStart() {
// TODO Auto-generated method stub
super.onStart();
//Get an Analytics tracker to report app starts & uncaught exceptions etc.
GoogleAnalytics.getInstance(this).reportActivityStart(this);
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
//Stop the analytics tracking
GoogleAnalytics.getInstance(this).reportActivityStop(this);
}
}//MainActivity
package com.Signals4Trading.push.android;
import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.os.StrictMode;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
public class HistoryAdapt extends BaseAdapter {
private final List<HistoryItems>items;
private final Context context;
public HistoryAdapt(Context context,List<HistoryItems>items){
this.context=context;
this.items=items;
}//constructor
#Override
public int getCount() {
// TODO Auto-generated method stub
return items.size();
}
#Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return items.get(position);
}
#Override
public long getItemId(int id) {
// TODO Auto-generated method stub
return id;
}
//###FUNCTION THAT RETURN LAST ELEMENT
public HistoryItems getLastElement(){
return items.get(items.size()-1);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(context, R.layout.historyitems, null);
holder = new ViewHolder();
holder.itemTitle = (TextView) convertView.findViewById(R.id.itemTitleTV);
holder.itemDate=(TextView) convertView.findViewById(R.id.itemDateTV);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.itemTitle.setText(items.get(position).getTitle());
holder.itemDate.setText(items.get(position).getDate());
}
return convertView;
}
class ViewHolder {
TextView itemTitle;
TextView itemDate;
}
}
//HistoryAdapt
11-13 15:18:34.702: E/AndroidRuntime(7737): FATAL EXCEPTION: main
11-13 15:18:34.702: E/AndroidRuntime(7737): Process: com.Signals4Trading.push.android, PID: 7737
11-13 15:18:34.702: E/AndroidRuntime(7737): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.Signals4Trading.push.android/com.Signals4Trading.push.android.MainActivity}:
java.lang.ArrayIndexOutOfBoundsException: length=0; index=-1
11-13 15:18:34.702: E/AndroidRuntime(7737): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2184)

Your onCreate() call in the Activity is calling:
m_myLastItem= HistoryAdapt.getLastElement();
But, the constructor for HistoryAdapt has not been called yet, and that is where you initialize the static field items within it. I would recommend rethinking this class and its usage as you're using it in an instance manner but have it partially coded as if it is a static class. A good rule of thumb to follow is don't make your static methods dependent on the class being constructed. At that point it's just an instance method.

Looks like you're trying to perform an asynchronous operation using a runnable, then before the asynchronous operation is completed you're trying to use the value it is supposed to set. So I'm assuming you're trying to create a background thread that waits for the data and updates/notifies the UI/UI-thread when it's done loading. This would mean that the asynchronous operation is created and started by the UI-thread during OnCreate and while it is running the UI-thread can happily go on executing the OnCreate method and loading the UI etc.
First of all, it looks like you're creating a runnable but not running it. If you want to run the runnable in a different (asynchronous) thread, try this:
new Thread(
new Runnable()
{
#Override
public void Run()
{
//your code here
}
}
).Run();
*note: Translated this from C#, may not be 100% correct (caps?) but should be really easy to get working.
It's not entirely clear from your code but I assume the code in your runnable fills the HistoryAdapt class with data. Be careful not to create concurrency issues this way. If two threads are trying to access the same object at the same time you may have problems so be sure to properly "lock" objects before using them in seperate threads. In java you can use a "synch" clause to do this:
synchronized(this){
this = newvalue;
}
This makes sure that while the code inside the braces is executing the "this" object cannot be accessed by other threads. Other threads will then wait untill the code is executed and the object is released before continuing.
Secondly, you should be aware that the runnable/thread you've created is basicly a thread that is run once and then disposed (so not a dedicated thread). In this case monitor/wait is not really necessary. You could simply include the code that updates the UI (mSignal.setText(m_myLastItem.getTitle());) in the finally clause of your try/catch statement. Be sure to use the runOnUiThread method when you're trying to update/edit UI-elements from seperate threads like so:
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
m_myLastItem = HistoryAdapt.getLastElement();
//set last signal into TextView
mSignal.setText(m_myLastItem.getTitle());
}
});
This way, the thread is created in OnCreate, does its job seperately from the UI-thread, then updates the UI as soon as it is done. One possible problem you might have is that mSignal.setText() could get called at a moment when the UI isnt ready to be updated, not sure this will really be a problem in this case though. Also, when using this technique in other places you may need a reference to your activity to use inside your thread (you can't always get a reference from the place where you're defining the thread/runnable), that's easy enough, just make a static reference to your activity somewhere in your app and reference that (i prefer an App_Session class for static variables used app-wide).
Hope I understood the question correctly and this helps. In any case, good luck!
*Edit: Full example
new Thread(
new Runnable()
{
#Override
public void run()
{
if(!HistoryAdapt.hasBeenInitiated)
{
try
{
///Need to wait until the data to be downloaded inside HistoryAdapt so it can show the last element from the ListView here
HistoryParser parser = new HistoryParser();
parser.parse(getInputStream(HistoryAct.RSS_LINK));
} catch (XmlPullParserException e) {
Log.w(e.getMessage(), e);
} catch (IOException e) {
Log.w(e.getMessage(), e);
} finally {
//notify that the data finished to download
MainActivity.this.runOnUiThread(
new Runnable()
{
public void run()
{
if (!HistoryAdapt.hasBeenInitiated)
{
m_myLastItem = HistoryAdapt.getLastElement();
//set last signal into TextView
mSignal.setText(m_myLastItem.getTitle());
}
}
}
);
}
}
}
}
).run();
Could not test this unfortunately since i can't run Java (i use xamarin), should be correct though, maybe the #override isnt needed and im not sure if it's supposed to be 'Run' or 'run'. I'm sure you'll figure that out though ;)

Thread run = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
}
run = new Thread( new Runnable(){
public void run(){
if(!HistoryAdapt.hasBeenInitiated){
try {
///Need to wait until the data to be downloaded inside HistoryAdapt so it can show the last element from the ListView here
HistoryParser parser = new HistoryParser();
parser.parse(getInputStream(HistoryAct.RSS_LINK));
} catch (XmlPullParserException e) {
Log.w(e.getMessage(), e);
} catch (IOException e) {
Log.w(e.getMessage(), e);
}finally{
//notify that the data finished to download
}
}
}
});
run.start();
run.join();
use this but it will wait until your run thread finish.

Related

The content of the adapter has changed but ListView did not receive a notification.(Using background Service)

I my activity i am using a background service from were i get data from json every 7 second.and update my list view on UI thread.some time when there is large number of date is coming and list view is scrolling its crashing. with this java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread.
Updating service
android.os.Handler handler_service = new android.os.Handler() {
public void handleMessage(android.os.Message msg) {
runOnUiThread(new Runnable() {
#Override
public synchronized void run() {
if (jon_list_Adapter == null) {
try {
jon_list_Adapter = new Different_Job_List_Adapter(
getApplicationContext(),
Different_Job_List.getInstance(),
Driver_Request_JobActivity.this);
listdriver_invites.setAdapter(jon_list_Adapter);
jon_list_Adapter.notifyDataSetChanged();
// notifyDataSetChanged();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
} else {
try {
jon_list_Adapter.notifyDataSetChanged();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
});
};
};
You need to call notifyDataSetChanged() in the adapter, so set up a method called refreshData in the adapter like this:
public void refreshData(ArrayList<yourList> data) {
mValues.clear();
mValues.addAll(data);
notifyDataSetChanged();
}
and call from your activity:
json_list_Adapter.refreshData(newData)
with the new data as an argument.

how load fragment in ViewPager only when its selected

I'm using 3 Fragments inside a Viewpager, the problem is that I am loading big data in an Asynctask and loaders. On devices like HTC one, it works fine, however, on low-end devices, it takes a lot of time. This is mainly because when I implement the pagerAdapter, I put the Fragments inside an ArrayList, this force the fragments instantiate when the main activity is loaded. What I need is that it just "load" the first fragment (main) and when the user Swype, load the other fragment. its any way to achieve this? this is my pageAdapater
public class PagerAdapter extends FragmentPagerAdapter {
private final ArrayList<Fragment> mFragments = new ArrayList<Fragment>();
// private final ArrayList<String> titulos = new ArrayList<String>();
// private int NUM_PAGES =0;
public PagerAdapter(FragmentManager manager,int num_pages) {
super(manager);
// this.NUM_PAGES = num_pages;
}
public void addFragment(Fragment fragment,String title) {
mFragments.add(fragment);
notifyDataSetChanged();
}
#Override
public int getCount() {
//return NUM_PAGES;
return mFragments.size();
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
}
The above method from Sun did not work for me (maybe it does for you), but I thought I would share my edit of his method also. Very good method by the way Sun!
private boolean _hasLoadedOnce= false; // your boolean field
#Override
public void setUserVisibleHint(boolean isFragmentVisible_) {
super.setUserVisibleHint(true);
if (this.isVisible()) {
// we check that the fragment is becoming visible
if (isFragmentVisible_ && !_hasLoadedOnce) {
new NetCheck().execute();
_hasLoadedOnce = true;
}
}
}
I'm gonna add my solution here since I faced a similar issue. My asynchronous task wasn't loading huge amounts of data, but it prevents unnecessary network calls. Here's what I added in my Fragment:
private boolean _hasLoadedOnce= false; // your boolean field
#Override
public void setUserVisibleHint(boolean isFragmentVisible_) {
super.setUserVisibleHint(isVisibleToUser);
if (this.isVisible()) {
// we check that the fragment is becoming visible
if (!isFragmentVisible_ && !_hasLoadedOnce) {
//run your async task here since the user has just focused on your fragment
_hasLoadedOnce = true;
}
}
}
With the above code, your Fragment will be loaded, but your async task will not run until the user actually scrolls to the Fragment for the first time. Once displayed, your async task will run for the first time automatically. Then you can provide a way to load more data via a button or pull to refresh. The above Fragment was in my ViewPager and seemed to work fine.
Slightly modified version to fix potential NPE caused by some views not fully initialised
private boolean _hasLoadedOnce= false; // your boolean field
#Override
public void setUserVisibleHint(boolean isFragmentVisible_) {
super.setUserVisibleHint(isVisibleToUser);
if (this.isVisible()) {
// we check that the fragment is becoming visible
if (!isFragmentVisible_ && !_hasLoadedOnce) {
new Handler().post(() -> {
makeAsyncRequest();//do your asyn stuffs
_hasLoadedOnce = true;
});
}
}
}
Use fragmentStatePageAdapter if you have a lot of pages and you want to destroy them when not visible.
It has implemented a setMenuVisibility(boolean menuVisible) when fragment becomes visible, so use that.
I might be late for the party but here's my solution and it works as expected. In all of your child fragments create a boolean variable:
private boolean loadFragmentExecuted = false;
in the child fragments create a generic method called loadFragment and move all of the logic you added in onCreateView to that method:
public void loadFragment()
{
if(!loadFragmentExecuted)
{
//Add your logic to manipulate the UI or load data etc...
loadFragmentExecuted = true;
}
}
in your pageview logic create the fragments dynamically like:
//add the fragment
String fragmentName = "com.something." + fragmentId;
//check if the class exists
try
{
Class myFragmentClass = Class.forName(fragmentName);
Fragment myFragment = (Fragment) myFragmentClass.newInstance();
mFragments.add(myFragment);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (InstantiationException e)
{
e.printStackTrace();
}
then set your pager adapter and attach a tablayout with it:
//set our pager adapter that contains different fragments
mPagerAdapter = new BasePagerAdapter(mFragmentManager, mFragments);
//link the adapter to the viewpager
mViewPager.setAdapter(mPagerAdapter);
//cache fragments
int limit = (mPagerAdapter.getCount() > 0 ? mPagerAdapter.getCount() : 1);
mViewPager.setOffscreenPageLimit(limit);
//add the page listner to the viewPager and link it to the tabLayout
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(mTabLayout));
//on tab selected select current viewpager item
mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener()
{
#Override
public void onTabSelected(TabLayout.Tab tab)
{
mViewPager.setCurrentItem(tab.getPosition());
//get fragment for the selected tab
Fragment f = mPagerAdapter.getItem(tab.getPosition());
//load the content of the fragment
try
{
Class c = f.getClass();
Method loadFragment = c.getMethod("loadFragment");
loadFragment.invoke(f);
}
catch (IllegalAccessException e){}
catch (InvocationTargetException e){}
catch (NoSuchMethodException e){}
}
#Override
public void onTabUnselected(TabLayout.Tab tab)
{
}
#Override
public void onTabReselected(TabLayout.Tab tab)
{
}
});

Android Setting Up Splash Screen(Activity) Like Iphone Part1

I have three images with me and i want them to appear on first layout xml like a splash view so that they can be viewed only once i.e that activity will be called only once when app get's installed or if app get's a new update otherwise app should always start from the Second activity, i don't know how should i begin with this :
Can any one tell me any idea how this can be done.
To show splash for only once.
Next part of this question is here
Coding will be much appreciated.
Save a flag in the Preferences when you start up the application, after you've done the welcome screen stuff. Check for this flag before you show the welcome screen. If the flag is present (in other words, if it's not the first time), don't show it.
In your activity:
SharedPreferences mPrefs;
final String welcomeScreenShownPref = "welcomeScreenShown";
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
// second argument is the default to use if the preference can't be found
Boolean welcomeScreenShown = mPrefs.getBoolean(welcomeScreenShownPref, false);
if (!welcomeScreenShown) {
// here you can launch another activity if you like
// the code below will display a popup
String whatsNewTitle = getResources().getString(R.string.whatsNewTitle);
String whatsNewText = getResources().getString(R.string.whatsNewText);
new AlertDialog.Builder(this).setIcon(android.R.drawable.ic_dialog_alert).setTitle(whatsNewTitle).setMessage(whatsNewText).setPositiveButton(
R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
}).show();
SharedPreferences.Editor editor = mPrefs.edit();
editor.putBoolean(welcomeScreenShownPref, true);
editor.commit(); // Very important to save the preference
}
}
Try this :
public class MainActivity extends Activity {
private Thread mSplashThread;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
try {
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.splash);
final MainActivity sPlashScreen = this;
mSplashThread = new Thread() {
#Override
public void run() {
try {
synchronized (this) {
wait(4000);
}
} catch (InterruptedException ex) {
}
finish();
Intent intent = new Intent();
intent.setClass(sPlashScreen, StartNewActivity.class);// <-- Activity you want to start after Splash
startActivity(intent);
}
};
mSplashThread.start();
} catch (Exception e) {
}
}
#Override
public boolean onTouchEvent(MotionEvent evt) {
try {
if (evt.getAction() == MotionEvent.ACTION_DOWN) {
synchronized (mSplashThread) {
mSplashThread.notifyAll();
}
}
} catch (Exception e) {
}
return true;
}
}
you put an Image in splash.xml to show
to do this you have to detect the first launch of your application. To do so you can store a boolean value as #Nirav suggested.
And for the splash screen, You can consider using Fragments and ViewPager to create an activity which will only be shown for the first time

Parse.com Android API and Android Dialog

I have placed the parse method inside onCreate method. But my problem is how to show the Android Loading... Dialog??
Parse.initialize(this, "a", "b");
ParseQuery query = new ParseQuery("Category");
query.findInBackground(new FindCallback() {
#Override
public void done(List<ParseObject> catObjects, ParseException arg1) {
Log.d("Catlength", String.valueOf(catObjects.size()));
for(int i =0; i<catObjects.size(); i++){
Log.d("lengthName"+String.valueOf(i), String.valueOf(catObjects.get(i).getInt("Id")));
Category category = new Category();
category.Name= catObjects.get(i).getString("CatName");
category.id= catObjects.get(i).getInt("Id");
categories.add(category);
}
if(categories.size()>0){
setListAdapter(new CategoryArrayAdapter(CategoryListActivity.this, R.layout.row_category, categories));
}
else{
Toast.makeText(CategoryListActivity.this, "Our servers are busy. Hit refresh..", 3000).show();
}
}
});
Everything works fine in the above code but I couldn't figure out how to show the Dialog.
I'm unable to use AsycTask also as parse sdk invokes its own thread in the background and before the findInBackground execution finishes, the doInBackground completes the Asyc thread. That's why I invoked it in the main thread.
As the result I always get no results in my ArrayList.
Can someone please enlighten me.
I was in the same situation regarding the progress dialog, tried a few tricks and finally just declared a ProgressDialog class member:
protected ProgressDialog proDialog;
then created two methods:
protected void startLoading() {
proDialog = new ProgressDialog(this);
proDialog.setMessage("loading...");
proDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
proDialog.setCancelable(false);
proDialog.show();
}
protected void stopLoading() {
proDialog.dismiss();
proDialog = null;
}
and called startLoading() before the background operation and stopLoading()
inside the background operation after I got the the results.
startLoading();
ParseUser.logInInBackground(userName.getText().toString(), hashedPass, new LogInCallback() {
public void done(ParseUser user, ParseException e) {
if (user != null) {
Log.d(Constants.TAG, "User Loged in.");
ParseManager.sCurrentUser = user;
stopLoading();
finish();
} else {
stopLoading();
invalidCreds();
}
}
});
if you want to use AsyncTask don't call findInBackground() you can use find().
you can check it out in the api https://parse.com/docs/android/api/com/parse/ParseQuery.html#find()
hope this helps.
It's easy to get the progress of both uploads and downloads using ParseFile by passing a ProgressCallback to saveInBackground and getDataInBackground. For example:
byte[] data = "Working at Parse is great!".getBytes();
ParseFile file = new ParseFile("resume.txt", data);
file.saveInBackground(new SaveCallback() {
public void done(ParseException e) {
// Handle success or failure here ...
}
}, new ProgressCallback() {
public void done(Integer percentDone) {
// Update your progress spinner here. percentDone will be between 0 and 100.
}
});

AsyncTask not displaying onPreExecute

I have two AsyncTask Activities, whose sole responsibilties are to execute an AsyncTask on the UI Thread. One of them works great, displays the progressBar, and updates the ProgressBar message. This is for my SearchActivity, which is actually a search activity with the Google Search metadata for declaring a search activity. I created a clone of the AsyncTask, and only changed doInBackground code, and also clone the SearchActivity and called it browseActivity.
I start BrowseActivity for result in a class that extends listView when an item is clicked. What I intended to happen was for the onPreExecute code of the AsyncTask to display a progressBar as the SearchActivity does, between the ListView activity, and the ResultActivity that the ListView will start onActivityResult. The onPreExcute code is being called but the ProgressBar is never displayed. (I thought I ran it on the UI thread correctly?)
Here is BrowseActivity:
AsyncTask<String, String, ArrayList<SearchResult>> browseTask;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
String genreExtra = getIntent().getStringExtra(Genre.BUNDLE_TAG);
if(genreExtra!=null){
genre = Genre.getgenreFromExtra(genreExtra);
}
String categoryExtra = getIntent().getStringExtra(Category.BUNDLE_TAG);
if(categoryExtra!=null){
this.category = Category.getCategoryFromExtra(categoryExtra);
}
final Connector connector = GlobalVars.getCurrentConnector();
Thread browseThread = new Thread(){
#Override
public void run() {
try {
browseTask = connector.browse(BrowseActivity.this, category, genre, 1);
browseTask.execute("");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
runOnUiThread(browseThread);
ArrayList<SearchResult> result = null;
try {
result = browseTask.get();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Bundle queryBundle = new Bundle();
queryBundle.putSerializable(SERIALIZABLE_KEY, result);
Intent i = new Intent();
i.putExtra(BUNDLE_KEY, queryBundle); //Put in our browse results
i.putExtra(Category.BUNDLE_TAG, category.putExtra()); //put our category in return intent
i.putExtra(Genre.BUNDLE_TAG, genre.putExtra()); //put our genre in return intent
setResult(RESULT_OK, i);
finish();
}
Here is the CategoryActivity (extends ListActivity) activity where browseActivity is started:
#Override
protected void onListItemClick(ListView l, View v, final int position, long id) {
super.onListItemClick(l, v, position, id);
Intent i = new Intent(CategorySelectionView.this, BrowseActivity.class);
final Category[] categories = Category.values();
i.putExtra(Category.BUNDLE_TAG, categories[position].putExtra());
i.putExtra(Genre.BUNDLE_TAG, genre.putExtra());
CategorySelectionView.this.startActivityForResult(i, BrowseActivity.RESULT_REQUEST_CODE);
}
// Listen for results.
protected void onActivityResult(int requestCode, int resultCode, Intent data){
// See which child activity is calling us back.
switch (requestCode) {
case BrowseActivity.RESULT_REQUEST_CODE:
// This is the standard resultCode that is sent back if the
// activity crashed or didn't doesn't supply an explicit result.
if (resultCode == RESULT_CANCELED){
}
else {
ArrayList<SearchResult> recentQueryResults = (ArrayList<SearchResult>)data.getBundleExtra(
BrowseActivity.BUNDLE_KEY).getSerializable(BrowseActivity.SERIALIZABLE_KEY);
GlobalVars.setRecentQueryResults(recentQueryResults);
Category category = Category.getCategoryFromExtra(data.getStringExtra(Category.BUNDLE_TAG));
Intent i = new Intent(CategorySelectionView.this, RomListView.class);
i.putExtra(Category.BUNDLE_TAG, category.putExtra());
i.putExtra(Genre.BUNDLE_TAG, genre.putExtra());
startActivity(i);
}
default:
break;
}
}
Firstly, you can't run an AsyncTask on the main/UI Thread, that's the whole point. When you call browseTask.execute() its going to call its methods on the appropriate Thread (main vs background), regardless of calling runOnUiThread(). By calling runOnUiThread() all you are doing is wrapping the code that starts it in another Runnable. I think is not what you want since after calling runOnUiThread() you try to get the result of the task, which hasn't run yet. The code in onCreate() runs on the UI thread, so this has to finish before something else can run, namely the onPreExecute() of the AsyncTask.
What you should be doing is to create the AsyncTask and put everything you currently have after runOnUiThread() in onPostExecute().

Categories