人人网(www.renren.com)前身为校内网,是中国最大、最受用户欢迎的网络SNS平台。人人网目前针对不同领域的开发者,提供了相应的教程和文档,目前主要分为“站内应用开发”、“第三方网站接入”、“移动客户端接入”和“桌面客户端接入”四大模块。以下结合自己实际做过的一个项目(人人好友电话簿)介绍基于Android开发的移动客户端接入基本流程。

手机等移动设备的客户端应用(如手机游戏、实用工具等)接入人人网,可以使用人人网帐号登录移动客户端,并利用人人网开放平台提供的社交图谱(Social Graph)和传播渠道,增进用户与好友的交互,提升使用体验,并获得广泛传播。

Android手机客户端接入人人网,可以有两种实现方式:

第一种是直接使用人人网开放平台提供的各种接口,如用作验证和授权的OAuth 2.0,提供数据的底层Rest API,以及嵌入各种Widget。关于OAuth 2.0详细协议介绍,请参考https://datatracker.ietf.org/doc/draft-ietf-oauth-v2/,后文会介绍在人人中的相关用法。

第二种是使用人人网开放平台官方封装的开源Android SDK。 人人网最新版的Android SDK实际上是将OAuth 2.0、Rest API等平台提供的底层接口封装起来而已。

在我的项目中我采用了第一种方式,即直接使用人人网开放平台提供的各种接口,这种方式比较简单,自己可以随心所欲的发挥。第二种方式,我看过人人提供的SDK,比较晦涩难懂。如有兴趣,可以自己去研究。

整体流程如下图:

第一步:注册一个人人账户,如果有了,跳过此步。

第二步:用自己的账号密码登陆到人人网,然后跳转到http://dev.renren.com/app,点击创建客户端应用按钮,并按相应的流程填写完相应信息,你就可以提交应用审核。完成后你将得到:API Key和Secret Key,如下图:

API Key就是人人OAuth2.0中的“client_id”,Secret Key就是“client_secret”。

第三、四步:

人人网开放平台提供了上述的OAuth2.0验证与授权流程以支持不同类型的应用,包括:网站、站内应用、手机客户端和桌面客户端。

由于OAuth 2.0协议定义的桌面客户端的授权流程,用户体验方面过于复杂,所以桌面客户端应用可以在应用中嵌入浏览器控件(很多框架都支持嵌入浏览器,例如:.NET、AIR、Cocoa等)使用客户端流程。

由于大部分桌面客户端软件是没有后端没有Web服务器支持,没办法提供一个'redirect_uri',所以人人网为没有Web服务器的客户端应用提供了一个通用的URL:http://graph.renren.com/oauth/login_success.html。

流程如下:

