本篇博客继续接入第三方SDK——接入微信分享。在接入微信分享之前,我们完善一下上篇博客的广告问题,当我从分数榜场景退出到开始场景的时候迷你广告还在上边,如果用户再进入一个关于作者的场景再玩游戏,上下俩个地方都会出现广告,也就是说广告在退出场景的时候并没有消失,我们需要做的就是解决这个问题。其实接入第三方SDK的原理很简单,以Android为例,在java层写接入代码,实现接入的逻辑,同时留下给c++层调用的接口,c++层通过jni来调用Android层的代码,整个接入SDK的逻辑就是这样。所以为了让广告消失,我们需要在Android层实现具体的逻辑,留下给c++层调用的接口,c++层必要的时候进行调用。

Android的具体实现逻辑如下,主要修改的是内部类AdHandler。

class AdHandler extends Handler{
		//保存俩个layout,广告卸载的时候用
		private LinearLayout adBannerLayout=null;
		private LinearLayout adMiniLayout=null;

		public void handleMessage(Message msg) {
			switch (msg.what){
			case 0:
				// 显示推荐列表(综合)
				AppConnect.getInstance(mContext).showOffers(mContext);
				break;
			case 1:
				// 显示插屏广告
				// 判断插屏广告是否已初始化完成,用于确定是否能成功调用插屏广告
				boolean hasPopAd = AppConnect.getInstance(mContext).hasPopAd(mContext);
				if (hasPopAd) {
					AppConnect.getInstance(mContext).showPopAd(mContext);
				}
				break;
			case 2:
				// 显示推荐列表(软件)
				AppConnect.getInstance(mContext).showAppOffers(mContext);
				break;
			case 3:
				// 显示推荐列表(游戏)
				AppConnect.getInstance(mContext).showGameOffers(mContext);
				break;
			case 4:
				// 获取全部自定义广告数据
				break;
			case 5:
				// 获取一条自定义广告数据
				AdInfo adInfo = AppConnect.getInstance(mContext).getAdInfo();
				AppDetail.getInstanct().showAdDetail(mContext, adInfo); break;
			case 6:
				// 消费虚拟货币.
				AppConnect.getInstance(mContext).spendPoints(10,AppActivity.this);
				break;
			case 7:
				// 奖励虚拟货币
				AppConnect.getInstance(mContext).awardPoints(10,AppActivity.this);
				break;
			case 8:
				// 显示自家应用列表
				AppConnect.getInstance(mContext).showMore(mContext);
				break;
			case 9:
				// 根据指定的应用app_id展示其详情
				AppConnect.getInstance(mContext).showMore(mContext,app_id);
				break;
			case 10:
				// 调用功能广告接口(使用浏览器接口)
				String uriStr = "http://www.baidu.com"; AppConnect.getInstance(mContext).showBrowser(mContext,uriStr);
				break;
			case 11:
				// 用户反馈
				AppConnect.getInstance(mContext).showFeedback(mContext);
				break;
			case 12:
				// 退屏广告
				QuitPopAd.getInstance().show(mContext);
				break;
			case 13:
				// banner
				AppConnect.getInstance(mContext).showBannerAd(mContext,getBannerAd());
				break;
			case 14:
				// 迷你广告
				AppConnect.getInstance(mContext).showMiniAd(mContext,getMiniAd(), 10);
				break;
			default:
				this.uninstallAd(msg.what-100);
				break;
			}
		}

		//添加互动广告
		private LinearLayout getBannerAd(){
			// 互动广告
			adBannerLayout = new LinearLayout(mContext);
			adBannerLayout.setOrientation(LinearLayout.VERTICAL);
			FrameLayout.LayoutParams lp_banner = new FrameLayout.LayoutParams(
			FrameLayout.LayoutParams.FILL_PARENT,
			FrameLayout.LayoutParams.WRAP_CONTENT);
			// 设置adBannerLayout的悬浮位置,具体的位置开发者根据需要设置
			lp_banner.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
			AppActivity.this.addContentView(adBannerLayout, lp_banner);

			LinearLayout bannerLayout = new LinearLayout(mContext);
			adBannerLayout.addView(bannerLayout);

			return bannerLayout;
		}

