Skip to content

Commit 85658ed

Browse files
committed
Release 1.0.0
1 parent f9e79d1 commit 85658ed

File tree

23 files changed

+744
-154
lines changed

23 files changed

+744
-154
lines changed

CHANGELOG.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
1-
## 0.0.1
1+
## 1.0.0
22

3-
* Flutter A/B Testing 插件,支持 Android 和 iOS 中使用
3+
* 单个接口和全局设置,支持自定义属性请求分流
4+
* 支持自定义主体请求分流
5+
6+
## 0.0.4
47

8+
* 修复 Android MethodChannel 偶现回调多次异常
9+
10+
## 0.0.3
11+
12+
* SDK 依赖由 jcenter 调整为 mavenCentral
513

614
## 0.0.2
715

816
* 新增初始化接口
917

10-
## 0.0.3
18+
## 0.0.1
1119

12-
* SDK 依赖由 jcenter 调整为 mavenCentral
20+
* Flutter A/B Testing 插件,支持 Android 和 iOS 中使用

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
```yml
2727
dependencies:
2828
# 添加神策 flutter plugin
29-
abtesting_sdk_flutter: ^0.0.3
29+
abtesting_sdk_flutter: ^1.0.0
3030
```
3131
3232
执行 flutter packages get 命令安装插件

android/build.gradle

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,17 @@ rootProject.allprojects {
2020
}
2121

2222
apply plugin: 'com.android.library'
23+
import com.android.Version
24+
def agpVersion = Version.ANDROID_GRADLE_PLUGIN_VERSION
25+
def needNamespace = agpVersion.tokenize('.')[0].toInteger() >= 7 &&
26+
agpVersion.tokenize('.')[1].toInteger() >= 3
27+
println ">>> Current AGP Version: $agpVersion"
2328

2429
android {
2530
compileSdkVersion 30
31+
if (needNamespace) {
32+
namespace 'com.sensorsdata.analytics.abtesting.abtesting_sdk_flutter'
33+
}
2634

2735
defaultConfig {
2836
minSdkVersion 16

android/src/main/java/com/sensorsdata/analytics/abtesting/abtesting_sdk_flutter/AbtestingSdkFlutterPlugin.java

Lines changed: 157 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.sensorsdata.abtest.OnABTestReceivedData;
1010
import com.sensorsdata.abtest.SensorsABTest;
1111
import com.sensorsdata.abtest.SensorsABTestConfigOptions;
12+
import com.sensorsdata.abtest.SensorsABTestExperiment;
1213

1314
import org.json.JSONObject;
1415

@@ -27,9 +28,11 @@
2728

2829
/** AbtestingSdkFlutterPlugin */
2930
public class AbtestingSdkFlutterPlugin implements FlutterPlugin, MethodCallHandler {
30-
/// The MethodChannel that will the communication between Flutter and native Android
31+
/// The MethodChannel that will the communication between Flutter and native
32+
/// Android
3133
///
32-
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
34+
/// This local reference serves to register the plugin with the Flutter Engine
35+
/// and unregister it
3336
/// when the Flutter Engine is detached from the Activity
3437
private MethodChannel channel;
3538
private Context applicationContext;
@@ -61,15 +64,38 @@ public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
6164
case "fastFetchABTest":
6265
fastFetchABTest(call, result);
6366
break;
67+
case "setCustomIDs":
68+
setCustomIDs(call, result);
69+
break;
70+
case "setCustomProperties":
71+
setCustomProperties(call, result);
72+
break;
73+
case "fastFetchABTestWithExperiment":
74+
fastFetchABTestWithExperiment(call, result);
75+
break;
76+
case "asyncFetchABTestWithExperiment":
77+
asyncFetchABTestWithExperiment(call, result);
78+
break;
6479
default:
6580
result.notImplemented();
6681
break;
6782
}
6883
}
6984

7085
private void startWithConfigOptions(MethodCall call, Result result) {
71-
String url = (String) call.arguments;
72-
SensorsABTestConfigOptions abTestConfigOptions = new SensorsABTestConfigOptions(url);
86+
Map<String, Object> args = call.arguments();
87+
88+
String urlString = (String) args.get("urlString");
89+
Object customObj = args.get("customProperties");
90+
Map<String, Object> customProperties = null;
91+
if (customObj instanceof Map) {
92+
customProperties = (Map<String, Object>) customObj;
93+
}
94+
95+
SensorsABTestConfigOptions abTestConfigOptions = new SensorsABTestConfigOptions(urlString);
96+
if (customProperties != null) {
97+
abTestConfigOptions.setCustomProperties(assertMapToJSON(customProperties));
98+
}
7399
SensorsABTest.startWithConfigOptions(applicationContext, abTestConfigOptions);
74100
result.success(null);
75101
}
@@ -127,26 +153,139 @@ public void run() {
127153
});
128154
}
129155

