Android VNC Server New

         关于VNC请参见维基百科:http://zh.wikipedia.org/wiki/VNC

         关于执行Android VNC Server,请参见前一篇文章:点击链接

 
一、VNC下载
1)fastdroid-vnc

         Android VNC Server开源项目

2)TightVNC
         免费的VNC软件(这个都不需要填邮箱^^)
 
二、程序执行
         这种方式看手机了==。Google HTC上倒是可以,详细的说明看注释了。
 
1)简易UI
         布局一点没变动…
 
VNCServerNewActivity.java

  1. public class VNCServerNewActivity extends Activity { 
  2.  
  3.     private static final String TAG = "VNCServer"
  4.     private static final boolean LOGD = true
  5.  
  6.     /** assets目录下的VNCServer文件名 */ 
  7.     private static final String VNC_SERV_NAME = "fastdroid-vnc"
  8.  
  9.     /** Shell工具类 */ 
  10.     private ShellUtil mShellUtil; 
  11.  
  12.     /** dialog基础标识值 */ 
  13.     private static final int DLG_BASE = 0
  14.     /** 获root权限失败的dialog id */ 
  15.     private static final int DLG_ROOT_FAILED = DLG_BASE + 1
  16.     /** 开启VNC服务失败的dialog id */ 
  17.     private static final int DLG_EXEC_FAILED = DLG_BASE + 2
  18.  
  19.     /** VNC服务端口:5901 */ 
  20.     private static final String VNC_SERV_PORT = "5901"
  21.  
  22.     /** 按钮 */ 
  23.     private Button startBtn, stopBtn; 
  24.     /** 标签 */ 
  25.     private TextView statusView, connectView; 
  26.  
  27.     /** 'ps -l VNC_SERV_NAME'命令 */ 
  28.     private static final String[] PS_VNC_SERVER = new String[] { "ps""-l"
  29.             VNC_SERV_NAME }; 
  30.     /** 执行上述命令获取的pid结果集 */ 
  31.     private ArrayList<String> pidList; 
  32.  
  33.     @Override 
  34.     public void onCreate(Bundle savedInstanceState) { 
  35.         super.onCreate(savedInstanceState); 
  36.         setContentView(R.layout.main); 
  37.  
  38.         mShellUtil = ShellUtil.getInstance(); // 获取实例 
  39.  
  40.         initApp(); // 初始化应用 
  41.         initViews(); // 初始化组件 
  42.     } 
  43.  
  44.     /** 初始化组件 */ 
  45.     private void initViews() { 
  46.         startBtn = (Button) findViewById(R.id.startBtn); 
  47.         stopBtn = (Button) findViewById(R.id.stopBtn); 
  48.         statusView = (TextView) findViewById(R.id.statusView); 
  49.         connectView = (TextView) findViewById(R.id.connectView); 
  50.         updateViews(isServerOn()); // 由服务状态更新界面 
  51.     } 
  52.  
  53.     /** 初始化应用 */ 
  54.     private void initApp() { 
  55.         boolean result = mShellUtil.root(); // 获取ROOT权限 
  56.         if (LOGD) 
  57.             Log.d(TAG, "获取Root权限:" + result); 
  58.         if (result) { 
  59.             copyVNCServer(); // 检查VNCServer文件 
  60.         } else { 
  61.             showDialog(DLG_ROOT_FAILED); // 提示DLG_ROOT_FAILED对话框 
  62.         } 
  63.     } 
  64.  
  65.     /** 检查VNCServer文件,不存在时复制进去 */ 
  66.     private void copyVNCServer() { 
  67.         String filePath = "/data/local/" + VNC_SERV_NAME; 
  68.         File file = new File(filePath); 
  69.  
  70.         /* 文件不存在时,从assets复制进去 */ 
  71.         if (!file.exists()) { 
  72.             /* /data/local/目录增加所有用户的写权限 */ 
  73.             boolean result = mShellUtil.rootCommand("chmod a+x /data/local/"); 
  74.             if (LOGD) 
  75.                 Log.d(TAG, "/data/local/增加写权限:" + result); 
  76.  
  77.             // 避免某些机子new FileOutputStream(file)时报System.err== 
  78.             result = mShellUtil.rootCommand("touch " + filePath); 
  79.             if (LOGD) 
  80.                 Log.d(TAG, "创建一个空文件:" + result); 
  81.  
  82.             /* VNCServer文件设为777权限 */ 
  83.             result = mShellUtil.rootCommand("chmod 777 " + filePath); 
  84.             if (LOGD) 
  85.                 Log.d(TAG, "/data/local/设为777权限:" + result); 
  86.  
  87.             if (result) { 
  88.                 try { 
  89.                     /* 将VNCServer文件复制入/data/local/ */ 
  90.                     InputStream is = getAssets().open(VNC_SERV_NAME); 
  91.                     FileOutputStream fos = new FileOutputStream(file); 
  92.                     byte[] buffer = new byte[4096]; 
  93.                     int count = 0
  94.                     while ((count = is.read(buffer)) > 0) { 
  95.                         fos.write(buffer, 0, count); 
  96.                     } 
  97.                     fos.close(); 
  98.                     is.close(); 
  99.                     if (LOGD) 
  100.                         Log.d(TAG, VNC_SERV_NAME + "文件写入/data/local/!"); 
  101.                 } catch (IOException e) { 
  102.                     e.printStackTrace(); 
  103.                 } 
  104.             } 
  105.         } else { 
  106.             if (LOGD) 
  107.                 Log.d(TAG, VNC_SERV_NAME + "文件已存在/data/目录下!"); 
  108.         } 
  109.     } 
  110.  
  111.     /** startBtn点击事件 */ 
  112.     public void startBtn(View v) { 
  113.         /* 执行VNCServer文件 */ 
  114.         String cmd = "/data/local/" + VNC_SERV_NAME + " &"
  115.         boolean result = mShellUtil.rootCommand(cmd); 
  116.         if (LOGD) 
  117.             Log.d(TAG, cmd + ":" + result); 
  118.         if (isServerOn()) { // 否开启了服务 
  119.             updateViews(true); 
  120.         } else { 
  121.             /** 
  122.              * 1)有些ROM执行开启不了T^T(ps进程显示Done,再一会就没了) 
  123.              * 2)用其他VNC文件发现未开启也好提示用户 
  124.              */ 
  125.             showDialog(DLG_EXEC_FAILED); // 提示DLG_EXEC_FAILED对话框 
  126.         } 
  127.     } 
  128.  
  129.     /** stopBtn点击事件 */ 
  130.     public void stopBtn(View v) { 
  131.         boolean result; 
  132.         /** 直到关闭为止。避免kill进程仅改变了状态(遇到S->Z的情况) */ 
  133.         while (isServerOn()) { 
  134.             for (String pid : pidList) { 
  135.                 result = mShellUtil.rootCommand("kill " + pid); 
  136.                 if (LOGD) 
  137.                     Log.d(TAG, "kill " + pid + ":" + result); 
  138.             } 
  139.         } 
  140.         updateViews(false); // 更新服务关闭时的界面状态 
  141.     } 
  142.  
  143.     @Override 
  144.     protected void onDestroy() { 
  145.         super.onDestroy(); 
  146.         boolean result = mShellUtil.rootRelease(); // 释放占用资源 
  147.         if (LOGD) 
  148.             Log.d(TAG, "释放占用资源:" + result); 
  149.     } 
  150.  
  151.     /** 由服务状态更新界面 */ 
  152.     private void updateViews(boolean isServerOn) { 
  153.         /* 更新按钮状态 */ 
  154.         startBtn.setEnabled(!isServerOn); 
  155.         stopBtn.setEnabled(isServerOn); 
  156.         /* 更新标签显示 */ 
  157.         if (isServerOn) { 
  158.             statusView.setText(R.string.status_run); 
  159.             connectView.setText(getLocalIpAddress() + ":" + VNC_SERV_PORT); 
  160.         } else { 
  161.             statusView.setText(R.string.status_stop); 
  162.             connectView.setText(R.string.address); 
  163.         } 
  164.     } 
  165.  
  166.     /** 是否开启了服务,并处理得到pid列表 */ 
  167.     private boolean isServerOn() { 
  168.         mShellUtil.setFilter(new PsLineFilter()); // 设置过滤器 
  169.         // 获取ps命令的pid列表 
  170.         pidList = mShellUtil.execCommand(PS_VNC_SERVER, nulltrue);
  171. mShellUtil.resetFilter(); // 重置过滤器 
  172.         boolean result = (null != pidList) && (pidList.size() >= 1); 
  173.         if (LOGD) 
  174.             Log.d(TAG, "VNC服务开启状态:" + result); 
  175.         return result; 
  176.     } 
  177.  
  178.     /** 获取IP地址 */ 
  179.     private String getLocalIpAddress() { 
  180.         try { 
  181.             // 遍历网络接口 
  182.             for (Enumeration<NetworkInterface> en = NetworkInterface 
  183.                     .getNetworkInterfaces(); en.hasMoreElements();) { 
  184.                 NetworkInterface intf = en.nextElement(); 
  185.                 // 遍历IP地址 
  186.                 for (Enumeration<InetAddress> enumIpAddr = intf 
  187.                         .getInetAddresses(); enumIpAddr.hasMoreElements();) { 
  188.                     InetAddress inetAddress = enumIpAddr.nextElement(); 
  189.                     // 非回传地址时返回 
  190.                     if (!inetAddress.isLoopbackAddress()) { 
  191.                         return inetAddress.getHostAddress().toString(); 
  192.                     } 
  193.                 } 
  194.             } 
  195.         } catch (SocketException e) { 
  196.             e.printStackTrace(); 
  197.         } 
  198.         return null
  199.     } 
  200.  
  201.     @Override 
  202.     protected Dialog onCreateDialog(int id) { 
  203.         省略…… 
  204.     } 
  205.  
 
2)Shell工具类
ShellUtil.java

  1. /** Shell工具类 */ 
  2. public final class ShellUtil { 
  3.  
  4.     /** 内部类ShellUtilHolder */ 
  5.     static class ShellUtilHolder { 
  6.         static ShellUtil instance = new ShellUtil(); 
  7.     } 
  8.  
  9.     /** 返回ShellUtil的单例 */ 
  10.     public static ShellUtil getInstance() { 
  11.         return ShellUtilHolder.instance; 
  12.     } 
  13.  
  14.     /** \link #root()\endlink后的进程 */ 
  15.     private Process process; 
  16.  
  17.     /** \link #root()\endlink后的父进程的标准输入 */ 
  18.     private DataOutputStream dos; 
  19.  
  20.     /** 标准输出的过滤 */ 
  21.     private IStdoutFilter<String> mIStdoutFilter; 
  22.  
  23.     /** 设置标准输出的过滤器 */ 
  24.     public void setFilter(IStdoutFilter<String> filter) { 
  25.         this.mIStdoutFilter = filter; 
  26.     } 
  27.  
  28.     /** 重置过滤器为空 */ 
  29.     public void resetFilter() { 
  30.         this.mIStdoutFilter = null
  31.     } 
  32.  
  33.     /** 
  34.      * @brief 切换至ROOT用户 
  35.      * @details 执行su命令,变更为root用户 
  36.      * @pre 设备已经破解,否则su不可用 
  37.      *  
  38.      * @return 是否成功 
  39.      */ 
  40.     public boolean root() { 
  41.         try { 
  42.             // 执行su变更用户身份为root 
  43.             process = Runtime.getRuntime().exec("su"); 
  44.             // 转成DataOutputStream方便写入字符串 
  45.             dos = new DataOutputStream(process.getOutputStream()); 
  46.         } catch (Exception e) { 
  47.             e.printStackTrace(); 
  48.             return false
  49.         } 
  50.         return true
  51.     } 
  52.  
  53.     /** 
  54.      * @brief ROOT权限下执行命令 
  55.      * @pre 执行\link #root()\endlink 
  56.      *  
  57.      * @param cmd 命令 
  58.      */ 
  59.     public boolean rootCommand(String cmd) { 
  60.         if (null != dos) { 
  61.             try { 
  62.                 dos.writeBytes(cmd + "\n"); 
  63.                 dos.flush(); 
  64.             } catch (IOException e) { 
  65.                 e.printStackTrace(); 
  66.                 return false
  67.             } 
  68.             return true
  69.         } 
  70.         return false
  71.     } 
  72.  
  73.     // /** 
  74.     // * @brief \link #rootCommand()\endlink后的结果 
  75.     // * @pre 执行\link #rootCommand()\endlink 
  76.     // * 
  77.     // * @warning 不能在stdin流输入命令后再从stdout获输出结果 
  78.     // * (之前测试版也放在不同位置试过,都不成,死锁?没找到更多资料) 
  79.     // * 
  80.     // * @return 输出结果的集合 
  81.     // */ 
  82.     // public ArrayList<String> getStdout() { 
  83.     // ArrayList<String> lineArray = new ArrayList<String>(); 
  84.     // try { 
  85.     // handleStdout(lineArray, process); 
  86.     // } catch (IOException e) { 
  87.     // e.printStackTrace(); 
  88.     // } 
  89.     // return lineArray; 
  90.     // } 
  91.  
  92.     /** 释放占用资源 */ 
  93.     public boolean rootRelease() { 
  94.         try { 
  95.             dos.writeBytes("exit\n"); 
  96.             dos.flush(); 
  97.             process.waitFor(); // 等待执行完成 
  98.         } catch (Exception e) { 
  99.             e.printStackTrace(); 
  100.             return false
  101.         } finally { 
  102.             try { 
  103.                 if (null != process) { 
  104.                     process.destroy(); 
  105.                 } 
  106.                 if (null != dos) { 
  107.                     dos.close(); 
  108.                 } 
  109.             } catch (Exception e) { 
  110.                 e.printStackTrace(); 
  111.             } 
  112.         } 
  113.         return true
  114.     } 
  115.  
  116.     /** 
  117.      * @brief 执行一个shell命令 
  118.      *  
  119.      * @param cmd 命令&参数组成的数组 
  120.      * @param workDir 命令工作目录 
  121.      * @param isStdout 是否输出结果 
  122.      * @return 输出结果的集合 
  123.      */ 
  124.     public ArrayList<String> execCommand(String[] cmd, String workDir, 
  125.             boolean isStdout) { 
  126.         ArrayList<String> lineArray = null
  127.         try { 
  128.             // 创建操作系统进程(也可以由Runtime.exec()启动) 
  129.             ProcessBuilder builder = new ProcessBuilder(cmd); 
  130.             // 设置命令工作目录 
  131.             if (workDir != null) { 
  132.                 builder.directory(new File(workDir)); 
  133.             } 
  134.             // 合并标准错误和标准输出 
  135.             builder.redirectErrorStream(true); 
  136.             // 启动一个新进程 
  137.             Process process = builder.start(); 
  138.  
  139.             // 如果输出结果的话 
  140.             if (isStdout) { 
  141.                 lineArray = new ArrayList<String>(); // 创建对象 
  142.                 handleStdout(lineArray, process); 
  143.             } 
  144.         } catch (Exception e) { 
  145.             e.printStackTrace(); 
  146.         } 
  147.         return lineArray; 
  148.     } 
  149.  
  150.     /** 
  151.      * 处理标准输出内容 
  152.      *  
  153.      * @throws IOException 
  154.      */ 
  155.     private void handleStdout(ArrayList<String> lineArray, Process process) 
  156.             throws IOException { 
  157.         InputStream is = process.getInputStream(); // 获得标准输出流 
  158.         if (null != mIStdoutFilter) { // 如果设置了过滤 
  159.             // 判断是否是行过滤器 
  160.             if (mIStdoutFilter instanceof AbstractLineFilter) { 
  161.                 // 转成BufferedReader 
  162.                 BufferedReader br = new BufferedReader( 
  163.                         new InputStreamReader(is)); 
  164.                 String line; 
  165.                 while (null != (line = br.readLine())) { 
  166.                     /* 如果未被过滤,则将处理后内容加入List */ 
  167.                     if (!mIStdoutFilter.filter(line)) { 
  168.                         lineArray.add(mIStdoutFilter.handle()); 
  169.                     } 
  170.                 } 
  171.                 if (br != null) { 
  172.                     br.close(); 
  173.                 } 
  174.             } else { 
  175.                 // 默认把流直接转成字符串返回 
  176.                 lineArray.add(inputStream2Str(is)); 
  177.             } 
  178.         } else { 
  179.             // 默认把流直接转成字符串返回 
  180.             lineArray.add(inputStream2Str(is)); 
  181.         } 
  182.         if (is != null) { 
  183.             is.close(); 
  184.         } 
  185.     } 
  186.  
  187.     /** 
  188.      * 输入流转成字符串 
  189.      *  
  190.      * @throws IOException 
  191.      */ 
  192.     public String inputStream2Str(InputStream is) throws IOException { 
  193.         StringBuffer out = new StringBuffer(); 
  194.         byte[] b = new byte[4096]; 
  195.         for (int n; (n = is.read(b)) != -1;) { 
  196.             out.append(new String(b, 0, n)); 
  197.         } 
  198.         return out.toString(); 
  199.     } 
  200.  
 
3)过滤器
IStdoutFilter.java

  1. /** 标准输出过滤接口 */ 
  2. public interface IStdoutFilter<T> { 
  3.  
  4.     /** 
  5.      * @brief 过滤操作 
  6.      * @param stdout 
  7.      *            标准输出的内容 
  8.      * @return true:过滤;false:保留 
  9.      */ 
  10.     boolean filter(T stdout); 
  11.  
  12.     /** 
  13.      * @brief 处理操作 
  14.      * @return 处理后的内容 
  15.      */ 
  16.     T handle(); 
  17.  
 
AbstractLineFilter.java

  1. /** 
  2.  * @brief 抽象的行过滤器 
  3.  * @details 以行的方式遍历标准输出,都进行一次过滤判断 
  4.  */ 
  5. public abstract class AbstractLineFilter implements IStdoutFilter<String> { 
  6.  
  7.     /** 行内容 */ 
  8.     protected String line; 
  9.  
  10.     /** 
  11.      * @brief 行过滤操作 
  12.      * @param line 
  13.      *            标准输出的某行内容 
  14.      * @return true:过滤;false:保留 
  15.      */ 
  16.     protected abstract boolean lineFilter(String line); 
  17.  
  18.     @Override 
  19.     public boolean filter(String stdout) { 
  20.         this.line = stdout; 
  21.         return lineFilter(stdout); 
  22.     } 
  23.  
  24.     @Override 
  25.     public String handle() { 
  26.         return line; // 默认返回原行 
  27.     } 
  28.  
 
PsLineFilter.java(应该加个单例==)

  1. /** 
  2.  * ps命令的行过滤及处理成pid的实现 
  3.  */ 
  4. public final class PsLineFilter extends AbstractLineFilter { 
  5.  
  6.     @Override 
  7.     protected boolean lineFilter(String line) { 
  8.         // 过滤空行及标题行 
  9.         if (null == line || "".endsWith(line) || line.startsWith("USER")) { 
  10.             return true
  11.         } 
  12.         return false
  13.     } 
  14.  
  15.     @Override 
  16.     public String handle() { 
  17.         try { 
  18.             return line.trim().split("\\s+")[1]; // 获取PID列 
  19.         } catch (Exception e) { // null和越界异常 
  20.             return line; 
  21.         } 
  22.     } 
  23.  
 
三、后记
         这个东西貌似还得动手改源码么T^T。小弟告罄了,不想碰那个东西==。
 
         附件工程,随便看看了……
 
         ps:Doxygen简单生成了个chm帮助文件,在docs目录下。

 
         我是不是很无聊了-_-!