		//添加迷你广告
		private LinearLayout getMiniAd(){
			adMiniLayout = new LinearLayout(mContext);
			adMiniLayout.setOrientation(LinearLayout.VERTICAL);
			FrameLayout.LayoutParams lp_mini = new FrameLayout.LayoutParams(
			FrameLayout.LayoutParams.FILL_PARENT,
			FrameLayout.LayoutParams.WRAP_CONTENT);
			// 设置adMiniLayout的悬浮位置,具体的位置开发者根据需要设置
			lp_mini.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
			AppActivity.this.addContentView(adMiniLayout, lp_mini);
			LinearLayout miniLayout = new LinearLayout(mContext);
			adMiniLayout.addView(miniLayout);

			return miniLayout;
		}

		//在创建的广告的线程中卸载广告
		public void uninstallAd(int adTag){
			if(adTag == 13){
				adBannerLayout.setVisibility(View.INVISIBLE);
			}
			else if(adTag == 14){
				adMiniLayout.setVisibility(View.INVISIBLE);
			}
		}

留给c++层调用的接口如下:

//根据传入的id,卸载广告
	public static void uninstallAd(int adTag){
		Message msg = handler.obtainMessage();
		// 私有静态的整型变量,开发者请自行定义值
		msg.what = adTag+100;
		handler.sendMessage(msg);
	}

c++层使用jni来调用这个接口,代码如下:

void WapsAd::uninstallAd(int adTag)
{
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo minfo;
    bool isHave = JniHelper::getStaticMethodInfo(minfo,"org/cocos2dx/cpp/AppActivity","uninstallAd", "(I)V");
    if(!isHave)
    {
        CCLog("jni:showAdStatic is null");
    }
    else
    {
        //调用此函数
        minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,adTag);
    }
#endif
}

这样的话整个广告的代码就完善了,接下来接入微信SDK。

1、首先是准备工作,到官网上注册账号,下载SDK,将下载的jar包放到项目的libs目录下,主要步骤的截图如下。现在如果注册的是游戏会让你到腾讯开放平台注册,还要注册提交审核,我的第一次提交就失败了,所以我把描述信息给改了,让他看不出我是游戏还是应用,为了让你的应用在使用分享功能的时候是正常的,第一注意包名,就是你要上线游戏的包名,还有一个是应用签名,你需要下载微信获取签名的一个工具,使用这个工具来获取你安装包的签名,所以打包的时候就打一个release包吧,安装到手机上,然后使用微信提供的工具去获取签名,工具的下载地址。然后就等待审核了,工作日的话半天就OK了,不是很严格。

用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享

2、接下来我们使用刚才上边提到的思路来写代码,先完成java层的代码,将接口函数留给c++层调用。注意APP_ID是从微信后台获取的,然后在onCreate函数中初始化一下连接微信的代码。

//微信SDK
	private static final String APP_ID = "wx42ca6be8e52eebfb";
	private static IWXAPI api;
	private static AppActivity instance;

	protected void onCreate(Bundle savedInstanceState) {
		//先调用父类的onCreate方法
		super.onCreate(savedInstanceState);

		//给成员变量赋值
		mContext = this;
		handler = new AdHandler();
		instance = this;

		initAds();
		regToWX();
	}

	//初始化微信
	private void regToWX(){
	    api = WXAPIFactory.createWXAPI(this, APP_ID, true);
	    api.registerApp(APP_ID);
	}

