Android应用开发中,第三方集成新浪微博(sinaWeiboSDK)的过程记录
作为一个android开发人员,不可避免的要学会使用和集成第三方API的能力
而新浪微博作为现在最主要的新闻速递媒体,使用十分普遍,并且提供了较为详细的API接入方法,故此选择集成sinaWeibiSdk.
step1.准备步骤:
首先要注册成为sina开发者身份,并在sina的开发平台政策和指导下,创建和注册自己将要集成的应用,其中包括sniaWeiboSdk下载,应用创建,获取APP_KEY 和APP secret的信息等基本的集成信息准备。
step2.集成sdk内容到自己的project
注:具体的步骤在sina给出的开发者文档中有详细的实例和讲解,这里仅仅记录自己遇到的问题。
两种sdk的权限范围和导入方法:
1.weibosdkcore.jar包:适用于只需要授权、分享、网络请求框架功能的项目
2.WeiboSDK工程(Library):适用于微博授权、分享,以及需要登陆按钮、调用OpenAPI的项目
导入weibosdkcore.jar包的方法:
切换project的显示视角到project选项->直接将weibosdkcore.jar包文件复制粘贴到libs文件夹下->add as external Library->sync
将整个WeiboSDK工程(Library)作为library的方法:
1.切换project的显示视角到Android选项
2.将WeiboSDK工程整个目录拷贝到和你自己的工程相同的目录下/import new module
3.找到WeiboSDK项目的build.gradle文件夹,找到apply plugin: 'com.android.application' 这条设置选项,将这条选项更改为 apply plugin: 'com.android.library',然后选择sync
4.点击自己的项目->右键->open module setting ->选择 Dependencies -> 加号,添加sinaSDK到Dependencies->sync
注:
1.两种sdk同时导入工程会引发Exception,没有找到解决办法,由于SDK工程功能更加的全面,故尝试删除导入的weibosdkcore.jar包,sync/rebuild工程,即能同时运行。
具体的网络数据交互,json数据解析,界面设计,用户交互等在全部设计完成后,再行一次性更新。
————————————————————————————————————————————————————————————
接上文更新,要使用新浪微博的SDK,需要做一些准备工作,其中包括:
1.上文中提到的SDK工程导入,as Library
2.配置Constants类中的信息,此类中的信息在注册应用的时候可以获得,将对应的项目改为自己应用的数据(此数据需严格保密)
3.为了能够顺利使用SDK的相关API,在assets文件中,导入libweibosdkcore.co文件,并在manifests文件中注册用来授权的Activity
注册该Activity的代码:(该Activity是作为授权切换界面的)
<activity android:name=".WeiboManager"> <intent-filter> <action android:name="com.sina.weibo.sdk.action.ACTION_SDK_REQ_ACTIVITY" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity>
以上准备工作完成后,下面这是用来获取微博授权的Activity代码
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.view.View; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取微博的Token信息(即为凭证) findViewById(R.id.btn_sina).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) {
//WBAuthActivity即为授权界面的Activity Intent i = new Intent(MainActivity.this, WBAuthActivity.class); startActivity(i); } }); } }
获取微博返回的Token信息后,获取微博内容的操作:
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.TextView; import android.widget.Toast; import com.sina.weibo.sdk.auth.AuthInfo; import com.sina.weibo.sdk.auth.Oauth2AccessToken; import com.sina.weibo.sdk.auth.WeiboAuthListener; import com.sina.weibo.sdk.auth.sso.SsoHandler; import com.sina.weibo.sdk.exception.WeiboException; import java.text.SimpleDateFormat; public class WBAuthActivity extends Activity { private static final String TAG = "MyApplication"; /** 显示认证后的信息,如 AccessToken */ private TextView mTokenText; private AuthInfo mAuthInfo; /** 封装了 "access_token","expires_in","refresh_token",并提供了他们的管理功能 */ private Oauth2AccessToken mAccessToken; /** 注意:SsoHandler 仅当 SDK 支持 SSO 时有效 */ private SsoHandler mSsoHandler; /** * @see {@link Activity#onCreate} */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_auth); // 创建微博实例 //mWeiboAuth = new WeiboAuth(this, Constants.APP_KEY, Constants.REDIRECT_URL, Constants.SCOPE); // 快速授权时,请不要传入 SCOPE,否则可能会授权不成功 mAuthInfo = new AuthInfo(this, Constants.APP_KEY, Constants.REDIRECT_URL, Constants.SCOPE); mSsoHandler = new SsoHandler(WBAuthActivity.this, mAuthInfo); mTokenText = (TextView) findViewById(R.id.mTokenText); // SSO 授权, ALL IN ONE 如果手机安装了微博客户端则使用客户端授权,没有则进行网页授权 findViewById(R.id.obtain_token_via_allInOne).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mSsoHandler.authorize(new AuthListener()); } }); //获取微博信息,这里是已经获得授权信息后,进行获取微博返回的操作切入点 findViewById(R.id.btn_sinaWeiBo).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent i = new Intent(WBAuthActivity.this,WeiboManager.class); startActivity(i); } }); // 用户登出,即销毁已获得的Token信息 findViewById(R.id.logout).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { AccessTokenKeeper.clear(getApplicationContext()); mAccessToken = new Oauth2AccessToken(); updateTokenView(false); } });// 从 SharedPreferences 中读取上次已保存好 AccessToken 等信息, // 第一次启动本应用,AccessToken 不可用 mAccessToken = AccessTokenKeeper.readAccessToken(this); if (mAccessToken.isSessionValid()) { updateTokenView(true); } } /** * 当 SSO 授权 Activity 退出时,该函数被调用。 * * @see {@link Activity#onActivityResult} */
//此方法必须写上,原因由于时间太长,已然忘记,只记得必须写 - -!!!! @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); // SSO 授权回调 // 重要:发起 SSO 登陆的 Activity 必须重写 onActivityResults if (mSsoHandler != null) { mSsoHandler.authorizeCallBack(requestCode, resultCode, data); } } /** * 微博认证授权回调类。 * 1. SSO 授权时,需要在 {@link #onActivityResult} 中调用 {@link SsoHandler#authorizeCallBack} 后, * 该回调才会被执行。 * 2. 非 SSO 授权时,当授权结束后,该回调就会被执行。 * 当授权成功后,请保存该 access_token、expires_in、uid 等信息到 SharedPreferences 中。 */ class AuthListener implements WeiboAuthListener { @Override public void onComplete(Bundle values) { // 从 Bundle 中解析 Token mAccessToken = Oauth2AccessToken.parseAccessToken(values); //从这里获取用户输入的 电话号码信息 String phoneNum = mAccessToken.getPhoneNum(); if (mAccessToken.isSessionValid()) { // 显示 Token updateTokenView(false); // 保存 Token 到 SharedPreferences AccessTokenKeeper.writeAccessToken(WBAuthActivity.this, mAccessToken); Toast.makeText(WBAuthActivity.this, "保存Accesstoken成功", Toast.LENGTH_SHORT).show(); } else { // 以下几种情况,您会收到 Code: // 1. 当您未在平台上注册的应用程序的包名与签名时; // 2. 当您注册的应用程序包名与签名不正确时; // 3. 当您在平台上注册的包名和签名与您当前测试的应用的包名和签名不匹配时。 String code = values.getString("code"); String message = "get token failed"; if (!TextUtils.isEmpty(code)) { message = message + "\nObtained the code: " + code; } Toast.makeText(WBAuthActivity.this, message, Toast.LENGTH_LONG).show(); } } @Override public void onCancel() { Toast.makeText(WBAuthActivity.this, "取消", Toast.LENGTH_LONG).show(); } @Override public void onWeiboException(WeiboException e) { Toast.makeText(WBAuthActivity.this, "Auth exception : " + e.getMessage(), Toast.LENGTH_LONG).show(); } } /** * 显示当前 Token 信息。 * * @param hasExisted 配置文件中是否已存在 token 信息并且合法 */ private void updateTokenView(boolean hasExisted) { String date = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss").format( new java.util.Date(mAccessToken.getExpiresTime())); String format = getString(R.string.weibosdk_demo_token_to_string_format_1); mTokenText.setText(String.format(format, mAccessToken.getToken(), date)); String message = String.format(format, mAccessToken.getToken(), date); if (hasExisted) { message = getString(R.string.weibosdk_demo_token_has_existed) + "\n" + message; } mTokenText.setText(message); } }
//注:以上很多东西均来自SDK Demo的示例代码,为了适应自己的工程稍作修改所以保留了绝大部分不需要改动的部分
已经通过了Token验证,并连接到weibo后,进入自己编写的管理窗口Activity:
具体代码:
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.text.TextUtils; import android.view.View; import android.widget.Toast; import com.sina.weibo.sdk.auth.Oauth2AccessToken; import com.sina.weibo.sdk.exception.WeiboException; import com.sina.weibo.sdk.net.RequestListener; import com.sina.weibo.sdk.openapi.UsersAPI; import com.sina.weibo.sdk.openapi.legacy.StatusesAPI; public class WeiboManager extends Activity { private Oauth2AccessToken mAccessToken; private UsersAPI mUsersAPI; private StatusesAPI statusesAPI; // private IWeiboShareAPI mWeiboShareAPI; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weibo_manager); // mWeiboShareAPI = WeiboShareSDK.createWeiboAPI(this,Constants.APP_KEY); // mWeiboShareAPI.registerApp(); // 获取当前已保存过的 Token mAccessToken = AccessTokenKeeper.readAccessToken(this); mUsersAPI = new UsersAPI(WeiboManager.this,Constants.APP_KEY,mAccessToken); // 获取用户信息接口 statusesAPI = new StatusesAPI(WeiboManager.this,Constants.APP_KEY,mAccessToken); //创建微博分享接口实例 findViewById(R.id.btn_StartWeibo).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //这是获取绑定微博的最新微博信息的静态方法
//下面各自的参数有着不同的代表意义,在API的源代码文档中有详细的说明,其中第三个参数30代表的是返回30条该用户的最新微博信息
//需要注意的是最后需要传入RequestListener对象,此对象会收到服务器的返回信息
statusesAPI.friendsTimeline(0,0,30,1,false,0,false,mListener); } }); } //新建一个RequestListener对象,此对象用来接收微博服务器给出的回应消息
// 对于返回值的具体操作由自己编写 private RequestListener mListener = new RequestListener() { @Override public void onComplete(String response) { if (!TextUtils.isEmpty(response)) {
//这里的response即为微博服务器返回的信息流,格式为json
//将此信息流通过Intent传递到对应的显示Activity中,再行解析和显示
Intent i = new Intent(WeiboManager.this, WeiboListView.class); i.putExtra("response", response); startActivity(i); } } @Override public void onWeiboException (WeiboException e){ Toast.makeText(WeiboManager.this, "WeiboException", Toast.LENGTH_LONG).show(); } }; }
通过Intent对象传递的response数据的对应Activity对应的代码:
import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import com.sina.weibo.sdk.openapi.models.User; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; public class WeiboListView extends Activity{ private ListView WeiboListView; private ArrayList<CustomWeibo> CustomWeibos; private String response; private CustomWeibo customWeibo; private Handler handler = null; private ListAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weibo_list_view); WeiboListView = (ListView) findViewById(R.id.WeiboListView); CustomWeibos = new ArrayList<CustomWeibo>(); //获取微博返回的数据流对象(注:通过Intent传递信息的数据量有限制,使用需慎重!) Intent i = getIntent(); response = i.getStringExtra("response"); // 将获得的response信息传递到自己编写的方法中,解析JSON数据流的信息内容 parsingJSON(response);
//以下的异步方法是为了实现对于图片的异步加载 Runnable runnable = new Runnable() { @Override public void run() { try {
//一定要线程休眠一段时间,因为新建handler在下一步,若不休眠会产生空指针异常 Thread.sleep(2000);
//休眠2s后,当解析完成了,将解析后的微博数据传递给handler更新UI handler.sendMessage(handler.obtainMessage(0,CustomWeibos)); } catch (InterruptedException e) { e.printStackTrace(); } } }; new Thread(runnable).start(); handler = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what == 0){
//但接收的消息是来自上面的代码发送,则启动程序,更新UI
//(但有一点表示怀疑,数据解析的速度和上文中执行代码的速度是否能够保证数据的完整和线程安全) //CustomWeibo是自己为了更方便地显示微博信息而自定义的类,里面包含了一条微博所要显示的内容,通过ArrayList来保存
ArrayList<CustomWeibo> CustomeWeibos = (ArrayList<CustomWeibo>) msg.obj; //这是为了保证数据在每个控件正确显示而写出来的调用函数
BinderListData(CustomeWeibos); } } }; } //解析JSon数据内容不做解释,简单易懂,相信自己不会以后再来看不懂 public void parsingJSON(String response){ try { JSONObject jsonObject = new JSONObject(response); JSONArray array = jsonObject.getJSONArray("statuses"); JSONObject temp = null; String text = null; int count = -1; JSONObject usertemp = null; User user = null; for (int i = 0;i<array.length();i++){ customWeibo = new CustomWeibo(); if ((temp=array.getJSONObject(i))!=null){ //微博正文 if ((text = temp.getString("text"))!=null){ customWeibo.setText(text); } //发布时间 if ((text = temp.getString("created_at"))!=null){ customWeibo.setCreated_at(text); } //用户信息 if ((usertemp = temp.getJSONObject("user"))!=null){ user = User.parse(usertemp); customWeibo.setUser(user); user=null; } //转发数 if ((count = temp.getInt("reposts_count"))!=-1){ customWeibo.setReports_count(count); count = -1; } //评论数 if ((count = temp.getInt("comments_count"))!=-1){ customWeibo.setComment_count(count); count = -1; } //点赞数 if ((count = temp.getInt("attitudes_count"))!=-1){ customWeibo.setAttitudes_count(count); count = -1; } CustomWeibos.add(customWeibo); customWeibo = null; } } } catch (JSONException e) { e.printStackTrace(); } } public void BinderListData(final ArrayList<CustomWeibo> CustomeWeibos){
//由下可以明显看出我们还自定义了一个用于显示微博信息的适配器内容的adapter,下文贴出 adapter = new ListViewAdapter(R.layout.item,WeiboListView.this,CustomeWeibos); WeiboListView.setAdapter(adapter); } private void LoadImage(ImageView imageView,String path){
//为了实现图片异步加载和显示,自定义了一个异步加载图片并显示的类,下文贴出 AsyncTaskImageLoad asyncTaskImageLoad = new AsyncTaskImageLoad(imageView); asyncTaskImageLoad.execute(path); } }
这是用于显示微博信息的界面适配器的具体代码:
import android.app.Activity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.TextView; import java.util.ArrayList; /** * Created by Andrew on 2016/7/21. */ public class ListViewAdapter extends BaseAdapter implements ListAdapter { private ArrayList<CustomWeibo> CustomWeibos; private int id; private Context context; private LayoutInflater inflater; public ListViewAdapter(int item, Activity mainActivity, ArrayList<CustomWeibo> data){ this.context = mainActivity; this.id = item; inflater = LayoutInflater.from(context); this.CustomWeibos = data; } @Override public int getCount() { return CustomWeibos.size(); } @Override public Object getItem(int position) { return CustomWeibos.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ImageView img = null; ImageView iv_sina_head = null; TextView tv_UserName = null; TextView tv_sinaWeibo = null; Button reports_count = null; Button comment_count = null; Button attitudes_count = null; if (convertView == null){ convertView = inflater.inflate(id,null); iv_sina_head = (ImageView) convertView.findViewById(R.id.iv_sina_head); tv_UserName = (TextView) convertView.findViewById(R.id.tv_UserName); tv_sinaWeibo = (TextView) convertView.findViewById(R.id.tv_sinaWeibo); reports_count = (Button) convertView.findViewById(R.id.reports_count); comment_count = (Button) convertView.findViewById(R.id.comment_count); attitudes_count = (Button) convertView.findViewById(R.id.attitudes_count);
//整段代码最重要的部分就是这一句!//整段代码最重要的部分就是这一句!//整段代码最重要的部分就是这一句!//整段代码最重要的部分就是这一句!
convertView.setTag(new ObjectClass(iv_sina_head,tv_UserName,tv_sinaWeibo,reports_count,comment_count,attitudes_count));
//另外需要提醒自己的是,这段程序不是仅仅执行一次,而是每一次对界面的滑动和操作都会调用此方法,故自定义了一个对象,用于绑定对应的控件内容,保证正确显示
} else { ObjectClass objectClass = (ObjectClass) convertView.getTag(); iv_sina_head = objectClass.iv_sina_head; tv_UserName = objectClass.tv_UserName; tv_sinaWeibo = objectClass.tv_sinaWeibo; reports_count = objectClass.reports_count; comment_count = objectClass.comment_count; attitudes_count = objectClass.attitudes_count; } tv_sinaWeibo.setText(CustomWeibos.get(position).getText()); reports_count.setText("转发数:"+CustomWeibos.get(position).getReports_count()); comment_count.setText("评论数:"+CustomWeibos.get(position).getComment_count()); attitudes_count.setText("点赞数:"+CustomWeibos.get(position).getAttitudes_count()); tv_UserName.setText(CustomWeibos.get(position).getUser().name); String Url = CustomWeibos.get(position).getUser().profile_image_url; LoadImage(iv_sina_head,Url); return convertView; } private void LoadImage(ImageView imageView,String path){ AsyncTaskImageLoad asyncTaskImageLoad = new AsyncTaskImageLoad(imageView); asyncTaskImageLoad.execute(path); } private final class ObjectClass{ ImageView iv_sina_head = null; TextView tv_UserName = null; TextView tv_sinaWeibo = null; Button reports_count = null; Button comment_count = null; Button attitudes_count = null; public ObjectClass(ImageView iv_sina_head,TextView tv_UserName,TextView tv_sinaWeibo,Button reports_count,Button comment_count,Button attitudes_count){ this.attitudes_count = attitudes_count; this.comment_count = comment_count; this.iv_sina_head = iv_sina_head; this.reports_count = reports_count; this.tv_sinaWeibo = tv_sinaWeibo; this.tv_UserName = tv_UserName; } } }
下面是一部加载图片的方法:
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import android.widget.ImageView; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; /** * Created by Andrew on 2016/7/21. */ public class AsyncTaskImageLoad extends AsyncTask<String,Integer,Bitmap> { private ImageView image = null; public AsyncTaskImageLoad(ImageView imageView){ this.image = imageView; } @Override protected Bitmap doInBackground(String... params) { try { URL Url = new URL(params[0]); HttpURLConnection connection = (HttpURLConnection) Url.openConnection(); connection.setRequestMethod("POST"); connection.setConnectTimeout(5000); if (connection.getResponseCode() == 200){ InputStream inputStream = connection.getInputStream(); Bitmap map = BitmapFactory.decodeStream(inputStream); return map; } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Bitmap bitmap) { if (image!=null&&bitmap!=null){ image.setImageBitmap(bitmap); } } }
最后对于看到本段代码的说明:
作为一个菜鸟,这里写的代码多半是为了写给自己看
这里仅仅实现了微博信息的获取和显示,图片异步加载功能,有很多功能并没有实现,其中有很多方法有严重的性能问题,但为了先行实现功能而未作修改
若是对于本文代码有任何问题,欢迎留言联系或直接询问,不吝指点,共同提高。