Android HTTP 요청 방법: HttpURLConnection
이 섹션 소개:
이전 두 섹션에서 우리는 HTTP 프로토콜과 프로토콜 헤더의 몇 가지 개념적인 내용을 연구했지만 이 섹션에서는 이제 코드를 쌓아볼 차례인데, 이번 섹션에서는 안드로이드에서 제공하는 Http 요청 메소드 중 하나인 HttpURLConnection에 대해 설명합니다. 이 외에도 다음 섹션에서 설명할 또 다른 종류의 HttpClient가 있습니다! 다만, 요청이 복잡해지면 전자를 사용할 수 있습니다. 매우 번거로운 작업이며 후자는 Java 패킷 캡처에서 자주 사용됩니다. 결국 Google의 자체 아들이 아니라 버전 4.4입니다. HttpURLConnection이 OkHttp로 대체되었습니다! 뭐, 시대의 흐름에 맞춰서 HttpClient에 대한 이야기를 한 뒤 이 이야기를 하기로 했습니다. 알았어Http! 그런데 일반적으로 실제 개발에서는 HttpURLConnection 및 HttpClient를 사용하지 않고 다른 것을 사용하여 캡슐화합니다. 네트워크 작업에는 Volley, android-async-http, loopj 등과 같은 좋은 타사 네트워크 요청 프레임워크가 필요합니다. 비동기식과 멀티스레딩은 직접 해보면 매우 번거롭기 때문에 실제 개발을 위해서는 타사를 직접 활용해야 합니다! ! 물론 나도 공부할 거야 결국, 제3자도 이러한 기반 위에 고품질 아키텍처와 다양한 최적화를 통해 구축된다는 것은 중요하지 않습니다! 좋아, 더 이상 고민하지 말고 시작해 보자 이 섹션의 내용입니다!
1. HttpURLConnection 소개
답변: HTTP 작업에 사용할 수 있고 대부분의 애플리케이션에 적합한 다목적 경량 HTTP 클라이언트입니다. HttpURLConnection에서 제공하는 API는 상대적으로 간단하지만 사용하기가 더 쉽습니다. 사용하고 확장하세요. 추상 클래스인 URLConnection을 상속하며 객체를 직접 인스턴스화할 수 없습니다. openCollection()을 호출하여 이 메소드는 기본적으로 gzip으로 압축된 객체 인스턴스를 얻습니다.
2. HttpURLConnection 사용 단계
HttpURLConnection 사용 단계는 다음과 같습니다.
- URL 객체 생성: URL url = new URL(http://www.baidu.com);
- URL 객체의 openConnection()을 호출하여 HttpURLConnection 객체 인스턴스를 가져옵니다: HttpURLConnection conn = (HttpURLConnection ) url .openConnection();
- HTTP 요청에 사용되는 방법을 설정합니다: GET 또는 POST 또는 기타 요청 방법: PUTconn.setRequestMethod("GET");
- 연결 시간 제한을 설정하고 시간 초과에 대한 밀리초 수, 그리고 서버가 가져오기를 원하는 일부 메시지 헤더conn.setConnectTimeout(6*1000);conn.setReadTimeout(6 * 1000);
- getInputStream() 메서드를 호출하여 서버에서 반환된 입력 스트림을 읽은 다음 입력 스트림을 읽습니다. InputStream in = conn.getInputStream();
- 마지막으로 connect() 메서드를 호출하여 HTTP 연결을 끕니다conn.disconnect();
PS: 위의 내용 외에도 때로는 200과 같은 응답 코드를 판단해야 할 수도 있습니다. if(conn.getResponseCode() != 200) 그런 다음 일부 처리를 수행할 수도 있습니다. 매개변수를 전달할 필요는 없지만 페이지에 직접 액세스하려면 다음을 직접 사용할 수 있습니다. 최종 InputStream in = new URL("url").openStream(); 그런 다음 스트림을 직접 읽으십시오. 그러나 이 방법은 실제로 페이지에 직접 액세스하는 데 적합합니다. openConnection().getInputStream()을 반환하지만 아직 일부를 설정할 수 없습니다. 그냥 부탁일 뿐이니 이렇게 쓸지 말지는 본인이 직접 가늠해 보아야 할 것 같아요!
3.HttpURLConnection 사용 예제
여기에서는 주로 GET 및 POST 요청에 대한 두 가지 다른 사용 예제를 작성합니다. conn.getInputStream()을 사용할 수 있습니다. 얻은 것은 스트림이므로 스트림을 이진 배열로 변환하는 클래스를 작성해야 합니다! 도구 클래스는 다음과 같습니다.
StreamTool.java:
/** * Created by Jay on 2015/9/7 0007. */ public class StreamTool { //从流中读取数据 public static byte[] read(InputStream inStream) throws Exception{ ByteArrayOutputStream outStream = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len = 0; while((len = inStream.read(buffer)) != -1) { outStream.write(buffer,0,len); } inStream.close(); return outStream.toByteArray(); } }
이제 예제를 가지고 놀 수 있습니다!
1) HttpURLConnection이 GET 요청 코드 예제
Running 렌더링을 보냅니다:
핵심 부품 코드:
Layout: activity_main.xml
Get 데이터 클래스: GetData.java :
/** * Created by Jay on 2015/9/7 0007. */ public class GetData { // 定义一个获取网络图片数据的方法: public static byte[] getImage(String path) throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 设置连接超时为5秒 conn.setConnectTimeout(5000); // 设置请求类型为Get类型 conn.setRequestMethod("GET"); // 判断请求Url是否成功 if (conn.getResponseCode() != 200) { throw new RuntimeException("请求url失败"); } InputStream inStream = conn.getInputStream(); byte[] bt = StreamTool.read(inStream); inStream.close(); return bt; } // 获取网页的html源代码 public static String getHtml(String path) throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5000); conn.setRequestMethod("GET"); if (conn.getResponseCode() == 200) { InputStream in = conn.getInputStream(); byte[] data = StreamTool.read(in); String html = new String(data, "UTF-8"); return html; } return null; } }
MainActivity.java:
public class MainActivity extends AppCompatActivity { private TextView txtMenu, txtshow; private ImageView imgPic; private WebView webView; private ScrollView scroll; private Bitmap bitmap; private String detail = ""; private boolean flag = false; private final static String PIC_URL = "http://ww2.sinaimg.cn/large/7a8aed7bgw1evshgr5z3oj20hs0qo0vq.jpg"; private final static String HTML_URL = "http://www.baidu.com"; // 用于刷新界面 private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case 0x001: hideAllWidget(); imgPic.setVisibility(View.VISIBLE); imgPic.setImageBitmap(bitmap); Toast.makeText(MainActivity.this, "图片加载完毕", Toast.LENGTH_SHORT).show(); break; case 0x002: hideAllWidget(); scroll.setVisibility(View.VISIBLE); txtshow.setText(detail); Toast.makeText(MainActivity.this, "HTML代码加载完毕", Toast.LENGTH_SHORT).show(); break; case 0x003: hideAllWidget(); webView.setVisibility(View.VISIBLE); webView.loadDataWithBaseURL("", detail, "text/html", "UTF-8", ""); Toast.makeText(MainActivity.this, "网页加载完毕", Toast.LENGTH_SHORT).show(); break; default: break; } } ; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setViews(); } private void setViews() { txtMenu = (TextView) findViewById(R.id.txtMenu); txtshow = (TextView) findViewById(R.id.txtshow); imgPic = (ImageView) findViewById(R.id.imgPic); webView = (WebView) findViewById(R.id.webView); scroll = (ScrollView) findViewById(R.id.scroll); registerForContextMenu(txtMenu); } // 定义一个隐藏所有控件的方法: private void hideAllWidget() { imgPic.setVisibility(View.GONE); scroll.setVisibility(View.GONE); webView.setVisibility(View.GONE); } @Override // 重写上下文菜单的创建方法 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { MenuInflater inflator = new MenuInflater(this); inflator.inflate(R.menu.menus, menu); super.onCreateContextMenu(menu, v, menuInfo); } // 上下文菜单被点击是触发该方法 @Override public boolean onContextItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.one: new Thread() { public void run() { try { byte[] data = GetData.getImage(PIC_URL); bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); } catch (Exception e) { e.printStackTrace(); } handler.sendEmptyMessage(0x001); } ; }.start(); break; case R.id.two: new Thread() { public void run() { try { detail = GetData.getHtml(HTML_URL); } catch (Exception e) { e.printStackTrace(); } handler.sendEmptyMessage(0x002); }; }.start(); break; case R.id.three: if (detail.equals("")) { Toast.makeText(MainActivity.this, "先请求HTML先嘛~", Toast.LENGTH_SHORT).show(); } else { handler.sendEmptyMessage(0x003); } break; } return true; } }
마지막으로 네트워크 권한을 추가하는 것을 잊지 마세요:
Notes:
핸들러를 사용하는 이유는 굳이 설명할 필요가 없습니다~ 또한 html 코드를 로드할 때 LoadData 대신 webView의 loadDataWithBaseURL을 사용합니다. LoadData를 사용하는데 중국어 글자 깨짐 문제를 걱정해야 한다면... loadDataWithBaseURL을 사용하면 크게 걱정하지 않아도 됩니다. 또한 일부 페이지에서는 계정 및 비밀번호와 같은 일부 매개변수를 제출하도록 요구할 수 있습니다. 다음과 같이 해당 매개변수를 URL 끝에 연결하기만 하면 됩니다. http://192.168.191.1:8080/ComentServer/LoginServlet?passwd=123&name=Jack 그러면 서버측 getParamater("passwd")가 해당 매개변수를 얻을 수 있습니다. 이러한 사항은 요청 시 명확하게 표시됩니다. , 따라서 GET 메소드는 안전하지 않습니다! 또 한 가지 주의할 점은 Android 4.0부터 UI가 아닌 스레드에서는 UI 작업을 허용하지 않는다는 점입니다!
2) HttpURLConnection은 POST 요청 코드를 보냅니다.
openConnection을 통해 얻는 GET과 POST가 있습니다. HttpURLConnection은 기본적으로 Get 요청을 수행합니다. 따라서 POST를 사용하여 데이터를 제출할 때 관련 매개변수를 미리 설정해야 합니다. conn.setRequestMethod("POST"); 다음도 있습니다: conn.setDoOutput(true); conn.setDoInput(true); 또한: conn.setUseCaches(false); POST 메소드는 캐시될 수 없으며 수동으로 false로 설정되어야 합니다. 특정 구현에 대한 코드를 참조하세요:
Running renders:
Core code:
PostUtils.java
public class PostUtils { public static String LOGIN_URL = "http://172.16.2.54:8080/HttpTest/ServletForPost"; public static String LoginByPost(String number,String passwd) { String msg = ""; try{ HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection(); //设置请求方式,请求超时信息 conn.setRequestMethod("POST"); conn.setReadTimeout(5000); conn.setConnectTimeout(5000); //设置运行输入,输出: conn.setDoOutput(true); conn.setDoInput(true); //Post方式不能缓存,需手动设置为false conn.setUseCaches(false); //我们请求的数据: String data = "passwd="+ URLEncoder.encode(passwd, "UTF-8")+ "&number="+ URLEncoder.encode(number, "UTF-8"); //这里可以写一些请求头的东东... //获取输出流 OutputStream out = conn.getOutputStream(); out.write(data.getBytes()); out.flush(); if (conn.getResponseCode() == 200) { // 获取响应的输入流对象 InputStream is = conn.getInputStream(); // 创建字节输出流对象 ByteArrayOutputStream message = new ByteArrayOutputStream(); // 定义读取的长度 int len = 0; // 定义缓冲区 byte buffer[] = new byte[1024]; // 按照缓冲区的大小,循环读取 while ((len = is.read(buffer)) != -1) { // 根据读取的长度写入到os对象中 message.write(buffer, 0, len); } // 释放资源 is.close(); message.close(); // 返回字符串 msg = new String(message.toByteArray()); return msg; } }catch(Exception e){e.printStackTrace();} return msg; } }
PS: MyEclipse는 컴퓨터에 설치되지 않으며 시간 제약이 있기 때문에, Demo는 따로 작성하지 않고 이전 Eclipse 데모를 사용하세요! 사실 핵심코드를 직접 보는 것만으로도 충분해요~ 코드 다운로드: HttpURLConnection example.zip
4. 쿠키 문제 처리
이에 대해 이야기하기 전에 먼저 두 가지 개념을 이해해야 합니다. Session 및 CookieCookie는 세션 메커니즘의 일반적인 형태일 뿐입니다. 다른 방법을 클라이언트의 고유 식별자로 사용할 수 있습니다. 이는 서버에 의해 결정되며 이를 증명할 수 있는 유일한 것은 클라이언트 ID입니다! 이 방법 외에도 URL 재작성을 사용할 수도 있습니다! 그것을하는 방법! 그러니 앞으로는 다른 사람들에게 어리석게 말하지 마세요. 세션은 단순한 쿠키가 아닙니다!
다음은 이 쿠키에 대한 이해를 돕기 위한 예입니다. Xiaozhu는 계정과 비밀번호를 입력하고 학교 학사 관리 시스템에 로그인한 후 수업 일정 정보에 성공적으로 액세스했습니다. 그런 다음 Chrome을 사용하는 경우 F12를 눌러 개발 모드로 들어갑니다. 리소스 인터페이스로 이동하면 쿠키를 볼 수 있습니다.
클릭하면 이름 값으로 구성된 쿠키에 저장된 콘텐츠를 볼 수 있습니다. 쿠키가 위치한 곳(도메인) 쿠키가 있는 디렉터리(경로)는 기본적으로 루트 디렉터리인 /입니다.
요청 헤더에 쿠키 필드가 있음을 확인할 수 있습니다.
이제 쿠키를 지우고(또는 몇 분 정도 기다린 후) 다음 링크를 방문합니다.
이 때 페이지는 자동으로 로그인 페이지로 돌아갑니다! 물론 일부 다른 웹사이트에서는 다음과 같은 대화 상자가 나타날 수도 있습니다. "로그인 시간 초과"와 같은 것!
Http 요청 로그인의 간단한 프로세스를 요약하면 다음과 같습니다. 일반적으로 로그인할 때 서버는 Set-Cookie 응답 헤더를 통해 쿠키를 반환하고 브라우저는 기본적으로 이 쿠키를 저장합니다. 이 쿠키는 나중에 관련 페이지를 방문할 때 함께 가져오며, 쿠키가 없거나 쿠키가 없는 경우에는 쿠키 요청 헤더를 통해 방문이 완료됩니다. 쿠키가 만료되면 사용자에게 로그인하지 말라는 메시지가 표시되고 로그인 시간이 초과되었으며 액세스하려면 로그인 등의 정보가 필요합니다!
그리고 HttpClient 및 HttpURLConnection을 사용할 때 실제로 이 프로세스를 시뮬레이션하고 로그인 후 쿠키를 가져옵니다. 요청을 보내려면 다음을 수행하십시오. 키 코드는 다음과 같습니다. Get Cookie: conn.getHeaderField("Set-Cookie"); 요청 시 쿠키 가져오기: conn.setRequestProperty("Cookie", cookie); 요청 헤더 이 방법 외에도 다른 타협 방법을 사용할 수도 있습니다: URL rewriting
: 원래 요청 링크를 기반으로...&sessionid=xxxxx와 같은 매개변수가 추가된 후 서버가 이를 구문 분석합니다. 판사!여기서는 JSON 문자열 형식을 사용합니다. 서버는 요청을 받으면 세션의 콘텐츠를 꺼내어 쿼리합니다~
5. HttpURLConnection을 사용하여 PUT 요청을 보냅니다
Put 요청. 많은 친구들에게는 다소 생소할 수도 있겠네요. 결국 우리가 주로 접하는 상황은 GET과 POST입니다. Xiaozhu는 처음에는 몰랐지만 나중에 보니 실제로 POST와 유사하고 POST를 기반으로 수정하기만 하면 됩니다. 주문만 하면 바로 먹을 수 있습니다! 그리고 HttpClient는 HttpPut API도 제공합니다. 다음은 Xiaozhu의 자체 프로젝트에 작성된 요청 코드입니다.
public static String LoginByPut(Context mContext, String mobile, String password, int from, String devid,String version_name, int remember_me) { String resp = ""; try { HttpURLConnection conn = (HttpURLConnection) new URL(LOGIN_URL).openConnection(); conn.setRequestMethod("PUT"); conn.setReadTimeout(5000); conn.setConnectTimeout(5000); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); String data = "mobile=" + mobile + "&password=" + password + "&from=" + from + "&devid=" + "devid" + "&version_name=" + "version_name" + "&remember_me=" + remember_me; ; // 获取输出流: OutputStreamWriter writer = new OutputStreamWriter(conn.getOutputStream()); writer.write(data); writer.flush(); writer.close(); // 获取相应流对象: InputStream in = conn.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder response = new StringBuilder(); String line; while ((line = reader.readLine()) != null) response.append(line); SPUtils.put(mContext, "session", conn.getHeaderField("Set-Cookie")); // 资源释放: in.close(); // 返回字符串 Log.e("HEHE", response.toString()); return response.toString(); } catch (Exception e) { e.printStackTrace(); } return ""; }이 섹션 요약:
자, HttpUrlConnection 사용에 대한 이 섹션의 소개는 여기서 끝납니다. 또한 대부분의 HTTP 섹션은 Xiaozhu pig에서 나옵니다.
Android용 HTTP 통신이전에 작성한 작은 모음입니다. 이 시리즈를 읽었다면 대부분의 내용을 건너뛰어도 됩니다. 같은! 그럼 그게 다입니다. 감사합니다~