Skip to content

Commit 562ddbc

Browse files
author
Lyor Goldstein
committed
Added capability to provide application context via static method
1 parent 27950e5 commit 562ddbc

File tree

2 files changed

+65
-17
lines changed

2 files changed

+65
-17
lines changed

README.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,43 @@ See [Wiki](https://github.com/tony19/logback-android/wiki) for documentation.
8686
7. Open logcat for your device (via the _Android Monitor_ tab in Android Studio).
8787
8. Click the app menu, and select the menu-option. You should see "hello world" in logcat.
8888
89+
## Providing Android context externally
90+
91+
In order to support various [special properties](https://github.com/tony19/logback-android/wiki#special-properties-for-xml-config) the code requires
92+
access to an Android [Context](https://developer.android.com/reference/android/content/Context) instance. By default, the framework uses a workaround based
93+
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)
94+
this code might not work anymore. Therefore, the framework provides a special API that enables the application to provide an Android context instance that
95+
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
96+
would be in the application's *onCreate* callback:
97+
98+
```java
99+
public class MyApplication extends Application {
100+
@Override
101+
public void onCreate() {
102+
super.onCreate();
103+
104+
// Assuming no logging occurs before this
105+
AndroidContextUtils.setApplicationContext(this);
106+
}
107+
}
108+
```
109+
110+
If an earlier initialization is required, then one might consider overriding `attachBaseContext`, although at this stage the context instance might not be
111+
fully initialized. This might be good enough though if by the time the context is used by the framework it becomes fully initialized.
112+
113+
```java
114+
public class MyApplication extends Application {
115+
116+
@Override
117+
protected void attachBaseContext(Context base) {
118+
super.attachBaseContext(base);
119+
AndroidContextUtils.setApplicationContext(base);
120+
}
121+
}
122+
```
123+
124+
*Note:* despite the fact that the method is called `setApplicationContext` - the user may use *any* `ContextWrapper` component (*Application, Activity, Service*) - the
125+
framework will actually use the ["pure" application context](https://developer.android.com/reference/android/content/Context#getApplicationContext()).
89126

90127
## Download
91128

logback-android/src/main/java/ch/qos/logback/core/android/AndroidContextUtil.java

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package ch.qos.logback.core.android;
1717

1818
import android.annotation.TargetApi;
19-
import android.content.ContextWrapper;
2019
import android.content.pm.PackageInfo;
2120
import android.content.pm.PackageManager;
2221
import android.os.Build;
@@ -25,6 +24,7 @@
2524
import java.io.File;
2625
import java.lang.reflect.InvocationTargetException;
2726
import java.lang.reflect.Method;
27+
import java.util.concurrent.atomic.AtomicReference;
2828

2929
import ch.qos.logback.core.Context;
3030
import ch.qos.logback.core.CoreConstants;
@@ -37,14 +37,14 @@
3737
* @since 1.0.8-1
3838
*/
3939
public class AndroidContextUtil {
40-
private ContextWrapper context;
40+
private final android.content.Context context;
4141

4242
public AndroidContextUtil() {
4343
this(getContext());
4444
}
4545

46-
public AndroidContextUtil(ContextWrapper contextWrapper) {
47-
this.context = contextWrapper;
46+
public AndroidContextUtil(android.content.Context context) {
47+
this.context = (context == null) ? null : context.getApplicationContext();
4848
}
4949

5050
public static boolean containsProperties(String value) {
@@ -71,11 +71,22 @@ public void setupProperties(Context context) {
7171
context.putProperty(CoreConstants.VERSION_NAME_KEY, getVersionName());
7272
}
7373

74-
protected static ContextWrapper getContext() {
74+
private static final AtomicReference<android.content.Context> CONTEXT_HOLDER = new AtomicReference<>();
75+
public static void setApplicationContext(android.content.Context context) {
76+
CONTEXT_HOLDER.set((context == null) ? null : context.getApplicationContext());
77+
}
78+
79+
protected static android.content.Context getContext() {
80+
android.content.Context context = CONTEXT_HOLDER.get();
81+
if (context != null) {
82+
return context.getApplicationContext();
83+
}
84+
7585
try {
7686
Class<?> c = Class.forName("android.app.AppGlobals");
7787
Method method = c.getDeclaredMethod("getInitialApplication");
78-
return (ContextWrapper)method.invoke(c);
88+
context = (android.content.Context) method.invoke(c);
89+
return (context == null) ? null : context.getApplicationContext();
7990
} catch (ClassNotFoundException e) {
8091
//e.printStackTrace();
8192
} catch (NoSuchMethodException e) {
@@ -116,10 +127,10 @@ public String getMountedExternalStorageDirectoryPath() {
116127
*
117128
* @return the absolute path to the external storage directory
118129
*/
119-
@TargetApi(8)
130+
@TargetApi(Build.VERSION_CODES.FROYO)
120131
@SuppressWarnings("deprecation")
121132
public String getExternalStorageDirectoryPath() {
122-
if (Build.VERSION.SDK_INT >= 29) {
133+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
123134
return getExternalFilesDirectoryPath();
124135
} else {
125136
return absPath(Environment.getExternalStorageDirectory());
@@ -134,7 +145,7 @@ public String getExternalStorageDirectoryPath() {
134145
*
135146
* @return the absolute path to the external storage directory
136147
*/
137-
@TargetApi(8)
148+
@TargetApi(Build.VERSION_CODES.FROYO)
138149
public String getExternalFilesDirectoryPath() {
139150
return this.context != null
140151
? absPath(this.context.getExternalFilesDir(null))
@@ -185,9 +196,9 @@ public String getFilesDirectoryPath() {
185196
* @return the absolute path to the files directory
186197
* (example: "/data/data/com.example/nobackup/files")
187198
*/
188-
@TargetApi(21)
199+
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
189200
public String getNoBackupFilesDirectoryPath() {
190-
return Build.VERSION.SDK_INT >= 21 &&
201+
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP &&
191202
this.context != null
192203
? absPath(this.context.getNoBackupFilesDir())
193204
: "";
@@ -201,10 +212,8 @@ public String getNoBackupFilesDirectoryPath() {
201212
* (example: "/data/data/com.example/databases")
202213
*/
203214
public String getDatabaseDirectoryPath() {
204-
return this.context != null
205-
&& this.context.getDatabasePath("x") != null
206-
? this.context.getDatabasePath("x").getParent()
207-
: "";
215+
File dbPath = (this.context == null) ? null : this.context.getDatabasePath("x");
216+
return (dbPath != null) ? dbPath.getParent() : "";
208217
}
209218

210219
public String getDatabasePath(String databaseName) {
@@ -217,7 +226,8 @@ public String getVersionCode() {
217226
String versionCode = "";
218227
if (this.context != null) {
219228
try {
220-
PackageInfo pkgInfo = this.context.getPackageManager().getPackageInfo(getPackageName(), 0);
229+
PackageManager pm = this.context.getPackageManager();
230+
PackageInfo pkgInfo = pm.getPackageInfo(getPackageName(), 0);
221231
versionCode = "" + pkgInfo.versionCode;
222232
} catch (PackageManager.NameNotFoundException e) {
223233
}
@@ -229,7 +239,8 @@ public String getVersionName() {
229239
String versionName = "";
230240
if (this.context != null) {
231241
try {
232-
PackageInfo pkgInfo = this.context.getPackageManager().getPackageInfo(getPackageName(), 0);
242+
PackageManager pm = this.context.getPackageManager();
243+
PackageInfo pkgInfo = pm.getPackageInfo(getPackageName(), 0);
233244
versionName = pkgInfo.versionName;
234245
} catch (PackageManager.NameNotFoundException e) {
235246
}

0 commit comments

Comments
 (0)