微信支付有一些需要注意的细节,官方文档上并没有做出详细的说明,需要开发者自己探索;这里结合自己的经验,把需要注意的事项尽可能多的列出来,避免再次踩坑。
1–要测试微信支付,必须要打包apk,而且打包时用的keystore,要和微信开放平台上的keystore要一致;否则微信在校验的时候就不会通过,无法调出支付界面。
2–微信支付返回的结果码,会在微信官方给的WXPayEntryActivity中得到。
WXPayEntryActivity的路径微信官方有严格的要求,要依照官方的要求,将WXPayEntryActivity放到对应的包下,才会正确的回调。
官方要求的路径:你的包名.wxapi.WXPayEntryActivity
也就是说,要在包名路径下创建wxapi包,将WXPayEntryActivity放在这个wxapi下。这样才会得到正确的回调。
3–因为WXPayEntryActivity是被微信app来调用的,所以:
AndroidManifest.xml中WXPayEntryActivity的android:exported=”true”
exported值的作用在于:当前Activity是否可以被另一个Application的组件启动:true允许被启动;false不允许被启动。
所以,如果这个值没有被设置成true,WXPayEntryActivity是不允许被其他app调用的,微信也就无法将结果码返回。
4–关于WXPayEntryActivity的返回结果码的获得和传出。
支付结果返回码errCode在onResp中返回,但是传出却有不少问题。
因为WXPayEntryActivity是被其他app(微信)调用的,所以其对应的context,并不是自己程序中的context。
也就是说,如果通过WXPayEntryActivity来获取本程序的application或者SharedPreferences,并将一些结果码存到application或SharedPreferences中,用传统的方法是不可取的。得到的application和SharedPreferences都将是null。
4.1–以下是在WXPayEntryActivity中的测试代码:
//这里application打印出来的结果为null
MyApplication application = (MyApplication)WXPayEntryActivity.this.getApplication();
LogUtil.d("tag", "application == "+application );
//SharedPreferencesUtil是自己写的一个获取SharedPreferences的工具类
//传自己程序的activity可正常获得SharedPreferences并修改或查询里面的数据,但传入WXPayEntryActivity后,打印值为null
SharedPreferencesUtil spUtil = new SharedPreferencesUtil(WXPayEntryActivity.this);
LogUtil.d("tag", "spUtil == "+spUtil );
4.2–传统方法不行,那就换个方法——获取指定app的Context:
try {
//PACKNAME是指定app的包名,比如com.myapp.app,你只要知道包名,就能获得这个包的Context
Context mContext = WXPayEntryActivity.this.createPackageContext(PACKNAME, Context.CONTEXT_IGNORE_SECURITY);
String packName = mContext.getPackageName();
LogUtil.d("tag", "mContext == "+mContext );
LogUtil.d("tag", "packName == "+packName );
} catch (NameNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
原本以为这样就可以成功获得自己程序的Context,这样就可以很方便的保存或传出支付结果码,然而打印结果显示,mContext 和 packName 都 == null。
这里没有弄明白为什么会是null,但总之这条路是不通的。
4.3–此路不通,再换一条路:通过activity的intent进行通信。
在拿到支付结果码时,做界面的跳转,携带intent将结果码带出去,WXPayEntryActivity中的代码如下:
Intent intent = new Intent(this, WaitPayListActivity.class);
intent.putExtra("errCode", "0");
startActivity(intent);
在WaitPayListActivity获取errCode
String errCode = getIntent().getStringExtra("errCode");
LogUtil.d("tag", "errCode == "+errCode);
得到的结果是,errCode == null
虽然界面跳转了,但是intent并没有穿过来。
判断应该是因为WXPayEntryActivity是其他app中的,与自己的activity不在同一个进程中,普通的intent不能进行跨进程通信。
4.4–activity的intent进行跨进程通信
因为WaitPayListActivity用来接受支付结果码,那么WaitPayListActivity的android:exported=”true”以让其他进程调用,把WaitPayListActivity共享给其他应用。
代码如下:
<activity android:name=".activity.WaitPayListActivity" android:exported="true" android:screenOrientation="portrait" android:theme="@android:style/Theme.Holo.Light.NoActionBar" >
<intent-filter>
<action android:name="com.myapp.ACTION"/>
<data android:scheme="info"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
1)action 是指定的动作,可以自定义,只要知道action的name,其他应用就可以找到这个activity
2)data 的 android:scheme 是intent的通信协议,只要采用的相同的协议,就可以携带数据过来。
3)category 是intent的类型
关于activity中的跨进程访问原理和细节,可参考如下博客:
http://blog.csdn.net/dj0379/article/details/50852659
http://blog.csdn.net/smile_yingying/article/details/39351981
将WaitPayListActivity共享后,WXPayEntryActivity便可以通过隐式intent来跳转到WaitPayListActivity。
//URI定义了通信协议,要符合WaitPayListActivity的<data android:scheme="info"/>
//"info://"是协议格式,类似"http://",后面的wxpay是传过去的值
Uri rui = Uri.parse("info://wxpay");
//通过Action和URI调用其他进程中的Activity,并传递数据
Intent intent = new Intent(ACTION, rui);
intent.putExtra("errCode", "0");
WXPayEntryActivity.this.startActivity(intent);
在WaitPayListActivity中接收intent。
if (getIntent().getData() != null) {//getData方法获取的是传过来的URI
LogUtil.d("tag", "getIntent().getData() != null");
//要对URI做非空判断,否则报空指针;有URI,才会有getExtras的数据
String errCode = getIntent().getExtras().getString("errCode");
LogUtil.d("tag", "errCode == "+errCode);
}else {
LogUtil.d("tag", "getIntent().getData() == null");
}
得到的结果是:getIntent().getData() == null
也就是说,intent中的Uri都没有传过来,更不要说里面携带的数据了。
但是界面执行了正确的跳转,跳到了WaitPayListActivity。
Uri协议格式并没有不一致,但没有数据过来,这个原因没有弄清楚,不知道是不是实现了微信接口的原因。(IWXAPIEventHandler)
以上尝试的几种办法都没有将errCode传出来,当然,还可以尝试用ContentProvider,Service或广播。
在这里,我通过对不同结果码跳转到不同界面的方法,来对结果码做判断,完成自己所需要的效果。
注:事后发现,errCode传不出来,是因为在WXPayEntryActivity中,api.handleIntent(getIntent(), this)方法执行后,会直接跳转到onResp()方法,而不是继续执行紧跟api.handleIntent()之后的代码;所以,application的初始化在api.handleIntent()之前执行,就可以得到一个正确的application,而不再是null,此时解决了传不出errCode值的问题。补充于2017.05.23
//初始化IWXAPI
api = WXAPIFactory.createWXAPI(this, UpostConstants.WECHAT_APP_ID);
//此代码将Intent传给后面的onReq、onResp方法,再执行此代码之后的代码
api.handleIntent(getIntent(), this);
5–在WXPayEntryActivity的onCreate和onNewIntent方法中需要调用IWXAPI.handleIntent(intent, context);否则调不起微信。
public class WXPayEntryActivity extends AppCompatActivity implements IWXAPIEventHandler {
private IWXAPI api;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ac_wxpay_entry);
api = WXAPIFactory.createWXAPI(this, WxPayUtils.APP_ID);
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
@Override
public void onReq(BaseReq req) {
//...
}
@Override
public void onResp(BaseResp resp) {
//这里处理返回结果码resp.errCode
}
}
6–最后但不是不重要的注意点:在执行跳转到微信支付界方法【api.sendReq(req)】前,要先注册应用到微信。否则调用不成功。
// 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
api.sendReq(req);
可以在执行跳转的activity被创建时注册,也可以在其他地方注册,总是是要在sendReq方法前要进行注册。
微信官方的Demo,是通过BroadcastReceiver来注册的,可以参考官方的,以下是代码:
/**将App注册到微信*/
public class AppWXRegister extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
IWXAPI api = WXAPIFactory.createWXAPI(context, null);
// 将该App注册到微信
api.registerApp(UpostConstants.WECHAT_APP_ID);
}
}
清单配置文件中注册这个广播:
<receiver android:name=".receiver.AppWXRegister" >
<intent-filter>
<action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP" />
</intent-filter>
</receiver>
以上是微信支付中,很可能会碰到的问题,总结出来,让自己和其他人都少走弯路,少踩坑。
PS:如果不需要显示WXPayEntryActivity,可以将主题设置成
android:theme=”@android:style/Theme.NoDisplay”
以及在拿到结果码就立即跳转界面,并finish掉WXPayEntryActivity。
此外,WXPayEntryActivity还有一个小惊喜,那就是如果在WXPayEntryActivity中碰到了空指针异常,WXPayEntryActivity界面就将一直停留,并不像普通activity一样立即消失。
所以如果WXPayEntryActivity突然卡住不动了,快去看看是否有空指针。