2013年9月16日 星期一

Unity3D研究院之在Unity中分享QQ空间(四十九)

http://www.xuanyusong.com/archives/2159

最近研究了一下Android下 Unity分享 for QQ空间,作法类似以前我在博客中写过分享新浪微博的方法,也就是自己写插件。测试用的手机Google Nexus  ,Unity版本4.1,如下图所示,这是我发布成功后在QQ空间的截图。
屏幕快照 2013-03-24 下午8.56.16



写插件的方式我就不再说了,如果不懂的朋友看我之前的博客吧,写的很清楚。
QQ空间分享属于腾讯开放平台。然而腾讯开放平台的东西也比较多,移动平台包括:空间分享、说说分享、微薄分享。这一篇就说空间分享,如果空间分享搞定后可以按照同样的方式实现微薄分享。
先下载腾讯开放平台的API。
API我下载后里面有一个DEMO。我在MAC OS系统下Eclipse尽然不能运行,提示错误。后来我简单改了一下才可以运行,好蛋疼啊。
废话就不多说了,先来在Elipse下Android的插件。下段代码是Eclipse中的Android代码。

package com.example.qqzone;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.Date;

import org.apache.http.conn.ConnectTimeoutException;
import org.json.JSONException;
import org.json.JSONObject;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Menu;
import android.widget.Toast;

import com.tencent.open.HttpStatusException;
import com.tencent.open.NetworkUnavailableException;
import com.tencent.tauth.Constants;
import com.tencent.tauth.IRequestListener;
import com.tencent.tauth.IUiListener;
import com.tencent.tauth.Tencent;
import com.tencent.tauth.UiError;
import com.unity3d.player.UnityPlayerActivity;

public class MainActivity extends UnityPlayerActivity {
//	 public class MainActivity extends Activity {

	public Tencent mTencent;
	public SharedPreferences sharedPrefrences;
	public Editor editor;
	public Context context; 
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

