コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

Socketを使ってDSと通信してみる

わからないことだらけですが、初めてのAndroidjavaのプログラムに一歩踏み出したので、とりあえず何か作ってみようと思い、DSと繋げてみることにしました。
先ずはSocketを使ってDSと通信するところを作ってみたいと思います。

SurfaceViewを使う

基本的には、ActivityからSurfaceViewを呼び出して、そこでプログラミングしました。日経ソフトウェア6月号の記事を参考にさせていただいています。

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;

public class WifiTestActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //アプリケーションタイトルを非表示
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        // SurfaceViewを登録
        setContentView(new WifiTestSurfaceView(this));
    }
    @Override
    public void onResume(){
    super.onResume();		//各メソッドはsuper.***と自分を最初に呼ぶのが決まり
    }
    @Override
    public void onPause(){
    	super.onPause();		//各メソッドはsuper.***と自分を最初に呼ぶのが決まり
    	//一時停止の処理を記述
    	//ここではアプリを終了させる
    	finish();	//onDestroy()が呼ばれる。
    }
    @Override
    public void onDestroy(){
    	super.onDestroy();		//各メソッドはsuper.***と自分を最初に呼ぶのが決まり
    }
}

メインプログラムはスレッドとして動かしたいので、スレッドプログラムを含むコールバック用のclassを作り、SurfaceViewからはこれを呼び出すようにしました。ここら辺も、日経ソフトウェア6月号の記事を参考にさせていただいています。

import android.content.Context;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class WifiTestSurfaceView extends SurfaceView {
    private WifiTestSurfaceHolderCallback cb = null;
    //コンストラクタ
    public WifiTestSurfaceView(Context context) {
        super(context);
        // TODO Auto-generated constructor stub
        SurfaceHolder holder = getHolder();
        cb = new WifiTestSurfaceHolderCallback();
        holder.addCallback(cb);		
    }
    @Override
    public boolean onTouchEvent(MotionEvent event){
        float x = event.getX();		//x座標の取得
        float y = event.getY();		//y座標の取得
        cb.getTouchPosition((int)x, (int)y);
        return true;
    }
}

コールバックclass

コールバックのプログラムは、まだ不完全です。AndroidとDSをConnectionすることはできたのですが、Androidの受信側の作りこみが全くわからなくて、進んでいない状態です。
とりあえず、Connectionのところの説明だけ書きたいと思います。
下記はコールバックclassのimportと変数宣言のところです。SurfaceHolder.Callback(コールバック用)とRunnable(スレッド用)を実装しています。ここら辺も、日経ソフトウェア6月号の記事を参考にさせていただいています。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.Log;
import android.view.SurfaceHolder;

public class WifiTestSurfaceHolderCallback implements SurfaceHolder.Callback, Runnable  {
    private SurfaceHolder holder = null;
    private Thread thread = null;
    private boolean isThreadrunning = true;
    private Socket sock = null;
    private BufferedWriter sockout = null;
    private BufferedReader sockin = null;
    private Paint paintf;
    private Paint paintb;
	
    private int x = 0;
    private int x1 = 0;
    private int y = 0;
    private int y1 = 0;
    private InputStream inputsock = null;

コンストラクタは下記です。文字を描きたいので、文字色と背景色用に2つのPaintインスタンスを作っています。とりあえず、これだけです。

