MainActivity.this is not an enclosing class AsyncTask - java

I'm trying to create an AsyncTask for the 1st time, but I don't have much luck.
My AsyncTask needs to get some information from a server and then add new layouts to the main layout to display this information.
Everything seems to be more or less clear but, the error message "MainActivity is not an enclosing class" is bothering me.
Nobody else seems to have this problem, so I think I miss something very obvious, I just don't know what it is.
Also, I'm not sure if I used the right way to get the context, and because my application doesn't compile so I can't test it.
Your help is much appreciated.
Here is my code:
public class BackgroundWorker extends AsyncTask<Context, String, ArrayList<Card>> {
Context ApplicationContext;
#Override
protected ArrayList<Card> doInBackground(Context... contexts) {
this.ApplicationContext = contexts[0];//Is it this right way to get the context?
SomeClass someClass = new SomeClass();
return someClass.getCards();
}
/**
* Updates the GUI before the operation started
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
/**
* Updates the GUI after operation has been completed
*/
protected void onPostExecute(ArrayList<Card> cards) {
super.onPostExecute(cards);
int counter = 0;
// Amount of "cards" can be different each time
for (Card card : cards) {
//Create new view
LayoutInflater inflater = (LayoutInflater) ApplicationContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ViewSwitcher view = (ViewSwitcher)inflater.inflate(R.layout.card_layout, null);
ImageButton imageButton = (ImageButton)view.findViewById(R.id.card_button_edit_nickname);
/**
* A lot of irrelevant operations here
*/
// I'm getting the error message below
LinearLayout insertPoint = (LinearLayout)MainActivity.this.findViewById(R.id.main);
insertPoint.addView(view, counter++, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
}

Eclipse is probably right, and you are trying to access a class (MainActivity) that is inside it's own file from another class that is in its own file (BackgroundWorker) . There is no way to do that - how is one class supposed to know about the other's instance magically? What you can do:
Move the AsyncTask so it is an inner class in MainActivity
Pass off your Activity to the AsyncTask (via its constructor) then acess using activityVariable.findViewById(); (I am using mActivity in the example below) Alternatively, your ApplicationContext (use proper naming convention, the A needs to be lowercase) is actually an instance of MainActivity you're good to go, so do ApplicationContext.findViewById();
Using the Constructor example:
public class BackgroundWorker extends AsyncTask<Context, String, ArrayList<Card>>
{
Context ApplicationContext;
Activity mActivity;
public BackgroundWorker (Activity activity)
{
super();
mActivity = activity;
}
//rest of code...
As for
I'm not sure if I used the right way to get the context
It is fine.

Above example is inner class, here is standalone class...
public class DownloadFileFromURL extends AsyncTask<String, String, String> {
ProgressDialog pd;
String pathFolder = "";
String pathFile = "";
Context ApplicationContext;
Activity mActivity;
public DownloadFileFromURL (Activity activity)
{
super();
mActivity = activity;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
pd = new ProgressDialog(mActivity);
pd.setTitle("Processing...");
pd.setMessage("Please wait.");
pd.setMax(100);
pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
pd.setCancelable(true);
pd.show();
}
#Override
protected String doInBackground(String... f_url) {
int count;
try {
pathFolder = Environment.getExternalStorageDirectory() + "/YourAppDataFolder";
pathFile = pathFolder + "/yourappname.apk";
File futureStudioIconFile = new File(pathFolder);
if(!futureStudioIconFile.exists()){
futureStudioIconFile.mkdirs();
}
URL url = new URL(f_url[0]);
URLConnection connection = url.openConnection();
connection.connect();
// this will be useful so that you can show a tipical 0-100%
// progress bar
int lengthOfFile = connection.getContentLength();
// download the file
InputStream input = new BufferedInputStream(url.openStream());
FileOutputStream output = new FileOutputStream(pathFile);
byte data[] = new byte[1024]; //anybody know what 1024 means ?
long total = 0;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
// After this onProgressUpdate will be called
publishProgress("" + (int) ((total * 100) / lengthOfFile));
// writing data to file
output.write(data, 0, count);
}
// flushing output
output.flush();
// closing streams
output.close();
input.close();
} catch (Exception e) {
Log.e("Error: ", e.getMessage());
}
return pathFile;
}
protected void onProgressUpdate(String... progress) {
// setting progress percentage
pd.setProgress(Integer.parseInt(progress[0]));
}
#Override
protected void onPostExecute(String file_url) {
if (pd!=null) {
pd.dismiss();
}
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Intent i = new Intent(Intent.ACTION_VIEW);
i.setDataAndType(Uri.fromFile(new File(file_url)), "application/vnd.android.package-archive" );
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplicationContext().startActivity(i);
}
}

Related

Static ArrayList from a service is empty when accessed from my MainActivity

I am making a basic app that just reads RSS files from google and displays the headlines in a listview. The problem I am having is that the static ArrayList I am using in an IntentService is empty when I access it after I start the intent for the service. There could be something very basic I am missing here but tit used to work before I added more RSS links to the service. I tried commenting them out to see if there was some sort of overloading somewhere but nothing changed, which I guess means that I changed something else without realizing or remembering it.
Here is my relavent code, the ArrayList I am trying to access is the newsList variable in RSSsearcher. In the past I have logged the string values of the arraylist and confirmed that it was being populated in the RSSsearcher class, but empty in the MainActivity class.
Relevant code:
RSSsearcher:
public class RSSsearcher extends IntentService {
public static ArrayList<NewsCard> newsList = new ArrayList<>();
private static String TAG = "RSS";
public RSSsearcher() {
super("RSSsearcher");
}
#Override
protected void onHandleIntent(#Nullable Intent intent) {
parseNewsLists();
Log.i(TAG,"newsList in onHandleIntent" + newsList);
}
public static ArrayList<NewsCard> getNewsCards() {
return newsList;
}
//helper method to create all the RSSfeed objects
private void parseNewsLists() {
Log.i(TAG, "the service has been started");
//Creating the RSS feed objects
RSSfeed topStories = new RSSfeed("https://news.google.com/news?cf=all&hl=en&pz=1&ned=us&output=rss");
RSSfeed worldNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/WORLD?ned=us&hl=en");
RSSfeed usNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/NATION?ned=us&hl=en");
RSSfeed buisnessNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/BUSINESS?ned=us&hl=en");
RSSfeed technologyNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/TECHNOLOGY?ned=us&hl=en");
RSSfeed entertainmentNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/ENTERTAINMENT?ned=us&hl=en");
RSSfeed sportsNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/SPORTS?ned=us&hl=en");
RSSfeed scienceNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/SCIENCE?ned=us&hl=en");
RSSfeed healthNews = new RSSfeed("https://news.google.com/news/rss/headlines/section/topic/HEALTH?ned=us&hl=en");
try {
//getting RSS feeds
topStories.FileReader();
worldNews.FileReader();
usNews.FileReader();
buisnessNews.FileReader();
technologyNews.FileReader();
entertainmentNews.FileReader();
sportsNews.FileReader();
scienceNews.FileReader();
healthNews.FileReader();
} catch (Exception e) {
Log.i(TAG, Log.getStackTraceString(e));
}
//adding stories to main
newsList.addAll(topStories.getNews());
newsList.addAll(worldNews.getNews());
newsList.addAll(usNews.getNews());
newsList.addAll(buisnessNews.getNews());
newsList.addAll(technologyNews.getNews());
newsList.addAll(entertainmentNews.getNews());
newsList.addAll(sportsNews.getNews());
newsList.addAll(scienceNews.getNews());
newsList.addAll(healthNews.getNews());
}
}
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//TODO remove and have actual tags show up
TagCard onecard = new TagCard("TestCard");
TagCard twocard = new TagCard("TestCard2");
dummyTags.add(onecard);
dummyTags.add(twocard);
mainListView = (ListView) findViewById(R.id.main_content_list);
drawerLayout = (DrawerLayout) findViewById(R.id.main_layout);
tagDrawerList = (ListView) findViewById(R.id.tag_drawer);
//setting an intent for the RSSsearcher to fetch the news
//TODO make this happen every 15 minutes or so
Intent intent = new Intent(this, RSSsearcher.class);
startService(intent);
NewsCard card = new NewsCard("Testing", "linkeroni");
//adding all the NewsCard objects to this classes newsList
newsList.addAll(RSSsearcher.getNewsCards());
MainAdapter adapter = new MainAdapter(this, newsList);
mainListView.setAdapter(adapter);
Log.i(TAG, "This is the array from main activity" + newsList.toString());
actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.open_drawer, R.string.close_drawer){
public void onDrawerClosed(View view) {
super.onDrawerClosed(view);
getActionBar().setTitle("Closed");
}
public void onDrawerOpened(View view){
super.onDrawerOpened(view);
getActionBar().setTitle("Open");
}
};
//TODO removed dummytags and add actual tag implementation
tagDrawerList.setAdapter(new TagAdapter(this,dummyTags));
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
}
Thank you in advance!
I think that when you call
newsList.addAll(RSSsearcher.getNewsCards());
The intent is still in execution

Assigned a ArrayList in AsyncTask, but the change discard afterwords

I'm writing a simple program to request a JOSN request of a list of earthquakes to display for users. I use Asynctask to put the request in the background thread and use an ArrayList Adaptor to display the relevant information. I declare an empty ArrayList and then extract the JOSN request and put them in a temporary list and then assign the temporary list to the empty ArrayList.
I use a debugger tool to see that in the updateEarthquakeList method. I set the break point in the updateEarthquakeList method. this.earthquak and earthquakes both have 10 elements. Pics are as follow:
But when I set the break point after task.execute(USGS_REQUEST_URL) in the onCreate method, I got this:
As the pics shown after execute the AsyncTask the ArrayList is empty. But inside the AsyncTask The array was actually updated. (To do a little experiment I create an int haha as 0 and change it to 1 in the Asynctask, but it changed back to 0 afterwards)
How is this happen and how do I supposted to make it right?
public class EarthquakeActivity extends AppCompatActivity {
public static final String LOG_TAG = EarthquakeActivity.class.getName();
ArrayList<Earthquake> earthquak = new ArrayList<Earthquake>();
int haha = 0;
private static final String USGS_REQUEST_URL = "http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&orderby=time&minmag=6&limit=10";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.earthquake_activity);
EarthquakeAsyncTask task = new EarthquakeAsyncTask();
task.execute(USGS_REQUEST_URL);
// Create a fake list of earthquake locations.
// Find a reference to the {#link ListView} in the layout
ListView earthquakeListView = (ListView) findViewById(R.id.list);
// Create a new {#link ArrayAdapter} of earthquakes
EarthquakeAdapter adapter = new EarthquakeAdapter(this, earthquak);
// Set the adapter on the {#link ListView}
// so the list can be populated in the user interface
earthquakeListView.setAdapter(adapter);
//OPEN a web page of a specific when textview is clicked.
earthquakeListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(Intent.ACTION_VIEW, earthquak.get(position).getUrl());
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
});
}
private void updateEarthquakeList(ArrayList<Earthquake> earthquake) {
this.earthquak = earthquake;
haha = 1;
}
private class EarthquakeAsyncTask extends AsyncTask<String, Void, ArrayList<Earthquake>> {
#Override
protected ArrayList<Earthquake> doInBackground(String... urls) {
if (urls.length < 1 || urls[0] == null) {
return null;
}
ArrayList<Earthquake> earthquakes = QueryUtils.fetchEarthquakeData(urls[0]);
return earthquakes;
}
#Override
protected void onPostExecute(ArrayList<Earthquake> earthquakes) {
updateEarthquakeList(earthquakes);
}
}

