---
title: Simple Android App Networking Request
description: "Quickly develop an Android page (user login & jump) - Android UI and background logic - Network requests - Serialization and deserialization - Save XML file (cookie) ## Android UI and background logic ### android UI ![](https://support.nullflag.com/wp-content/uploads/2025/10/image-1024x873.png) ![](https://support.nullflag.com/wp-content/uploads/2025/10/image-1-1024x888.png) ### User…"
url: "https://www.nullflag.com/support/kb/develop_hook/android-example/simple-android-app-networking-request/"
updated: "2025-11-02"
category: Android Example
---

# Simple Android App Networking Request

Quickly develop an Android page (user login & jump)

- Android UI and background logic
- Network requests
- Serialization and deserialization
- Save XML file (cookie)

## Android UI and background logic

### android UI

![](https://support.nullflag.com/wp-content/uploads/2025/10/image-1024x873.png)

![](https://support.nullflag.com/wp-content/uploads/2025/10/image-1-1024x888.png)

### User Login Interface

```xml

                <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">

<!-- Left and right borders for centered layout -->
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.1" /> <androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.9" />

<!-- Username input box -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/usernameTextInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="username"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="@id/guideline_start"
app:layout_constraintEnd_toEndOf="@id/guideline_end" android:layout_marginTop="100dp">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"/>
</com.google.android.material.textfield.TextInputLayout>

<!-- Password input box -->
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordTextInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="password"
app:passwordToggleEnabled="true"
app:endIconMode="password_toggle" app:layout_constraintTop_toBottomOf="@id/usernameTextInputLayout"
app:layout_constraintStart_toStartOf="@id/guideline_start"
app:layout_constraintEnd_toEndOf="@id/guideline_end"
android:layout_marginTop="16dp">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"/>
</com.google.android.material.textfield.TextInputLayout>

<!-- Login button -->
<Button
android:id="@+id/buttonLogin"
android:layout_width="0dp"
android:layout_height="wrap_content" android:text="Login"
app:layout_constraintTop_toBottomOf="@id/passwordTextInputLayout"
app:layout_constraintStart_toStartOf="@id/guideline_start"
app:layout_constraintEnd_toEndOf="@id/guideline_end"
android:layout_marginTop="24dp" />

<!-- Loading progress bar -->
<ProgressBar
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintTop_toBottomOf="@id/buttonLogin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="16dp" </androidx.constraintlayout.widget.ConstraintLayout>
```

### Function Interface

```xml

                <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Fn1Fragment">

    <LinearLayout
        android:orientation="vertical"
        android:padding="16dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <TextView
            android:id="@+id/textView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@string/hello_blank_fragment"
            android:textSize="18sp"
            android:paddingBottom="16dp" />

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/usernameTextInputLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="Enter_password_usernameTextInputLayout">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/editTextUsername"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textMultiLine"
                android:maxLines="5"
                android:minLines="1"
                android:scrollHorizontally="false"
                android:gravity="start|top"/>
        </com.google.android.material.textfield.TextInputLayout>

        <com.google.android.material.textfield.TextInputLayout
            android:id="@+id/passwordTextInputLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:hint="Display_password_text_passwordEditText"
            app:passwordToggleEnabled="true">

            <com.google.android.material.textfield.TextInputEditText
                android:id="@+id/passwordEditTextFn1"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textPassword|textMultiLine"
                android:maxLines="5"
                android:minLines="1"
                android:gravity="start|top"/>
        </com.google.android.material.textfield.TextInputLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="20dp">

            <Button
                android:id="@+id/buttonFn2"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="clang_Call_Java_Static(v8)_button_Fn2" />

            <Button
                android:id="@+id/buttonRun1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="jniCall_static()_button_Run1" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="20dp">

            <Button
                android:id="@+id/buttonFn3"
                android:layout_width="0dp"
                android:layout_height="112dp"
                android:layout_weight="1"
                android:text="clang_Call_Java_NewInstantiated_Object_(v9)_button_Fn3" />

            <Button
                android:id="@+id/buttonRun2"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="jNI_Dynamic_Register()_button_Run2" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:layout_marginTop="60dp">
            <Button
                android:id="@+id/buttonFn1"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="jniCall()_button_Fn1" />
            <Button
                android:id="@+id/buttonRun3"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="Calculate_Ciphertext_(Result_v3)_button_Run3" />

        </LinearLayout>

    </LinearLayout>
</ScrollView>
```

### Function Button Background Logic

```java

                package ca.netfilter; // Define package name as ca.netfilter
import android.os.Bundle; // Import Android Bundle class for data passing
import androidx.fragment.app.Fragment; // Import Fragment class for creating UI fragments
import android.text.InputType;
import android.util.Log;
import android.view.LayoutInflater; // Import layout inflater class
import android.view.View; // Import view class
import android.view.ViewGroup; // Import view container class
import android.widget.Button;

import android.widget.CompoundButton;
import android.widget.TextView;

import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.yoloho.libcore.util.Crypt;

/**
 * A simple {@link Fragment} subclass.
 * Use the {@link Fn1Fragment#newInstance} factory method to
 * create an instance of this fragment.
 *
 * A simple {@link Fragment} subclass.
 * Use the {@link Fn1Fragment#newInstance} factory method to
 * create an instance of this fragment.
 */

//public class Fn1Fragment extends androidx.fragment.app.Fragment {
//}
public class Fn1Fragment extends Fragment {   // Define a Fragment subclass named BlankFragment

    private static final String TAG = "Fn1Fragment"; // ✅ Add TAG

    static {
        System.loadLibrary("netfilter");
    }

    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    // TODO: Rename parameter names to match initialization parameters, e.g. ARG_ITEM_NUMBER

    // Variables are declared at the head and type.  you can use them Alone!
    public Button fn1Button,fn2Button,fn3Button,run1Button,run2Button,run3Button;
    public TextView txtUser,txtPwd;
    public TextInputEditText passwordEditText;
    public TextInputLayout passwordTextInputLayout;

    private static final String ARG_PARAM1 = "param1";   // Define key name for parameter 1
    private static final String ARG_PARAM2 = "param2";    // Define key name for parameter 2

    // TODO: Rename and change types of parameters  ---> Rename and change parameter types
    private String mParam1; // Used to save the value of param1
    private String mParam2;  // Used to save the value of param2

    public Fn1Fragment() {
        // Required empty public constructor  --> Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of  .--> Use this factory method to create a new instance of BlankFragment,
     * this fragment using the provided parameters. .--> and pass two parameters.
     *
     * @param param1 Parameter 1.--> param1 Parameter 1
     * @param param2 Parameter 2. -->param2 Parameter 2
     * @return A new instance of fragment BlankFragment.--> Return a new instance of BlankFragment
     */
    // TODO: Rename and change types and number of parameters -->Rename and modify parameter types and count
    public static Fn1Fragment newInstance(String param1, String param2) {
        Fn1Fragment fragment = new Fn1Fragment();// Create a new BlankFragment instance
        Bundle args = new Bundle(); // Create a new Bundle for passing parameters
        args.putString(ARG_PARAM1, param1);  // Put param1 into Bundle
        args.putString(ARG_PARAM2, param2); // Put param2 into Bundle
        fragment.setArguments(args);  // Set parameters to fragment
        return fragment; // Return the created fragment instance
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);  // Call parent class's onCreate method
        if (getArguments() != null) {  // If there are passed parameters
            mParam1 = getArguments().getString(ARG_PARAM1); // Get param1 and assign to mParam1
            mParam2 = getArguments().getString(ARG_PARAM2); // Get param2 and assign to mParam2
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment -->  Load layout file fragment_fn1.xml for this fragment
//        return inflater.inflate(R.layout.fragment_fn1, container, false);
        View rootView = inflater.inflate(R.layout.fragment_fn1, container, false);
        initView(rootView);  // Pass rootView
        initListener(); // ✅ Add click event binding
        return rootView;
    }

    public void initView(View rootView){
        // find the page useful tag
        // Set button binding events.
        txtUser = rootView.findViewById(R.id.editTextUsername);
        txtPwd = rootView.findViewById(R.id.passwordEditTextFn1);
        fn1Button = rootView.findViewById(R.id.buttonFn1);
        run1Button = rootView.findViewById(R.id.buttonRun1);
        fn2Button = rootView.findViewById(R.id.buttonFn2);
        run2Button = rootView.findViewById(R.id.buttonRun2);
        fn3Button = rootView.findViewById(R.id.buttonFn3);
        run3Button = rootView.findViewById(R.id.buttonRun3);
        passwordEditText = rootView.findViewById(R.id.passwordEditTextFn1);
        passwordTextInputLayout = rootView.findViewById(R.id.passwordTextInputLayout);

    }

    public void initListener(){
        run1Button.setOnClickListener(new View.OnClickListener() {  // First type: full form
            @Override
            public void onClick(View v){
                jniCall_static();
            }
        });
        fn2Button.setOnClickListener(v -> clang_Call_Java_v8());  // Second type: abbreviated form
        fn3Button.setOnClickListener(v -> clang_Call_Java_v9());
        run2Button.setOnClickListener(v -> jNI_Dynamic_Register());
        run3Button.setOnClickListener(v -> Call_Encrypted_Result());

    }

    public void Call_Encrypted_Result(){
        //Send network + connect queue, get task

//        String Plaintext = String.valueOf(txtUser.getText());
////        String Ciphertext = String.valueOf(txtPwd.getText());
//
//        //Call method and get corresponding result
//        String sign = Crypt.encrypt_data(0, Plaintext, 85);
//
//
//        passwordEditText.setText(sign);
//
//        // Send + write result
        String Plaintext = txtUser.getText().toString();
        String Ciphertext = txtPwd.getText().toString();

        // Call method and get corresponding result
        String sign = Crypt.encrypt_data(0, Plaintext, 85);

        // Display encryption result in passwordEditText
        passwordEditText.setText(sign);

    }

    public void jNI_Dynamic_Register(){
//        DynamicUtils utils = new DynamicUtils();
        int result = DynamicUtils.add(5, 7);
        System.out.println("jNI_Dynamic_Register ---> Result: " + result); // Output: Result: 12
//        String value = DynamicUtils.ssss(11,22);
//        Log.e("---->", value); //  rwxt

    }

    public void clang_Call_Java_v9(){
        String resultn9 = clangCallJavaNew.v9("October 2024",2024);
        System.out.println("Instance method---C calls Java---->Result: " + resultn9);

        // -----------------Call native method ------------- kanxue MissKing's ROM JNI tracing function test-----------
        // Create JniTraceMikrom instance and call lookForDemo method
        JniTraceMikrom jniTrace = new JniTraceMikrom();
        String result = jniTrace.lookForDemo();

        // Print return value of lookForDemo method
        Log.i(TAG, "Jni-Trace-Mikrom--case---->Result: " + result);

        // -----------------Call native method ------------- kanxue MissKing's ROM JNI tracing function test-----------------

        clangCallJavaNewExampleEncrypted resultv10 = EncryptUtils.v10("aid=123488384&page=199&size=100");
        if (resultv10 != null) {
            String resultv10_string = resultv10.toString();
            Log.e("---->", resultv10_string);
        } else {
            Log.e("---->", "Error: resultv10 is null.");
        }
    }
    public void clang_Call_Java_v8(){
        // Static method  (C calls Java)
        String resultn8 = clangCallJavaStatic.v8();
//        System.out.println(clangCallJavaStatic.v8());
        System.out.println("Static method---C calls Java---->Result: " + resultn8);
    }
    public void jniCall_static(){
        // 5.5 Character processing
//        String resultn5 = EncryptUtils.v5("name=wupeiqi&age=19");
        EncryptUtils.v5("name=wupeiqi&age=19");
//        System.out.println("5.5 Character processing--> Result: " + resultn5);
        // 5.6 Byte processing
//        String resultn6 = EncryptUtils.v6(   "name=wupeiqi&age=19".getBytes()    );
        EncryptUtils.v6(   "name=wupeiqi&age=19".getBytes()    );
//        System.out.println("5.6 Byte processing--> Result: " + resultn6);

        EncryptUtils.v7(   "name=wupeiqi&age=19".getBytes()    );
    }

    public void jniCall(){

        // JNI C (native variable already declared in Java's EncryptUtils class)
        // Call core algorithm, get signature value, send to backend API.
        String signString = EncryptUtils.v0(11, "alex");
        // More awesome algorithm, let it be implemented in C language.
        int sign1 = EncryptUtils.v1(2, 3);
        Log.e("Fn1--》Test Java calls C V1 function---->", String.valueOf(sign1)); //  133

        // Java calls JNI C language --V2 function
        String sign2 = EncryptUtils.v2("root");
        Log.e("Fn1--》Test calling Native V2 function----->",sign2);

        // Java calls JNI C language --V4 function
        // JNI CPP
//        TextView tv4 = findViewById(R.id.buttonRun1);
        String signV4 = EncryptUtils.v4("called","students");
        //tv.setText(EncryptUtils.v4("called","students"));
        fn1Button.setText(signV4);

        Log.e("Fn1--》Test calling Native V4 function----->",signV4);
    }

}
```

### Definition of user login request

```java

                private void doLogin() {
        String username = binding.username.getText().toString();
        String password = binding.password.getText().toString();

        TreeMap<String, String> map = new TreeMap<>();
        map.put("user", username);
        map.put("pwd", password);

        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
        }
        sb.deleteCharAt(sb.length() - 1); // Remove the final '&'

        String signString = md5(sb.toString());
        map.put("sign", signString);

        new Thread(() -> {
            try {
                Retrofit retrofit = new Retrofit.Builder()
                        .baseUrl("http://192.168.9.101:8081")
                        .build();
                HttpReq req = retrofit.create(HttpReq.class);   // Create a Retrofit interface
                // Send using form format
                // Call<ResponseBody> call = req.postLogin("xianning","password_good");
                // Send using form format

                // Send using JSON format

                JSONObject json = new JSONObject(map);
                String jsonString = json.toString();

                RequestBody form = RequestBody.create(
                        MediaType.parse("application/json;charset=utf-8"),
                        jsonString);
                Call<ResponseBody> call = req.postLoginJson(form); // Call method name, method name needs to pass parameters
                // Send using JSON format

                //  ResponseBody responseBody =  req.postLoginJson( RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString)).execute().body()
                ResponseBody responseBody = call.execute().body();// Execute method,
                if (responseBody == null) {
                    Log.e("Login", "Response body is null");
                    return;
                }

                String responseString = responseBody.string();//   Get return value after execution
                // The continuous operation is , req.postLoginJson( RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString)).execute().body().string()

                // {"code":200,"msg":"ok","token":"jfieefjeifjei-jefjeifejf-ejfiefi-100"}
                HttpResponse obj = new Gson().fromJson(responseString, HttpResponse.class);

                //Log.e("Retrofit return result",responseString);
                Log.e("Retrofit return result", obj.token);// Output Gson deserialization

                // Get server credentials, write to local xml file
                // Write to SharedPreferences
                Context context = getContext();
                if (context != null) {
                    SharedPreferences sp = context.getSharedPreferences("s_city", Context.MODE_PRIVATE);
                    SharedPreferences.Editor editor = sp.edit();
                    editor.putString("token", obj.token);
                    editor.apply();

                    // Read token value from local xml file
                    String token = sp.getString("token", "");
                    Log.e("Read token value from local xml file", token);
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
    }
```

### user login request api

```python

                from flask import Flask, jsonify, request

app = Flask(__name__)

@app.route('/auth',methods=["GET","POST"])
def auth():

    """
    # 1,Get data (Form format)
    username = request.form.get("user")
    password = request.form.get("pwd")
    sign = request.form.get('sign')
    print(username,password,sign)
    """
    # 1,Get data
    print(request.data)  # Get raw format
    # print(request.json)
    print(request.form)
    print(request.headers)

    # 2,Verify signature validity

    # 3,Business processing
    return jsonify({"code":200,"msg":"ok","token":"jfieefjeifjei-jefjeifejf-ejfiefi-100"})

@app.route('/users',methods=["GET","POST"])
def users():
    # print(request.data)  # Get raw format
    print(request.json)
    print(request.form)
    print(request.headers)

    return jsonify({"code":200,"msg":"ok","token":"jfieefjeifjei-jefjeifejf-ejfiefi-100"})

if __name__ == '__main__':
    # app.run(host='127.0.0.1', port=8080)
    # app.run(host='10.10.10.236', port=8080)
    app.run(host='0.0.0.0', port=8081)
```

## Network Request Flow

### **Step 1**: **Request Method**

```java

                private void doLogin() {
    String username = binding.username.getText().toString();
    String password = binding.password.getText().toString();

    // 1. Construct request data
    TreeMap<String, String> map = new TreeMap<>();
    map.put("user", username);
    map.put("pwd", password);

    // 2. Generate MD5 signature
    String signString = md5("pwd=" + password + "&user=" + username);
    map.put("sign", signString);

    // 3. Async network request
    new Thread(() -> {
        try {
            // Use Retrofit to send request
            Retrofit retrofit = new Retrofit.Builder()
                    .baseUrl("http://192.168.9.101:8081")
                    .build();

            HttpReq req = retrofit.create(HttpReq.class);

            // Convert to JSON format
            JSONObject json = new JSONObject(map);
            String jsonString = json.toString();

            // Create request body
            RequestBody form = RequestBody.create(
                    MediaType.parse("application/json;charset=utf-8"),
                    jsonString);

            // Send request
            Call<ResponseBody> call = req.postLoginJson(form);
            ResponseBody responseBody = call.execute().body();

            if (responseBody != null) {
                String responseString = responseBody.string();
                // Handle response data...
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
}
```

### **Step2:**Form Format Comparison**** -->**Form Format** (`application/x-www-form-urlencoded`)

```java

                // Construct Form data
FormBody.Builder formBuilder = new FormBody.Builder()
    .add("user", "calleng")
    .add("pwd", "2022")
    .add("sign", "5f4dcc3b5aa765d61d8327deb882cf99");

RequestBody formBody = formBuilder.build();
```

**HTTP Request Example:**

```
POST /auth HTTP/1.1
Host: 192.168.9.101:8081
Content-Type: application/x-www-form-urlencoded
Content-Length: 58

pwd=2022&user=calleng&sign=5f4dcc3b5aa765d61d8327deb882cf99
```

**JSON Format** (`application/json`) - **Currently Used**

```java

                // Construct JSON data
TreeMap<String, String> map = new TreeMap<>();
map.put("user", "calleng");
map.put("pwd", "2022");
map.put("sign", "5f4dcc3b5aa765d61d8327deb882cf99");

JSONObject json = new JSONObject(map);
String jsonString = json.toString();

RequestBody jsonBody = RequestBody.create(
    MediaType.parse("application/json;charset=utf-8"),
    jsonString
);
```

**HTTP Request Example:**

```
POST /auth HTTP/1.1
Host: 192.168.9.101:8081
Content-Type: application/json;charset=utf-8
Content-Length: 78

{
  "pwd": "2022",
  "user": "calleng",
  "sign": "5f4dcc3b5aa765d61d8327deb882cf99"
}
```

### ****Step 3: Retrofit Configuration Code**** **. Retrofit Interface Definition**

```java

                import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;
import retrofit2.http.GET;

public interface HttpReq {

    // JSON format POST request
    @POST("/auth")
    Call<ResponseBody> postLoginJson(@Body RequestBody body);

    // Form format POST request (optional)
    @POST("/auth")
    @FormUrlEncoded
    Call<ResponseBody> postLoginForm(
        @Field("user") String user,
        @Field("pwd") String pwd,
        @Field("sign") String sign
    );

    // GET request to get user info
    @GET("/users")
    Call<ResponseBody> getUserInfo();
}
```

```java

                import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

public class RetrofitClient {

    private static Retrofit retrofit = null;

    // Basic configuration
    public static Retrofit getBasicClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl("http://192.168.9.101:8081")
                    .build();
        }
        return retrofit;
    }

    // Configuration with Gson converter
    public static Retrofit getGsonClient() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl("http://192.168.9.101:8081")
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }

    // Configuration with OkHttp client
    public static Retrofit getFullClient(OkHttpClient client) {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl("http://192.168.9.101:8081")
                    .client(client)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }
        return retrofit;
    }
}
```

### **Step 4: Request Interceptor**

```java

                import okhttp3.logging.HttpLoggingInterceptor;

public class LoggingInterceptor {
    public static HttpLoggingInterceptor getInterceptor() {
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY); // Log complete request and response info
        return logging;
    }
}
```

```java

                import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public class HeaderInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();

        Request requestWithHeaders = originalRequest.newBuilder()
                .addHeader("User-Agent", "Android-NetFilter/1.0")
                .addHeader("Accept", "application/json")
                .addHeader("Content-Type", "application/json;charset=utf-8")
                .addHeader("X-App-Version", "1.0.0")
                .build();

        return chain.proceed(requestWithHeaders);
    }
}
```

```java

                import android.content.Context;
import android.content.SharedPreferences;

public class AuthInterceptor implements Interceptor {
    private Context context;

    public AuthInterceptor(Context context) {
        this.context = context;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request originalRequest = chain.request();

        // Read Token from SharedPreferences
        SharedPreferences sp = context.getSharedPreferences("s_city", Context.MODE_PRIVATE);
        String token = sp.getString("token", "");

        if (!token.isEmpty()) {
            Request requestWithAuth = originalRequest.newBuilder()
                    .addHeader("Authorization", "Bearer " + token)
                    .build();
            return chain.proceed(requestWithAuth);
        }

        return chain.proceed(originalRequest);
    }
}
```

```java

                import okhttp3.OkHttpClient;
import java.util.concurrent.TimeUnit;

public class OkHttpClientFactory {

    public static OkHttpClient createClient(Context context) {
        return new OkHttpClient.Builder()
                // Add interceptors
                .addInterceptor(new HeaderInterceptor())
                .addInterceptor(new AuthInterceptor(context))
                .addInterceptor(LoggingInterceptor.getInterceptor())

                // Set timeout
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)

                // Retry mechanism
                .retryOnConnectionFailure(true)

                .build();
    }
}
```

### **Step 5: Gson Deserialization Parsing**

```javascript

                // Response data model
public class HttpResponse {
    public int code;
    public String msg;
    public String token;
    public User data; // Optional user data

    @Override
    public String toString() {
        return "HttpResponse{" +
                "code=" + code +
                ", msg='" + msg + ''' +
                ", token='" + token + ''' +
                '}';
    }
}

// User data model (optional)
public class User {
    public String id;
    public String username;
    public String email;
    public long createTime;
}
```

```java

                import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class GsonParser {

    // 1. Basic object parsing
    public static HttpResponse parseResponse(String jsonString) {
        Gson gson = new Gson();
        HttpResponse response = gson.fromJson(jsonString, HttpResponse.class);
        return response;
    }

    // 2. Parsing with generics
    public static List<User> parseUserList(String jsonString) {
        Gson gson = new Gson();
        Type type = new TypeToken<List<User>>(){}.getType();
        return gson.fromJson(jsonString, type);
    }

    // 3. Manual parsing (more flexible)
    public static void manualParse(String jsonString) {
        try {
            JSONObject jsonObject = new JSONObject(jsonString);

            int code = jsonObject.getInt("code");
            String msg = jsonObject.getString("msg");
            String token = jsonObject.optString("token", ""); // optString avoids exceptions

            if (code == 200) {
                Log.i("GsonParse", "Login successful: " + token);
            } else {
                Log.e("GsonParse", "Login failed: " + msg);
            }

        } catch (Exception e) {
            Log.e("GsonParse", "Parsing failed: " + e.getMessage());
        }
    }
}
```

```java

                private void doLoginWithGson() {
    // ... previous network request code

    // After receiving response
    String responseString = responseBody.string();

    // Method 1: Direct parsing
    HttpResponse response = GsonParser.parseResponse(responseString);
    Log.e("GsonParsingResult", "Token: " + response.token);

    // Method 2: Manual parsing
    GsonParser.manualParse(responseString);

    // Method 3: Use Gson instance
    Gson gson = new Gson();
    HttpResponse obj = gson.fromJson(responseString, HttpResponse.class);
    Log.e("DirectGsonParsing", obj.token);
}
```

### **Step 6: XML File Save Code**

```java

                import android.content.Context;
import android.content.SharedPreferences;

public class PreferenceManager {
    private Context context;
    private SharedPreferences sharedPreferences;

    public PreferenceManager(Context context) {
        this.context = context;
        this.sharedPreferences = context.getSharedPreferences("s_city", Context.MODE_PRIVATE);
    }

    // Save Token
    public void saveToken(String token) {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("token", token);
        editor.putString("login_time", String.valueOf(System.currentTimeMillis()));
        editor.apply(); // Async save

        Log.i("SharedPreferences", "Token saved: " + token);
    }

    // Read Token
    public String getToken() {
        return sharedPreferences.getString("token", "");
    }

    // Save user info
    public void saveUserInfo(String username, String userId) {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.putString("username", username);
        editor.putString("user_id", userId);
        editor.putBoolean("is_logged_in", true);
        editor.apply();
    }

    // Clear all data
    public void clearAllData() {
        SharedPreferences.Editor editor = sharedPreferences.edit();
        editor.clear();
        editor.apply();
        Log.i("SharedPreferences", "All data cleared");
    }
}
```

```java

                import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

public class XmlManager {
    private Context context;
    private String xmlFileName = "login_data.xml";

    public XmlManager(Context context) {
        this.context = context;
    }

    // Save data to XML file
    public void saveToXml(String token, String username, String userId) {
        try {
            // Get file path
            File xmlFile = new File(context.getFilesDir(), xmlFileName);

            // Create Document
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.newDocument();

            // Create root element
            Element rootElement = doc.createElement("LoginData");
            doc.appendChild(rootElement);

            // Create user info element
            Element userElement = doc.createElement("User");
            rootElement.appendChild(userElement);

            // Add child elements
            createElement(doc, userElement, "Token", token);
            createElement(doc, userElement, "Username", username);
            createElement(doc, userElement, "UserId", userId);
            createElement(doc, userElement, "LoginTime", String.valueOf(System.currentTimeMillis()));

            // Save to file
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);
            StreamResult result = new StreamResult(xmlFile);

            transformer.transform(source, result);

            Log.i("XMLFile", "Data saved successfully: " + xmlFile.getAbsolutePath());

        } catch (Exception e) {
            Log.e("XMLFile", "Save failed: " + e.getMessage());
        }
    }

    // Helper method to create elements
    private void createElement(Document doc, Element parent, String tagName, String textContent) {
        Element element = doc.createElement(tagName);
        element.setTextContent(textContent);
        parent.appendChild(element);
    }

    // Read XML file
    public void readFromXml() {
        try {
            File xmlFile = new File(context.getFilesDir(), xmlFileName);

            if (!xmlFile.exists()) {
                Log.w("XMLFile", "File does not exist");
                return;
            }

            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(xmlFile);

            Element root = doc.getDocumentElement();
            Element userElement = (Element) root.getElementsByTagName("User").item(0);

            String token = getElementText(userElement, "Token");
            String username = getElementText(userElement, "Username");
            String loginTime = getElementText(userElement, "LoginTime");

            Log.i("XMLRead", "Token: " + token);
            Log.i("XMLRead", "Username: " + username);
            Log.i("XMLRead", "LoginTime: " + loginTime);

        } catch (Exception e) {
            Log.e("XMLFile", "Read failed: " + e.getMessage());
        }
    }

    // Helper method to get element text
    private String getElementText(Element parent, String tagName) {
        Element element = (Element) parent.getElementsByTagName(tagName).item(0);
        return element != null ? element.getTextContent() : "";
    }
}
```

```java

                private void handleLoginResponse(HttpResponse response) {
    if (response.code == 200) {
        // 1. Use SharedPreferences to save
        PreferenceManager preferenceManager = new PreferenceManager(getContext());
        preferenceManager.saveToken(response.token);
        preferenceManager.saveUserInfo("calleng", "12345");

        // 2. Use XML file to save
        XmlManager xmlManager = new XmlManager(getContext());
        xmlManager.saveToXml(response.token, "calleng", "12345");

        // 3. Read and verify
        String savedToken = preferenceManager.getToken();
        Log.i("StorageVerification", "Saved Token: " + savedToken);

        xmlManager.readFromXml(); // Read XML file content

    } else {
        Log.e("LoginFailed", response.msg);
    }
}
```

## **Summary**

### **Data Flow:**

```
User Input
→ Construct Form
→ MD5 Signature
→ JSON Serialization
→ Retrofit Send
→ Interceptor Process
→ Server Response
→ Gson Parse
→ XML Save
```

```bash

                dipper:/data/data/ca.netfilter # cd shared_prefs/
dipper:/data/data/ca.netfilter/shared_prefs # ls
s_city.xml
dipper:/data/data/ca.netfilter/shared_prefs # cat s_city.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="token">jfieefjeifjei-jefjeifejf-ejfiefi-100</string>
</map>
dipper:/data/data/ca.netfilter/shared_prefs #
```

### **Key Components:**

1. **Retrofit2** - Network request framework
2. **OkHttp** - HTTP client + interceptors
3. **Gson** - JSON serialization/deserialization
4. **SharedPreferences** - Lightweight XML storage
5. **DOM Parser** - Standard XML file operations
