Facebook广告中使用Deferred Deep Link

在运营时,学习使用了App Link和Deep Link,之前也使用了Firebase中的Dynamic Links在用户未安装App时跳转至Google Play安装后再打开仍然能获取到链接内容,带用户到推荐的个性化页面。
后面由于运营需要在Facebook上做推广,需要接入Meta的Deep Link
运营时有3个场景,我们的App,以下简称A

  1. 用户已安装A,点击Facebook广告链接,正常打开A并跳转至推广页面
  2. 用户未安装A,点击Facebook广告链接,打开Google play store,下载安装,并在Google play store中点击打开,正常打开A并跳转至推广页面
  3. 用户未安装A,点击Facebook广告链接,打开Google play store,下载安装,回到桌面点击A,正常打开A并跳转至推广页面
    实现1,只需要实现Meta中的Deep Link
    实现2、3则需要在实现Deep Link的基础外加上Deferred Deep Link

Read More

不动态创建链接情况下在Firebase Dynamic Link获取动态参数

在使用Firebase的Dynamic Links做运营时,需要在链接中带去不同的参数,APP做不同的响应。如,在链接中带商品ID,用户点击不同链接进入不同商品详情页。Firebase中有两种方法可以实现。
1、创建动态链接
创建动态链接-简介
2、通过动态链接参数来实现不创建动态链接APP接收自定义参数目的。缺点就是短链接变成了长链接。
下面看看做实验的demo
这是之前建立的链接
链接详情

Read More

如何找出so依赖来源

问题

上架Google Play时提示引用了有缺陷的OpenSSL版本
缺陷的库

解决思路

  • 找出该so属于哪个库
  • 升级或如何排除该so

找出so属于哪个库

在app下的build.gradle中添加如下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//列出所有包含有so文件的库信息
tasks.whenTaskAdded { task ->
if (task.name.contains("DebugNativeLibs")) {
task.doFirst {
println("------------------- find so files start -------------------")

it.inputs.files.each { file ->
printDir(new File(file.absolutePath))
}

println("------------------- find so files end -------------------")
}
}
}

def printDir(File file) {
if (file != null) {
if (file.isDirectory()) {
file.listFiles().each {
printDir(it)
}
} else if (file.absolutePath.endsWith(".so")) {
println "find so file: $file.absolutePath"
}
}
}

Read More

表达式引擎预研

背景

最后业务有需求需要动态计算表达式,基于公式对某些值进行再加工计算。所以对表达式引擎进行一个简单的了解。
表达式引擎就是为了表达式的动态求值计算。
通常对于各个类库对比,我们需要看厂商、License、社区活跃度、优缺点等来判断其符合的使用场景,最终决定应该用哪个。
由于时间较紧,只做简单收集,在项目中本已使用AviatorScript的情况下就选择了它。

特性对比

AviatorScript Groovy SpEL QLExpress
Version 5.3.2 4.0.4 5.3.22 3.3.0
厂商 killme2008 Apache Spring alibaba
License GPL V3 Apache 2 License Apache License 2.0 Apache License 2.0
Language 基于Java 基于Java Java Java
优点 高性能
轻量级
Flat learning curve
Powerful features
Smooth Java integration
Domain-Specific Languages
Vibrant and rich ecosystem
Scripting and testing glue
线程安全
高性能
弱类型脚本语言
安全控制
代码精简

还有OGNLDroolsEasyRules

另外MVELIKExpression很久未更新,不列

浏览器打开图片链接时直接显示或下载到底怎么回事

最近瞎JB搞了一通,各个项目都是喊组装上就交付,难以有时间学习总结,简直违反程序员的基本原则。很多时候已经忘记踩了哪些坑,如何解决的了。

问题

图片上传至阿里云OSS生成链接在浏览器中打开是直接下载文件,而需求需要打开图片而非下载。

常用方式

浏览器中的图片,通常印象中,前端上传文件获取url,终端当然是下载再展示。浏览器端都是img标签展示