AsyncTask onPostExecute UI Changes

It is always the little things that stump me for hours.
I have an onPostExecute method from an AsyncTask class that looks like so:
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
showColumnCounts();
dataDownloadCompleteToast();
}
The toast works just fine. However, my showColumnCounts() method refuses to work. It works just fine on the main thread. I use it during onCreate() just not here. I thought the onPostExecute ran on the UI thread?
Here is my showColumnCounts() method if it is relevant.
public void showColumnCounts() {
TextView totalView = (TextView) findViewById(R.id.totalColumn2);
TextView ignoredView = (TextView) findViewById(R.id.ignoredColumn2);
TextView rView = (TextView) findViewById(R.id.rColumn2);
Cursor c = myDB.getEmptyRColumn("");
int count = c.getCount();
if (count == 0) {
c.close();
return;
}
String unread = String.valueOf(count);
String total = getTotalCount();
int tTotal = Integer.parseInt(total);
int r = tTotal - count;
String read = String.valueOf(r);
totalView.setText(total);
ignoredView.setText(unread);
rView.setText(read);
c.close();
}
I've been fiddling with it for a while now assuming the answer should be obvious but I'm calling uncle. Can't figure it.
Edit***** 6/30
I THINK I've found my problem. In my background thread I am using a parse.com method "query.findInBackground" which I assume is starting a third thread? I'm trying to update this to "query.find" and I'm hoping that will fix.
First of all you should move all your TextView declarations inside your onCreate method
if you want to change or perform some UI operation, if you want to perform some non UI based operations while the thread is running then do that in doInBackground() method
You should move …setText(...) lines into
runOnUiThread(new Runnable(){
void run(){
// UI stuff
});
You need to tell where is your async class located and the showColumnCounts() function located.
If they both where in different class then you should create a context to call the function from the async class.
Take this as example and try.
Example:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.main);
new AsyncClass(this);
super.onCreate(savedInstanceState);
}
public void showColumnCounts() {
TextView totalView = (TextView) findViewById(R.id.totalColumn2);
TextView ignoredView = (TextView) findViewById(R.id.ignoredColumn2);
TextView rView = (TextView) findViewById(R.id.rColumn2);
Cursor c = myDB.getEmptyRColumn("");
int count = c.getCount();
if (count == 0) {
c.close();
return;
}
String unread = String.valueOf(count);
String total = getTotalCount();
int tTotal = Integer.parseInt(total);
int r = tTotal - count;
String read = String.valueOf(r);
totalView.setText(total);
ignoredView.setText(unread);
rView.setText(read);
c.close();
}
public class AsyncClass extends AsyncTask<String, Void, Boolean> {
private Activity activity;
public AsyncClass(Activity main_activity) {
this.activity = main_activity;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
activity.showColumnCounts();
dataDownloadCompleteToast();
}
}
}
If you want to do some UI change process in background running operation(ASYNC TASK),you write that codes in UI thread. Example:
YourActivity.this.runOnUiThread(
new Runnable()
void run()
{
//UI changes
showColumnCounts();
});

