diff --git a/README.md b/README.md index 98171f6486..6f42353956 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,43 @@ See [Wiki](https://github.com/tony19/logback-android/wiki) for documentation. 7. Open logcat for your device (via the _Android Monitor_ tab in Android Studio). 8. Click the app menu, and select the menu-option. You should see "hello world" in logcat. +## Providing Android context externally + +In order to support various [special properties](https://github.com/tony19/logback-android/wiki#special-properties-for-xml-config) the code requires +access to an Android [Context](https://developer.android.com/reference/android/content/Context) instance. By default, the framework uses a workaround based +on reflection that seems to work for the time being. However, in view of Google's [restrictions on non-SDK interfaces](https://developer.android.com/guide/app-compatibility/restrictions-non-sdk-interfaces) +this code might not work anymore. Therefore, the framework provides a special API that enables the application to provide an Android context instance that +will be used instead of the workaround. **Note:** the context instance must be provided *before* it is needed by the framework, so the best place for it +would be in the application's *onCreate* callback: + +```java +public class MyApplication extends Application { + @Override + public void onCreate() { + super.onCreate(); + + // Assuming no logging occurs before this + AndroidContextUtils.setApplicationContext(this); + } +} +``` + +If an earlier initialization is required, then one might consider overriding `attachBaseContext`, although at this stage the context instance might not be +fully initialized. This might be good enough though if by the time the context is used by the framework it becomes fully initialized. + +```java +public class MyApplication extends Application { + + @Override + protected void attachBaseContext(Context base) { + super.attachBaseContext(base); + AndroidContextUtils.setApplicationContext(base); + } +} +``` + +*Note:* despite the fact that the method is called `setApplicationContext` - the user may use *any* `ContextWrapper` component (*Application, Activity, Service*) - the +framework will actually use the ["pure" application context](https://developer.android.com/reference/android/content/Context#getApplicationContext()). ## Download diff --git a/logback-android/build.gradle b/logback-android/build.gradle index 55533648c4..a25f3c6e8e 100644 --- a/logback-android/build.gradle +++ b/logback-android/build.gradle @@ -6,8 +6,8 @@ android { buildToolsVersion '30.0.3' compileOptions { - sourceCompatibility JavaVersion.VERSION_1_6 - targetCompatibility JavaVersion.VERSION_1_6 + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 } defaultConfig { diff --git a/logback-android/src/main/java/ch/qos/logback/core/android/AndroidContextUtil.java b/logback-android/src/main/java/ch/qos/logback/core/android/AndroidContextUtil.java index 056456a1b7..535fdee0f7 100644 --- a/logback-android/src/main/java/ch/qos/logback/core/android/AndroidContextUtil.java +++ b/logback-android/src/main/java/ch/qos/logback/core/android/AndroidContextUtil.java @@ -16,7 +16,6 @@ package ch.qos.logback.core.android; import android.annotation.TargetApi; -import android.content.ContextWrapper; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Build; @@ -25,6 +24,7 @@ import java.io.File; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.concurrent.atomic.AtomicReference; import ch.qos.logback.core.Context; import ch.qos.logback.core.CoreConstants; @@ -37,14 +37,16 @@ * @since 1.0.8-1 */ public class AndroidContextUtil { - private ContextWrapper context; + private static final AtomicReference CONTEXT_HOLDER = new AtomicReference<>(); + + private final android.content.Context context; public AndroidContextUtil() { this(getContext()); } - public AndroidContextUtil(ContextWrapper contextWrapper) { - this.context = contextWrapper; + public AndroidContextUtil(android.content.Context context) { + this.context = (context == null) ? null : context.getApplicationContext(); } public static boolean containsProperties(String value) { @@ -71,11 +73,21 @@ public void setupProperties(Context context) { context.putProperty(CoreConstants.VERSION_NAME_KEY, getVersionName()); } - protected static ContextWrapper getContext() { + public static void setApplicationContext(android.content.Context context) { + CONTEXT_HOLDER.set((context == null) ? null : context.getApplicationContext()); + } + + protected static android.content.Context getContext() { + android.content.Context context = CONTEXT_HOLDER.get(); + if (context != null) { + return context.getApplicationContext(); + } + try { Class c = Class.forName("android.app.AppGlobals"); Method method = c.getDeclaredMethod("getInitialApplication"); - return (ContextWrapper)method.invoke(c); + context = (android.content.Context) method.invoke(c); + return (context == null) ? null : context.getApplicationContext(); } catch (ClassNotFoundException e) { //e.printStackTrace(); } catch (NoSuchMethodException e) { @@ -116,10 +128,10 @@ public String getMountedExternalStorageDirectoryPath() { * * @return the absolute path to the external storage directory */ - @TargetApi(8) + @TargetApi(Build.VERSION_CODES.FROYO) @SuppressWarnings("deprecation") public String getExternalStorageDirectoryPath() { - if (Build.VERSION.SDK_INT >= 29) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { return getExternalFilesDirectoryPath(); } else { return absPath(Environment.getExternalStorageDirectory()); @@ -134,7 +146,7 @@ public String getExternalStorageDirectoryPath() { * * @return the absolute path to the external storage directory */ - @TargetApi(8) + @TargetApi(Build.VERSION_CODES.FROYO) public String getExternalFilesDirectoryPath() { return this.context != null ? absPath(this.context.getExternalFilesDir(null)) @@ -185,9 +197,9 @@ public String getFilesDirectoryPath() { * @return the absolute path to the files directory * (example: "/data/data/com.example/nobackup/files") */ - @TargetApi(21) + @TargetApi(Build.VERSION_CODES.LOLLIPOP) public String getNoBackupFilesDirectoryPath() { - return Build.VERSION.SDK_INT >= 21 && + return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && this.context != null ? absPath(this.context.getNoBackupFilesDir()) : ""; @@ -201,10 +213,8 @@ public String getNoBackupFilesDirectoryPath() { * (example: "/data/data/com.example/databases") */ public String getDatabaseDirectoryPath() { - return this.context != null - && this.context.getDatabasePath("x") != null - ? this.context.getDatabasePath("x").getParent() - : ""; + File dbPath = (this.context == null) ? null : this.context.getDatabasePath("x"); + return (dbPath != null) ? dbPath.getParent() : ""; } public String getDatabasePath(String databaseName) { @@ -217,8 +227,12 @@ public String getVersionCode() { String versionCode = ""; if (this.context != null) { try { - PackageInfo pkgInfo = this.context.getPackageManager().getPackageInfo(getPackageName(), 0); - versionCode = "" + pkgInfo.versionCode; + PackageManager pm = this.context.getPackageManager(); + PackageInfo pkgInfo = pm.getPackageInfo(getPackageName(), 0); + versionCode = (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) + ? Long.toString(pkgInfo.getLongVersionCode()) + : Integer.toString(pkgInfo.versionCode) + ; } catch (PackageManager.NameNotFoundException e) { } } @@ -229,7 +243,8 @@ public String getVersionName() { String versionName = ""; if (this.context != null) { try { - PackageInfo pkgInfo = this.context.getPackageManager().getPackageInfo(getPackageName(), 0); + PackageManager pm = this.context.getPackageManager(); + PackageInfo pkgInfo = pm.getPackageInfo(getPackageName(), 0); versionName = pkgInfo.versionName; } catch (PackageManager.NameNotFoundException e) { } @@ -237,7 +252,7 @@ public String getVersionName() { return versionName != null ? versionName : ""; } - private String absPath(File file) { + private static String absPath(File file) { return file != null ? file.getAbsolutePath() : ""; } }