Android - Socket 功能在 Service 中实现【这才是实际的使用情况】

前几天学习了 Android 下 Socket 编程,由于个人是刚开始学习 Android 相应的知识。所以特意将学习中的代码与过程,写成 BLOG,如:http://blog.csdn.net/91program/article/details/39177401
学习 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 编程,就不能确定这个问题了!
在 CSDN Android 论坛提问,帖子:http://bbs.csdn.net/topics/390884423。得到的答案是:(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 中定义全局的变量,如下:

1 public static final String SOCKER_ACTION = "com.jia.Socket.Control";
2 public static final String SOCKER_RCV = "com.jia.Socket.ReceiveStr";
3
4 SocketReceiver socketReceiver

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

1 socketReceiver = new SocketReceiver();
2 IntentFilter socketIntentFilter = new IntentFilter();
3 socketIntentFilter.addAction(SOCKER_RCV);
4 registerReceiver(socketReceiver,socketIntentFilter);
5
6 Intent socketIntent = new Intent();
7 socketIntent.setClass(MainActivity.this, SocketService.class);
8 startService(socketIntent);       // 启动  Socket 服务

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

 1 public class SocketReceiver extends BroadcastReceiver {
 2
 3
 4   @Override
 5   public void onReceive(Context context, Intent intent) {
 6     // TODO Auto-generated method stub
 7     String action = intent.getAction();
 8           if(action.equals(SOCKER_RCV)) {
 9             String url = intent.getExtras().getString("action");
10             if(url.equals("ClientIP")) {
11               String strIP = intent.getExtras().getString("content");
12             }
13             else if(url.equals("RcvStr")) {
14               String strContent = intent.getExtras().getString("content");
15             }
16             else if(url.equals("Disconnect")) {
17               String strContent = intent.getExtras().getString("content");
18             }
19         }
20     }
21 }

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

 1 // 读取输入的数据(阻塞读)
 2 try {
 3     this.mInputStream.read(buf);
 4 } catch (IOException e1) {
 5     // TODO Auto-generated catch block
 6     e1.printStackTrace();
 7 }
 8 修改为如下的代码:
 9 try {
10     int length = this.mInputStream.read(buf);
11     if(-1 == length) {
12       try {
13         sUsed.sendUrgentData(0xff);
14       }
15       catch(Exception ex) {
16         // 链接已断开
17         Log.v("service", "disconnect!!!");
18         stop = true;
19         if(null != mServerSocket) {
20           mServerSocket.close();
21         }
22
23
24         Intent sendIntent = new Intent(SOCKER_RCV);
25         sendIntent.putExtra("action", "Disconnect");
26         sendIntent.putExtra("content", "read is -1 & Urgent Exception!");
27         sendBroadcast(sendIntent);
28
29
30         continue;
31       }
32     }
33 } catch (IOException e1) {
34     // TODO Auto-generated catch block
35     e1.printStackTrace();
36 }
时间: 2024-07-30 20:20:52

Android - Socket 功能在 Service 中实现【这才是实际的使用情况】的相关文章

android wear开发之:增加可穿戴设备功能到通知中 - Adding Wearable Features to Notifications

注:本文内容来自:https://developer.android.com/training/wearables/notifications/index.html 翻译水平有限,如有疏漏,欢迎批评指教. 译:山人 增加可穿戴设备功能到通知中 Adding Wearable Features to Notifications When an Android handheld (phone or tablet) and Android wearable are connected, the han

[Android Pro] service中显示一个dialog 或者通过windowmanage显示view

转载: http://blog.csdn.net/huxueyan521/article/details/8954844 通过windowmananger来在窗口上添加view的时候,需要设置alert参数,而且要添加alert权限 mLayoutParams = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, LayoutParams.TYPE_SYSTEM_ALERT,

android菜鸟学习笔记28----Android中的Service生命周期及本地和远程服务绑定的实现

Service是Android中长期在后台运行的没有界面的组件,使用服务的优势在于:能够提高进程的优先级,系统不容易回收掉进程,即便回收了,内存充足的时候,会把进程重新创建. 1.服务的简单使用示例: 1.1.定义一个服务: 定义一个服务的方式是定义一个类继承自Service: 1 public class MyService extends Service { 2 3 @Override 4 5 public IBinder onBind(Intent intent) { 6 7 // TOD

android 中activity调用远程service中的方法之 aidl的使用

服务端:只有服务,没有界面 1.编写interface文件,复制到 .aidl 文件中,并去掉其中的public 等修饰符.系统会自动在gen目录下生成对应的java文件  (对应本地调用中的接口文件) 2.编写service,其中内部类的自定义bind 只需要继承Stub即可.(本地调用则需要继承Bind 并实现 interface接口) 1 public class PayService extends Service { 2 3 @Override 4 public IBinder onB

Android activity与service中的子线程 (入门级)

1.首先 android 一个程序中 的activity 都是一个线程,service和activity也是一个线程 2.在activity 中启动一个子线程,当前activity finish destroy掉 子线也会运行的. 3.在service里的线程 与activity很类似  service即使停止了 线程也在运行(要先停止服务 再把最近使用的进程杀掉 线程会停止 ,如果直接杀掉进程 android会再次自动启动这个service的 此时即使再停止service服务 线程也会一直运行

android Service中启动Dialog

        在Service 中弹出Dialog与在Activity中弹出Dialog的方式一样,可是activity finish后,dialog也会随着关闭.他是依附着activity的.在Service中就不一样了,Service没有界面,言弹出Dialog.他依附什么呢? 首先,看看在Service中怎样弹出对话框: dialog = new AlertDialog.Builder(ctx).create(); dialog.getWindow().setType(WindowMan

关于如何关闭android中service中新开启的线程问题?

今天遇到一个问题,既在一个activity中设置一个按钮,点击按钮关闭service,而service中又新开启了一个线程,每隔一秒输出一句话,但是当调用按钮实现stopService方法后,service是可以关闭,但是service中开启的线程却一直在运行,原理上来讲从service的onDestory方法中关闭当前线程即可,可即便如此,还是会报错,贴下代码: 1 stopBtn.setOnClickListener(new OnClickListener() { 2 3 @Override

【起航计划 025】2015 起航计划 Android APIDemo的魔鬼步伐 24 App->Notification->Notifying Service Controller service中使用Notification

这个例子介绍了如何在Service中使用Notification,相关的类为NotifyingController和NotifyingService. 在Service中使用Notification的基本方法和前一例子相同.我们暂时还没有介绍Service的用法. Service的基本概念和Windows OS中Service基本相同:没有UI,在后台运行.Notification可以说是Service最好的用来通知用户的方法.后面有专门介绍 Service的用法,这里就不说明了. Notify

【android】在Service中新建TextView

在Activity中new TextView的时候,发现传入的参数是Context,不是必须为Activity,就想:在Service中新建一个View的话能否正常使用? Service中:` public class MyJobService extends JobService { public static TextView myView; @Override public boolean onStartJob(JobParameters params) { myView = new Te