    // コンストラクタ
    public WifiTestSurfaceHolderCallback(){
        paintf = new Paint();
        paintf.setStyle(Style.FILL);
        paintf.setARGB(255, 255, 255, 255);
        paintf.setTextSize(16);	
        paintb = new Paint();
        paintb.setStyle(Style.FILL);
        paintb.setARGB(255, 0, 0, 0);
	}

surfaceCreated

surfaceCreatedはSurfaceが作られるときに呼び出される関数です。AvtivityがonCreate()するときに呼ばれるはずなので、ここにsocketの処理を書きました。
192.168.24.63(DSのIPアドレス)の8520ポートに接続に行きます。DSの方はこのポートへのアクセスを待たせています。
5000msの待ち時間で接続しに行きます。タイムアウトした場合、メッセージを出し、isFinish=trueにします。
そして、isFinish==trueのときは、socketをcloseし、falseのときは、threadを起動します。

public void surfaceCreated(SurfaceHolder holder) {
  // TODO Auto-generated method stub
  this.holder = holder;
  boolean isFinish = false;
  try {
    // ソケットの作成
    String host = "192.168.24.63";
    int port = 8520;
    sock = new Socket();
    SocketAddress adr = new InetSocketAddress(host,port);
    sock.connect(adr, 5000);
    sockout = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
    inputsock = sock.getInputStream();
  }
  catch (SocketTimeoutException e) {
    Canvas canvas = holder.lockCanvas(); // ロックをかける
    drawOnCanvas(canvas, "接続がタイムアウトしました","再度起動してください");
    older.unlockCanvasAndPost(canvas); // ロックを解除
    isFinish = true;
    e.printStackTrace();
  }
  catch (Exception ex) {
    ex.printStackTrace();
  }
  if( isFinish==true){
    try {
      // ソケットのクローズ
      sock.close();
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
  else{
    // スレッドの開始		
    thread = new Thread(this);
    thread.start();
  }
}

surfaceDestroyed

surfaceDestroyedは、Activityを終了させるときに呼び出される関数です。このプログラムでは、ActivityがonPause()してもonDestroy()が呼ばれるように作っているので、画面が消えれば呼ばれることになります。
threadが動いていれば、スレッドを切ります。日経ソフトウェア6月号の記事を参考にさせていただいています。isThreadrunning = false;はif文の中でもいいですね。
そして、Socketをクローズします。sockout.close();とかありますが、プログラムはまだ未完成です。

public void surfaceDestroyed(SurfaceHolder holder) {
  // TODO Auto-generated method stub
  boolean retry = true;
  synchronized (this.holder) {
    isThreadrunning = false;
  }
  if( thread!=null ){
    while (retry) {
      try {
        thread.join();
        retry = false;
      } catch (InterruptedException e) {
      }
    }
    thread = null;
  }
  try {
    // ソケットのクローズ
    sockout.close();
    sock.close();
  } catch (Exception ex) {
    ex.printStackTrace();
  }
}

スレッド

スレッドはrun()関数が起動します。プログラムはisThreadrunningがtrueの間、動作し続ける形になっています。ここら辺も、日経ソフトウェア6月号の記事を参考にさせていただいています。
プログラム的にはxとyの値をせっせとsocketでDSに送信します。DSからの受信部分がうまく動かないので、ソースから削除しています。本当はバイナリで送信したいのですが、javaがわからないので、テキストで送信しています。今後、勉強したいと思います。とりあえず、何を送信したかを、drawOnCanvas()を用いてSurfaceViewに表示しています。

@Override
public void run() {
  // TODO Auto-generated method stub
  String outtxt = "";
  while (isThreadrunning) {
    synchronized (holder) {
      if( x!=x1 || y!=y1){
        if( x>999 ){
          x = 999;
        }
        else if( x<0 ){
          x = 0;
        }
        if(y>999){
          y=999;
        }else if( y<0 ){
          y = 0;
        }
        outtxt = String.format("%03d", x) + "," + String.format("%03d", y);
        try {
          sockout.write(outtxt);
          sockout.flush();
          x1 = x;
          y1 = y;
        } catch (Exception ex) {
          ex.printStackTrace();
        }
      }
    }
    // 描画の処理
    Canvas canvas = holder.lockCanvas(); // ロックをかける
    drawOnCanvas(canvas, intxt, outtxt);
    holder.unlockCanvasAndPost(canvas); // ロックを解除
  }
}

drawOnCanvas()

SurfaceViewに文字を表示します。黒色で画面を塗りつぶして、文字を表示しているだけです。

public void drawOnCanvas(Canvas canvas, String txt1, String txt2){
  canvas.drawPaint(paintb);  //画面黒塗り
  canvas.drawText( txt1, 0, 16, paintf);  //txt1表示
  canvas.drawText( txt2, 0, 48, paintf);  //txt2表示
}

タッチ座標の取得

SurfaceViewがタッチされたとき、コールバックされてその座標を取得する関数です。

public void getTouchPosition(int x, int y){
  this.x = x;
  this.y = y;
}

動作しているところ

とりあえず、通信が一方通行ですが、DSとAndroidをつなげて動かしている様子です。文字表示は、このときはさせていなかったです。

DS側のプログラムの説明はいいかな(^^;。

本当にやりたいことリスト

(ブログの終わりにやりたいことを書いておきたいと思います)

  • 求職活動・・・このブログで興味を持った人一声かけてください。m(_ _)m
  • Androidプログラム・・・始めつつあります。
  • Web系のプログラム