3、下面把接口留给c++层调用。调用微信API代码的逻辑是首先生成需要发送的消息对象,然后将消息对象附加到媒体对象中,接着建立请求对象,最后使用通信类发送即可。代码中还用到一个Util类,这个类我们需要在包下新建一个,代码是参考别人的,大家就当工具来用就可以了。消息对象有很多种,包括文本,音乐,视频等等,我们需要根据自己的需要创建不同的消息对象,这些消息对象的类名可以参考官方的文档。

	public static void sendMsgToFriend(){
	    if(api.openWXApp())
	    {
	    		//初始化一个对象WebpageObject
	        WXTextObject textObject = new WXTextObject();
	        textObject.text = "小塔博客写的Plane教程,最后游戏Demo完成,博客地址http://www.zaojiahua.com/tag/3-0%E9%A3%9E%E6%9C%BA%E5%A4%A7%E6%88%98!";

	        //媒体对象中
	        WXMediaMessage msg = new WXMediaMessage(textObject);
	        msg.title = "飞机大战";
	        msg.description = "分享给你的好友,让更多的人来玩!";

	        //建立请求对象
	        SendMessageToWX.Req req = new SendMessageToWX.Req();
	        //transaction是用来表示一个请求的唯一标示符
	        req.transaction = buildTransaction("textObject");
	        req.message = msg;
	        req.scene = SendMessageToWX.Req.WXSceneSession;

	        //使用通信类发送
	        api.sendReq(req);
	    }
	    else
	    {
	         Toast.makeText(instance, "启动微信失败!", Toast.LENGTH_SHORT).show();
	    }
	}

	public static void sendMsgToTimeLine(){
	    if(api.openWXApp())
	    {
	        if(api.getWXAppSupportAPI() >= 0x21020001)
	        {
	            WXWebpageObject webpage = new WXWebpageObject();
	            webpage.webpageUrl = "http://m.baidu.com/app?action==content&docid=6561264&f=s1001";

	            WXMediaMessage msg = new WXMediaMessage(webpage);
	            msg.title = "飞机大战";
	            msg.description = "分享到我的朋友圈,让更多的人来玩!";

	            Bitmap thumb = BitmapFactory.decodeResource(instance.getResources(), com.zaojiahua.plane.R.drawable.icon);
	            msg.thumbData = Util.bmpToByteArray(thumb, true);

	            SendMessageToWX.Req req = new SendMessageToWX.Req();
	            req.transaction = buildTransaction("webpage");
	            req.message = msg;
	            req.scene = SendMessageToWX.Req.WXSceneTimeline;
	            api.sendReq(req);
	        }
	        else{
	            Toast.makeText(instance, "微信版本过低", Toast.LENGTH_SHORT).show();
	        }
	    }
	    else
	    {
	         Toast.makeText(instance, "启动微信失败", Toast.LENGTH_SHORT).show();
	    }
	}
	private static String buildTransaction(final String type) {
	    return (type == null) ? String.valueOf(System.currentTimeMillis()) : type + System.currentTimeMillis();
	}

用3.0实现飞机大战——接入微信分享

package org.cocos2dx.cpp;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import junit.framework.Assert;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Bitmap.CompressFormat;
import android.util.Log;

public class Util {

	private static final String TAG = "SDK_Sample.Util";

	public static byte[] bmpToByteArray(final Bitmap bmp, final boolean needRecycle) {
		ByteArrayOutputStream output = new ByteArrayOutputStream();
		bmp.compress(CompressFormat.PNG, 100, output);
		if (needRecycle) {
			bmp.recycle();
		}

		byte[] result = output.toByteArray();
		try {
			output.close();
		} catch (Exception e) {
			e.printStackTrace();
		}

		return result;
	}

	public static byte[] getHtmlByteArray(final String url) {
		 URL htmlUrl = null;
		 InputStream inStream = null;
		 try {
			 htmlUrl = new URL(url);
			 URLConnection connection = htmlUrl.openConnection();
			 HttpURLConnection httpConnection = (HttpURLConnection)connection;
			 int responseCode = httpConnection.getResponseCode();
			 if(responseCode == HttpURLConnection.HTTP_OK){
				 inStream = httpConnection.getInputStream();
			  }
			 } catch (MalformedURLException e) {
				 e.printStackTrace();
			 } catch (IOException e) {
				e.printStackTrace();
		  }
		byte[] data = inputStreamToByte(inStream);

		return data;
	}

	public static byte[] inputStreamToByte(InputStream is) {
		try{
			ByteArrayOutputStream bytestream = new ByteArrayOutputStream();
			int ch;
			while ((ch = is.read()) != -1) {
				bytestream.write(ch);
			}
			byte imgdata[] = bytestream.toByteArray();
			bytestream.close();
			return imgdata;
		}catch(Exception e){
			e.printStackTrace();
		}

		return null;
	}

	public static byte[] readFromFile(String fileName, int offset, int len) {
		if (fileName == null) {
			return null;
		}

		File file = new File(fileName);
		if (!file.exists()) {
			Log.i(TAG, "readFromFile: file not found");
			return null;
		}

		if (len == -1) {
			len = (int) file.length();
		}

		Log.d(TAG, "readFromFile : offset = " + offset + " len = " + len + " offset + len = " + (offset + len));

		if(offset <0){
			Log.e(TAG, "readFromFile invalid offset:" + offset);
			return null;
		}
		if(len <=0 ){
			Log.e(TAG, "readFromFile invalid len:" + len);
			return null;
		}
		if(offset + len > (int) file.length()){
			Log.e(TAG, "readFromFile invalid file len:" + file.length());
			return null;
		}

		byte[] b = null;
		try {
			RandomAccessFile in = new RandomAccessFile(fileName, "r");
			b = new byte[len];
			in.seek(offset);
			in.readFully(b);
			in.close();

		} catch (Exception e) {
			Log.e(TAG, "readFromFile : errMsg = " + e.getMessage());
			e.printStackTrace();
		}
		return b;
	}

