SoFunction
Updated on 2025-04-06

Detailed explanation of some operations and security that can be used as a Log switch in Android

Preface

This article mainly introduces some related content about the operation and security of the Log switch in Android. We will share it for your reference and learning. I won’t say much below, let’s take a look at the detailed introduction together.

Custom constants

It is very common to use Log logs to facilitate code debugging in the development stage. For security reasons, this practice is limited to Debug mode and must be turned off when packaged and released in Release mode. So in our project, there must be a tool class or method to control the use of Log logs, such as:

public class LogUtils {
 
 public static final Boolean DEBUG_MODE = true;
 
 public static void d(String message) {
  if (DEBUG_MODE) {
   ("TAG", message);  
  }
 }
 
}

A common approach is to customize a Boolean constant as a switch to control whether to print a log. However, this approach has one disadvantage, that is, every time the Release package is released, the value of this constant needs to be manually modified to false, and then manually modified to true in the next development stage.

Although it is a very simple manual modification operation, it is also easy to forget. So is there a way to achieve automated management? Of course, there is a answer, use the BuildConfig class.

BuildConfig

Similar to R resource files, BuildConfig is also a class file automatically generated by the Gradle plug-in during the compilation stage. This file contains some constant information that helps developers identify the current build type. Of course, you can also add other auxiliary content to the file through the customization function provided by Gradle. Here we take a look at what the BuildConfig file contains by default:

public final class BuildConfig {
 public static final boolean DEBUG = ("true");
 public static final String APPLICATION_ID = "";
 public static final String BUILD_TYPE = "debug";
 public static final String FLAVOR = "";
 public static final int VERSION_CODE = 1;
 public static final String VERSION_NAME = "1.0";
}

It can be seen that they are all very familiar information. This includes a DEBUG constant whose value can be used to determine the current build type. True in debug mode and false in release mode. So, use It can replace the constants we customized earlier to automatically manage the printing of Log logs:

public static void d(String message) {
 if () {
  ("TAG", message);
 }
}

It looks perfect, but it still has flaws. The generation of the BuildConfig class file is based on the Module, which means that each Module will generate its own file when it is compiled. If your main app module uses the DEBUG value in the BuildConfig file in other dependent modules, you need to pay more attention.

By default, the library's build is always executed in Release mode, so itThe value must be false! This is true even if the main Module is built using Debug mode.

So, is there a way to modify the default build method of Library Module? The answer is yes. Open the corresponding Library file and add a line of configuration code like this:

android {
 // Other contents are omitted here publishNonDefault true
}

That is, it means that the default build method is not used, and other build types BuildConfig class files will be automatically generated during compilation. You can view the generation of BuildConfig files before and after configuring this command in the corresponding Library path. The directory address is:

libraryName/build/generated/source/buildConfig/ + debug/release

Then, when our main Module dependency is introduced, debug and release configurations, we take extras/PullToRefresh as Library as an example, and look at the dependency code:

dependencies {
 releaseCompile project(path: ':extras:PullToRefresh', configuration: 'release')
 debugCompile project(path: ':extras:PullToRefresh', configuration: 'debug')
}

In this way, the aforementioned dependency module problem can be solved. Of course, if your project is relatively simple and just a single Module, this problem does not exist.

However, if there are more dependencies in the project, this method of handling is still a bit troublesome. You need to process each module one by one where you use it. In fact, there is another better solution, which is to use the debuggable property in the application tag in the Manifest manifest file.

ApplicationInfo

There is a one in the application tagandroid:debuggableAttribute, indicating whether the current application can be debugged (it is generally not recommended to manually set this property). This property will also automatically change with the build type. Therefore, using this feature can also determine whether the application is in Debug mode, such as:

public static boolean isDebug(Context context) {
 return (().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
}

In addition to the methods mentioned above, there are actually other ways to control the switches for log printing. For example, using Gradle's flexibility to customize a Boolean variable in a file and dynamically assign values ​​according to the build type can also achieve our goal.

Android custom log switch

Sometimes too much Log will affect the speed, and you need to switch Log as needed. The Android IDE environment does not have this function, at least Eclipse does not. Then we can write a class to encapsulate Log, and control whether Log is valid by calling this class to set the boolean variable.

public class MLog
{
public static final boolean DEBUG = true;//Switch controlpublic static void i(String tag,String msg)
{
if(DEBUG)
{
(tag,msg);
}
public static void e(String tag,String msg)
{
if(DEBUG)
{
(tag,msg);
}
//The same as above for other levels...}

When using it, just call MLog to replace Log.

Safer Log Usage

All of the previous practices are just to make the release package not displaying the Log log, thereby improving security. However, have you ever thought that if apk is decompiled, these Log-related codes can still be not recognized. Others only need to modify and repackage them slightly, and the Log can still be reproduced.

Of course, if constants are used as judgment conditions in LogUtils, according to proguard's optimization rules, the Release package does not include operation codes such as 'conditions' bodies. Regarding this, you can decompile apk yourself and try to see it.

However, our intentions are still exposed where the LogUtils tool class is called. Therefore, although defining a LogUtils class improves the efficiency of using Log, it still cannot solve the problem of Log security. In comparison, we have made so much effort and only slightly raised the barrier to safety.

So, the best way is to not include any log code for debugging in the Release package (and also include calls to this class if LogUtils is used). In other words, you do not use the LogUtils tool package, and add judgment conditions one by one without any trouble wherever you need it: (You can use Live Template to improve efficiency)

if () {
 ("TAG", message);  
}

In this way, when packaging, after opening proguard, the Release package will automatically delete the above code, completely eradicating the security problems caused by Log. For details on this part, you can refer to these two articles:

  • Analysis of Android Apk file decompilation and repackaging process
  • How to print logs safely

Summarize

The above is the entire content of this article. I hope that the content of this article has a certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.