130-
private void checkResult(Object abResult, Result result) {
131-
if (abResult instanceof JSONObject) {
132-
JSONObject jsonObject = (JSONObject) abResult;
133-
result.success(jsonObject.toString());
156+
private void fastFetchABTestWithExperiment(MethodCall call, final Result result) {
157+
List list = (List) call.arguments;
158+
if (list == null || list.size() == 0) {
159+
result.success(null);
134160
return;
135161
}
136-
result.success(abResult);
162+
163+
Map<String, Object> experimenMap = (Map<String, Object>) list.get(0);
164+
// 构建最终的 Experiment
165+
SensorsABTestExperiment<Object> experiment = buildExperimentFromMap(experimenMap);
166+
167+
SensorsABTest.shareInstance().fastFetchABTest(experiment, new OnABTestReceivedData() {
168+
@Override
169+
public void onResult(final Object value) {
170+
uiThreadHandler.post(new Runnable() {
171+
@Override
172+
public void run() {
173+
checkResult(value, result);
174+
}
175+
});
176+
}
177+
});
137178
}
138179

139-
private static Map<String, Object> toMap(JSONObject obj) {
140-
if (obj == null) {
141-
return null;
180+
private void asyncFetchABTestWithExperiment(MethodCall call, final Result result) {
181+
List list = (List) call.arguments;
182+
if (list == null || list.size() == 0) {
183+
result.success(null);
184+
return;
142185
}
143-
Map<String, Object> data = new HashMap<>();
144-
Iterator<String> it = obj.keys();
145-
while (it.hasNext()) {
146-
String key = it.next();
147-
data.put(key, obj.opt(key));
186+
187+
Map<String, Object> experimenMap = (Map<String, Object>) list.get(0);
188+
189+
// 构建最终的 Experiment
190+
SensorsABTestExperiment<Object> experiment = buildExperimentFromMap(experimenMap);
191+
192+
SensorsABTest.shareInstance().asyncFetchABTest(experiment, new OnABTestReceivedData() {
193+
@Override
194+
public void onResult(final Object value) {
195+
uiThreadHandler.post(new Runnable() {
196+
@Override
197+
public void run() {
198+
checkResult(value, result);
199+
}
200+
});
201+
}
202+
});
203+
}
204+
205+
private void setCustomIDs(MethodCall call, Result result) {
206+
Map<String, String> customIDs = call.arguments();
207+
SensorsABTest.shareInstance().setCustomIDs(customIDs);
208+
result.success(null);
209+
}
210+
211+
private void setCustomProperties(MethodCall call, Result result) {
212+
Map<String, Object> customProperties = call.arguments();
213+
SensorsABTest.shareInstance().setCustomProperties(assertMapToJSON(customProperties));
214+
result.success(null);
215+
}
216+
217+
// 从 Map 构建 Experiment 对象
218+
private SensorsABTestExperiment<Object> buildExperimentFromMap(Map<String, Object> experimentDic) {
219+
String paramName = (String) experimentDic.get("paramName");
220+
Object defaultValue = experimentDic.get("defaultValue");
221+
if (defaultValue instanceof Map) {
222+
defaultValue = new JSONObject((Map) defaultValue);
223+
}
224+
225+
SensorsABTestExperiment.ExperimentBuilder<Object> builder = SensorsABTestExperiment.newBuilder(paramName,
226+
defaultValue);
227+
Object timeoutObj = experimentDic.get("timeoutInterval");
228+
if (timeoutObj instanceof Number) {
229+
int timeout = ((Number) timeoutObj).intValue(); // 毫秒
230+
builder.setTimeoutMillSeconds(timeout);
231+
}
232+
233+
Map<String, Object> properties = (Map<String, Object>) experimentDic.get("properties");
234+
if (properties != null) {
235+
for (String key : properties.keySet()) {
236+
Object value = properties.get(key);
237+
238+
if (value == null) {
239+
builder.addProperty(key, (String) null);
240+
} else if (value instanceof String) {
241+
builder.addProperty(key, (String) value);
242+
} else if (value instanceof Boolean) {
243+
builder.addProperty(key, (Boolean) value);
244+
} else if (value instanceof Integer) {
245+
builder.addProperty(key, (Integer) value);
246+
} else if (value instanceof Long) {
247+
builder.addProperty(key, (Long) value);
248+
} else if (value instanceof Double) {
249+
builder.addProperty(key, (Double) value);
250+
} else if (value instanceof Float) {
251+
builder.addProperty(key, ((Float) value).doubleValue());
252+
} else if (value instanceof List) {
253+
try {
254+
builder.addProperty(key, (List<String>) value);
255+
} catch (Exception e) {
256+
}
257+
} else {
258+
// 兜底处理,直接转字符串
259+
builder.addProperty(key, String.valueOf(value));
260+
}
261+
}
262+
}
263+
264+
// 构建最终的 Experiment
265+
SensorsABTestExperiment<Object> experiment = builder.create();
266+
return experiment;
267+
}
268+
269+
private void checkResult(Object abResult, Result result) {
270+
try {
271+
if (abResult instanceof JSONObject) {
272+
JSONObject jsonObject = (JSONObject) abResult;
273+
result.success(jsonObject.toString());
274+
return;
275+
}
276+
result.success(abResult);
277+
} catch (java.lang.Exception e) {
278+
// ignored
279+
}
280+
}
281+
282+
private static JSONObject assertMapToJSON(Map<String, Object> map) {
283+
if (map != null) {
284+
return new JSONObject(map);
285+
} else {
286+
// SALog.d(TAG, "传入的属性为空");
287+
return null;
148288
}
149-
return data;
150289
}
151290

152291
@Override

example/android/app/build.gradle

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ apply plugin: 'kotlin-android'
2626
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
2727

2828
android {
29-
compileSdkVersion flutter.compileSdkVersion
29+
compileSdkVersion 34
3030
ndkVersion flutter.ndkVersion
31-
31+
namespace 'com.sensorsdata.analytics.abtesting.example'
3232
compileOptions {
3333
sourceCompatibility JavaVersion.VERSION_1_8
3434
targetCompatibility JavaVersion.VERSION_1_8
@@ -47,17 +47,17 @@ android {
4747
applicationId "com.sensorsdata.analytics.abtesting.example"
4848
// You can update the following values to match your application needs.
4949
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
50-
minSdkVersion flutter.minSdkVersion
50+
minSdkVersion 21
5151
targetSdkVersion flutter.targetSdkVersion
5252
versionCode flutterVersionCode.toInteger()
5353
versionName flutterVersionName
5454
}
5555

5656
buildTypes {
5757
release {
58-
// TODO: Add your own signing config for the release build.
59-
// Signing with the debug keys for now, so `flutter run --release` works.
60-
signingConfig signingConfigs.debug
58+
minifyEnabled false // 禁用 R8
59+
shrinkResources false // 可选
60+
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
6161
}
6262
}
6363
}

example/android/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ buildscript {
66
}
77

88
dependencies {
9-
classpath 'com.android.tools.build:gradle:7.1.2'
9+
classpath 'com.android.tools.build:gradle:8.3.2'
1010
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1111
}
1212
}
@@ -26,6 +26,6 @@ subprojects {
2626
project.evaluationDependsOn(':app')
2727
}
2828

29-
task clean(type: Delete) {
29+
tasks.register("clean", Delete) {
3030
delete rootProject.buildDir
3131
}

example/android/gradle.properties

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1-
org.gradle.jvmargs=-Xmx1536M
21
android.useAndroidX=true
3-
android.enableJetifier=true
2+
# 必须开启,否则构建性能差
3+
org.gradle.jvmargs=-Xmx4096M -Dfile.encoding=UTF-8
4+
5+
# 使用 Android Studio 内置 JDK(Java 21)
6+
org.gradle.java.home=/Applications/Android\ Studio.app/Contents/jbr/Contents/Home
7+
8+
# 减少构建错误的推荐配置
9+
android.useAndroidX=true
10+
android.enableJetifier=true

example/android/gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
33
zipStoreBase=GRADLE_USER_HOME
44
zipStorePath=wrapper/dists
5-
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
5+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-all.zip

example/ios/Flutter/AppFrameworkInfo.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@
2121
<key>CFBundleVersion</key>
2222
<string>1.0</string>
2323
<key>MinimumOSVersion</key>
24-
<string>8.0</string>
24+
<string>12.0</string>
2525
</dict>
2626
</plist>

example/ios/Podfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Uncomment this line to define a global platform for your project
2-
# platform :ios, '9.0'
2+
platform :ios, '12.0'
33

44
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
55
ENV['COCOAPODS_DISABLE_STATS'] = 'true'

0 commit comments

Comments
 (0)