	private static final int MAX_DECODE_PICTURE_SIZE = 1920 * 1440;
	public static Bitmap extractThumbNail(final String path, final int height, final int width, final boolean crop) {
		Assert.assertTrue(path != null && !path.equals("") && height > 0 && width > 0);

		BitmapFactory.Options options = new BitmapFactory.Options();

		try {
			options.inJustDecodeBounds = true;
			Bitmap tmp = BitmapFactory.decodeFile(path, options);
			if (tmp != null) {
				tmp.recycle();
				tmp = null;
			}

			Log.d(TAG, "extractThumbNail: round=" + width + "x" + height + ", crop=" + crop);
			final double beY = options.outHeight * 1.0 / height;
			final double beX = options.outWidth * 1.0 / width;
			Log.d(TAG, "extractThumbNail: extract beX = " + beX + ", beY = " + beY);
			options.inSampleSize = (int) (crop ? (beY > beX ? beX : beY) : (beY < beX ? beX : beY));
			if (options.inSampleSize <= 1) {
				options.inSampleSize = 1;
			}

			// NOTE: out of memory error
			while (options.outHeight * options.outWidth / options.inSampleSize > MAX_DECODE_PICTURE_SIZE) {
				options.inSampleSize++;
			}

			int newHeight = height;
			int newWidth = width;
			if (crop) {
				if (beY > beX) {
					newHeight = (int) (newWidth * 1.0 * options.outHeight / options.outWidth);
				} else {
					newWidth = (int) (newHeight * 1.0 * options.outWidth / options.outHeight);
				}
			} else {
				if (beY < beX) {
					newHeight = (int) (newWidth * 1.0 * options.outHeight / options.outWidth);
				} else {
					newWidth = (int) (newHeight * 1.0 * options.outWidth / options.outHeight);
				}
			}

			options.inJustDecodeBounds = false;

			Log.i(TAG, "bitmap required size=" + newWidth + "x" + newHeight + ", orig=" + options.outWidth + "x" + options.outHeight + ", sample=" + options.inSampleSize);
			Bitmap bm = BitmapFactory.decodeFile(path, options);
			if (bm == null) {
				Log.e(TAG, "bitmap decode failed");
				return null;
			}

			Log.i(TAG, "bitmap decoded size=" + bm.getWidth() + "x" + bm.getHeight());
			final Bitmap scale = Bitmap.createScaledBitmap(bm, newWidth, newHeight, true);
			if (scale != null) {
				bm.recycle();
				bm = scale;
			}

			if (crop) {
				final Bitmap cropped = Bitmap.createBitmap(bm, (bm.getWidth() - width) >> 1, (bm.getHeight() - height) >> 1, width, height);
				if (cropped == null) {
					return bm;
				}

				bm.recycle();
				bm = cropped;
				Log.i(TAG, "bitmap croped size=" + bm.getWidth() + "x" + bm.getHeight());
			}
			return bm;

		} catch (final OutOfMemoryError e) {
			Log.e(TAG, "decode bitmap failed: " + e.getMessage());
			options = null;
		}

		return null;
	}
}

4、下面来写c++层的代码,大家可以参考上篇博客介绍的jni调用来完成微信分享接口的调用,我新建了一个WeixinShare类,专门来进行分享,代码如下。

#include "WapsAd.h"

WapsAd::WapsAd(){}

WapsAd::~WapsAd(){}

void WapsAd::showAd(int adTag)
{

    //判断当前是否为Android平台 JniMethodInfo showAd;
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo minfo;
    bool isHave = JniHelper::getStaticMethodInfo(minfo,"org/cocos2dx/cpp/AppActivity","showAdStatic", "(I)V");
    if(!isHave)
    {
        CCLog("jni:showAdStatic is null");
    }
    else
    {
        //调用此函数
        minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,adTag);
    }
#endif
}

void WapsAd::uninstallAd(int adTag)
{
#if(CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)
    JniMethodInfo minfo;
    bool isHave = JniHelper::getStaticMethodInfo(minfo,"org/cocos2dx/cpp/AppActivity","uninstallAd", "(I)V");
    if(!isHave)
    {
        CCLog("jni:showAdStatic is null");
    }
    else
    {
        //调用此函数
        minfo.env->CallStaticVoidMethod(minfo.classID, minfo.methodID,adTag);
    }
#endif
}

最后在合适的地方调用我们的分享接口就好了,这样的话整个Demo就完成的差不多了,下一步我们要做的就是上各个应用平台了,期待好运吧!

用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享 用3.0实现飞机大战——接入微信分享