第三方相关

申请第三方参数所需数据

  1. 定包名
  2. 定签名文件(生成签名文件)
  3. 取签名文件的SHA1(第三方参数文档中的“安全码”)
  4. 取签名文件的MD5值(要去掉冒号)(或者用微信签名工具输入包名得到的)32位数字(第三方参数文档中的“应用签名”)
  5. 给运维打好的包
    js中字符串全部替换
    1
    "CD:B0:3E:73:53:2B:C2:A2:54:5D:2E:75:67:74:BB:92".replace(/\:/g,"")

支付宝支付

支付宝sdk升级

支付宝sdk改版为aar的升级

升级步骤:

  • 删除AliSDK–》libs下的alipaySDK-20150818.jar
  • 往AliSKD的libs目录下增加alipaySdk-15.6.8-20191021122455-noUtdid.aar
  • 在AliSDK的build.gradle中dependencies添加
1
compile(name: 'alipaySdk-15.6.8-20191021122455-noUtdid', ext:'aar')
  • 另外,调用支付的方法的参数列表有变动
1
AliPayHelper.java的 alipay.pay(payInfo,true);//添加了true

微信支付

微信支付业务流程图

结合来电易项目理解:

一进入支付页面就会调用查询订单状态(对应图中14步之后的“后台查询实际支付结果”)

完整流程:app一进入支付页面就向接口请求这个单的状态(是否已经支付);若查到未支付,则向接口请求生成支付订单(对应图中3);接口去跟微信平台调用统一下单API(对应图中4);微信平台返回预付单给接口(对应图中5);接口把预付单信息和调起微信支付sdk所需的参数sign等返回给app(对应图中7);app根据这些参数调起微信sdk,弹出微信支付输入密码页面,输入密码后,微信客户端告知微信平台(对应图中9、10、11、12、13);微信平台告知微信客户端支付成功,微信客户端跳到微信支付成功页(对应图中14);此时用户点“返回商户”,返回到App的订单页面(此时会调用刚开始的那个查询订单状态的接口)

微信sdk升级

兼容android10的升级

升级到5.5.8步骤:

  • 删除UmengCenter–》libs下wechat-sdk-android-without-mta-5.1.4.jar
  • UmengCenter的build.gradle的dependencies下增加compile 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:5.5.8'
  • WXPayHelper.java内注释掉m_WXApi.isWXAppSupportAPI()相关的部分

微信支付调换AppId可以正常调起支付(安卓ios都这样)

做法:微信开放平台上有两套参数(本来是两个不一样的包名、不一样的AppId),然后两个AppId底下的包名和应用签名都改成一样。(可以推广到多套AppId的情况)

最终效果:“调换AppId”可以正常调起微信支付。(即微信开放平台上多个应用底下的包名和应用签名配置成一样的,appId的取值直接从接口获取,包名和应用签名影响的是支付完成返回和)

结论:包名和应用签名与微信开放平台配置的有关。AppId是获取的接口返回的,与微信开放平台配置的AppId无关。

但是:微信分享只会显示的原来那一套参数的

森马app 使用腾讯加固流程

1、登录腾讯云平台

https://console.cloud.tencent.com/ms/index#

2、在线方式上传要加固的app包

3、等待加固成功后,下载加固包

4、用签名文件重新把已加固的包二次打包

1
jarsigner -verbose -keystore /Users/zhuojianhai/Desktop/senma/smldy.jks -signedjar /Users/zhuojianhai/Desktop/senma/qb_customer.apk  /Users/zhuojianhai/Desktop/senma/qb_customer_legu.apk smldy

输入秘钥库密码:123456

1
jarsigner -verbose -keystore /Users/zhuojianhai/Desktop/senma/smldy.jks -signedjar /Users/zhuojianhai/Desktop/senma/verificationed.apk /Users/zhuojianhai/Desktop/senma/com.xiaomi.appstore.signature.verification.apk  smldy
1
jarsigner -verbose -keystore /Users/zhuojianhai/Desktop/senma/smldy.jks -signedjar /Users/zhuojianhai/Desktop/senma/Qihuunsigned.apk  /Users/zhuojianhai/Desktop/senma/Qihuunsign.apk smldy 
1
2
jarsigner -verbose -keystore /Users/zhuojianhai/Desktop/senma/smldy.jks -signedjar
/Users/zhuojianhai/Desktop/signfile/meizuemptyapk-release-signed.apk /Users/zhuojianhai/Desktop/signfile/meizuemptyapk-release-unsigned.apk smldy

