Butter Knife is an annotation processing library that helps streamline boilerplate Java code in Android views. At compile time, Butter Knife annotations are processed to generate the relevant UI code to make views function properly.
When obfuscating applications that use Butter Knife, there are specific configuration patterns you should follow. The code that connects generated classes to their views uses runtime reflection under the hood. Because of this, the original name of the View is required.
When configuring DashO we need to:
1. Exclude from Renaming any activity, view or fragment containing the Butterknife.bind() directive.
2. Exclude from Renaming any generated class named *_ViewBinding.
The DashO wizard helps us with proper configuration by identifying the MainActivity and any fragment as a non-renameable entry point. This means that you do not need to explicitly exclude the View or Fragment from Renaming. You only need to exclude the generated MainActivity_ViewBinding or Fragment_ViewBinding class. However, please note that it is also possible to have a class containing the Butterknife.bind() directive which is not identified as an entry point. If this is the case, we need to exclude this class in addition to its ViewBinding class.
Let’s look at an example.
I have a MainActivity with a TextView which has been decorated with the ButterKnife @BindView annotation.
I have a fragment with a button with a ButterKnife @OnClick that shows a toast.
After going through the DashO Wizard, the MainActivity and BlankFragment have been identified as non-renameable entry points:
I then create a Rename pattern Rule to ensure that any _ViewBinding doesn’t get renamed:
Based on this configuration, after obfuscation, my app loads and runs properly:
If I didn’t explicitly specify the *_ViewBinding Rename exclusion, I would see an exception in LogCat: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.the.basicexample/com.example.the.basicexample.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Please note: this example uses a newer version of Butter Knife. In versions prior to 8, instead of _ViewBinding, we would add **$$ViewBinder.
In conclusion, applications using Butter Knife can be obfuscated with a just few basic configurations to ensure that generated code is handled properly.
You can download the full example here.
If you have any feedback on this topic, or other topics you would like us to discuss in the Support Corner, please feel free to contact us at support@preemptive.com.