当前位置:首页 > 芯闻号 > 充电吧
[导读]前几天学习了 Android 下 Socket 编程。学习 Socket 编程是有目的的,需要完成在手机与 PC 之间的通讯。通讯的内容是将手机上播放的 MP3 信息,通过 Socket 传输到 PC

前几天学习了 Android 下 Socket 编程。

学习 Socket 编程是有目的的,需要完成在手机与 PC 之间的通讯。通讯的内容是将手机上播放的 MP3 信息,通过 Socket 传输到 PC 端。
在参考网上相关 Socket 的文章后,基本上完成了 Socket 功能。所以就继续学习 Android 下音乐播放器的实现。在实现音乐播放器过程中,发现由于音乐播放器至少要有播放列表和正在播放两个 Activity,这样问题就来了:
(1). Socket 只是在第一个 Activity 中实现了,当这个 Activity 活动时没有问题。但此 Activity 非活动时,不能处理 Socket。
(2). 当反复进入第一个 Activity 时,会出现 Socket 初始化报错的问题。出现这样的错误,是由于 Sokcet 的初始化放在第一个 Activity 的 onCreate 中。

由于在做音乐播放器时使用了 Service,所以想到用 Serivce 来处理 Socket 应该没有问题。但是否有其它的方法呢?由于个人是刚刚接触 Android 编程,就不能确定这个问题了!
在论坛提问,得到的答案是:(1) Serivce; (2) 也可以更改activity的启动方式,让串口不重复创建。显然,第二种方法还没有接触过。采用第一种 Serivce 来实现更可靠一些。


首先,实现 Socket Service。

package com.jia.leozhengfirstapp;


import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;


import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;


public class SocketService extends Service {


  private Socket clientSocket = null;
  private ServerSocket mServerSocket = null;


  private SocketAcceptThread socketAcceptThread = null;
  private SocketReceiveThread socketReceiveThread = null;


  private SocketReceiver socketReceiver;


  public static final String SOCKER_ACTION = "com.jia.Socket.Control";
  public static final String SOCKER_RCV = "com.jia.Socket.ReceiveStr";


  private boolean stop = true;


  @Override
  public IBinder onBind(Intent intent) {
    // TODO Auto-generated method stub
    return null;
  }
   @Override
  public void onCreate() {
          super.onCreate();
          Log.d("service", "socket service created");
          socketReceiver = new SocketReceiver();
          IntentFilter filter = new IntentFilter();
          filter.addAction(SOCKER_ACTION);
          registerReceiver(socketReceiver, filter);


          socketAcceptThread = new SocketAcceptThread();
            // 开启 Socket 监听线程
            socketAcceptThread.start();
   }


  @Override
  public void onStart(Intent intent, int startId) {
     Log.d("service", "socket service start");


  }


  @Override
  public void onDestroy() {
  Log.d("service", "socket service destroy!");


  }


  public class SocketReceiver extends BroadcastReceiver {


    @Override
    public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
            if(action.equals(SOCKER_ACTION)) {
              String sub_action = intent.getExtras().getString("ACTION");
              if(sub_action.equals("reconnect")) {
                Log.d("service", "socket service: reconnect.");


                 socketAcceptThread = new SocketAcceptThread();
                  // 开启 Socket 监听线程
                  socketAcceptThread.start();
              }
            }
    }
  }


  private class SocketAcceptThread extends Thread
  {
       @Override
       public void run()
       {
         Log.d("service", "socket service - SocketAcceptThread::run");
           try {
               // 实例化ServerSocket对象并设置端口号为 12589
               mServerSocket = new ServerSocket(12589);
           } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }


           try {
               // 等待客户端的连接(阻塞)
               clientSocket = mServerSocket.accept();
           } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }


           socketReceiveThread = new SocketReceiveThread(clientSocket);
           stop = false;
           // 开启接收线程
           socketReceiveThread.start();


             Intent sendIntent = new Intent(SOCKER_RCV);
             sendIntent.putExtra("action", "ClientIP");
             sendIntent.putExtra("content", clientSocket.getInetAddress().getHostAddress());
             // 发送广播,将被Activity组件中的BroadcastReceiver接收到
             sendBroadcast(sendIntent);
       }
   }


   private class SocketReceiveThread extends Thread
   {
       private InputStream mInputStream = null;
       private byte[] buf;
       private String str = null;
       Socket sUsed;


       SocketReceiveThread(Socket s)
       {
         Log.d("service", "socket service - SocketReceiveThread");
           try {
               // 获得输入流
               this.mInputStream = s.getInputStream();
               sUsed = s;
           } catch (IOException e) {
               // TODO Auto-generated catch block
               e.printStackTrace();
           }
       }


       @Override
       public void run()
       {
         Log.d("service", "socket service - SocketReceiveThread::run");
           while((!stop) && (!mServerSocket.isClosed()))
           {
               this.buf = new byte[2048];


               // 读取输入的数据(阻塞读)
               try {
                   this.mInputStream.read(buf);
               } catch (IOException e1) {
                   // TODO Auto-generated catch block
                   e1.printStackTrace();
               }


               // 字符编码转换
               try {
                   this.str = new String(this.buf, "GB2312").trim();
               } catch (UnsupportedEncodingException e) {
                   // TODO Auto-generated catch block
                   e.printStackTrace();
               }


               Intent sendIntent = new Intent(SOCKER_RCV);
               sendIntent.putExtra("action", "RcvStr");
               sendIntent.putExtra("content", this.str);
               // 发送广播,将被Activity组件中的BroadcastReceiver接收到
               sendBroadcast(sendIntent);
           }
       }
   }
}