apk 上架之前需要加固

  • 乐固只有 exe,会把签名去掉,只有加固的功能
  • 360 加固宝,会把签名去掉,有加固和重新签名的功能
  • jarsigner 命令可以(给加固后未签名的)重新签名
    jarsigner -verbose -keystore [私钥存放路径] -signedjar [签名后文件存放路径][未签名文件路径][证书名称]
    例:jarsigner -verbose -keystore /Users/用户名/mykeystore -signedjar ./signed.apk ./unsigned.apk haha
    注意:证书名称即 Alias(你设置的别名);去掉 keystore 的扩展名称即可解决“keystore No such file or directory”问题;

热修复

腾讯系(类加载方案)

原理

当有多个dex文件时,他们会组成一个有序数组,按顺序加载。对于已经加载的class是不会重新加载–》得出热修复方案:把需要修复的类打包成一个dex文件下发,并在APP启动时通过反射,将这个dex文件放在dexElements的最前面,这样修复类的Class就会比有Bug的Class优先加载了。

Qzone

最核心的东西

反射修改dexElements

弊端

在实现过程中,会遇到unexpected DEX problem异常,Qzone方案为了解决这个问题采用了插> 桩的策略来规避这个异常

  • 牺牲类加载速度:

在Dalvik虚拟机,APP在安装的时候会被执行dexopt操作,同一个dex文件内的Class会被打上CLASS_ISPREVERIFIED标志,而补丁包中的类并没有打上此标志,因此抛出异常。解决方法就是在第一次打包APK时让所有类都引用另一个dex文件中的类,这样所有的类始终不会打上CLASS_ISPREVERIFIED标志,因此补丁包可以顺利加载,但是Dalvik虚拟机在检测到一个类未打上CLASS_ISPREVERIFIED之后会再次在类加载的时候进行dexopt相关的操作,如果一次性加载很多类,速度将明显变慢。

  • 牺牲补丁包大小:

在Art虚拟机,dex文件最终会编译成本地机器码,在dex2oat时fast *已经将各个类的地址写死,若补丁包中的类出现字段或者方法的修改,会出现内存地址错乱,解决办法是将这个类的父类和调用这个类的类都加入补丁包。但这样会导致补丁包急剧增大。(实际上要理解清楚这个问题需要熟悉Dalvik和Art的完整流程,并非三言两语能解释清楚)

Tinker

  • 因为Qzone的缺陷,在参考Instant Run的冷插拔与buck的exopackage后,Tinker采用了全量替换的策略
    全量替换可以避免插桩和地址写死问题,但是补丁包会很大,因此可以在新旧两个Dex的差异放在补丁包中,下发到移动端后再在本地合成完整的dex文件。由于需要下发的全量补丁包体积过大,Tinker采用了后台求diff,下发diff文件,移动端合成全量包的策略。
  • 创新处一:Tinker采用了全量替换的策略
    全量替换可以避免插桩和地址写死问题
  • 创新处二:(只要有diff/patch算法,就可以开发Tinker)采用了自研的DexDiff算法,大大优化了下发差异包的大小。

阿里系(底层替换方案)

HotFix

美团

Robust

项目接入Bugly

官方文档地址

SBC添加Bugly步骤(供参考):

  1. U1cityAndroidFrame/build.gradle中添加依赖
1
2
3
4
5
dependencies{
...
//bugly
compile 'com.tencent.bugly:crashreport:3.1.0'
}
  1. U1CityApplication.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
39
40
41
42
43
44
45
46
47
48
protected void buglyInit(String buglyAppId, boolean isRelease){

try {
Context context = getApplicationContext();
// 获取当前包名
String packageName = context.getPackageName();
// 获取当前进程名
String processName = getProcessName(android.os.Process.myPid());
// 设置是否为上报进程
CrashReport.UserStrategy strategy = new CrashReport.UserStrategy(context);
strategy.setUploadProcess(processName == null || processName.equals(packageName));
// 初始化Bugly
CrashReport.initCrashReport(context, buglyAppId, isRelease, strategy);
// 如果通过“AndroidManifest.xml”来配置APP信息,初始化方法如下
// CrashReport.initCrashReport(context, strategy);
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取进程号对应的进程名
*
* @param pid 进程号
* @return 进程名
*/
private static String getProcessName(int pid) {
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));
String processName = reader.readLine();
if (!TextUtils.isEmpty(processName)) {
processName = processName.trim();
}
return processName;
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
try {
if (reader != null) {
reader.close();
}
} catch (IOException exception) {
exception.printStackTrace();
}
}
return null;
}
  1. 在导购端、顾客端、商家端的App.java中添加调用