1
<img src="/css/images/favicon.png"/>

下载图片及附件时都是右键保存或者点击按钮下载写入文件流中。
最近的需求中提到上传的图片生成的url,在浏览器中是打开而非下载文件 。我们正常上传到我们服务器上的是没问题的。这个问题出在阿里云OSS上?到底要怎么玩?

就经验来讲肯定是响应头header的问题。Android有类似的机制,view-type啥的,你是直接在别的应用中打开,还是怎么操作。
对比下载和打开图片两种不同链接

Read More

VUE中使用Mock模拟数据请求


Mock.js 是一款模拟数据生成器,旨在帮助前端攻城师独立于后端进行开发,帮助编写单元测试。提供了以下模拟功能:

  • 根据数据模板生成模拟数据
  • 模拟 Ajax 请求,生成并返回模拟数据
  • 基于 HTML 模板生成模拟数据

记录一下在项目中的使用方法

安装

1
npm install mockjs

当然通常在项目中会在package.json中添加mockjs依赖,再npm install所有项目中需要依赖的库

1
2
3
"dependencies": {
"mockjs": "^1.1.0"
}

引用

src/mock/index.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//引入mock
import Mock from 'mockjs'

let configArray = []
// 使用webpack的require.context()遍历所有mock文件
const files = require.context('.', true, /\.js$/)
files.keys().forEach((key) => {
if (key === './index.js') return
configArray = configArray.concat(files(key).default)
})

// 设置拦截ajax请求的相应时间
Mock.setup({
timeout: '200-600'
});

// 注册所有的mock服务
configArray.forEach((item) => {
for (const [path, target] of Object.entries(item)) {
const protocol = path.split('|')
Mock.mock(protocol[1], protocol[0], target)
}
})

main.js中引入

1
2
3
4
//开发环境引入
if (process.env.NODE_ENV === 'development') {
require('@/mock')
}

mock文件下建立personList.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let personList = [{
id: 1,
name: '小丽',
age: '18',
number: '8'
},{
id: 1,
name: '小芳',
age: '20',
number: '6'
}]

export default {
'get|/parameter/query': option => {
return {
status: 200,
message: 'success',
data: personList
};
}
}

接下来在网络请求时,发现如果是get请求路径是/parameter/query,就会返回我们mock定义好的数据。另外还有其它语法来生成随机数据。详细参考数据模板定义 DTD

参考

Mock.js
Mock

Gson序列化、反序列化枚举

后端返回了N多枚举,前端Gson解析报错?

后端接口返回很多枚举,Android复用实体,解析时却报错

1
2
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: 
Expected a string but was BEGIN_OBJECT at line 1 column 70 path $.data.type

先定义一个枚举例子 Type.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public enum Type{
GSON(0, "Gson"),
JACKSON(1, "Jackson"),
FASTJSON(2, "FastJson");
private final int code;
private final String name;

Type(int code, String name){
this.code = code;
this.name = name;
}

public static Type code(int code){
for (Type i : Type.values()){
if(i.code() == code){
return i;
}
}
return null;
}

public static Type name(String name){
for (Type i : Type.values()){
if(i.name().equals(name)){
return i;
}
}
return null;
}

public int code(){
return code;
}

public String name(){
return name;
}
}

在序列化时服务端有两种试返回
1、完整返回枚举

1
{"type":{"code":0,"name":"Gson"}}

2、序列化code或name

1
2
{"type":0}
//{"type":"Gson"}

最好的解决办法——解决提出问题的人

很不幸后端是我负责的,所以我们有三个解决方法
1、让前端自己解决
2、为前端提供技术方案
3、修改后端枚举
我应该怎么办?
以上,当然是认怂,直接奉上代码喽
Json是一种广泛使用的数据交换格式,在Java中序列化和反序列化的各API名称也都类似,下面正式介绍Gson如何序列化、反序列化枚举

Read More