Skip to content

Commit

Permalink
feat: support enable and disable SDK (#46)
Browse files Browse the repository at this point in the history
Co-authored-by: xiaoweii <[email protected]>
  • Loading branch information
zhu-xiaowei and xiaoweii authored Oct 12, 2023
1 parent effe568 commit 3e07b5b
Show file tree
Hide file tree
Showing 11 changed files with 318 additions and 72 deletions.
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ Your `appId` and `endpoint` are already set up in it, here's an explanation of e

**3.Initialize the SDK**

Please Initialize the SDK in the Application `onCreate()` method.
It is recommended that you initialize the SDK in the Application `onCreate()` method. Please note that the initialization code needs to run in the main thread.

```java
import software.aws.solution.clickstream.ClickstreamAnalytics;
Expand Down Expand Up @@ -200,10 +200,26 @@ If you want to use custom DNS for network request, you can create your `CustomOk
#### Send event immediately

```java
import software.aws.solution.clickstream.ClickstreamAnalytics;

// for send event immediately.
ClickstreamAnalytics.flushEvent();
```

#### Disable SDK
You can disable the SDK in the scenario you need. After disabling the SDK, the SDK will not handle the logging and sending of any events. Of course you can enable the SDK when you need to continue logging events.

Please note that the disable and enable code needs to be run in the main thread.
```java
import software.aws.solution.clickstream.ClickstreamAnalytics;

// disable SDK
ClickstreamAnalytics.disable();

// enable SDK
ClickstreamAnalytics.enable();
```

## How to build locally

open an terminal window, at the root project folder to execute:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public final class AWSClickstreamPlugin extends AnalyticsPlugin<Object> {
private AnalyticsClient analyticsClient;
private AutoEventSubmitter autoEventSubmitter;
private ActivityLifecycleManager activityLifecycleManager;
private ClickstreamManager clickstreamManager;
private boolean isEnable = true;

/**
* Constructs a new {@link AWSClickstreamPlugin}.
Expand Down Expand Up @@ -76,15 +78,25 @@ public void identifyUser(@NonNull String userId, @Nullable UserProfile profile)
}

@Override
public void disable() {
autoEventSubmitter.stop();
activityLifecycleManager.stopLifecycleTracking(context);
public synchronized void disable() {
if (isEnable) {
autoEventSubmitter.stop();
activityLifecycleManager.stopLifecycleTracking(context, ProcessLifecycleOwner.get().getLifecycle());
clickstreamManager.disableTrackAppException();
isEnable = false;
LOG.info("Clickstream SDK disabled");
}
}

@Override
public void enable() {
autoEventSubmitter.start();
activityLifecycleManager.startLifecycleTracking(context, ProcessLifecycleOwner.get().getLifecycle());
public synchronized void enable() {
if (!isEnable) {
autoEventSubmitter.start();
activityLifecycleManager.startLifecycleTracking(context, ProcessLifecycleOwner.get().getLifecycle());
clickstreamManager.enableTrackAppException();
isEnable = true;
LOG.info("Clickstream SDK enabled");
}
}

@Override
Expand Down Expand Up @@ -182,7 +194,7 @@ public void configure(
}

AWSClickstreamPluginConfiguration clickstreamPluginConfiguration = configurationBuilder.build();
ClickstreamManager clickstreamManager = ClickstreamManagerFactory.create(
clickstreamManager = ClickstreamManagerFactory.create(
context,
clickstreamPluginConfiguration
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,10 @@ void startLifecycleTracking(final Context context, Lifecycle lifecycle) {
}
}

void stopLifecycleTracking(final Context context) {
void stopLifecycleTracking(final Context context, Lifecycle lifecycle) {
if (context instanceof Application) {
((Application) context).unregisterActivityLifecycleCallbacks(this);
lifecycle.removeObserver(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,18 @@
import com.amplifyframework.AmplifyException;
import com.amplifyframework.core.Amplify;

import com.amazonaws.logging.Log;
import com.amazonaws.logging.LogFactory;
import software.aws.solution.clickstream.client.AnalyticsClient;
import software.aws.solution.clickstream.client.ClickstreamConfiguration;
import software.aws.solution.clickstream.client.Event;
import software.aws.solution.clickstream.client.util.ThreadUtil;

/**
* This is the top-level customer-facing interface to The ClickstreamAnalytics.
*/
public final class ClickstreamAnalytics {
private static final Log LOG = LogFactory.getLog(ClickstreamAnalytics.class);

private ClickstreamAnalytics() {
throw new UnsupportedOperationException("No instances allowed.");
Expand All @@ -41,6 +45,9 @@ private ClickstreamAnalytics() {
* @throws AmplifyException Exception of init.
*/
public static void init(@NonNull Context context) throws AmplifyException {
if (ThreadUtil.notInMainThread()) {
throw new AmplifyException("Clickstream SDK initialization failed", "Please initialize in the main thread");
}
Amplify.addPlugin(new AWSClickstreamPlugin(context));
Amplify.configure(context);
}
Expand Down Expand Up @@ -71,7 +78,7 @@ public static void flushEvents() {
}

/**
* add user clickstreamAttribute.
* Add user clickstreamAttribute.
*
* @param clickstreamAttribute the global clickstreamAttribute.
*/
Expand All @@ -80,7 +87,7 @@ public static void addGlobalAttributes(ClickstreamAttribute clickstreamAttribute
}

/**
* delete global attributes.
* Delete global attributes.
*
* @param attributeName the attribute name to delete.
*/
Expand All @@ -89,7 +96,7 @@ public static void deleteGlobalAttributes(@NonNull String... attributeName) {
}

/**
* add user attributes.
* Add user attributes.
*
* @param userProfile user
*/
Expand All @@ -98,7 +105,7 @@ public static void addUserAttributes(ClickstreamUserAttribute userProfile) {
}

/**
* set user id.
* Set user id.
*
* @param userId user
*/
Expand All @@ -111,7 +118,29 @@ public static void setUserId(String userId) {
}

/**
* get clickstream configuration
* Enable clickstream SDK.
*/
public static void enable() {
if (ThreadUtil.notInMainThread()) {
LOG.error("Clickstream SDK enabled failed, please execute in the main thread");
return;
}
Amplify.Analytics.enable();
}

/**
* Disable clickstream SDK.
*/
public static void disable() {
if (ThreadUtil.notInMainThread()) {
LOG.error("Clickstream SDK disabled failed, please execute in the main thread");
return;
}
Amplify.Analytics.disable();
}

/**
* Get clickstream configuration
* please config it after initialize.
*
* @return ClickstreamConfiguration configurationF
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,35 +31,41 @@ public final class ClickstreamExceptionHandler implements Thread.UncaughtExcepti
private static final Log LOG = LogFactory.getLog(ClickstreamExceptionHandler.class);
private static ClickstreamExceptionHandler handlerInstance;
private static final int SLEEP_TIMEOUT_MS = 500;
private final Thread.UncaughtExceptionHandler defaultExceptionHandler;
private ClickstreamContext clickstreamContext;
private Thread.UncaughtExceptionHandler defaultExceptionHandler;
private final ClickstreamContext clickstreamContext;

private ClickstreamExceptionHandler() {
private ClickstreamExceptionHandler(ClickstreamContext context) {
this.clickstreamContext = context;
}

/**
* start listening the exception events.
*/
public void startTrackException() {
defaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
}

/**
* stop listening the exception events.
*/
public void stopTackException() {
Thread.setDefaultUncaughtExceptionHandler(null);
}

/**
* init static method for ClickstreamExceptionHandler.
*
* @param context the clickstream context for initial the ClickstreamExceptionHandler
* @return ClickstreamExceptionHandler the instance.
*/
public static synchronized ClickstreamExceptionHandler init() {
public static synchronized ClickstreamExceptionHandler init(ClickstreamContext context) {
if (handlerInstance == null) {
handlerInstance = new ClickstreamExceptionHandler();
handlerInstance = new ClickstreamExceptionHandler(context);
}
return handlerInstance;
}

/**
* setter for clickstreamContext.
*
* @param context ClickstreamContext
*/
public void setClickstreamContext(ClickstreamContext context) {
this.clickstreamContext = context;
}

/**
* fetch uncaught exception and record crash event.
*
Expand All @@ -69,32 +75,35 @@ public void setClickstreamContext(ClickstreamContext context) {
@Override
public void uncaughtException(@NonNull Thread thread, @NonNull Throwable throwable) {
try {
String exceptionMessage = "";
String exceptionStack = "";
try {
if (throwable.getMessage() != null) {
exceptionMessage = throwable.getMessage();
}
final Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
throwable.printStackTrace(printWriter);
Throwable cause = throwable.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
if (clickstreamContext.getClickstreamConfiguration().isTrackAppExceptionEvents()) {
String exceptionMessage = "";
String exceptionStack = "";
try {
if (throwable.getMessage() != null) {
exceptionMessage = throwable.getMessage();
}
final Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
throwable.printStackTrace(printWriter);
Throwable cause = throwable.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
exceptionStack = writer.toString();
} catch (Exception exception) {
LOG.error("exception for get exception stack:", exception);
}
printWriter.close();
exceptionStack = writer.toString();
} catch (Exception exception) {
LOG.error("exception for get exception stack:", exception);
}

final AnalyticsEvent event =
this.clickstreamContext.getAnalyticsClient().createEvent(Event.PresetEvent.APP_EXCEPTION);
event.addInternalAttribute("exception_message", exceptionMessage);
event.addInternalAttribute("exception_stack", exceptionStack);
this.clickstreamContext.getAnalyticsClient().recordEvent(event);
final AnalyticsEvent event =
this.clickstreamContext.getAnalyticsClient().createEvent(Event.PresetEvent.APP_EXCEPTION);
event.addInternalAttribute("exception_message", exceptionMessage);
event.addInternalAttribute("exception_stack", exceptionStack);
this.clickstreamContext.getAnalyticsClient().recordEvent(event);
}

this.clickstreamContext.getAnalyticsClient().submitEvents();
try {
Thread.sleep(SLEEP_TIMEOUT_MS);
} catch (InterruptedException exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class ClickstreamManager {
private final AnalyticsClient analyticsClient;
private final SessionClient sessionClient;
private final AutoRecordEventClient autoRecordEventClient;
private ClickstreamExceptionHandler exceptionHandler;

/**
* Constructor.
Expand All @@ -57,6 +58,7 @@ public ClickstreamManager(@NonNull final ClickstreamConfiguration config) {
this.autoRecordEventClient = new AutoRecordEventClient(this.clickstreamContext);
this.clickstreamContext.setSessionClient(this.sessionClient);
if (config.isTrackAppExceptionEvents()) {
exceptionHandler = ClickstreamExceptionHandler.init(this.clickstreamContext);
enableTrackAppException();
}
LOG.debug(String.format(Locale.US,
Expand All @@ -68,9 +70,22 @@ public ClickstreamManager(@NonNull final ClickstreamConfiguration config) {
}
}

private void enableTrackAppException() {
ClickstreamExceptionHandler handler = ClickstreamExceptionHandler.init();
handler.setClickstreamContext(this.clickstreamContext);
/**
* Enable track app exception.
*/
public void enableTrackAppException() {
if (exceptionHandler != null) {
exceptionHandler.startTrackException();
}
}

/**
* Disable track app exception.
*/
public void disableTrackAppException() {
if (exceptionHandler != null) {
exceptionHandler.stopTackException();
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.aws.solution.clickstream.client.util;

import android.os.Looper;

/**
* Thread utility methods.
*/
public final class ThreadUtil {

/**
* Default constructor.
*/
private ThreadUtil() {
}

/**
* method for return current thread whether not in main thread.
*
* @return boolean value notInMainThread.
*/
public static boolean notInMainThread() {
return Looper.getMainLooper() != Looper.myLooper();
}

}

Loading

0 comments on commit 3e07b5b

Please sign in to comment.