商户支持问题记录

背景:

 这篇文章用来记录一些商户支持过程中遇到的问题和解决思路,也算是对自己工作的一个记录吧。

1.module模块嵌入问题( AndroidManifest.xml合并冲突)

    在集成SDK时,若SDK与主module(通常为app)的AndroidManifest.xml发生合并冲突,尝试在主module的Manifest文件中配置tools属性。


商户支持问题记录_第1张图片

2.标题栏遮挡问题

    商户APP在加载我们的webview页面时,由于APP做了沉浸式处理,导致手机导航栏图标(电池,运营商标识等)遮挡了webview顶部的操作按钮,影响用户操作。

解决方式:

在AndroidManifest.xml文件中对activity的声明添加

android:theme="@android:style/Theme.Light.NoTitleBar"

      隐藏主工程APP设置的titlebar效果,使用系统titlebar,这样页面可以自动下移不遮挡了,后来我在对接支付宝的时候,发现阿里也在自己的H5页面配置上有加这一句,应该也是考虑到这个因素。


3.桥接方法不生效问题(混淆规则)

在targetSdkVersion 17以上需要添加接口JavaScriptInterface才能用,即在JS调用的Java代码上方加上@JavaScriptInterface注解,同时,在混淆代码时,注意不要混淆JavascriptInterface的注解,否则会出现js不能调用java代码的情况。

-keepattributes *Annotation*

-keepattributes *JavaScriptInterface*


4.返回页面问题(activity启动模式)

  唤起银行APP支付成功后,在成功页点击“返回商户”按钮,唤起商户内嵌回调activity,商户想直接收起该页面,返回自己的订单页,但finish 回调activity后,回到了手机银行。

我们假定商户APP为A,银行APP为B,由A唤起B时,由任务栈S1跳转到新任务栈S2执行,点击“返回商户”,虽然回到了商户APP,

商户支持问题记录_第2张图片

此时task还在S2中执行,所以执行完上述逻辑后,只是关闭了当前页面,并没有回到商户APP。

为什么还在S2执行呢?理论上不同的activity应该在不同的任务中执行,但android在设计时,考虑到无缝的用户体验,在唤起时,会将不同的activity保存在相同的任务中,这样看起来像是在一个应用中执行。

解决方案:

1. intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent添加唤起方式配置

2. 修改回调activity的启动类型,由singleTop改为singleTask。

android:name=".icbcPay.PayResultHandler"

android:exported="true"

android:launchMode="singleTask">

这两种方式是一样的,以singleTask方式启动后,系统会尝试寻找与目标activity相关联的任务,并尝试在该任务中启动。


微信支付的回调activity配置的是“singleTop”,猜测是intent启动的时候做了配置,不然感觉也会出这种问题。

关于activity的启动模式(任务和返回栈)

   (1)standard 默认启动模式,不管有没有已经存在的实例,都会生成新的实例

   (2)singleTop 跳转时系统会现在栈结构中寻找是否有一个activity实例位于栈顶,如果有,则不再生成新的实例。如果没有,或者有但不在栈顶,则生成新的实例。

   (3)singleTask 启动新任务执行,在启动activity时,若有一个运行着这个activity的task,那这个activity实例会被调到前台,处于该activity之上的其他页面会被清掉。

      (4) singleInstance 开辟一个只允许一个activity实例在里面运行的task. 如果用同样的intent再次启动这个activity,task会被调到前台,其Activity.onNewIntent() 会被调用. 如果这个activity实例要启动一个新activity,那么这个新activity会在一个新task中运行.

       singleTop适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。

        singleTask适合作为程序入口点。例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。之前打开过的页面,打开之前的页面就ok,不再新建。

         singleInstance适合需要与程序分离开的页面。例如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。

       当ActivityA的LaunchMode为SingleTop时,如果ActivityA在栈顶,且现在要再启动ActivityA,这时会调用onNewIntent()方法 

      当ActivityA的LaunchMode为SingleInstance,SingleTask时,如果已经ActivityA已经在堆栈中,那么此时会调用onNewIntent()方法 

       当ActivityA的LaunchMode为Standard时,由于每次启动ActivityA都是启动新的实例,和原来启动的没关系,所以不会调用原来ActivityA的onNewIntent方法


5.回调路径找不到(app packageName与applicationId的关系)

     SDK回调路径由app的packageName+固定名称activity组成,通常我们在工程里,如果在build.gradle里指定applicationId,那么getPackageName获得的是applicationId,否则才是AndroidManifest.xml 配置的 packageName,AndroidManifest.xml里配置的packageName与工程路径一致。applicationId可以视为应用的唯一标识符,运行ID,上架之后不能再改变。

     假设我们在AndroidManifest.xml里配置的是path1,applicationId是path2,那么通过getPackageName()获得的是path2,回调路径是path2+activity name,而这个路径,在工程下是找不到的。

    这个问题最后没有想到合适的方法的解决,当时是客户硬在工程路径下,创建了path2+activity文件解决的,微信也是通过packageName+activity来做回调的,有环境了试试会不会有问题。如果没问题,他们是怎么拿到正确的工程路径呢……

2018.6.6

 这个问题我今天想起来了。。。用activity-alias 控制目标跳转路径可以实现,在主工程AndroidManifest.xml中,声明path2+activity,配置activity-alias,path1的targetActivity指向path2.

2018.6.13

其实还是要新建path2+activity文件,这个方法行不通

6.Toast导致的闪退问题

出现“订单数据不完整”这种场景时,SDK引起了宿主APP的crash。

原因是该Toast在AsyncTask的doInBackground中显示,这段代码执行在非主线程中且未初始化Looper。

Toast一定要运行在主线程(UI线程)吗?

      其实不是的,看了Toast的源码后,其实Toast也是利用了进程间通讯,其构造函数里需传入looper。而Activity和service之所以不需要初始化looper,是因为主线程(ActivityThread)初始化了looper。

private void showMessage(final Context context, final String msg){

new Thread(){

public void run(){

Looper.prepare();

Toast.makeText(context,msg,Toast.LENGTH_SHORT).show();

Looper.loop();

}}.start();

}

Looper会一直死循环,占用内存,该线程不会被释放,新起线程执行,建议还是用Handler的方式去实现。

        Handler handler = new Handler(Looper.getMainLooper());          

        handler.post(new Runnable() {              

       @Override              

        public void run() {                 

         Toast toast = Toast.makeText(context, text, duration);                                                   toast.show();              }          });  

UI操作一定要在UI线程里执行吗?

Toast可能是一个特例,其实UI操作不一定要在UI线程里执行。之所以子线程不能更新界面,是因为Android在线程的方法里面采用checkThread进行判断是否是主线程,而这个方法是在ViewRootImpl中的,这个类是在onResume里面才生成的,因此,如果这个时候子线程在onCreate里面生成更新UI,而且没有做阻塞,就是耗时多的操作,还是可以更新UI的。

你可能感兴趣的:(商户支持问题记录)