LoginSignup
16
21

More than 5 years have passed since last update.

Android Studio3 OkHttp3クライアントアプリを作ろう

Last updated at Posted at 2017-12-21

前書き

前回 、スマホで「『Hello World!』を表示するだけ」アプリの動作確認はできましたので、そのアプリを改良して、適当なWebサイトにGET/POSTするアプリを作成していきます。


連載記事リスト:Androidアプリ開発

アプリ構築ポイント

・AndroidManifest のパーミッションで、「INTERNET」と「STORAGE(READ)」を許可
・http クライアントは『OkHttp3』ライブラリを利用
・非同期処理のThreadは『AsyncTask』で実装
・AsyncTaskへのデータ渡しは、Object配列で
・POST送信で使うファイルは、すでにスマホのストレージにあるファイルを利用

build.gradleに依存関係追記

OkHttp3 を追加します。

app/build.gradle
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:26.1.0'
        implementation 'com.android.support.constraint:constraint-layout:1.0.2'
        implementation 'com.squareup.okhttp3:okhttp:3.9.1'  // ①OkHttp3 を追加
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.1'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
    }

①でOkHttp3を追加 OkHttp公式サイト
執筆時点(2017-12-15)では、最新のOkHttp3 は『ver 3.9.1』ですね。
AndroidStudio2までは、

app/build.gradle
compile 'com.squareup.okhttp3:okhttp:3.9.1'

という書式でしたが、AndroidStudio3 からは、

app/build.gradle
implementation 'com.squareup.okhttp3:okhttp:3.9.1'

と書くようです。エラーにはなりませんが…。

パーミッション設定追加

今回、パーミッションとして必要な「INTERNET」と「STORAGE(read)」を追加します。

AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.co.visionarts.noriseto.pocapp">

    <uses-permission android:name="android.permission.INTERNET" />              <!--①-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>  <!--②-->

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

①「INTERNET」パーミッション追加
②「READ_EXTERNAL_STORAGE」パーミッション追加

レイアウトを修正

「GET」「POST」ボタンを用意し、レスポンスは「TextView」で表示します。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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="jp.co.visionarts.noriseto.pocapp.MainActivity">

    <LinearLayout
        android:orientation="vertical"
        android:gravity="center_horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <Button
            android:id="@+id/button_get"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="GET"/>
        <Button
            android:id="@+id/button_post"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="POST"/>
        <!-- ① -->
        <ScrollView
            android:orientation="vertical"
            android:gravity="center_horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <TextView
                android:id="@+id/textview"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Response..."/>
        </ScrollView>
    </LinearLayout>

</android.support.constraint.ConstraintLayout>

①レスポンス表示の「TextView」は「ScrollView」タグで囲って縦スクロールできるようにします。

ソースコードを修正

MainActivity.java

MainActivity.java
package jp.co.visionarts.noriseto.pocapp;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity 
                implements View.OnClickListener { //①OnClickListener インタフェイスを継承

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

        //②ボタンにこのActivityのonClickListenerを割り当てる
        findViewById(R.id.button_get).setOnClickListener(this);
        findViewById(R.id.button_post).setOnClickListener(this);
    }

    //③onClickコールバックをここですべてキャッチして、viewのidで動作を決める
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button_get:
                //④GET リクエストサンプルとして「Livedoor天気情報」にアクセス
                String weatherURL = "http://weather.livedoor.com/forecast/webservice/json/v1";
                String queryString = "?city=130010"; //東京

                //⑤GetAsyncTaskに渡すパラメータをObject配列に設定
                Object[] getParams = new Object[2];
                getParams[0] = weatherURL;
                getParams[1] = queryString;

                //⑥GetAsyncTaskを実行
                new GetAsyncTask(this).execute(getParams);
                break;

            case R.id.button_post:
                //⑦POSTリクエストサンプルとして、自前のファイル受信サイトへアクセス
                String uploadURL = "http://va-fileupload.azurewebsites.net/uploadserver/api/upload/";
                String title = "Upload image file(JPEG)";
                String uploadFile = "/storage/emulated/0/Pictures/cat.jpg";

                //⑧PostAsyncTaskに渡すパラメータをObject配列に設定
                Object[] postParams = new Object[3];
                postParams[0] = uploadURL;
                postParams[1] = title;
                postParams[2] = uploadFile;

                //⑨PostAsyncTaskを実行
                new PostAsyncTask(this).execute(postParams);
                break;

            default:
                break;
        }
    }

}