Attempt to call a method but I get a NullPointerException

I want to create a connection through socket but I'm having trouble with the graphic of my App:
This is my activity:
public class Messaggi2 extends ActionBarActivity{
LinearLayout mLayout;
ScrollView scroll;
EditText scriviMessaggi;
Button invia;
Socket connessione;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messaggi2);
mLayout = (LinearLayout) findViewById(R.id.LinearScrollLayout);
scroll = (ScrollView) findViewById(R.id.scrollView2);
scriviMessaggi = (EditText) findViewById(R.id.Scrivi);
invia = (Button) findViewById(R.id.Invia);
LavoraDietro asd = new LavoraDietro(connessione);
asd.execute();
}
private TextView createNewTextView(String text) {
final LinearLayout.LayoutParams lparams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); final TextView textView = new TextView(this);
textView.setLayoutParams(lparams);
textView.setText(text);
textView.setBackgroundResource(R.drawable.balloon_broadcast_incoming_pressed);
return textView;
}
private TextView createNewTextViewSent(String text) {
final LinearLayout.LayoutParams llparams = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
llparams.gravity = Gravity.RIGHT;
final TextView textViewSent = new TextView(this);
textViewSent.setLayoutParams(llparams);
textViewSent.setText(text);
textViewSent.setBackgroundResource(R.drawable.balloon_outgoing_normal);
return textViewSent;
}
public void AggiungiTextALlayout(String messaggio){
mLayout.addView(createNewTextView(messaggio));
aggiornaScroll();
}
public void AggiungiTextInviatoALlayout(String messaggio){
mLayout.addView(createNewTextViewSent(messaggio));
aggiornaScroll();
}
public void aggiornaScroll(){
scroll.post(new Runnable() {
#Override
public void run() {
scroll.fullScroll(View.FOCUS_DOWN);
}
});
}
}
This is my AsynTask class:
public class LavoraDietro extends AsyncTask<Void, Void, Socket> {
Socket connessione;
boolean vediSeDeviPartire;
Messaggi2 mess = new Messaggi2();
public LavoraDietro(Socket connessione){
this.connessione = connessione;
}
#Override
protected Socket doInBackground(Void... params){
try {
InetAddress local = InetAddress.getByName("192.168.1.71");
connessione = new Socket(local , 7000);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
return null;
}catch(Exception e){
e.printStackTrace();
}
return connessione;
}
#Override
protected void onPostExecute(Socket result) {
super.onPostExecute(result);
if(result != null){
vediSeDeviPartire = true;
mess.AggiungiTextALlayout("Sono connesso al server");
mess.AggiungiTextALlayout("I canali sono aperi..");
}
else{
mess.AggiungiTextALlayout("ERRORE CONNESSIONE AL SERVER ");
}
}
}
Using this code when I start my app the connection is established and then it crashes. What I see on my Logcat is this Exception:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on a null object reference
So I tried to delete the content of my onPostExecute and everything works perfect. So the mistake is to try to call the method AggiungiTextAlLayout on my AsyncTask class.
Can someone help me with this? Can someone suggest me something? I'm new in this field so I know that this is a stupid thing but I need help.
Thanks guys in advance
EDITED WITH THE SOLUTION
Thanks to Ataulm I got the problem and I solved it I changed the costructor of my LavoraDietro class (unfortunatly I can't change the name of variables and classes in English. But next time I ll use english Name of course )
LavoraDietro Class
public class LavoraDietro extends AsyncTask<Void, Void, Socket> {
Socket connessione;
boolean vediSeDeviPartire;
Messaggi2 action;
public LavoraDietro(Socket connessione, Messaggi2 action){
this.connessione = connessione;
this.action = action;
}
#Override
protected Socket doInBackground(Void... params){
try {
InetAddress local = InetAddress.getByName("192.168.1.71");
connessione = new Socket(local , 7000);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
return null;
}catch(Exception e){
e.printStackTrace();
}
return connessione;
}
#Override
protected void onPostExecute(Socket result) {
super.onPostExecute(result);
if(result != null){
vediSeDeviPartire = true;
action.AggiungiTextALlayout("Sono connesso al server");
action.AggiungiTextALlayout("I canali sono aperi..");
}
else{
action.AggiungiTextALlayout("ERRORE CONNESSIONE AL SERVER ");
}
}
}
And in the Messaggi2 class I changed the call of the constructor in this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.messaggi2);
mLayout = (LinearLayout) findViewById(R.id.LinearScrollLayout);
scroll = (ScrollView) findViewById(R.id.scrollView2);
scriviMessaggi = (EditText) findViewById(R.id.Scrivi);
invia = (Button) findViewById(R.id.Invia);
LavoraDietro asd = new LavoraDietro(connessione, this);
asd.execute();
Your AsyncTask has a reference to mess which is an object of type Messaggi2.
Messaggi2 is a subclass of Activity. You attempt, inside your AsyncTask, to create a new instance of that class.
The Android system has no awareness of this object; it has called none of the life cycle methods, such as onCreate() where the activity's layout would typically be inflated. This means that none of your views are inflated nor even initialised.
When you call mess.AggiungiTextALlayout("Sono connesso al server");, mLayout is null.
The NPE you see may likely not even be this one.
TL;DR: don't instantiate your Activity objects like Java objects; use them as specified within the Android framework.
I suspect you have this Activity starting correctly somewhere. The mistake is that you've not associated that activity with your Asynctask. When you create LavoraDietro, you pass a reference to the Socket in the constructor; you can also pass a reference to your activity, and assign that to the mess field, instead of calling new Messaggi2(). I'm not advocating this structure. But that is the issue at hand.
A few general tips to help you avoid this in future / or spot it faster:
be consistent with your naming; it's difficult to read your code when you're switching between English and Italian.
it's equally difficult for others to read your code if you don't maintain follow Java conventions with class/method names.
When you're extending Activity, it's typical to append the word "Activity" after your class; in this example new Messaggi2Activity() would have been easier to spot.
Where you're able, pass a Class's dependencies as parameters in the constructor; don't rely on constructing these dependencies yourself inside that class. Once you do this, you can begin to draw lines around what your class is responsible for; the less it's responsible for, the harder it is for your class to mess up.
That problem is for put a variable with no "data", verify your variables