		sharedPrefrences = this.getSharedPreferences("info",
				MODE_WORLD_READABLE);
		editor = sharedPrefrences.edit();
		context = this.getApplicationContext();

	}

	//这个方法是在Unity中调用的。参数是当前的Activity。
	public void LoginAndSend(final Activity currentActivity) 
	{
		//一定要在UI线程中来执行腾讯API中的方法。
		//API中的方法应该是开启了一个线程,一切由Unity调用的方法里面只要开启线程就会出现 
		//Can’t create handler inside thread that has not called Looper.prepare() 错误
		//写在runOnUiThread 的线程中可以避免这个错误没,以前在处理Unity发送新浪微博的时候也遇到这样的错误。
		//同样可以按照这种方法来解决。

		currentActivity.runOnUiThread(new Runnable() {

			@Override
			public void run() {

                //得到Tencent实例, 腾讯API的方法都写在这里类中。
				//"222222"是APPID 这个是腾讯DEMO里带的, 你最好自己申请一个。
				mTencent = Tencent.createInstance("222222",context
						);
				//这里是处理Token的, 避免用户重复授权应用,每次登录成功后会获取一个新Token
				//得到新Token后一定要保存在本地中。用上次保存的token就可以避免再次登录。
				//Token的过期时间是3个月,3个月后需要重新登录。

				//登录后返回值
				String openid = sharedPrefrences.getString("openid", null);
				//登录后返回值
				String access_token = sharedPrefrences.getString("access_token", null);
				//过期时间
				long expires_in = sharedPrefrences.getLong("expires_in", 0);

				if (openid != null && access_token != null && expires_in != 0) 
				{
                    //如果以前保存过Token 这里直接使用上次保存的openid 和access_token
					//这里最好在判断一下token过期时间
					mTencent.setOpenId(openid);
					mTencent.setAccessToken(access_token, expires_in + "");
					//发送说说
					SendMessage();

				} else 
				{
					//如果之前没有保存过token信息,在这里让用户先登录
					if (!mTencent.isSessionValid()) {
						IUiListener listener = new IUiListener() {

							@Override
							public void onError(UiError arg0) {
								Toast.makeText(getApplicationContext(), "登录错误",
										Toast.LENGTH_SHORT).show();
							}

							@Override
							public void onComplete(JSONObject arg0) {
								Log.v("登录成功", "登录成功");
								//发送信息
								SendMessage();

								//登录成功后把token信息保存在本地中
								editor.putString("openid", mTencent.getOpenId());
								editor.putString("access_token",
										mTencent.getAccessToken());
								//777600 表示token的过期时间。 它是以秒为单位的, 24(小时)*60(分钟)*60(秒) *90(天) = 777600(秒)
								//每次登录后需要获取现在时间 然后减去以前保存的
								//如果过期了 让用户重新登录
								editor.putLong("expires_in", 7776000);
								editor.commit();

							}

							@Override
							public void onCancel() {
								Log.v("登录取消", "登录取消");
							}
						};

						mTencent.login(currentActivity, "all", listener);
					} else {
						mTencent.logout(context);
					}

				}

			}
           });

	}

	private void SendMessage() {

		//下面这段代码基本上是API中的,我简单的改了一下。
		if (mTencent.isSessionValid() && mTencent.getOpenId() != null) {

			Bundle parmas = new Bundle();
			parmas.putString("title", "雨松MOMO程序研究院");// 必须。feeds的标题,最长36个中文字,超出部分会被截断。
			parmas.putString(
					"url",
					"http://www.xuanyusong.com" + "#"
							+ System.currentTimeMillis());// 必须。分享所在网页资源的链接,点击后跳转至第三方网页,
			// 请以http://开头。
			parmas.putString("comment", ("欢迎光临雨松MOMO程序研究院。" + new Date()));// 用户评论内容,也叫发表分享时的分享理由。禁止使用系统生产的语句进行代替。最长40个中文字,超出部分会被截断。
			parmas.putString("summary", "在Unity3D中调用QQ空间分享接口,哇咔咔哇咔咔");// 所分享的网页资源的摘要内容,或者是网页的概要描述。
			// 最长80个中文字,超出部分会被截断。
			parmas.putString("images",
					"http://www.xuanyusong.com/wp-content/uploads/2012/04/0.jpg");// 所分享的网页资源的代表性图片链接",请以http://开头,长度限制255字符。多张图片以竖线(|)分隔,目前只有第一张图片有效,图片规格100*100为佳。
			parmas.putString("type", "4");// 分享内容的类型。

			mTencent.requestAsync(Constants.GRAPH_ADD_SHARE, parmas,
					Constants.HTTP_POST, new BaseApiListener(), null);

		}
	}

	//这个方法一定要加, 不然有错误、、
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		mTencent.onActivityResult(requestCode, resultCode, data);
	}

	private class BaseApiListener implements IRequestListener {

		@Override
		public void onComplete(final JSONObject response, Object state) {
			doComplete(response, state);
		}

		protected void doComplete(JSONObject response, Object state) {
			Log.v("发送成功", "发送成功");
		}

		@Override
		public void onIOException(final IOException e, Object state) {
			Log.v("发表错误", "onIOException");
		}

		@Override
		public void onMalformedURLException(final MalformedURLException e,
				Object state) {
			Log.v("发表错误", "onMalformedURLException");
		}

		@Override
		public void onJSONException(final JSONException e, Object state) {
			Log.v("发表错误", "onJSONException");
		}

		@Override
		public void onConnectTimeoutException(ConnectTimeoutException arg0,
				Object arg1) {
			Log.v("发表错误", "onConnectTimeoutException");
		}

		@Override
		public void onSocketTimeoutException(SocketTimeoutException arg0,
				Object arg1) {
			Log.v("发表错误", "onSocketTimeoutException");
		}

		@Override
		public void onUnknowException(Exception arg0, Object arg1) {
			Log.v("发表错误", "onUnknowException");
		}

		@Override
		public void onHttpStatusException(HttpStatusException arg0, Object arg1) {
			Log.v("发表错误", "onHttpStatusException");
		}

		@Override
		public void onNetworkUnavailableException(
				NetworkUnavailableException arg0, Object arg1) {
			Log.v("发表错误", "onNetworkUnavailableException");
		}
	}

}


如果你不需要在U3d中分享说说?把上段代码简单的改一改吧。把登录放在Creat()方法中吧,会安卓的都知道嘿嘿。
然后在U3D中,绑定在任意对象中, 把Activity传递给Android插件中。
using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour 
{

	void OnGUI()
	{

		if(GUILayout.Button(" Send QQ zone", GUILayout.Height(50)))
		{

             AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");

			 using (AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"))
  			 {
				jo.Call("LoginAndSend",jo);

  		  	 }

		}

	}
}

 下面是Andorid插件的路径,记住把open_Sdk放进去,这个是腾讯提供的第三方包。运行后点击按钮就可以登录与分享说说。
屏幕快照 2013-03-24 下午9.30.59

然后在Android手机上编译运行吧。
最后我在说说我使用QQ空间的感受。 空间分享包括2部分 分享链接 和分享说说,仔细查了一下才发现这两个东西差别挺大的。 文档上说这两个分享接口会被合并,最重要的是暂时不能分享本地图片、如果想分享本地图片有一个办法就是先调用上传图片的接口,把本地图片上传至QQ相册中,然后拿到URL后分享。总体来说希望企鹅的文档以后能写的在清楚一点,我是“连蒙带猜” 哎~
本文的源码下载,包括Android工程和U3D工程。