①OnClickListener インタフェイスを継承
②ボタンにこのActivityのonClickListenerを割り当てる
③onClickコールバックをここですべてキャッチして、viewのidで動作を決める
④GET リクエストサンプルとして「Livedoor天気情報」にアクセス
⑤GetAsyncTaskに渡すパラメータをObject配列に設定
⑥GetAsyncTaskをパラメータ付で実行
⑦POSTリクエストサンプルとして、自前のファイル受信サイトへアクセス
 POSTリクエストは、適当なサイトが無かったので、自前サイトにファイルをアップロードしてみます。
⑧PostAsyncTaskに渡すパラメータをObject配列に設定
⑨PostAsyncTaskをパラメータ付で実行
 HTTP通信部分は、GET、POSTメソッドそれぞれ、AsyncTaskを継承した独自クラスに実装していきます。

自前のアップロードサイトは、Azure WebApp + Springboot で構築してまして、『簡単にWebサーバ作れますよ!』的なところも、いつの日か触れたいと思います。

GetAsyncTask.java

GetAsyncTask.java
package jp.co.visionarts.noriseto.pocapp;

import android.app.Activity;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.IOException;
import java.lang.ref.WeakReference;

import okhttp3.Call;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

/*
 * AsyncTask for GET Method
 * Created by noriseto on 2017/11/28.
 */

public class GetAsyncTask extends AsyncTask<Object, Void, Object> {

    //①USE WEEK REFERENCE
    private final WeakReference<Activity> w_Activity;

    //②コンストラクタで、 呼び出し元Activityを弱参照で変数セット
    GetAsyncTask(Activity activity) {
        this.w_Activity = new WeakReference<>(activity);
    }