在应用中嵌入一个浏览器控件,并使用客户端流程定向控件到人人OAuth 2.0 Authorize Endpoint(https://graph.renren.com/oauth/authorize):

https://graph.renren.com/oauth/authorize?client_id=YOUR_API_KEY&redirect_uri=http://graph.renren.com/oauth/login_success.html

经过用户验证、应用授权,人人OAuth2.0将把浏览器控件定向导'redirect_uri'(http://graph.renren.com/oauth/login_success.html),并在URI Fragment中追加Access Token:http://graph.renren.com/oauth/login_success.html#access_token=...当应用发现浏览器的控件的URL跳转到这个URL上时,从URL中解析出Access Token。

在Android中加载html用WebView控件,由于Android主要是触屏,因此需要加display=touch参数,完整的URL为

https://graph.renren.com/oauth/authorize?client_id=05d3794614f244c39e300c65f5f68a9e&response_type=token&display=touch&redirect_uri=http://graph.renren.com/oauth/login_success.html

登陆授权流程如下:

主要代码和注释如下:

RenrenLoginActivity.java

[java] view plaincopy

  1. <span style="font-size:16px;">import android.app.Activity;
  2. ……
  3. /**
  4. * @author hexiaoling
  5. */
  6. public class RenrenLoginActivity extends Activity {
  7. public final static String TAG = "RenrenLoginActivity";
  8. private WebView renrenLoginWebView; // WebView 控件,用于显示从人人网请求得到html授权页面
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.renren_login_web_view);
  13. renrenLoginWebView = (WebView) findViewById(R.id.renren_login_web_view); // 得到 WebView 控件
  14. //对WebView进行设置(对JS的支持,对缩放的支持,对缓存模式的支持)
  15. WebSettings webSettings = renrenLoginWebView.getSettings();
  16. webSettings.setJavaScriptEnabled(true);
  17. webSettings.setBuiltInZoomControls(true);
  18. webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
  19. // 根据client_id取得到人人服务器人人对你的应用授权,如果成功则返回人人网登陆页面的html文件,并在WebView控件上显示
  20. // 此时用户需要输入自己人人账号的用户名、密码并点击登陆
  21. renrenLoginWebView.loadUrl("https://graph.renren.com/oauth/authorize?"+
  22. "client_id=05d3794614f244c39e300c65f5f68a9e&response_type=token"+
  23. "&display=touch&redirect_uri=http://graph.renren.com/oauth/login_success.html");
  24. renrenLoginWebView.setWebViewClient(new WebViewClient() {
  25. //击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边
  26. @Override
  27. public boolean shouldOverrideUrlLoading(WebView view, String url) {
  28. view.loadUrl(url);
  29. return true;
  30. }
  31. @Override
  32. public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
  33. handler.proceed();//让webview处理https请求
  34. }
  35. @Override
  36. public void onPageFinished(WebView view, String url) {
  37. String url0 = renrenLoginWebView.getUrl();
  38. String access_token = "";
  39. String expires_in = "";
  40. Log.i(TAG, "URL = " + url0);
  41. if(url0 != null) {
  42. if(url0.contains("access_token=")) { // 从URL中解析得到 access_token
  43. access_token = url0.substring(url0.indexOf("access_token=") + 13, url0.length() - 19);
  44. try {
  45. access_token = URLDecoder.decode(access_token, "utf-8"); // 制定为utf-8编码
  46. catch (UnsupportedEncodingException e) {
  47. e.printStackTrace();
  48. }
  49. Log.i(TAG, "access_token = " + access_token);
  50. }
  51. if(url0.contains("expires_in=")) { // 从URL中解析得到 expires_in
  52. expires_in = url0.substring(url0.indexOf("expires_in=") + 11, url0.length());
  53. Log.i(TAG, "expires_in = " + expires_in);
  54. }
  55. RenrenUtil.access_token = access_token; // 将解析得到的 access_token 保存起来
  56. RenrenUtil.expires_in = expires_in; // 将解析得到的 expires_in 保存起来
  57. //输入用户名、密码登陆成功,进行页面跳转
  58. if(RenrenUtil.access_token.length() != 0) {
  59. Intent intent = new Intent(RenrenLoginActivity.this, RenrenFriendsActivity.class);
  60. startActivity(intent);
  61. }
  62. }
  63. super.onPageFinished(view, url);
  64. }
  65. });
  66. }
  67. }
  68. </span>

下图表示加载人人授权应用的html登陆成果界面:

第五步:这里以访问我的人人好友列表为例,包括取得好友的名称和照片。主要流程如下:

①人人API利用不同功能的请求函数和相关参数,详细请参考http://wiki.dev.renren.com/wiki/API。此处以获得好友列表为例,其对应函数为friends.getFriends(得到用户的信息,支持批量获取),人人对friends.getFriends的描述和参数列表如下图:

下图表示将请求参数和Secret得到MD5值为签名,并组装请求参数核心代码:

[java] view plaincopy

  1. <span style="font-size:16px;">String method = " friends.getFriends "; // 人人API中定义的方法,用于得到当前登录用户的好友列表。
  2. int count = 30; // 得到用户数30个
  3. List<String> param = new ArrayList<String>();
  4. param.add("method=" + method);
  5. param.add("v=1.0"); // 版本固定参数
  6. param.add("access_token=" + RenrenUtil.access_token); //RenrenUtil.access_token 是在LoginActivity中已经保存的数据
  7. param.add("format=JSON"); // 返回JSON数据
  8. param.add("count=" + count); //得到用户数
  9. String signature = getSignature(param, "55f436828286417ab9db7ea17ee9cbde"); //第二个参数为 Secret Key
  10. List<BasicNameValuePair> paramList = new ArrayList<BasicNameValuePair>();
  11. paramList.add(new BasicNameValuePair("sig", signature)); // 签名
  12. paramList.add(new BasicNameValuePair("method", method));
  13. paramList.add(new BasicNameValuePair("v", "1.0"));
  14. paramList.add(new BasicNameValuePair("access_token", RenrenUtil.access_token));
  15. paramList.add(new BasicNameValuePair("format", "JSON"));
  16. paramList.add(new BasicNameValuePair("count", "" + count));
  17. </span>

下图表示将请求参数和SecretKey加密得到它们的MD5值的算法如下:

