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

391 Views Asked by At

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);

    }

}
2

There are 2 best solutions below

0
On BEST ANSWER

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.

2
On

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);