    //③バックグラウンド処理
    @Override
    protected Object doInBackground(Object[] data) {

        //Object配列でパラメータを持ってこれたか確認
        String url = (String)data[0];
        String queryString = (String)data[1];

        //④HTTP処理用オプジェクト作成
        OkHttpClient client = new OkHttpClient();

        //⑤送信用リクエストを作成
        Request request = new Request.Builder().url(url + queryString).get().build();

        //⑥受信用オブジェクトを作成
        Call call = client.newCall(request);
        String result = "";

        //⑦送信と受信
        try {
            Response response = call.execute();
            ResponseBody body = response.body();
            if (body != null) {
                result = body.string();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //⑧結果を返し、onPostExecute で受け取る
        return result;
    }

    //⑨バックグラウンド完了処理
    @Override
    protected void onPostExecute(Object result) {
        super.onPostExecute(result);
        Log.i("onPostExecute", (String)result);
        //⑩簡単にJSONレスポンスをパースしてみる
        String title = "no response";
        String description = "";
        String publicTime = "";
        try {
            JSONObject json = new JSONObject((String)result);
            title = json.getString("title");
            JSONObject descriptionObj = (JSONObject)json.get("description");
            description = descriptionObj.getString("text");
            publicTime = descriptionObj.getString("publicTime");
        } catch (JSONException je) {
            je.getStackTrace();
        }

        //⑪画面表示
        Activity activity = w_Activity.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            // activity is no longer valid, don't do anything!
            return;
        }
        TextView tv = activity.findViewById(R.id.textview);
        String showText = title + "\n" + publicTime + "\n" + description;
        tv.setText(showText);
    }

}

①最近は、Activity contextをそのまま変数に入れると「This field leaks context object」というワーニングが出るので、弱参照とする
②コンストラクタで、 呼び出し元Activityを弱参照で変数セット
③バックグラウンド処理でWebサイトにアクセス
④HTTP処理用オプジェクト作成
⑤送信用リクエストを作成。引数のURLとQueryパラメータをセットする
⑥受信用オブジェクトを作成
⑦送信と受信
⑧結果を返し、onPostExecute で受け取る
⑨バックグラウンド処理の最後にレスポンスを画面表示
⑩JSONレスポンスは以下のJSONフォーマット (使うところだけ抜粋)

{
  "title": "東京都 東京 の天気",
  "description": {
    "text": " 本州付近は...",
    "publicTime": "2017-11-28T16:39:00+0900"
  }
}

⑪画面表示部分で、表示する直前に弱参照Activityから強参照のActivityを取得する

PostAsyncTask.java

PostAsyncTask.java
package jp.co.visionarts.noriseto.pocapp;

/*
 * AsyncTask for POST Method
 * Created by noriseto on 2017/11/28.
 */

import android.app.Activity;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;

import java.io.File;
import java.io.IOException;
import java.lang.ref.WeakReference;

import okhttp3.Call;
import okhttp3.Headers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class PostAsyncTask extends AsyncTask<Object, Void, Object> {

    //①USE WEEK REFERENCE
    private final WeakReference<Activity> w_Activity;

    //②コンストラクタで、 呼び出し元Activityを弱参照で変数セット
    PostAsyncTask(Activity activity) {
        // USE WEEK REFERENCE
        this.w_Activity = new WeakReference<>(activity);
    }

    //③バックグラウンド処理
    @Override
    protected Object doInBackground(Object[] data) {

        //Object配列でパラメータを持ってこれたか確認
        String url = (String)data[0];
        String description = (String)data[1];
        String filePath = (String)data[2];

        //④HTTP処理用オプジェクト作成
        OkHttpClient client = new OkHttpClient();

        //⑤送信用POSTデータを構築(Multipartでお願いします!)
        final String BOUNDARY = String.valueOf(System.currentTimeMillis());
        final MediaType TEXT = MediaType.parse("text/plain; charset=utf-8");
        final MediaType IMAGE = MediaType.parse("image/jpeg");
        RequestBody requestBody = new MultipartBody.Builder(BOUNDARY)
                .setType(MultipartBody.FORM)
                .addPart(
                        Headers.of("Content-Disposition", "form-data; name=\"description\""),
                        RequestBody.create(TEXT, description)
                )
                .addFormDataPart(
                        "upload_file",
                        "cat.jpg",
                        RequestBody.create(IMAGE, new File(filePath))
                )
                .build();

        //⑥送信用リクエストを作成
        Request.Builder requestBuilder = new Request.Builder();
        requestBuilder.url(url);
        requestBuilder.post(requestBody);
        Request request = requestBuilder.build();

        //⑦受信用オブジェクトを作成
        Call call = client.newCall(request);
        String result = "";

        //⑧送信と受信
        try {
            Response response = call.execute();
            ResponseBody body = response.body();
            if (body != null) {
                result = body.string();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        //⑨結果を返し、onPostExecute で受け取る
        return result;
    }

    //⑩バックグラウンド完了処理
    @Override
    protected void onPostExecute(Object result) {
        super.onPostExecute(result);
        Log.i("onPostExecute", (String)result);

        //⑪画面表示
        Activity activity = w_Activity.get();
        if (activity == null || activity.isFinishing() || activity.isDestroyed()) {
            // activity is no longer valid, don't do anything!
            return;
        }
        TextView tv = activity.findViewById(R.id.textview);
        tv.setText((String)result);
    }

}

①最近は、Activity contextをそのまま変数に入れると「This field leaks context object」というワーニングが出るので、弱参照とする
②コンストラクタで、 呼び出し元Activityを弱参照で変数セット
③バックグラウンド処理でWebサイトにアクセス
④HTTP通信オプジェクト作成
⑤送信用POSTデータは、どうせならとMultipartでファイルを送ってみる
⑥送信用リクエストを作成
⑦受信用オブジェクトを作成
⑧送信と受信
⑨結果を返し、onPostExecute で受け取る
⑩バックグラウンド処理の最後にレスポンスを画面表示
⑪画面表示部分で、表示する直前に弱参照Activityから強参照のActivityを取得する

[POC App]アプリを実行

Storage の許可は、今回手抜きしたので、下記手順でPermissionを与えてください。
android-4-01.png
[設定] -> [アプリ] で、今回作成しているアプリ [POC App] をタップ
⇒いまはまだ、何も許可されてないですね。

[許可] -> [ストレージ] を On に変更
android-4-02.png
⇒ストレージが許可されました。

[GET]ボタンをタップ
android-4-03.png
⇒Livedoor天気情報から東京の概況を取ってこれました!

[POST]ボタンをタップ
android-4-04.png
⇒スマホの画像を自前のサーバにアップロードできました!

次回予告

次回は、スマホに搭載されている各種センサーについて、アプリを開発しながら探ってみようと思います。
Enjoy the holidays!

16
21
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
21