iT邦幫忙

2022 iThome 鐵人賽

DAY 29
0
Mobile Development

Android Studio 30天學習系列 第 29

Android Studio 30天學習-DAY29_API接取的綜合實作

  • 分享至 

  • xImage
  •  

綜合實作

今天我要把前幾天所學的功能實作成一個簡單的接收API專案,這邊以FakeAPI的資料來接取。
大致上的功能是判斷輸入的帳號是否正確接著拿取到資料,並且是以MVP架構下去撰寫。

首先XML的畫面刻劃

  • 畫面預覽狀態
  • 使用到的元件
    • GuideLine:用來對齊元件
    • EditText:輸入帳號以及密碼
    • TextView:結果輸出區&帳號密碼的標題文字
    • Button:確定送出帳號密碼
   <androidx.constraintlayout.widget.Guideline
       android:id="@+id/GLPersent01"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:orientation="horizontal"
       app:layout_constraintGuide_percent="0.1"/>
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/GLPersentvertcal01"
        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/GLPersentvertcal09"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.9"/>
    <TextView
        android:id="@+id/Account"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="帳號:"
        android:textAlignment="center"
        android:textSize="25dp"
        app:layout_constraintTop_toTopOf="@+id/GLPersent01"
        app:layout_constraintStart_toStartOf="@id/GLPersentvertcal01"/>
    <EditText
        android:id="@+id/ETaccount"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="請輸入帳號"
        app:layout_constraintTop_toTopOf="@id/GLPersent01"
        app:layout_constraintStart_toEndOf="@id/Account"
        app:layout_constraintEnd_toEndOf="@id/GLPersentvertcal09"/>
    <TextView
        android:id="@+id/password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="密碼:"
        android:textAlignment="center"
        android:textSize="25dp"
        app:layout_constraintTop_toBottomOf="@id/Account"
        app:layout_constraintStart_toStartOf="@id/GLPersentvertcal01"/>
    <EditText
        android:id="@+id/ETpassword"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:hint="請輸入密碼"
        app:layout_constraintTop_toBottomOf="@id/Account"
        app:layout_constraintStart_toEndOf="@id/password"
        app:layout_constraintEnd_toEndOf="@id/GLPersentvertcal09"/>

    <Button
        android:id="@+id/EnterButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="確定"
        app:layout_constraintTop_toBottomOf="@id/password"
        app:layout_constraintStart_toStartOf="@id/GLPersentvertcal01"
        app:layout_constraintEnd_toEndOf="@id/GLPersentvertcal09"
        android:layout_marginTop="10dp"
        tools:ignore="MissingConstraints" />

    <TextView
        android:id="@+id/showStatus"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:hint="結果顯示區"
        android:textAlignment="center"
        android:textSize="18dp"
        android:background="@color/teal_200"
        app:layout_constraintTop_toBottomOf="@id/EnterButton"
        app:layout_constraintStart_toStartOf="@id/GLPersentvertcal01"
        app:layout_constraintEnd_toEndOf="@id/GLPersentvertcal09"
        app:layout_constraintBottom_toBottomOf="parent"/>

創建Retrofit2

  • dependencies依賴
    //Retrofit2
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
    implementation("com.squareup.okhttp3:okhttp:4.10.0")
  • SiteManager
    用來設定連接API資料的網址,網址通常會使用前面皆為相同的網址,只有後面的所在資料區域不同。
    // 以Singleton模式建立
    private static SiteManager mInstance = new SiteManager ();

    private APIService myAPIService;

    private SiteManager () {

        // 設置baseUrl即要連的網站,addConverterFactory用Gson作為資料處理Converter
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://jsonplaceholder.typicode.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        myAPIService = retrofit.create(APIService.class);

    }

    public static SiteManager getInstance() {
        return mInstance;
    }

    public APIService getAPI() {
        return myAPIService;
    }
  • APIService(interface)
    後半的網址以及所需要的型態是哪一個,常用的有GET、POST、DELETE。
    @GET("posts")
    Call<List<APIResponse>> response();
  • APIResponse
    索取在posts的回覆資料型態有哪些並將其以對應的資料型態設定,然後設定Getter以及Setter,toString是將以上的資料以一組為包裝回覆。
    private int userId;
    private int id;
    private String title;
    private String body;

    @Override
    public String toString() {
        return "APIResponse{" +
                "userId=" + userId +
                ", id=" + id +
                ", title='" + title + '\'' +
                ", body='" + body + '\'' +
                '}';
    }

    public int getUserId() {
        return userId;
    }

    public void setUserId(int userId) {
        this.userId = userId;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getBody() {
        return body;
    }

    public void setBody(String body) {
        this.body = body;
    }
}

MVP架構

  • 建立一個interface:
    • View
    • Presenter
    • Model
    interface View{
        void setString(String string);
    }

    interface Presenter{
        void onButtonClick(String Password,String Account);
    }

    interface Model{
        interface FinishListener {
            void OnFinished(String string);
        }

        void OnNext(Contract.Model.FinishListener OnFinished);
    }
  • 三個對應的Class

    • MainActivity(一開始的主程式)
    • Presenter
    • Model
  • MainActivity(View)

    • 程式碼解析:
      • 先初始化各個元件所對應的位置。
      • 接著當我們按下按鈕之後將我們輸入的文字傳到Presenter。
      • 如果成功的話在SetString會收到回傳的資料,再將其寫在輸出TextView上面。
