之前在网上看过好多人用java写登录网站,这次正好学习android,自己写实习一个,就拿经常登录的javaeye(现在叫iteye,已经是csdn的了)试一下。
先来分析javaeye登录界面
查看源文件
- <formaction="/login"id="login_form"method="post"><tableborder="0"cellspacing="0"cellpadding="0"class="table_1">
- <colgroup><colwidth="60"/><col/></colgroup>
- <tr>
- <th>账号</th>
- <td>
- <inputclass="input_1required"id="user_name"name="name"placeholder="用户名或邮箱"tabindex="1"type="text"value=""/>
- </td>
- </tr>
- <tr>
- <th>密码</th>
- <td>
- <inputclass="input_1required"id="password"name="password"tabindex="2"type="password"value=""/></td>
- </tr>
- <tr>
- <th></th>
- <td>
- <inputid="auto"name="remember_me"tabindex="3"type="checkbox"value="1"/>
- <labelfor="auto">下次自动登录</label>
- <ahref="/users/forget">忘记密码?</a>
- </td>
- </tr>
- <tr>
- <th></th>
- <td><inputtype="submit"name="button"id="button"value="登 录"class="btn_1submit"tabindex="4"/></td>
- </tr>
- </table>
- </form>
可以分析出post地址为http://www.iteye.com/login,账号是name、密码是password。
登录以后,使用chrome的开发人员工具(其他浏览器也有相关工具,我就不一一说了)拦截获取响应消息
可以看出返回的是302地址重定向
还需要注意的是每次提交时,不能遗漏的cookie即seesion_id ,javaeye用的是_javaeye3_session
接下来是我做的android的例子,使用webview控件,它只负责内容展现,请求响应我分别使用了HttpURLConnection和HttpClient。
不说了,上代码:
主布局main.xml代码如下:
- <?xmlversion="1.0"encoding="utf-8"?>
- <RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/RelativeLayout1"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- android:orientation="vertical">
- <TextView
- android:id="@+id/textViewInfo"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"/>
- <WebView
- android:id="@+id/webViewInfo"
- android:layout_width="match_parent"
- android:layout_height="260dp"
- android:layout_below="@+id/textViewInfo"
- android:layout_marginTop="30dp"
- android:layout_alignParentLeft="true"/>
- <LinearLayout
- android:id="@+id/linearLayout2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_above="@+id/linearLayout1"
- android:layout_alignParentLeft="true">
- <TextView
- android:id="@+id/textView2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="使用Java"/>
- <Button
- android:id="@+id/buttonJavaLogin"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/textView2"
- android:text="登录ITeye"/>
- <Button
- android:id="@+id/buttonJavaMyMessage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/buttonJavaLogin"
- android:text="ITeye收件箱"/>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/linearLayout1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_alignParentLeft="true">
- <TextView
- android:id="@+id/textView1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="使用Apache"/>
- <Button
- android:id="@+id/buttonApacheLogin"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/textView1"
- android:text="登录ITeye"/>
- <Button
- android:id="@+id/buttonApacheMyMessage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_toRightOf="@+id/buttonApacheLogin"
- android:text="ITeye收件箱"/>
- </LinearLayout>
- </RelativeLayout>
登录对话框布局login.xml代码如下:
- <?xmlversion="1.0"encoding="utf-8"?>
- <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
- <LinearLayout
- android:id="@+id/linearLayout1"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="账号:"/>
- <EditText
- android:id="@+id/editTextUsername"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
- </LinearLayout>
- <LinearLayout
- android:id="@+id/linearLayout2"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="密码:"/>
- <EditText
- android:id="@+id/exitTextPassword"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:inputType="textPassword"/>
- </LinearLayout>
- </LinearLayout>
AndroidManifest.xml不要忘了加如下代码:
- <uses-permissionandroid:name="android.permission.INTERNET"/>
主程序代码如下:
- packagecom.zhang.test08_16;
- importjava.io.BufferedInputStream;
- importjava.io.BufferedOutputStream;
- importjava.io.BufferedReader;
- importjava.io.IOException;
- importjava.io.InputStream;
- importjava.io.InputStreamReader;
- importjava.io.OutputStream;
- importjava.io.OutputStreamWriter;
- importjava.io.UnsupportedEncodingException;
- importjava.io.Writer;
- importjava.net.HttpURLConnection;
- importjava.net.MalformedURLException;
- importjava.net.ProtocolException;
- importjava.net.URL;
- importjava.util.ArrayList;
- importjava.util.List;
- importorg.apache.http.HttpEntity;
- importorg.apache.http.HttpResponse;
- importorg.apache.http.HttpStatus;
- importorg.apache.http.NameValuePair;
- importorg.apache.http.client.ClientProtocolException;
- importorg.apache.http.client.entity.UrlEncodedFormEntity;
- importorg.apache.http.client.methods.HttpGet;
- importorg.apache.http.client.methods.HttpPost;
- importorg.apache.http.client.methods.HttpUriRequest;
- importorg.apache.http.impl.client.DefaultHttpClient;
- importorg.apache.http.message.BasicNameValuePair;
- importorg.apache.http.params.CoreProtocolPNames;
- importorg.apache.http.params.HttpParams;
- importorg.apache.http.protocol.HTTP;
- importorg.apache.http.util.EntityUtils;
- importandroid.app.Activity;
- importandroid.app.AlertDialog;
- importandroid.content.DialogInterface;
- importandroid.os.Bundle;
- importandroid.view.LayoutInflater;
- importandroid.view.View;
- importandroid.webkit.WebView;
- importandroid.widget.Button;
- importandroid.widget.EditText;
- importandroid.widget.TextView;
- publicclassTest08_16ActivityextendsActivity{
- privateTextViewtextViewInfo;
- privateWebViewwebViewInfo;
- privateButtonbuttonJavaLogin;
- privateButtonbuttonJavaMyMessage;
- privateButtonbuttonApaceLogin;
- privateButtonbuttonApacheMyMessage;
- privateViewloginView;
- privateAlertDialogloginDialog;
- privateEditTexteditTextUserName;
- privateEditTexteditTextPassword;
- privateStringclientType;
- privateDefaultHttpClientclient;
- privateStringcookie;
- privatestaticfinalStringLOGIN_URL="http://www.iteye.com/login";
- privatestaticfinalStringMESSAGE_URL="http://app.iteye.com/messages";
- @Override
- publicvoidonCreate(BundlesavedInstanceState){
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- textViewInfo=(TextView)findViewById(R.id.textViewInfo);
- webViewInfo=(WebView)findViewById(R.id.webViewInfo);
- buttonJavaLogin=(Button)findViewById(R.id.buttonJavaLogin);
- buttonJavaMyMessage=(Button)findViewById(R.id.buttonJavaMyMessage);
- buttonApaceLogin=(Button)findViewById(R.id.buttonApacheLogin);
- buttonApacheMyMessage=(Button)findViewById(R.id.buttonApacheMyMessage);
- LayoutInflaterinflater=LayoutInflater.from(this);
- loginView=inflater.inflate(R.layout.login,null);
- editTextUserName=(EditText)loginView.findViewById(R.id.editTextUsername);
- editTextPassword=(EditText)loginView.findViewById(R.id.exitTextPassword);
- loginDialog=getLoginDialog();
- buttonJavaLogin.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- clientType="java";
- loginDialog.show();
- }
- });
- buttonJavaMyMessage.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- showMyMessageJava();
- }
- });
- buttonApaceLogin.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- clientType="apache";
- loginDialog.show();
- }
- });
- buttonApacheMyMessage.setOnClickListener(newView.OnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- showMyMessageApache();
- }
- });
- }
- @Override
- protectedvoidonResume(){
- client=newDefaultHttpClient();//client.getCookieStore().getCookies()session_id
- HttpParamshttpParams=client.getParams();
- httpParams.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,false);
- super.onResume();
- }
- @Override
- protectedvoidonPause(){
- client.getConnectionManager().shutdown();
- super.onPause();
- }
- privateAlertDialoggetLoginDialog(){
- returnnewAlertDialog.Builder(Test08_16Activity.this)
- .setTitle("登录")
- .setView(loginView)
- .setPositiveButton("登录",newDialogInterface.OnClickListener(){
- @Override
- publicvoidonClick(DialogInterfacedialog,intwhich){
- if(clientType.equals("apache")){
- loginApache();
- }elseif(clientType.equals("java")){
- loginJava();
- }
- }
- })
- .setNegativeButton("取消",newDialogInterface.OnClickListener(){
- @Override
- publicvoidonClick(DialogInterfacedialog,intwhich){
- }
- })
- .create();
- }
- privatevoidloginJava(){
- Stringusername=editTextUserName.getText().toString();
- Stringpassword=editTextPassword.getText().toString();
- URLurl=null;
- try{
- url=newURL(LOGIN_URL);
- }catch(MalformedURLExceptione){
- }
- HttpURLConnectionurlConnection=null;
- try{
- urlConnection=(HttpURLConnection)url.openConnection();
- }catch(IOExceptione){
- textViewInfo.setText(e.getMessage());
- return;
- }
- try{
- urlConnection.setRequestMethod("POST");
- }catch(ProtocolExceptione){
- }
- urlConnection.setDoOutput(true);
- urlConnection.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
- urlConnection.setRequestProperty("Connection","keep-alive");
- urlConnection.setInstanceFollowRedirects(false);//
- OutputStreamout=null;
- try{
- out=newBufferedOutputStream(urlConnection.getOutputStream());//请求
- }catch(IOExceptione){
- urlConnection.disconnect();
- textViewInfo.setText(e.getMessage());
- return;
- }
- Writerwriter=null;
- try{
- writer=newOutputStreamWriter(out,"UTF-8");
- }catch(UnsupportedEncodingExceptione1){
- }
- try{
- writer.write("name="+username+"&password="+password);
- }catch(IOExceptione){
- urlConnection.disconnect();
- textViewInfo.setText(e.getMessage());
- return;
- }finally{
- try{
- writer.flush();
- writer.close();
- }catch(IOExceptione){
- }
- }
- getResponseJava(urlConnection);
- }
- privatevoidshowMyMessageJava(){
- URLurl=null;
- try{
- url=newURL(MESSAGE_URL);
- }catch(MalformedURLExceptione){
- }
- HttpURLConnectionurlConnection=null;
- try{
- urlConnection=(HttpURLConnection)url.openConnection();
- }catch(IOExceptione){
- textViewInfo.setText(e.getMessage());
- return;
- }
- //methodThedefaultvalueis"GET"
- getResponseJava(urlConnection);
- }
- //共用HttpClient
- privatevoidloginApache(){
- Stringusername=editTextUserName.getText().toString();
- Stringpassword=editTextPassword.getText().toString();
- List<NameValuePair>params=newArrayList<NameValuePair>(1);
- params.add(newBasicNameValuePair("name",username));
- params.add(newBasicNameValuePair("password",password));
- HttpEntityformEntity=null;
- try{
- formEntity=newUrlEncodedFormEntity(params,HTTP.UTF_8);
- }catch(UnsupportedEncodingExceptione){
- }
- HttpPostrequest=newHttpPost(LOGIN_URL);
- request.setEntity(formEntity);
- getResponseApache(request);
- }
- privatevoidshowMyMessageApache(){
- HttpGetrequest=newHttpGet(MESSAGE_URL);
- getResponseApache(request);
- }
- privatevoidgetResponseJava(HttpURLConnectionurlConnection){
- if(cookie!=null){//session_id
- urlConnection.setRequestProperty("Cookie",cookie);
- }
- InputStreamin=null;
- try{
- in=newBufferedInputStream(urlConnection.getInputStream());//响应
- }catch(IOExceptione){
- urlConnection.disconnect();
- textViewInfo.setText(e.getMessage());
- return;
- }
- cookie=urlConnection.getHeaderField("Set-Cookie");//session_id
- intresponseCode=-1;
- try{
- responseCode=urlConnection.getResponseCode();
- }catch(IOExceptione){
- urlConnection.disconnect();
- textViewInfo.setText(e.getMessage());
- return;
- }
- if(responseCode==301||responseCode==302||responseCode==307){//重定向
- Stringlocation=urlConnection.getHeaderField("location");
- URLurl=null;
- try{
- url=newURL(location);
- }catch(MalformedURLExceptione){
- }
- HttpURLConnectionurlConnectionRedirect=null;
- try{
- urlConnectionRedirect=(HttpURLConnection)url.openConnection();
- }catch(IOExceptione){
- textViewInfo.setText(e.getMessage());
- return;
- }
- getResponseJava(urlConnectionRedirect);
- return;
- }
- BufferedReaderreader=null;
- try{
- reader=newBufferedReader(newInputStreamReader(in,"UTF-8"));
- }catch(UnsupportedEncodingExceptione1){
- }
- StringBuilderresult=newStringBuilder();
- Stringtmp=null;
- try{
- while((tmp=reader.readLine())!=null){
- result.append(tmp);
- }
- }catch(IOExceptione){
- textViewInfo.setText(e.getMessage());
- return;
- }finally{
- try{
- reader.close();
- urlConnection.disconnect();
- }catch(IOExceptione){
- }
- }
- webViewInfo.loadDataWithBaseURL(null,result.toString(),"text/html","UTF-8",null);
- }
- privatevoidgetResponseApache(HttpUriRequestrequest){
- HttpResponseresponse=null;
- try{
- response=client.execute(request);//重定向RedirectStrategyexecutewhile(!done)
- }catch(ClientProtocolExceptione){
- textViewInfo.setText(e.getMessage());
- }catch(IOExceptione){
- textViewInfo.setText(e.getMessage());
- }
- if(response==null){
- return;
- }
- if(response.getStatusLine().getStatusCode()!=HttpStatus.SC_OK){
- textViewInfo.setText("errorresponse"+response.getStatusLine().toString());
- return;
- }
- Stringresult=null;
- try{
- result=EntityUtils.toString(response.getEntity(),"UTF-8");
- }catch(Exceptione){
- textViewInfo.setText("errorresponse"+response.getStatusLine().toString());
- return;
- }
- webViewInfo.loadDataWithBaseURL(null,result,"text/html","UTF-8",null);
- }
- }
看看运行效果:
主界面:
点击 使用java登录iteye,弹出登录对话框:
输入javaeye的账号和密码,点击登录(有点慢,耐心等待),结果如下:
登录成功了,点击 iteye收件箱(相当于在浏览器里点击收件箱),验证是同一session
成功,代码没有问题。
使用Apache的方式结果一样,我就不截图了。
做这个例子,我是想了解http协议,httpclient内部实现原理。写的过程中参照如下:
seesion问题:http://hi.baidu.com/%CE%A4%D7%D4%C9%FD/blog/item/e2ce0004f52f2c61030881fa.html
http://helin.iteye.com/blog/257115
302重定向:http://blog.csdn.net/hegch/article/details/1891146
http 417 :http://blog.yes2.me/archives/915
webview乱码:http://hongyang321.iteye.com/blog/1021564
接下来分析一下代码:
先分析java部分,代码和我上篇博客node.js+android http请求响应基本很像,关键的不同
1.解决session的代码:
- privateStringcookie;
- if(cookie!=null){//session_id
- urlConnection.setRequestProperty("Cookie",cookie);
- }
- cookie=urlConnection.getHeaderField("Set-Cookie");//session_id
共享cookie,获取响应前查看是否有cookie,如果有则放在请求头上,获取响应后,拿到cookie存下来。
2.解决重定向的代码:
- if(responseCode==301||responseCode==302||responseCode==307){//重定向
- Stringlocation=urlConnection.getHeaderField("location");
- URLurl=null;
- try{
- url=newURL(location);
- }catch(MalformedURLExceptione){
- }
- HttpURLConnectionurlConnectionRedirect=null;
- try{
- urlConnectionRedirect=(HttpURLConnection)url.openConnection();
- }catch(IOExceptione){
- textViewInfo.setText(e.getMessage());
- return;
- }
- getResponseJava(urlConnectionRedirect);
- return;
- }
再来分析一下httpclient部分:
1.解决407 error
- HttpParamshttpParams=client.getParams();
- httpParams.setParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE,false);
2.解决session
- privateDefaultHttpClientclient;
共用一个client就可以了,cookie存在这client.getCookieStore().getCookies(), 和我们使用一个浏览器,多个标签页共享session原理一样。
3.解决重定向
没有附加任何代码,httpclient内部已经实现,源码主要部分如下:
- classAbstractHttpClient
- publicfinalHttpResponseexecute(HttpUriRequestrequest)
- throwsIOException,ClientProtocolException{
- returnexecute(request,(HttpContext)null);
- }
- publicfinalHttpResponseexecute(HttpHosttarget,HttpRequestrequest,
- HttpContextcontext)
- RequestDirectordirector=null;
- director=createClientRequestDirectorreturnnewDefaultRequestDirector
- returndirector.execute(target,request,execContext);
- classDefaultRequestDirector
- publicHttpResponseexecute(HttpHosttarget,HttpRequestrequest,
- HttpContextcontext)
- while(!done){
- RoutedRequestfollowup=handleResponse(roureq,response,context);
- if(followup==null){
- done=true;
- }else{
- protectedRoutedRequesthandleResponse(RoutedRequestroureq,
- HttpResponseresponse,
- HttpContextcontext)
- if(HttpClientParams.isRedirecting(params)&&
- this.redirectStrategy.isRedirected(request,response,context)){
- classDefaultRedirectStrategy
- publicbooleanisRedirected(
- finalHttpRequestrequest,
- finalHttpResponseresponse,
- finalHttpContextcontext)
- intstatusCode=response.getStatusLine().getStatusCode();
- Stringmethod=request.getRequestLine().getMethod();
- HeaderlocationHeader=response.getFirstHeader("location");
- switch(statusCode){
- caseHttpStatus.SC_MOVED_TEMPORARILY:
- return(method.equalsIgnoreCase(HttpGet.METHOD_NAME)
- ||method.equalsIgnoreCase(HttpHead.METHOD_NAME))&&locationHeader!=null;
- caseHttpStatus.SC_MOVED_PERMANENTLY:
- caseHttpStatus.SC_TEMPORARY_REDIRECT:
- returnmethod.equalsIgnoreCase(HttpGet.METHOD_NAME)
- ||method.equalsIgnoreCase(HttpHead.METHOD_NAME);
- caseHttpStatus.SC_SEE_OTHER:
- returntrue;
- default:
- returnfalse;
- }//endofswitch
处理方法基本和我的一样,除了对象封装意外,它使用的条件循环while (!done) ,我使用的是条件递归。
ps:使用windows7以后,根据内容找文件很蛋疼,推荐大家使用UltraFileSearch 官网http://www.ultrafilesearch.com/,不是广告,只是用了这么多搜索替代工具以后,觉得最好的一个,和大家分享一下
敲了半天怪累的,就说到这了。