在每个 Activity 中处理 SOCKER_RCV action,以响应 Socket 状态的变化和接收到数据。
Service 与 Activity 之间通讯需要使用到广播: Broadcast。
(1) 在 Activity 中定义全局的变量,如下:

public static final String SOCKER_ACTION = "com.jia.Socket.Control";
public static final String SOCKER_RCV = "com.jia.Socket.ReceiveStr";


SocketReceiver socketReceiver;



(2) 在 Activity 的 onCreate 中注册广播和启动 Socket Service,如下:

socketReceiver = new SocketReceiver();
IntentFilter socketIntentFilter = new IntentFilter();
socketIntentFilter.addAction(SOCKER_RCV);
registerReceiver(socketReceiver,socketIntentFilter);


Intent socketIntent = new Intent();
socketIntent.setClass(MainActivity.this, SocketService.class);
startService(socketIntent);       // 启动  Socket 服务



(3) SocketReceiver 是继承自 BroadcastReceiver 的类,实现如下:

public class SocketReceiver extends BroadcastReceiver {


  @Override
  public void onReceive(Context context, Intent intent) {
    // TODO Auto-generated method stub
    String action = intent.getAction();
          if(action.equals(SOCKER_RCV)) {
            String url = intent.getExtras().getString("action");
            if(url.equals("ClientIP")) {
              String strIP = intent.getExtras().getString("content");
            }
            else if(url.equals("RcvStr")) {
              String strContent = intent.getExtras().getString("content");
            }
            else if(url.equals("Disconnect")) {
              String strContent = intent.getExtras().getString("content");
            }
        }
    }
}



(4) Socket 功能实现后,测试时发现客户端(也就是 PC 端)断开时手机端未检测到 Socket 连接断开。
以前使用 WinCE 时,Socket(TCP) 断开时,无论是客户端、还是服务器都可以检测到 TCP 断开的事件,并处理。但 Android 下的 Socket 编程机制竟然没有这个东东。
一,测试时发现当 PC 端断开后,手机端的服务程序在执行到下面的代码段时不会阻塞,且函数的返回值是: -1。
二,在网上查找发现这个问题是 Android 下 Socket 都有的问题,可以通过发心跳包来处理。
所以将下面这段代码:

// 读取输入的数据(阻塞读)
try {
    this.mInputStream.read(buf);
} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
修改为如下的代码: 
try {
    int length = this.mInputStream.read(buf);
    if(-1 == length) {
      try {
        sUsed.sendUrgentData(0xff);
      }
      catch(Exception ex) {
        // 链接已断开
        Log.v("service", "disconnect!!!");
        stop = true;
        if(null != mServerSocket) {
          mServerSocket.close();
        }


        Intent sendIntent = new Intent(SOCKER_RCV);
        sendIntent.putExtra("action", "Disconnect");
        sendIntent.putExtra("content", "read is -1 & Urgent Exception!");
        sendBroadcast(sendIntent);


        continue;
      }
    }
} catch (IOException e1) {
    // TODO Auto-generated catch block
    e1.printStackTrace();
}
本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

9月2日消息,不造车的华为或将催生出更大的独角兽公司,随着阿维塔和赛力斯的入局,华为引望愈发显得引人瞩目。

关键字: 阿维塔 塞力斯 华为

加利福尼亚州圣克拉拉县2024年8月30日 /美通社/ -- 数字化转型技术解决方案公司Trianz今天宣布,该公司与Amazon Web Services (AWS)签订了...

关键字: AWS AN BSP 数字化

伦敦2024年8月29日 /美通社/ -- 英国汽车技术公司SODA.Auto推出其旗舰产品SODA V,这是全球首款涵盖汽车工程师从创意到认证的所有需求的工具,可用于创建软件定义汽车。 SODA V工具的开发耗时1.5...

关键字: 汽车 人工智能 智能驱动 BSP

北京2024年8月28日 /美通社/ -- 越来越多用户希望企业业务能7×24不间断运行,同时企业却面临越来越多业务中断的风险,如企业系统复杂性的增加,频繁的功能更新和发布等。如何确保业务连续性,提升韧性,成...

关键字: 亚马逊 解密 控制平面 BSP

8月30日消息,据媒体报道,腾讯和网易近期正在缩减他们对日本游戏市场的投资。

关键字: 腾讯 编码器 CPU

8月28日消息,今天上午,2024中国国际大数据产业博览会开幕式在贵阳举行,华为董事、质量流程IT总裁陶景文发表了演讲。

关键字: 华为 12nm EDA 半导体

8月28日消息,在2024中国国际大数据产业博览会上,华为常务董事、华为云CEO张平安发表演讲称,数字世界的话语权最终是由生态的繁荣决定的。

关键字: 华为 12nm 手机 卫星通信

要点: 有效应对环境变化,经营业绩稳中有升 落实提质增效举措,毛利润率延续升势 战略布局成效显著,战新业务引领增长 以科技创新为引领,提升企业核心竞争力 坚持高质量发展策略,塑强核心竞争优势...

关键字: 通信 BSP 电信运营商 数字经济

北京2024年8月27日 /美通社/ -- 8月21日,由中央广播电视总台与中国电影电视技术学会联合牵头组建的NVI技术创新联盟在BIRTV2024超高清全产业链发展研讨会上宣布正式成立。 活动现场 NVI技术创新联...

关键字: VI 传输协议 音频 BSP

北京2024年8月27日 /美通社/ -- 在8月23日举办的2024年长三角生态绿色一体化发展示范区联合招商会上,软通动力信息技术(集团)股份有限公司(以下简称"软通动力")与长三角投资(上海)有限...

关键字: BSP 信息技术
关闭
关闭