1
2
3
4
5
6
7
8
//App.java中

public void onCreate() {
//在super.onCreate();下方添加
//新项目在bugly后台创建产品后用新的appId替换底下的"8b1782d99b"参数
//bugly初始化
buglyInit("8b1782d99b", !isRelease());
}

定位

GPS强弱

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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
@SuppressWarnings("MissingPermission")
private void requestLocationUpdates() {
try {
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 5, locationListener);
} catch (Exception e) {
DebugLog.e(TAG,e.getMessage());
}
}
private void removeUpdates() {
locationManager.removeUpdates(locationListener);
}

//gps强弱
private Boolean gnssStatusListenerAdded = false;
private LocationListener locationListener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {

}
};

private LocationManager locationManager;
private GnssStatusCompat.Callback gnssStatusListener;
private void gnssCallback() {
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
gnssStatusListener = new GnssStatusCompat.Callback() {
@Override
public void onSatelliteStatusChanged(@NonNull GnssStatusCompat status) {
super.onSatelliteStatusChanged(status);
//载波噪声密度大于30,并且卫星数量大于等于3就可以获取到定位信息。
// 那么可以简单的定义载波噪声密度大于30的卫星数量小于等于3时为信号弱,大于3小于7为信号中等,大于等于7为信号强。
int satelliteCount = status.getSatelliteCount();
int cn0DbHz30SatelliteCount = 0;// 可以搜索到的卫星总数
int cn0DbHz37SatelliteCount = 0;
String satelliteInfo = "";
for (int index = 0, size = satelliteCount; index < size; index++) {
float cn0DbHz = status.getCn0DbHz(index);// 每个卫星的载波噪声密度
satelliteInfo += ("svid:" + status.getSvid(index) + ",cnoDbHz:" + cn0DbHz);
if (cn0DbHz >= 30) {
cn0DbHz30SatelliteCount++;
}
if (cn0DbHz >= 37) {
cn0DbHz37SatelliteCount++;
}
}
System.out.println(String.format("total satellite count :%s", satelliteCount));
System.out.println(String.format("cno >37 count :%s ---cno >30 count %s", cn0DbHz37SatelliteCount, cn0DbHz30SatelliteCount));
System.out.println(String.format("satellite info :%s",satelliteInfo));
}
};
}
@SuppressWarnings("MissingPermission")
private void registerGnssStatusListener(){
try {
if (!gnssStatusListenerAdded){
gnssStatusListenerAdded =
LocationManagerCompat.registerGnssStatusCallback(locationManager,
gnssStatusListener,
new Handler(Looper.myLooper()==null?Looper.getMainLooper():Looper.myLooper()) );
}
} catch (Exception e) {
DebugLog.e(TAG,e.getMessage());
}
}
private void unRegisterGnssStatusListener(){
if (gnssStatusListenerAdded){
LocationManagerCompat.unregisterGnssStatusCallback(locationManager, gnssStatusListener);
}
}
1
2
3
4
5
6
7
Activity#onCreate()中调用 gnssCallback();

授权方法成功后调 registerGnssStatusListener(); 开始requestLocationUpdates();

停止 removeUpdates();

Activity#onDestroy()中调 unRegisterGnssStatusListener();

地图

高德地图

高德静态地图

https://restapi.amap.com/v3/staticmap?location=118.162963,24.528373&zoom=17&size=540*275&markers=mid,,A:118.162963,24.528373&key=bff4a194db711cd52b1d98531b79d842

size是宽高,zoom是缩放比例,17最大。key换成自己应用申请的

百度地图

百度静态地图

http://api.map.baidu.com/staticimage?width=300&height=200&center=118.162963,24.528373&zoom=18&markers=118.162963,24.528373&markerStyles=m,A

width、height:宽高;center是经纬度;markers是marker的经纬度位置;zoom是缩放比例,19最大

框架

MVPArms

module级模板

页面级模板