public class MainActivity extends AppCompatActivity implements Contract.View{
    private TextView showText;
    private Button button;
    private EditText account,password;
    private Contract.Presenter presenter;
    private static String TAG = "View";


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        button = findViewById(R.id.EnterButton);
        showText = findViewById(R.id.showStatus);
        account = findViewById(R.id.ETaccount);
        password = findViewById(R.id.ETpassword);
        presenter = new Presenter(this,new Model());


        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Log.e(TAG, "onClick: ");
                presenter.onButtonClick(account.getText().toString(),password.getText().toString());
            }
        });
    }

    @Override
    public void setString(String string) {
        Log.e(TAG, "setString: ");
        showText.setText(string);
    }
}
  • Presenter
    • 程式碼解析:
      • 修改的部分在OnButtonClick的回傳值,這邊我們會收到從View傳過來的輸入值
      • 接著拿取到之後進行簡單的身分判斷
      • 如果為正確就會進入Model拿取資料。
      • 若是錯誤或是空輸入就會Logcat錯誤訊息。
      • OnFinished拿取到資料數值並將其傳入View的setString。
public class Presenter implements Contract.Presenter,Contract.Model.FinishListener{

    private Contract.View mainView;
    private Contract.Model model;
    private static String TAG = "Presenter";
    private String password,account;

    public Presenter(Contract.View mainView, Contract.Model model) {
        this.mainView = mainView;
        this.model = model;
    }

    @Override
    public void OnFinished(String string) {
        if(mainView != null){
            Log.e(TAG, "OnFinished: ");
            mainView.setString(string);
        }
    }

    @Override
    public void onButtonClick(String Password,String Account) {
        Log.e(TAG, "onButtonClick: ");
        account = Account;
        password = Password;
        Log.e(TAG, "onButtonClick: "+password+"  "+account );
        if(account.equals("jay") && password.equals("jay")){
            Log.e(TAG, "Account is Correct: ");
            model.OnNext(this);
        }else{
            Log.e(TAG, "Account is Error: ");
        }
    }
}
  • Model
    • 程式碼解析:
      • 這邊將Retrofit所設定的抓取API並寫在副程式上。
      • 在副程式中所寫的是拿取到回覆的資料。
      • 接著在OnNext中套入這個副程式並回傳到在interface的FinishListener中,這樣就能在副程式的回傳值讀取到,也能透過副程式拿到正確的資料。
        • 回覆成功中的劃線部分是詳細的API回覆資料內容,透過逐一取出拿到所有資料。
public class Model implements Contract.Model{
    private static String TAG = "Model";
    private APIService apiService = SiteManager.getInstance().getAPI();

    @Override
    public void OnNext(Contract.Model.FinishListener OnFinished) {
        GetFakeAPI(OnFinished);
    }

    public void GetFakeAPI(Contract.Model.FinishListener OnFinished){
        Call<List<APIResponse>> call = apiService.response ();

        call.enqueue (new Callback<List<APIResponse>>() {
            @Override
            public void onResponse (Call<List<APIResponse>> call, Response<List<APIResponse>> response) {
//                for (int i = 0;i<response.body ().size ();i++){
//                    Log.e (TAG, "userid: "+response.body ().get (i).getUserId ());
//                    Log.e (TAG, "ID: "+response.body ().get (i).getId ());
//                    Log.e (TAG, "title: "+response.body ().get (i).getTitle ());
//                    Log.e (TAG, "body: "+response.body ().get (i).getBody ());
//                }
//                Log.e (TAG, "ToString Size = "+response.body().toString());
                OnFinished.OnFinished(response.body().toString());
            }

            @Override
            public void onFailure (Call<List<APIResponse>> call, Throwable t) {
                Log.e (TAG, "onFailure: " );
            }
        });
    }
}

Rxjava接收

這邊的Model撰寫方式與上面的GetFakeAPI一樣是寫在副程式並套入到OnNext中。

    public void GetObservableResponse(Contract.Model.FinishListener OnFinished){
        apiService.observableResponse()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DisposableObserver<Response<List<APIResponse>>>() {
                    @Override
                    public void onNext(@NonNull Response<List<APIResponse>> listResponse) {
//                        for(int i = 0;i<listResponse.body().size();i++){
//                            Log.e(TAG, "onNextId: "+listResponse.body().get(i).getId() );
//                            Log.e(TAG, "onNextUserId: "+listResponse.body().get(i).getUserId() );
//                            Log.e(TAG, "onNextTitle: "+listResponse.body().get(i).getTitle() );
//                            Log.e(TAG, "onNextBody: "+listResponse.body().get(i).getBody() );
//                        }
                        Log.e(TAG, "onNext_Observable: ");
                        OnFinished.OnFinished(listResponse.body().toString());
                    }

                    @Override
                    public void onError(@NonNull Throwable e) {
                        Log.e(TAG, "onError: "+e );
                    }

                    @Override
                    public void onComplete() {
                        Log.e(TAG, "已完成資料接收 ");
                    }
                });
    }
  • Rxjava的建立方式前幾天有寫到這邊就不再寫了,建立方法是一樣的。

結果圖

  • 尚未輸入帳號密碼的時候
  • 輸入帳號密碼不正確
  • 輸入帳號密碼正確並且收到資料的toString內容
  • Rxjava接取結果(畫面與上方都類似這邊就只放Logcat)
    • 這邊也能看到Rxjava的接取結果。
    • 當帳號正確並進到Model然後導入到副程式接取資料。
    • 接取完成後傳到Presenter然後接著到View,並顯示在TextView上。
    • 最後完成資料接取。

這邊是今天所做的綜合實作,結合了API_in_MVP以及rxJava接取的基本融合實作。


上一篇
Android Studio 30天學習-DAY28_SharedPerferences基本建立學習
下一篇
Android Studio 30天學習-DAY30_結語以及心得
系列文
Android Studio 30天學習30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言