Android ネットワーク リクエストの共通モジュール
一般的な Web HTTP リクエストは GET リクエストと POST リクエストに分けられます. Python は urllib と requests モジュールを使用し、Android はokhttpとretrofitモジュールを使用します. okhttp と retrofit の関係は urllib と requests の関係と同じです. どちらも再カプセル化されています.前者に基づいて、より便利に使用できるようにします。
Web HTTP リクエストのデータ パケットは、フォーム フォームとjson 文字列の2 つのフォームに分けることができます. Python では、これら 2 つのフォームは requests メソッドのパラメーターによって区別されますが、Android では、okhttp にも対応する送信があります.フォームの方法。
Web パケットを受信した場合、受信した json 文字列を受信した場合、データをデシリアライズする必要があります. Python の requests モジュールはこの作業を完了するのに役立ちますが、Android では、デシリアライズの変更を実現するために Gson モジュールを導入する必要があります.
ブラウザのWebアプリケーションではCookieファイルを使ってユーザー情報を保存し、xmlファイルを使ってAndroidでも同様の機能を実現しているため、Androidでのxmlファイルの操作方法についても学習する必要があります。
OKHTTP
okhttp を使用する前に、次の設定を行います。
インポート、build.gradle
implementation “com.squareup.okhttp3:okhttp:4.9.1”
構成、AndroidManifest.xml で、同じレベルの上に追加されたアプリケーション タグは、
https 要求のみを送信できます。
AndroidManifest.xml の構成で、アプリケーション タグ内に android:networkSecurityConfig="@xml/network_security_config" を追加します。
res ディレクトリに新しい xml フォルダーを作成し、その中に新しい network_security_config.xml ファイルを作成して、次の内容を記述します。
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!--禁用掉明文流量请求的检查-->
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
GET リクエストを作成 ---> [スレッドを作成] --> それ以外の場合はエラーを報告
String username = String.valueOf(txtUser.getText());
String password = String.valueOf(txtPwd.getText());
HashMap<String, String> dataMap = new HashMap<String, String>();
dataMap.put("username", username);
dataMap.put("password", password);
// 1. 将用户名和密码 发送到后台 (第三方 OKHttp)
// 1.1 引入依赖 bulid.gradle 文件中 引入 implementation "com.squareup.okhttp3:okhttp:4.9.1"
// 1.2 在Android中,默认不允许发送网络请求; 在AndroidManifest.xml 中 配置 <uses-permission android:name="android.permission.INTERNET"/>
// 1.3 调用 OKHttp包去发送请求。
// 1.3.1 创建 GET 请求 ---> [创建线程]--> 否则报错
// 创建线程并执行 run 方法
new Thread(){
@Override
public void run(){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/course/actual/?category_id=1").build();
Call call = client.newCall(request);
try {
Response response = call.execute(); // 发送请求
ResponseBody body = response.body();
String resString = body.string();
Log.i("登录界面--->", resString);
// 获取数据并处理
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
POST リクエストを作成 ---> [スレッドを作成]
フォームフォーム形式
パケットは、次のようなフォーム形式で送信されます。
username=xxxx&password=123
new Thread(){
@Override
public void run(){
OkHttpClient client = new OkHttpClient();
// 构建请求体
FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
// form ---> post 请求体
Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
Call call = client.newCall(request);
try {
Response response = call.execute(); // 发送请求
ResponseBody body = response.body();
String resString = body.string();
Log.i("登录界面--->", resString);
// 获取数据并处理
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
json形式
データ パッケージは、次のような json 形式で送信されます。
{
username:"xxxx",
password:123,
}
new Thread() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient();
// 构建请求体
// FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
//JSONObject json = new JSONObject();
//json.put("username", "dk");
//json.put("password", "123");
JSONObject json = new JSONObject(dataMap);
String jsonString = json.toString();
RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), toString());
// form ---> post 请求体
Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
Call call = client.newCall(request);
try {
Response response = call.execute(); // 发送请求
ResponseBody body = response.body();
String resString = body.string();
// Json反序列化,字符串转换成对象.
// json.load(json字符串) --> python
// Android 需要 GSON --> 引入 (implementation "com.google.code.gson:gson:2.8.6")
//
String responseString = "{\"token\": \"dsajkdhjksald\", \"url\": \"https://www.httpbin.org/post/\",\"dataList\":[{\"id\": 1, \"name\": \"dk\"},{\"id\": 2, \"name\": \"dkk\"}]}";
HttpResponse res = new Gson().fromJson(responseString, HttpResponse.class);
Log.e("登录界面--->", res.toString()); //HttpResponse{url='https://www.httpbin.org/post/', origin='101.248.149.62', dataList=[Item{id=1, name='dk'}, Item{id=2, name='dkk'}]}
// 保存起来:cookies, localstorage
// andorid -> xml 文件 -> data/data/com.example.myapplication/shared_prefs
SharedPreferences sharedPreferences = getSharedPreferences("sp_MyApplication", MODE_PRIVATE);
SharedPreferences.Editor editor= sharedPreferences.edit();
editor.putString("token", res.token);
editor.commit();
// 跳转首页
Intent in = new Intent(mcontext, IndexActivity.class);
startActivity(in);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
両者の違いは次のとおりです。
フォーム形式は FormBody を使用してリクエスト本文を構築します
json 文字列形式は、JSONObject と RequestBody の両方を使用して要求本文を作成します。
リクエストインターセプター
一部のリクエスト ヘッダー情報は、すべての HTTP リクエストによって運ばれます. これらの共通のリクエスト ヘッダー情報は、Python のスクレイピー クローラー フレームワークのミドルウェアによって、および Android アプリの okhttp のリクエスト インターセプターによって実装されます. 以下は、Interceptor の簡単なテスト コードです.
// 4. 发送请求拦截器
Interceptor interceptor = new Interceptor() {
@NonNull
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
String sign = "sfsadsadsa";
// 请求还未发送,在请求体中增加了一个请求头
Request request = chain.request().newBuilder().addHeader("x-gorgon", sign).build();
Response response = chain.proceed(request);
return response;
}
};
new Thread() {
@Override
public void run() {
// 加入拦截器
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
// 构建请求体
// FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
//JSONObject json = new JSONObject();
//json.put("username", "dk");
//json.put("password", "123");
JSONObject json = new JSONObject(dataMap);
String jsonString = json.toString();
RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), toString());
// form ---> post 请求体
Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
Call call = client.newCall(request);
try {
Response response = call.execute(); // 发送请求
ResponseBody body = response.body();
String resString = body.string();
// Json反序列化,字符串转换成对象.
// json.load(json字符串) --> python
// Android 需要 GSON --> 引入 (implementation "com.google.code.gson:gson:2.8.6")
//
String responseString = "{\"token\": \"dsajkdhjksald\", \"url\": \"https://www.httpbin.org/post/\",\"dataList\":[{\"id\": 1, \"name\": \"dk\"},{\"id\": 2, \"name\": \"dkk\"}]}";
HttpResponse res = new Gson().fromJson(responseString, HttpResponse.class);
Log.e("登录界面--->", res.toString()); //HttpResponse{url='https://www.httpbin.org/post/', origin='101.248.149.62', dataList=[Item{id=1, name='dk'}, Item{id=2, name='dkk'}]}
// 保存起来:cookies, localstorage
// andorid -> xml 文件 -> data/data/com.example.myapplication/shared_prefs
SharedPreferences sharedPreferences = getSharedPreferences("sp_MyApplication", MODE_PRIVATE);
SharedPreferences.Editor editor= sharedPreferences.edit();
editor.putString("token", res.token);
editor.commit();
// 跳转首页
Intent in = new Intent(mcontext, IndexActivity.class);
startActivity(in);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
以前のインターセプターとの違い:
okhttp を作成する前にインターセプターを作成しました
okhttpの作成時にインターセプタを追加
NO_PROXY
パケット キャプチャを防ぐために、一部のアプリでは、okhttp の NO_PROXY パラメータを使用して、Android フォンがシステム プロキシを設定することを禁止します.この場合、Drony アプリを使用して、プロキシなしでパケットをキャプチャするか、次のようなアプリを使用できます。 Little Yellow Bird として、ローカル VPN でパケットをキャプチャします。
エージェントレス パケット キャプチャについては、以下を参照してください:アプリ エージェントレス モードでのパケット キャプチャの Python クローラー分析、およびこのためのアンチクロール最適化スキーム - Eeyhan - ブログ ガーデン (cnblogs.com)
レトロフィット
レトロフィットを使用する前に、次の構成を行ってください。
はじめに、build.gradleに「com.squareup.retrofit2:retrofit:2.9.0」を実装
具体的な利用方法は以下の通りです。
インターフェイスの書き込み、ネットワーク リクエストの宣言
package com.example.liyang;
package com.example.liyang;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface HttpReq {
// 向/api/v1/post 发送POST请求,表单格式 name=xx&pwd=xxx
@POST("/api/v1/post")
@FormUrlEncoded
Call<ResponseBody> postLogin(@Field("name") String userName, @Field("pwd") String password);
// 向/api/v2/xxx 发送GET请求,表单格式 ?age=xxx
@GET("/api/v2/xxx")
Call<ResponseBody> getInfo(@Query("age") String age);
// 向/post/users 发送POST请求 json字符串格式 {name:xxxx,age:123}
@POST("/post/users")
Call<ResponseBody> postLoginJson(@Body RequestBody body);
}
インターフェイスを呼び出し、パラメーターをインターフェイスに渡し、リクエストを送信する
new Thread() {
@Override
public void run() {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
HttpReq httpRequest = retrofit.create(HttpReq.class);
// http://192.168.31.201:9999/api/v1/post
// name=xx&pwd=xxx
Call<ResponseBody> call = httpRequest.postLogin("wupeiqi", "666");
try {
ResponseBody responseBody = call.execute().body();
String responseString = responseBody.string();
Log.i("登录", responseString);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override
public void run() {
// http://192.168.31.201:9999/api/v2/xxx?age=123
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
HttpReq req = retrofit.create(HttpReq.class);
Call<ResponseBody> call = req.getInfo("123");
try {
ResponseBody responseBody = call.execute().body();
String responseString = responseBody.string();
Log.e("Retrofit返回的结果", responseString);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
new Thread() {
@Override
public void run() {
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
HttpReq httpRequest = retrofit.create(HttpReq.class);
JSONObject json = new JSONObject(dataMap);
String jsonString = json.toString();
RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
// http://192.168.31.201:9999/post/users
// {username:"root",password:"123456","sign":"xxxxdfsdfsdfsdfdfd"}
Call<ResponseBody> call = httpRequest.postLoginJson(form);
try {
ResponseBody responseBody = call.execute().body();
String responseString = responseBody.string();
Log.i("登录", responseString);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
予防:
Retrofit は、okhttp をさらにカプセル化したもので、要求された URL をいくつかのセグメントに分割し、異なる場所に指定することを特徴としています。URL をより柔軟に設定できます。
レトロフィットで書かれたインターフェースに遭遇した場合、それを実装するクラスを見つけるのではなく、それを呼び出すクラスを見つけ、要求された URL をつなぎ合わせ、渡されたパラメーターを見つける必要があります。
コードは比較的長く、リクエストのスタイルを決定するインターフェイスの定義方法、インターフェイスを呼び出すメソッド、および渡されるパラメーターに焦点を当てています.2 つの部分が一緒になってリクエストの URL を決定します.
GSON
Python の json モジュールと同様に、オブジェクトのシリアライズと json 文字列のデシリアライズに使用されます. Gson を使用する前に導入する必要があります.
implementation ‘com.google.code.gson:gson:2.8.6’
シリアル化、オブジェクト -> 文字列型
class HttpContext{
public int code;
public String message;
public HttpContext(int code,String msg){
this.code = code;
this.message = msg;
}
}
HttpContext obj = new HttpContext(1000,"成功");
String dataString = new Gson().toJson(obj); // '{"code":1000,"Message":"成功"}'
逆シリアル化、文字列 -> オブジェクト
// JSON格式
String dataString = "{\"status\": true, \"token\": \"fffk91234ksd\", \"name\": \"武沛齐\"}";
class HttpResponse{
public boolean status;
public String token;
public String name;
}
HttpResponse obj = new Gson().fromJson(dataString,HttpResponse.class);
// obj.status obj.name obj.token
デシリアライズ時に辞書の入れ子がある場合
String responseString = "{\"origin\": \"110.248.149.62\",\"url\": \"https://www.httpbin.org/post\",\"dataList\":[{\"id\":1,\"name\":\"一个小黑\"},{\"id\":2,\"name\":\"eric\"}]}";
class Item {
public int id;
public String name;
}
public class HttpResponse {
public String url;
public String origin;
public ArrayList<Item> dataList;
}
HttpResponse obj = new Gson().fromJson(dataString, HttpResponse.class);
// obj.url obj.origin
Item objItem = obj.dataList.get(1);
// objItem.name
XML ファイルに保存
電話の場所に保存:
/data/data/com.example.liyang/shared_prefs/sp_city.xml
トークンを保存
SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("token","111111");
editor.commit();
トークンを削除
SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove("token");
editor.commit();
読み取りトークン
SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
String token = sp.getString("token","");
SharedPreferencesオブジェクトは、モバイル アプリのローカル XML ファイルの読み取りと書き込みに使用されることに注意してください。
XML ファイルは Cookie に非常に似ており、ローカル アルゴリズムによって生成されるか、HTTP 要求サーバーによって返されます。
要約する
jadx、jeb を使用して Android コードを逆コンパイルします。
キーワード検索
依頼されたプロセスに沿って段階的にチェック
Java コール
注: 小さなアプリは、上記の内容を理解することで完全に逆転できます。