[java] view plaincopy

  1. <span style="font-size:16px;">/**
  2. * 得到MD5签名
  3. * @param paramList
  4. * @param secret
  5. * @return
  6. */
  7. public String getSignature(List<String> paramList,String secret) {
  8. Collections.sort(paramList);
  9. StringBuffer buffer = new StringBuffer();
  10. for (String param : paramList) {
  11. buffer.append(param);  //将参数键值对,以字典序升序排列后,拼接在一起
  12. }
  13. buffer.append(secret);  //符串末尾追加上应用的Secret Key
  14. try {//下面是将拼好的字符串转成MD5值,然后返回
  15. java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
  16. StringBuffer result = new StringBuffer();
  17. try {
  18. for (byte b : md.digest(buffer.toString().getBytes("utf-8"))) {
  19. result.append(Integer.toHexString((b & 0xf0) >>> 4));
  20. result.append(Integer.toHexString(b & 0x0f));
  21. }
  22. catch (UnsupportedEncodingException e) {
  23. for (byte b : md.digest(buffer.toString().getBytes())) {
  24. result.append(Integer.toHexString((b & 0xf0) >>> 4));
  25. result.append(Integer.toHexString(b & 0x0f));
  26. }
  27. }
  28. return result.toString();
  29. catch (java.security.NoSuchAlgorithmException ex) { }
  30. return null;
  31. }
  32. </span>

②使用HTTP协议,Android已经对其做了很好的封装,使用起来很方便。使用将第①步得到的参数列表想http://api.renren.com/restserver.do发送Http请求,同时得到HttpResponse响应,判断响应代码是否为200,为200表示成功。然后解析JSON数据为相关对象,核心代码如下:

[java] view plaincopy

  1. <span style="font-size:16px;">String returnValue = "0";
  2. try {
  3. HttpPost httpPost = new HttpPost("http://api.renren.com/restserver.do");
  4. httpPost.setEntity(new UrlEncodedFormEntity(paramList, HTTP.UTF_8));// 添加请求参数到请求对象
  5. HttpResponse httpResponse = httpClient.execute(httpPost);
  6. if(httpResponse.getStatusLine().getStatusCode() == 200) { //为200表示执行成功
  7. strResult = EntityUtils.toString(httpResponse.getEntity()); //得到返回数据(为JSON数据)
  8. if(! strResult.contains("error_code")) {
  9. renrenList = Renren.parseRenrenFromJson(strResult); //解析JSON数据为相应对象
  10. returnValue = "1"; //定义返回标志
  11. }
  12. }
  13. catch (ClientProtocolException e) {
  14. strResult = e.getMessage().toString();
  15. e.printStackTrace();
  16. catch (IOException e) {
  17. strResult = e.getMessage().toString();
  18. e.printStackTrace();
  19. catch (Exception e) {
  20. strResult = e.getMessage().toString();
  21. e.printStackTrace();
  22. }
  23. return returnValue;
  24. </span>

网络请求会比较耗时,因此建议使用多线程来处理。在Android中可以使用继承AsyncTask<String, Integer,String>类来实现多线程处理。

③解析JSON数据为人人对象方法:

[java] view plaincopy

  1. <span style="font-size:16px;">/**
  2. * 解析JSON数据为人人对象
  3. * @param renrenJsonData
  4. * @return
  5. */
  6. public List<Renren> parseRenrenFromJson(String renrenJsonData) {
  7. List<Renren> renrenList = new ArrayList<Renren>();
  8. try {
  9. JSONArray jsonArray = new JSONArray(renrenJsonData);
  10. int length = jsonArray.length();
  11. for (int i = 0; i < length; ++i) {
  12. JSONObject jsonObject = jsonArray.getJSONObject(i);
  13. Renren renren = new Renren();
  14. renren.setId(jsonObject.getString("id"));
  15. renren.setName(jsonObject.getString("name"));
  16. renren.setHeadurl(jsonObject.getString("headurl"));
  17. renrenList.add(renren);
  18. }
  19. return renrenList;
  20. catch (JSONException e) { }
  21. return null;
  22. }
  23. </span>

下图为JSON数据的数据结构:

人人类Renren.java

[java] view plaincopy

  1. <span style="font-size:16px;">public class Renren {
  2. protected String id;
  3. protected String name;
  4. protected String headurl;
  5. 省略get、set方法。
  6. }
  7. </span>

④在List中显示人人好友列表(图片、名称),并用Adapater来加载数据。由于请求friends.getFriends返回的只有图片的URL,因此图片还需要单独去请求,为了保障用户体验,图片采用异步加载。

[java] view plaincopy

  1. <span style="font-size:16px;">public class FriendsAdapater extends BaseAdapter {
  2. private AsyncImageLoader asyncImageLoader = new AsyncImageLoader();  // 异步获取图片
  3. @Override
  4. public int getCount() {
  5. return renrenList == null ? 0 : renrenList.size();
  6. }
  7. @Override
  8. public Object getItem(int position) {
  9. return  renrenList == null ? null :renrenList.get(position);
  10. }
  11. @Override
  12. public long getItemId(int position) {
  13. return position;
  14. }
  15. @Override
  16. public View getView(int position, View convertView, ViewGroup parent) {
  17. convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.friend, null);
  18. userName= (TextView) convertView.findViewById(R.id.username);
  19. userImage = (ImageView) convertView.findViewById(R.id.userimage);
  20. Renren renren = renrenList.get(position);
  21. if (renren != null) {
  22. convertView.setTag(renren.getId());
  23. userImage.setTag(renren.getHeadurl());
  24. userName.setText(renren.getName());
  25. //异步加载图片并显示
  26. Drawable cachedImage = asyncImageLoader.loadDrawable(renren, new ImageCallback() {
  27. @Override
  28. public void imageLoaded(Drawable imageDrawable, String imageUrl) {
  29. ImageView imageView = (ImageView) friendsList.findViewWithTag(imageUrl);
  30. if(imageView != null) {
  31. imageView.setImageDrawable(imageDrawable);
  32. }
  33. }
  34. });
  35. if (cachedImage != null) {
  36. userImage.setImageDrawable(cachedImage);
  37. else {//如果没有图片,就以一个载入的图片代替显示
  38. userImage.setImageResource(R.drawable.icon);
  39. }
  40. }
  41. return convertView;
  42. }
  43. }
  44. </span>

异步加载图片类AsyncImageLoader.java

[java] view plaincopy

  1. <span style="font-size:16px;">/**
  2. * 异步加载图片类
  3. * @author hexiaoling
  4. */
  5. public class AsyncImageLoader {
  6. private HashMap<String, SoftReference<Drawable>> imageCache; //缓存图片
  7. public AsyncImageLoader() {
  8. imageCache = new HashMap<String, SoftReference<Drawable>>();
  9. }
  10. public Drawable loadDrawable(final Renren renren, final ImageCallback imageCallback) {
  11. final String imageId = renren.getId();
  12. final String imageURL = renren.getHeadurl();
  13. //判断缓存中是否已经存在图片,如果存在则直接返回
  14. if (imageCache.containsKey(imageId)) {
  15. SoftReference<Drawable> softReference = imageCache.get(imageId);
  16. Drawable drawable = softReference.get();
  17. if (drawable != null) {
  18. return drawable;
  19. }
  20. }
  21. final Handler handler = new Handler() {
  22. public void handleMessage(Message message) {
  23. imageCallback.imageLoaded((Drawable) message.obj, imageURL);
  24. }
  25. };
  26. //开辟一个新线程去下载图片,并用Handler去更新UI
  27. new Thread() {
  28. @Override
  29. public void run() {
  30. Drawable drawable = loadImageFromUrl(imageURL);
  31. imageCache.put(imageURL, new SoftReference<Drawable>(drawable));
  32. Message message = handler.obtainMessage(0, drawable);
  33. handler.sendMessage(message);
  34. }
  35. }.start();
  36. return null;
  37. }
  38. //从URL下载图片
  39. public static Drawable loadImageFromUrl(String url) {
  40. URL m;
  41. InputStream i = null;
  42. try {
  43. m = new URL(url);
  44. i = (InputStream) m.getContent();
  45. catch (MalformedURLException e1) {
  46. e1.printStackTrace();
  47. catch (IOException e) {
  48. e.printStackTrace();
  49. }
  50. Drawable d = Drawable.createFromStream(i, "src");
  51. return d;
  52. }
  53. //回调接口
  54. public interface ImageCallback {
  55. //回调函数
  56. public void imageLoaded(Drawable imageDrawable, String imageUrl);
  57. }
  58. }
  59. </span>

下图为运行结果:(因涉及好友真实信息,不将部分图片和文字加码,此结果仅表示按照此步骤运行正确)

另外需要注意的是,访问人人API需要访问网络,因此需要在添加网络访问授权

<uses-permissionandroid:name="android.permission.INTERNET" />

此文仅简单介绍了基于人人网API的Android开发的基本步骤,如需了解更多信息,请研究人人官网http://wiki.dev.renren.com/wiki/API。欢迎就上述问题进一步交流。

参考文献:http://wiki.dev.renren.com/wiki/API

基于人人网的Android开发流程介绍相关推荐

  1. 基于模型的嵌入式开发流程_如何使用基于模型的测试来改善工作流程

    基于模型的嵌入式开发流程 Unit testing is not enough – so let's start using model-based testing to improve our wo ...

  2. Spring Security技术栈学习笔记(十三)Spring Social集成第三方登录验证开发流程介绍

    开发第三方登录,我们必须首先要了解OAuth协议(本文所讲述的OAuth协议指的是OAuth2协议),本文首先简单介绍OAuth协议,然后基于Spring Social来阐述开发第三方登录需要做哪些准 ...

  3. (2)FPGA开发流程介绍(第1天)

    (2)FPGA开发流程介绍(第1天) 1 文章目录 1)文章目录 2)FPGA初级课程介绍 3)FPGA初级课程架构 4)FPGA开发流程介绍(第1天) 5)技术交流 6)参考资料 2 FPGA初级课 ...

  4. sawtooth,井字棋演示和交易族开发流程介绍

    1.实例演示 这里以官网的XO交易族为例演示,该交易族是一个井字棋游戏,在开始之前,我们需要搭建起来一个单节点的sawtooth环境,详情可以查看上一篇博客: Sawtooth,使用docker启动单 ...

  5. android studio开发工具介绍,Android应用开发之Android开发工具介绍、Android Studio配置...

    本文将带你了解Android应用开发之Android开发工具介绍.Android Studio配置,希望本文对大家学Android有所帮助. 2.1   Android Studio配置 2.1.1 ...

  6. Android开发流程总结

    本文来自肥宝传说之路,引用必须注明出处! Android开发流程总结 首先,发个作品链接先 http://www.mumayi.com/android-550241.html 一.注册开发者账号: 开 ...

  7. 基于chromium for android开发Android浏览器

    在上一篇文章<< Chromium for Android开源了 >>中谈到了Google已经完全开源了Chromium for Android,这样我们就完全可以开发与Chr ...

  8. 网站的开发流程介绍(转)

    网站的开发流程介绍 从本章开始,我们将学习网站设计和布局技术,其中重点是熟悉网站的开发流程,DIV层和表格布局各自的使用场合,以及网站开发的一些经验.技巧:难点是如何进行网页布局. 创建一个商业网站, ...

  9. Android JNI开发流程介绍

    1.什么是JNI JNI全称是Java Native Interface,中文称为Java本地接口.JNI是JAVA语言和C/C++语言沟通的协议,通过JNI,Java代码可以调用C.C++等语言写的 ...

最新文章

  1. 按下开机键,计算机背后的故事
  2. 外包 银行数字签名U盘的用法
  3. android测试点整理
  4. java fileupload 进度_Java上传文件进度条的实现方法(附demo源码下载)
  5. Altium Designer Winter09 的使用心得
  6. Go语言的DES加密(CBC模式, ECB模式) ---- 与java加密互通(转)
  7. StringBuilder的实现与技巧
  8. 单点登录Redis存储Session及SessionId问题说明与集群实战-3
  9. 关闭word_Word教程第2讲:文档的基本操作(含视频)
  10. php函数库快速记忆法_PHP速成大法
  11. 【渝粤教育】国家开放大学2018年秋季 0692-22T化工设备机械基础 参考试题
  12. 企业级项目实战讲解!java的war包能直接改名么
  13. AngularJs自学心得
  14. 清华大学医学院 | 体外成熟人卵单细胞多组学研究及总结干细胞分化为配子进展文章...
  15. element ui el-dialog 居中,并且内容多的时候内部可以滚动
  16. SQL 高效的万能分页存储过程
  17. (1)-(Two SUM-在数组中找到两个数,他们的和为给定的数)-(数组遍历)
  18. C# WinForm程序中使用Unity3D控件
  19. 小米笔记本、小米游戏本重装原装出厂镜像教程-有百度盘的提取码
  20. 小程序图片上传及预览和删除

热门文章

  1. Catalyst 5.8: the Perl MVC Framework
  2. linux 定时任务,压缩 日志,并删除掉 指定日期之前的 日志
  3. linux(cat,more,less,head)——对文件显示进行查看操作
  4. ANDROID深度探索(卷1)HAL与驱动开发 第四章
  5. 缓存2 动态缓存 memcached
  6. Java 的布局管理器GridBagLayout的使用方法【图文说明】
  7. soapUI中多个TestCase之间传递参数
  8. oracle 10g 学习之函数和存储过程(12)
  9. c# 使用timer定时器操作,上次定时到了以后,下次还未执行完怎么处理
  10. 【征稿+竞赛】WACV 2021 第一届无人车视觉学术研讨会