彙總:本系列的彙總
Android Studio配置
-
新建Android Studio工程。
-
最重要的一步:添加網絡權限!添加網絡權限!!。還記得當時debug的時候,後面的我都寫好了…然後因爲沒有添加網絡權限怎麼都是錯…說起來都是淚…
在AndoidManifest中添加如下代碼:<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
-
添加下面的依賴,點擊sync now。okhttp用於發送http請求,fastjson用於解析JSON串和對象的轉化。
implementation 'com.squareup.okhttp:okhttp:2.4.0' implementation 'com.alibaba:fastjson:1.2.62'
前期準備
-
新建RegisterActivity、LoginActivity、SuccessActivity和其對應的xml,分別用於註冊、登錄、登錄成功之後跳轉的頁面。自行發揮吧,我這因爲登錄和註冊界面xml基本一樣就只貼activity_register.xml和activity_success.xml了。非常非常簡單的xml。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp" android:orientation="vertical" tools:context=".RegisterActivity"> <EditText android:id="@+id/reg_uid" android:layout_width="match_parent" android:layout_height="50dp" android:hint="請設置賬號" /> <EditText android:id="@+id/reg_password" android:layout_width="match_parent" android:layout_height="50dp" android:hint="請設置密碼" /> <Button android:id="@+id/reg_btn" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="注 冊" android:textSize="20dp" /> </LinearLayout>
<?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" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".SuccessActivity"> <TextView android:id="@+id/welcome" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Congratulations!" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/userId" android:layout_width="wrap_content" android:layout_height="28sp" android:text="UserId" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/welcome" app:layout_constraintStart_toStartOf="@+id/welcome" app:layout_constraintTop_toBottomOf="@+id/welcome" app:layout_constraintVertical_bias="0.05" /> </androidx.constraintlayout.widget.ConstraintLayout>
-
添加Entity包,在其中加入User類。因爲我們在服務端loginCheck時會返回一個User對象,我們客戶端也需要一個User對象來容納返回的數據。
下面是我編寫的User類,我特意將User類編寫得與服務端不一樣:服務端沒有age屬性。但是這並不會影響其對服務器對象的接收,因爲服務端的User裏面也有userId和password。public class User { String userId; //id String password; //密碼 String age; //age 故意多的一個和服務器不同的變量 public String getUserId() { return userId; } public void setUserId(String userId) { this.userId = userId; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
-
(非必需)這裏面我們可以加入一個小工具,檢測網絡是否開啓,以改善用戶體驗。在uitl包下加入NetStateUtil:
public class NetStateUtil { static String NETWORK_STATE_ERROR = "網絡連接錯誤"; public static boolean checkNetworkState(Context context){ ConnectivityManager connectivityManager = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo(); if(networkInfo == null){ //網絡連接異常 Toast.makeText(context,NETWORK_STATE_ERROR,Toast.LENGTH_SHORT).show(); return false; } return true; } }
發送網絡請求
-
以RegisterActivity爲例,我們設置在點擊“登錄”之後發起http請求,將userId和password送去服務器判斷是否能夠註冊。點擊事件和網絡相關的代碼如下:
//註冊對應的點擊事件 registerButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //1. 獲取控件中所填寫的值, 注意一定要寫在listener內! String userId = uidText.getText().toString(); String password = passwordText.getText().toString(); //2. 網絡爲連接則直接返回 if(!NetStateUtil.checkNetworkState(RegisterActivity.this)) return; //3. 發起http請求 OkHttpClient okHttpClient = new OkHttpClient(); //創建OkHttpClient實例 FormEncodingBuilder builder = new FormEncodingBuilder(); RequestBody requestBody = builder.add("userId", userId) .add("password",password) .build(); //需要傳輸的數據存入requestBody Request request = new Request.Builder() .url(registerUrl) //需要的url .post(requestBody) //註冊時需要向服務端上傳數據,所以使用post .build(); Call call = okHttpClient.newCall(request); call.enqueue(new Callback() { //開啓異步進程 @Override public void onFailure(Request request, IOException e) { runOnUiThread(new Runnable() { @Override public void run() { //連接超時時的提醒 Toast.makeText(RegisterActivity.this,"Fail",Toast.LENGTH_SHORT).show(); } }); } @Override public void onResponse(Response response) throws IOException { //成功獲取響應時 //不要toString String responseData = response.body().string(); //解析,responseData,獲取響應數據 final boolean data = JSON.parseObject(responseData,boolean.class); //將所接受的json數據轉換成boolean對象 runOnUiThread(new Runnable() { @Override public void run() { //顯示回調函數 Toast.makeText(RegisterActivity.this,"data"+data,Toast.LENGTH_SHORT).show(); if(data == true) LoginActivity.actionStart(RegisterActivity.this); else Toast.makeText(RegisterActivity.this,"賬號已被註冊",Toast.LENGTH_SHORT).show(); } }); } }); } });
其中三個主要的邏輯我們已經寫在註釋中了,這裏就直接解析一下發送http請求時的操作。
大致分爲這幾個步驟:- 創建OkHttpClient對象。
- 將所需要向服務器傳輸的數據放入requestBody。
- 使用requestBody和要訪問的Url創建request對象。
- 開啓異步進程進行網絡請求。
步驟很簡單,也很固定。需要注意的幾個地方:
1 關於requestBody:
.add(“userId”,userId)其實就是在需要傳輸的JSON數據中加入了一個鍵值對。和服務端這裏相對應:
2 關於Request對象:
不需要向服務端傳requestBody的時候,.post那部分不要。
3 關於異步請求:
- 網絡連接屬於耗時操作,直接在UI線程操作會阻塞、閃退。使用上面的寫法其實是開啓了一個異步進程,不會卡。
- 在異步進程中進行UI操作時需要用runOnUiThread進行UI操作,否則會閃退。當然也還有使用Handler的方法,這裏我們暫時留個坑。
- 其實okhttp裏面也有同步方式的寫法(excute)。但是我看有人說已經棄用了,這裏就不說了。
4 關於JSON解析
:獲取了responseData(String類型)之後,我們可以將其解析爲Java對象,上面是解析爲簡單對象,我們也可以解析爲自定義對象、List。例子:boolean data = JSON.parseObject(responseData,boolean.class) //簡單 User user = JSON.parseObject(responseData, User.class) //自定義 List<User> users = JSON.parseArray(responseData, User.class) //Array
5 非常重要的一點是,
response.body().string()不要寫成toString()
,特別細小的bug,犯了涼涼。 -
LoginActivity的編寫。實際上和RegisterActivity基本一模一樣,該注意的地方也說了,就不貼代碼了。主要是onResponse裏面有點不一樣,需要解析對象。
@Override public void onResponse(Response response) throws IOException { final String responseData = response.body().string(); //響應數據 final User user = JSON.parseObject(responseData,User.class); //將所接受的json數據轉換成boolean對象 runOnUiThread(new Runnable() { @Override public void run() { //顯示回調函數 if(user!=null) //找到這個user的時候頁面跳轉 SuccessActivity.actionStart(LoginActivity.this,user.getUserId()); else //錯誤提示 Toast.makeText(LoginActivity.this,"賬號或密碼錯誤",Toast.LENGTH_SHORT).show(); } }); }
-
SuccessActivity:與網絡通信沒什麼關係了,主要是模擬登錄成功之後的頁面:
public class SuccessActivity extends AppCompatActivity { String uid; TextView uidText; /** * 啓動本activity * @param context 上一個activity的運行環境 * @param uid 用戶id,頁面間傳遞數據的用 */ public static void actionStart(Context context,String uid){ Intent intent = new Intent(context,SuccessActivity.class); intent.putExtra("uid",uid); context.startActivity(intent); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_success); //獲取數據 Intent intent = getIntent(); uid = intent.getStringExtra("userId"); //跳轉到本頁面之後在上面顯示user的id uidText = findViewById(R.id.userId); uidText.setText(uid); } }
安卓客戶端目前已經編寫完畢了,可以完成我們一開始展示的效果了。但是我們注意到,其實每一次網絡請求步驟都是一樣的,不一樣的只有url和requestBody,故我們何不將通用的網絡操作提取到一個類中,每次需要的時候調用那個類就好了。
將重複代碼提取爲HttpPostUtil類
直接貼代碼吧,比較簡單。放在util包下:
public class HttpPostUtil {
/**
* 發起post的http請求
* @param address url地址
* @param requestBody 需要傳輸的數據
* @param callback 回調接口,請求的最終結果會回調到其中
*/
public static void sendOkHttpRequest(String address, RequestBody requestBody, Callback callback){
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(address)
.post(requestBody)
.build();
client.newCall(request).enqueue(callback);
}
}
以RegisterActivity爲例,我們可以在其中將網絡通信相關的代碼改寫爲:
FormEncodingBuilder builder = new FormEncodingBuilder();
RequestBody requestBody = builder.add("userId", userId)
.add("password",password)
.build(); //需要傳輸的數據存入requestBody
HttpPostUtil.sendOkHttpRequest(loginUrl, requestBody, new Callback() {
@Override
public void onFailure(Request request, IOException e) {
...
}
@Override
public void onResponse(Response response) throws IOException {
...
}
});
這樣就會少點代碼了。
好啦,客戶端也編寫完成了。在你的電腦上運行Springboot服務端,在手機註冊和登錄,就可以實現我們最開始所說的效果了。