Android Sliding Menu - user name and image in it. How can I load them once and forever

I have a sliding menu and an action bar in my Android app.
At the top of the sliding menu there is a user name and a user picture
If I set them once, they are lost when I close and open the menu again.
So every time its opened Im calling a user details downloader class and Im setting the name and the avatar again, which is very irritating.
How can I set them once and dont bother with this until the app is closed, no matter whether the sliding menu is opened or closed?
public class AsdActionBarAndSlidingMenu extends AsdActionBar implements IOnUserDetailsAndStatsReceivedListener{
private TextView tvSlidingMenuUserName;
private Typeface font2;
private UserDetailsAndStatsDownloader mUserDetailsDownloader;
private String userName;
private ImageView ivSlidingMenuUserAvatar;
private String avatarPath;
private Bitmap ivSlidingMenuUserBitmap;
private static final String APP_SHARED_PREFS = "asdasd_prefs";
SharedPreferences sharedPrefs;
public Editor editor;
protected int currentlyLoggedInUser;
protected String currentlyLoggedInUserString;
public AsdActionBarAndSlidingMenu(int titleRes) {
super(R.string.app_name);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setBehindContentView(R.layout.menu_frame);
sharedPrefs = getApplicationContext().getSharedPreferences(APP_SHARED_PREFS, Context.MODE_PRIVATE);
currentlyLoggedInUser = sharedPrefs.getInt("currentLoggedInUserId", 0);
currentlyLoggedInUserString = Integer.toString(currentlyLoggedInUser);
tvSlidingMenuUserName = (TextView) findViewById(R.id.tvSlidingMenuUserName);
tvSlidingMenuUserName.setTypeface(font2);
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.menu_frame, new AsdSlidingMenuListFragment()).commit();
getSlidingMenu().setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);
setSlidingActionBarEnabled(true);
getSlidingMenu().setOnOpenedListener(new OnOpenedListener() {
#Override
public void onOpened() {
mUserDetailsDownloader = new UserDetailsAndStatsDownloader(currentlyLoggedInUserString, AsdActionBarAndSlidingMenu.this, AsdActionBarAndSlidingMenu.this);
mUserDetailsDownloader.downloadUserDetailsAndStats();
}
});
}
#Override
public void onUserDetailsAndStatsReceivedListener(UserDetailsAndStats userDetailsAndStats) {
userName = userDetailsAndStats.getUserName();
tvSlidingMenuUserName = (TextView) findViewById(R.id.tvSlidingMenuUserName);
tvSlidingMenuUserName.setText(userName);
avatarPath = userDetailsAndStats.getUserAvatar();
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
ivSlidingMenuUserAvatar = (ImageView) findViewById(R.id.ivSlidingMenuUserAvatar);
ivSlidingMenuUserAvatar.setImageBitmap(ivSlidingMenuUserBitmap);
}
}
But, what gets unset is the BitMap, or the Views (ivSlidingMenuUserAvatar and tvSlidingMenuUserName)?
I dont know how you created UserDetailsAndStatsDownloader, but probably onUserDetailsAndStatsReceivedListener is called in a diferent thread. That could cause that when that thread is not running, and those views are unused, you can lose them. But im not sure.
Anyways, try to inflating the views in you onCreate, and also retrieving the data after that
public void onCreate(Bundle savedInstanceState) {
...
tvSlidingMenuUserName = (TextView) findViewById(R.id.tvSlidingMenuUserName);
ivSlidingMenuUserAvatar = (ImageView) findViewById(R.id.ivSlidingMenuUserAvatar);
mUserDetailsDownloader = new UserDetailsAndStatsDownloader(currentlyLoggedInUserString, AsdActionBarAndSlidingMenu.this, AsdActionBarAndSlidingMenu.this);
mUserDetailsDownloader.downloadUserDetailsAndStats();
}
and let the listener just like this
#Override
public void onUserDetailsAndStatsReceivedListener(UserDetailsAndStats userDetailsAndStats){
userName = userDetailsAndStats.getUserName();
tvSlidingMenuUserName.setText(userName);
avatarPath = userDetailsAndStats.getUserAvatar();
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
ivSlidingMenuUserAvatar.setImageBitmap(ivSlidingMenuUserBitmap);
}
then, remove getSlidingMenu().setOnOpenedListener(...) and lets see what happend.
Besides, you should use any cache method for your downloads, so even if you need to download again file, if you have already done, no network operation is involved. For example you can do it like is shown in android-imagedownloader that is a really easy example.
/*
* Cache-related fields and methods.
*
* We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the
* Garbage Collector.
*/
private static final int HARD_CACHE_CAPACITY = 10;
private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds
// Hard cache, with a fixed maximum capacity and a life duration
private final HashMap<String, Bitmap> sHardBitmapCache =
new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY / 2, 0.75f, true) {
#Override
protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) {
if (size() > HARD_CACHE_CAPACITY) {
// Entries push-out of hard reference cache are transferred to soft reference cache
sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue()));
return true;
} else
return false;
}
};
// Soft cache for bitmaps kicked out of hard cache
private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache =
new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY / 2);
private final Handler purgeHandler = new Handler();
private final Runnable purger = new Runnable() {
public void run() {
clearCache();
}
};
/**
* Adds this bitmap to the cache.
* #param bitmap The newly downloaded bitmap.
*/
private void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (sHardBitmapCache) {
sHardBitmapCache.put(url, bitmap);
}
}
}
/**
* #param url The URL of the image that will be retrieved from the cache.
* #return The cached bitmap or null if it was not found.
*/
private Bitmap getBitmapFromCache(String url) {
// First try the hard reference cache
synchronized (sHardBitmapCache) {
final Bitmap bitmap = sHardBitmapCache.get(url);
if (bitmap != null) {
// Bitmap found in hard cache
// Move element to first position, so that it is removed last
sHardBitmapCache.remove(url);
sHardBitmapCache.put(url, bitmap);
return bitmap;
}
}
// Then try the soft reference cache
SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url);
if (bitmapReference != null) {
final Bitmap bitmap = bitmapReference.get();
if (bitmap != null) {
// Bitmap found in soft cache
return bitmap;
} else {
// Soft reference has been Garbage Collected
sSoftBitmapCache.remove(url);
}
}
return null;
}
/**
* Clears the image cache used internally to improve performance. Note that for memory
* efficiency reasons, the cache will automatically be cleared after a certain inactivity delay.
*/
public void clearCache() {
sHardBitmapCache.clear();
sSoftBitmapCache.clear();
}
If we have the code of the rest of the classes involved (just the ones you writed) the help could be much more accurate.
This line of code is responsible for loading the bitmap from the avatarPath, right?
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
If you want to do it once and forever, you should only do this decoding once and store the value of it elsewhere in your code. You've already stored the value in a field, so you shouldn't need to keep decoding it from the file.
Adding a simple if (ivSlidingMenuUserBitmap != null) before that line should prevent that. Like so:
avatarPath = userDetailsAndStats.getUserAvatar();
if (ivSlidingMenuUserBitmap != null)
ivSlidingMenuUserBitmap = BitmapFactory.decodeFile(avatarPath);
ivSlidingMenuUserAvatar = (ImageView) findViewById(R.id.ivSlidingMenuUserAvatar);
ivSlidingMenuUserAvatar.setImageBitmap(ivSlidingMenuUserBitmap);

Categories

Resources