-2

Sometimes I call Snackbar.make() would raise an NoClassDefFoundErrorexception, I'm going to catch it and then show a Toast instead, but my below codes can't catch this exception, how can I do it?

public static void show(View v, CharSequence snapbarString, CharSequence snapbarTitle) {
    try {
        Snackbar.make(v, snapbarString, Snackbar.LENGTH_LONG)
                .setAction(snapbarTitle, new View.OnClickListener()     {
                    @Override
                    public void onClick(View v) {
                        // no implementation required
                    }
                }).show();
    } catch (Exception e) {
        Toast.makeText(v.getContext(), snapbarString, Toast.LENGTH_LONG).show();
    }
}
holi-java
  • 29,655
  • 7
  • 72
  • 83
sunjinbo
  • 2,137
  • 4
  • 22
  • 45

1 Answers1

2

How to catch a NoClassDefFoundError?

NoClassDefFoundError is an Error not an Exception. if you really want to catch it, you need catch it as an Error not an Exception.

 try {
    Snackbar.make(v, snapbarString, Snackbar.LENGTH_LONG)
            .setAction(snapbarTitle, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // no implementation required
                }
            }).show();
} catch (NoClassDefFoundError e) {
    Toast.makeText(v.getContext(), snapbarString, Toast.LENGTH_LONG).show();
}

How to use an optional case if library is not present?

if you want use local toast as an optional one, the advantage of isSnackbarPresent is that only checking the library whether present only once. for example:

private static final boolean isSnackbarPresent; 

static{                                                             
      isSnackbarPresent = classPresent("${package}.Snackbar");
}

private static boolean classPresent(String className) {
    try {
        ClassLoader.getSystemClassLoader().loadClass(className);
        return true;
    } catch (ClassNotFoundException ex) {
        return false;
    }
}

public static void show(View v
                       , CharSequence snapbarString
                       , CharSequence snapbarTitle) {

  if(isSnackbarPresent){
    Snackbar.make(v, snapbarString, Snackbar.LENGTH_LONG)
            .setAction(snapbarTitle, new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    // no implementation required
                }
            }).show();
  }else{
    Toast.makeText(v.getContext(), snapbarString, Toast.LENGTH_LONG).show();
  }
}

Advanced to use an optional case

if you don't want to checking isSnackbarPresent every the show method called you can do like this:

public static void show(View v
                       , CharSequence snapbarString
                       , CharSequence snapbarTitle) {
    toaster.show(v, snapbarString, snapbarTitle);
}

private static final Toaster toaster = classPresent("${package}.Snackbar") 
                                     ? snackbar() 
                                     : locally();

private static Toaster locally() {
    return (view, message, title) -> Toast.makeText(view.getContext()
                                                   , message
                                                   , Toast.LENGTH_LONG).show(); 
}

private static Toaster snackbar() {
    View.OnClickListener NOTHING = (view)->{};
    return (view, message, title) -> {
        Snackbar.make(view, message, Snackbar.LENGTH_LONG)
                .setAction(title, NOTHING).show();
    };
}

interface Toaster {
    void show(View view, CharSequence message, CharSequence title);
}

private static boolean classPresent(String className) {
    try {
        ClassLoader.getSystemClassLoader().loadClass(className);
        return true;
    } catch (ClassNotFoundException ex) {
        return false;
    }
}

Enhancement

and this pattern is so flexible that can choosing one available Toaster from all of Toasters, for examples:

interface ToasterProvider{
   /**
    *  @see classPresent(String)
    *  @return return true if the library is presented in classpath
    */
   boolean isPresent();
   /**
    * @throws NoSuchElementException thrown if toaster library is not present
    */
   Toaster toaster() throws NoSuchElementException;
}

List<ToasterProvider>  providers = ...;

Toaster toaster = providers.stream()
                           .filter(ToasterProvider::isPresent)
                           .findFirst().map(ToasterProvider::toaster)
                           .orElse(locally());
holi-java
  • 29,655
  • 7
  • 72
  • 83