前回はAndroidで外部Webサーバーにhttpリクエストを投げてその結果をLogcatビューに表示するというのを行った。

今回はその結果をTextViewに表示してみよう。

1.マニュフェストにインターネットの許可を記述する。</manifest>の1行上にでも挿入すればよい。
●AndroidManifest.xml(uses_permissionの1行を挿入する)

</application>
    <uses-permission android:name="android.permission.INTERNET"/>
</manifest>

前回も言ったが、Android開発におけるもっともミスが起こりやすい部分なので忘れずに実行しよう。

2.レイアウトファイルでTextViewを配置して、idをつけよう

3.MainActivityに以下を記述する。


import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    private Handler handler=new Handler();
    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv=(TextView)findViewById(R.id.txtResult);
        //メインスレッド通信できないのでスレッドを1本つくる
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    final StringBuilder sb=new StringBuilder();
                    URL url=new URL("http://joytas.net/works/");
                    HttpURLConnection con=(HttpURLConnection)url.openConnection();
                    con.setRequestMethod("GET");
                    InputStream is=con.getInputStream();
                    InputStreamReader isr=new InputStreamReader(is,"UTF-8");
                    BufferedReader br=new BufferedReader(isr);
                    String line;
                    while((line = br.readLine()) != null){
                        sb.append(line);
                    }
                    Log.d("RESULT",sb.toString());
                    handler.post(new Runnable(){
                        @Override
                        public void run() {
                            tv.setText(sb.toString());
                        }
                    });


                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
  //同時にもう一本スレッドを立てて通信する
        new Thread(new Runnable(){
            @Override
            public void run() {
                try {
                    final StringBuilder sb=new StringBuilder();
                    URL url=new URL("http://joytas.net/works/calc.php?x=10&y=20");
                    HttpURLConnection con=(HttpURLConnection)url.openConnection();
                    con.setRequestMethod("GET");
                    InputStream is=con.getInputStream();
                    InputStreamReader isr=new InputStreamReader(is,"UTF-8");
                    BufferedReader br=new BufferedReader(isr);
                    String line;
                    while((line = br.readLine()) != null){
                        sb.append(line);
                    }
                    Log.d("RESULT",sb.toString());
                    handler.post(new Runnable(){
                        @Override
                        public void run() {
                            tv.setText(sb.toString());
                        }
                    });

                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

    }
}

Logcatに2つの通信ログが表示され
テキストビューにどちらかの文言が表示されれば成功だ。


今回はバックグランドで同時に2つの通信を行い、結果をTextViewに反映させたわけだが、同時に一つのTextViewを操作しようとして問題はおこならいのだろうか?
この部分に対してまずは一番大事なことを述べておく
別スレッドから直接UI部品を操作してはいけない。

スレッド自体は上の実行例でも分かる通り何本でも走らせることができる。
ただし、UIスレッド(メインスレッド)はシングルスレッドで動作しているという点がポイントだ。

UIスレッドは内部的にLooperというメッセージキューを保持していて順番に一つずつ処理している。
異なるスレッドからの処理を受け取りキューに詰めるのがHandlerの仕事だ。

今回の場合は以下のhandler.postでTextViewの更新処理をLooperに詰めた。

handler.post(new Runnable(){
                        @Override
                        public void run() {
                            tv.setText(sb.toString());
                        }
                    });

キューイングされた処理が順番に行われるので今回もタイミングによってはTextViewが書き変わるのを確認することができる。

通信や重い処理は別スレッドで行い、UIの更新はHandlerに処理を依頼すると覚えておこう。

それではいつものように練習問題をやってみよう。
Q1
二つの正の整数を受け取り、その和を求めるプログラムを作成せよ。
ただし、計算結果は以下のURLのxの値とyの値を書き換え通信によって取得すること。
http://joytas.net/works/calc.php?x=10&y=20
[実行例]
●スタート画面

●エディットテキストに数値を入れて送信ボタンを押すと結果が表示される

●マニュフェストに追記

<uses-permission android:name="android.permission.INTERNET"/>

●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"
    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:layout_marginLeft="8dp"
        android:text="x:"
        android:textSize="24sp"
        app:layout_constraintBaseline_toBaselineOf="@+id/etX"
        app:layout_constraintLeft_toLeftOf="parent" />

    <EditText
        android:id="@+id/etX"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="number"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintLeft_toRightOf="@+id/textView"
        android:layout_marginLeft="8dp" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:text="Y:"
        android:textSize="24sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBaseline_toBaselineOf="@+id/etY" />

    <EditText
        android:id="@+id/etY"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:inputType="number"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/etX"
        app:layout_constraintLeft_toRightOf="@+id/textView2"
        android:layout_marginLeft="8dp" />

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:onClick="btClick"
        android:text="送信"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/etY" />

    <TextView
        android:id="@+id/tvResult"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="8dp"
        android:layout_marginRight="8dp"
        android:layout_marginTop="8dp"
        android:textSize="24sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button" />
</android.support.constraint.ConstraintLayout>

●MainActivity.java


import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;

public class MainActivity extends AppCompatActivity {
    //UIスレッドのHandler取得
    private Handler handler=new Handler();
    private EditText etX,etY;
    private TextView tvResult;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        etX=(EditText)findViewById(R.id.etX);
        etY=(EditText)findViewById(R.id.etY);
        tvResult=(TextView)findViewById(R.id.tvResult);
    }
    public void btClick(View v){
        String x=etX.getText().toString();
        String y=etY.getText().toString();
        String url="http://joytas.net/works/calc.php?x="+x+"&y="+y;
        //スレッド生成
        Thread mt=new MyThread(handler,tvResult,url);
        //スレッドスタート
        mt.start();

    }
    public static class MyThread extends Thread{
        private Handler handler;
        private TextView tv;
        private String url;

        public MyThread(Handler handler,TextView tv,String url){
            this.handler=handler;
            this.tv=tv;
            this.url=url;
        }

        @Override
        public void run() {
            try {
                URL url=new URL(this.url);
                HttpURLConnection con=(HttpURLConnection)url.openConnection();
                con.setRequestMethod("GET");
                InputStream is=con.getInputStream();
                InputStreamReader isr=new InputStreamReader(is,"UTF-8");
                BufferedReader br=new BufferedReader(isr);
                final StringBuilder sb=new StringBuilder();
                String line;
                while((line = br.readLine()) != null){
                    sb.append(line);
                }
                this.handler.post(new Runnable() {
                    @Override
                    public void run() {
                        tv.setText(sb.toString());
                    }
                });
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}