天天看點

Android VNC Server New

 <b>Android VNC Server New</b>

<b>一、</b><b>VNC</b><b>下載下傳</b>

<b>1</b><b>)fastdroid-vnc</b>

         Android VNC Server開源項目

<b>2</b><b>)TightVNC</b>

         免費的VNC軟體(這個都不需要填郵箱^^)

<b>二、程式執行</b>

         這種方式看手機了==。Google HTC上倒是可以,詳細的說明看注釋了。

<b>1</b><b>)簡易UI</b>

         布局一點沒變動…

VNCServerNewActivity.java<b></b>

public class VNCServerNewActivity extends Activity { 

    private static final String TAG = "VNCServer"; 

    private static final boolean LOGD = true; 

    /** assets目錄下的VNCServer檔案名 */ 

    private static final String VNC_SERV_NAME = "fastdroid-vnc"; 

    /** Shell工具類 */ 

    private ShellUtil mShellUtil; 

    /** dialog基礎辨別值 */ 

    private static final int DLG_BASE = 0; 

    /** 獲root權限失敗的dialog id */ 

    private static final int DLG_ROOT_FAILED = DLG_BASE + 1; 

    /** 開啟VNC服務失敗的dialog id */ 

    private static final int DLG_EXEC_FAILED = DLG_BASE + 2; 

    /** VNC服務端口:5901 */ 

    private static final String VNC_SERV_PORT = "5901"; 

    /** 按鈕 */ 

    private Button startBtn, stopBtn; 

    /** 标簽 */ 

    private TextView statusView, connectView; 

    /** 'ps -l VNC_SERV_NAME'指令 */ 

    private static final String[] PS_VNC_SERVER = new String[] { "ps", "-l", 

            VNC_SERV_NAME }; 

    /** 執行上述指令擷取的pid結果集 */ 

    private ArrayList&lt;String&gt; pidList; 

    @Override 

    public void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        setContentView(R.layout.main); 

        mShellUtil = ShellUtil.getInstance(); // 擷取執行個體 

        initApp(); // 初始化應用 

        initViews(); // 初始化元件 

    } 

    /** 初始化元件 */ 

    private void initViews() { 

        startBtn = (Button) findViewById(R.id.startBtn); 

        stopBtn = (Button) findViewById(R.id.stopBtn); 

        statusView = (TextView) findViewById(R.id.statusView); 

        connectView = (TextView) findViewById(R.id.connectView); 

        updateViews(isServerOn()); // 由服務狀态更新界面 

    /** 初始化應用 */ 

    private void initApp() { 

        boolean result = mShellUtil.root(); // 擷取ROOT權限 

        if (LOGD) 

            Log.d(TAG, "擷取Root權限:" + result); 

        if (result) { 

            copyVNCServer(); // 檢查VNCServer檔案 

        } else { 

            showDialog(DLG_ROOT_FAILED); // 提示DLG_ROOT_FAILED對話框 

        } 

    /** 檢查VNCServer檔案,不存在時複制進去 */ 

    private void copyVNCServer() { 

        String filePath = "/data/local/" + VNC_SERV_NAME; 

        File file = new File(filePath); 

        /* 檔案不存在時,從assets複制進去 */ 

        if (!file.exists()) { 

            /* /data/local/目錄增加所有使用者的寫權限 */ 

            boolean result = mShellUtil.rootCommand("chmod a+x /data/local/"); 

            if (LOGD) 

                Log.d(TAG, "/data/local/增加寫權限:" + result); 

            // 避免某些機子new FileOutputStream(file)時報System.err== 

            result = mShellUtil.rootCommand("touch " + filePath); 

                Log.d(TAG, "建立一個空檔案:" + result); 

            /* VNCServer檔案設為777權限 */ 

            result = mShellUtil.rootCommand("chmod 777 " + filePath); 

                Log.d(TAG, "/data/local/設為777權限:" + result); 

            if (result) { 

                try { 

                    /* 将VNCServer檔案複制入/data/local/ */ 

                    InputStream is = getAssets().open(VNC_SERV_NAME); 

                    FileOutputStream fos = new FileOutputStream(file); 

                    byte[] buffer = new byte[4096]; 

                    int count = 0; 

                    while ((count = is.read(buffer)) &gt; 0) { 

                        fos.write(buffer, 0, count); 

                    } 

                    fos.close(); 

                    is.close(); 

                    if (LOGD) 

                        Log.d(TAG, VNC_SERV_NAME + "檔案寫入/data/local/!"); 

                } catch (IOException e) { 

                    e.printStackTrace(); 

                } 

            } 

                Log.d(TAG, VNC_SERV_NAME + "檔案已存在/data/目錄下!"); 

    /** startBtn點選事件 */ 

    public void startBtn(View v) { 

        /* 執行VNCServer檔案 */ 

        String cmd = "/data/local/" + VNC_SERV_NAME + " &amp;"; 

        boolean result = mShellUtil.rootCommand(cmd); 

            Log.d(TAG, cmd + ":" + result); 

        if (isServerOn()) { // 否開啟了服務 

            updateViews(true); 

            /** 

             * 1)有些ROM執行開啟不了T^T(ps程序顯示Done,再一會就沒了) 

             * 2)用其他VNC檔案發現未開啟也好提示使用者 

             */ 

            showDialog(DLG_EXEC_FAILED); // 提示DLG_EXEC_FAILED對話框 

    /** stopBtn點選事件 */ 

    public void stopBtn(View v) { 

        boolean result; 

        /** 直到關閉為止。避免kill程序僅改變了狀态(遇到S-&gt;Z的情況) */ 

        while (isServerOn()) { 

            for (String pid : pidList) { 

                result = mShellUtil.rootCommand("kill " + pid); 

                if (LOGD) 

                    Log.d(TAG, "kill " + pid + ":" + result); 

        updateViews(false); // 更新服務關閉時的界面狀态 

    protected void onDestroy() { 

        super.onDestroy(); 

        boolean result = mShellUtil.rootRelease(); // 釋放占用資源 

            Log.d(TAG, "釋放占用資源:" + result); 

    /** 由服務狀态更新界面 */ 

    private void updateViews(boolean isServerOn) { 

        /* 更新按鈕狀态 */ 

        startBtn.setEnabled(!isServerOn); 

        stopBtn.setEnabled(isServerOn); 

        /* 更新标簽顯示 */ 

        if (isServerOn) { 

            statusView.setText(R.string.status_run); 

            connectView.setText(getLocalIpAddress() + ":" + VNC_SERV_PORT); 

            statusView.setText(R.string.status_stop); 

            connectView.setText(R.string.address); 

    /** 是否開啟了服務,并處理得到pid清單 */ 

    private boolean isServerOn() { 

        mShellUtil.setFilter(new PsLineFilter()); // 設定過濾器 

        // 擷取ps指令的pid清單 

        pidList = mShellUtil.execCommand(PS_VNC_SERVER, null, true);

mShellUtil.resetFilter(); // 重置過濾器 

        boolean result = (null != pidList) &amp;&amp; (pidList.size() &gt;= 1); 

            Log.d(TAG, "VNC服務開啟狀态:" + result); 

        return result; 

    /** 擷取IP位址 */ 

    private String getLocalIpAddress() { 

        try { 

            // 周遊網絡接口 

            for (Enumeration&lt;NetworkInterface&gt; en = NetworkInterface 

                    .getNetworkInterfaces(); en.hasMoreElements();) { 

                NetworkInterface intf = en.nextElement(); 

                // 周遊IP位址 

                for (Enumeration&lt;InetAddress&gt; enumIpAddr = intf 

                        .getInetAddresses(); enumIpAddr.hasMoreElements();) { 

                    InetAddress inetAddress = enumIpAddr.nextElement(); 

                    // 非回傳位址時傳回 

                    if (!inetAddress.isLoopbackAddress()) { 

                        return inetAddress.getHostAddress().toString(); 

        } catch (SocketException e) { 

            e.printStackTrace(); 

        return null; 

    protected Dialog onCreateDialog(int id) { 

        省略…… 

<b>2</b><b>)Shell</b><b>工具類</b>

ShellUtil.java

/** Shell工具類 */ 

public final class ShellUtil { 

    /** 内部類ShellUtilHolder */ 

    static class ShellUtilHolder { 

        static ShellUtil instance = new ShellUtil(); 

    /** 傳回ShellUtil的單例 */ 

    public static ShellUtil getInstance() { 

        return ShellUtilHolder.instance; 

    /** \link #root()\endlink後的程序 */ 

    private Process process; 

    /** \link #root()\endlink後的父程序的标準輸入 */ 

    private DataOutputStream dos; 

    /** 标準輸出的過濾 */ 

    private IStdoutFilter&lt;String&gt; mIStdoutFilter; 

    /** 設定标準輸出的過濾器 */ 

    public void setFilter(IStdoutFilter&lt;String&gt; filter) { 

        this.mIStdoutFilter = filter; 

    /** 重置過濾器為空 */ 

    public void resetFilter() { 

        this.mIStdoutFilter = null; 

    /** 

     * @brief 切換至ROOT使用者 

     * @details 執行su指令,變更為root使用者 

     * @pre 裝置已經破解,否則su不可用 

     *  

     * @return 是否成功 

     */ 

    public boolean root() { 

            // 執行su變更使用者身份為root 

            process = Runtime.getRuntime().exec("su"); 

            // 轉成DataOutputStream友善寫入字元串 

            dos = new DataOutputStream(process.getOutputStream()); 

        } catch (Exception e) { 

            return false; 

        return true; 

     * @brief ROOT權限下執行指令 

     * @pre 執行\link #root()\endlink 

     * @param cmd 指令 

    public boolean rootCommand(String cmd) { 

        if (null != dos) { 

            try { 

                dos.writeBytes(cmd + "\n"); 

                dos.flush(); 

            } catch (IOException e) { 

                e.printStackTrace(); 

                return false; 

            return true; 

        return false; 

    // /** 

    // * @brief \link #rootCommand()\endlink後的結果 

    // * @pre 執行\link #rootCommand()\endlink 

    // * 

    // * @warning 不能在stdin流輸入指令後再從stdout獲輸出結果 

    // * (之前測試版也放在不同位置試過,都不成,死鎖?沒找到更多資料) 

    // * @return 輸出結果的集合 

    // */ 

    // public ArrayList&lt;String&gt; getStdout() { 

    // ArrayList&lt;String&gt; lineArray = new ArrayList&lt;String&gt;(); 

    // try { 

    // handleStdout(lineArray, process); 

    // } catch (IOException e) { 

    // e.printStackTrace(); 

    // } 

    // return lineArray; 

    /** 釋放占用資源 */ 

    public boolean rootRelease() { 

            dos.writeBytes("exit\n"); 

            dos.flush(); 

            process.waitFor(); // 等待執行完成 

        } finally { 

                if (null != process) { 

                    process.destroy(); 

                if (null != dos) { 

                    dos.close(); 

            } catch (Exception e) { 

     * @brief 執行一個shell指令 

     * @param cmd 指令&amp;參數組成的數組 

     * @param workDir 指令工作目錄 

     * @param isStdout 是否輸出結果 

     * @return 輸出結果的集合 

    public ArrayList&lt;String&gt; execCommand(String[] cmd, String workDir, 

            boolean isStdout) { 

        ArrayList&lt;String&gt; lineArray = null; 

            // 建立作業系統程序(也可以由Runtime.exec()啟動) 

            ProcessBuilder builder = new ProcessBuilder(cmd); 

            // 設定指令工作目錄 

            if (workDir != null) { 

                builder.directory(new File(workDir)); 

            // 合并标準錯誤和标準輸出 

            builder.redirectErrorStream(true); 

            // 啟動一個新程序 

            Process process = builder.start(); 

            // 如果輸出結果的話 

            if (isStdout) { 

                lineArray = new ArrayList&lt;String&gt;(); // 建立對象 

                handleStdout(lineArray, process); 

        return lineArray; 

     * 處理标準輸出内容 

     * @throws IOException 

    private void handleStdout(ArrayList&lt;String&gt; lineArray, Process process) 

            throws IOException { 

        InputStream is = process.getInputStream(); // 獲得标準輸出流 

        if (null != mIStdoutFilter) { // 如果設定了過濾 

            // 判斷是否是行過濾器 

            if (mIStdoutFilter instanceof AbstractLineFilter) { 

                // 轉成BufferedReader 

                BufferedReader br = new BufferedReader( 

                        new InputStreamReader(is)); 

                String line; 

                while (null != (line = br.readLine())) { 

                    /* 如果未被過濾,則将處理後内容加入List */ 

                    if (!mIStdoutFilter.filter(line)) { 

                        lineArray.add(mIStdoutFilter.handle()); 

                if (br != null) { 

                    br.close(); 

            } else { 

                // 預設把流直接轉成字元串傳回 

                lineArray.add(inputStream2Str(is)); 

            // 預設把流直接轉成字元串傳回 

            lineArray.add(inputStream2Str(is)); 

        if (is != null) { 

            is.close(); 

     * 輸入流轉成字元串 

    public String inputStream2Str(InputStream is) throws IOException { 

        StringBuffer out = new StringBuffer(); 

        byte[] b = new byte[4096]; 

        for (int n; (n = is.read(b)) != -1;) { 

            out.append(new String(b, 0, n)); 

        return out.toString(); 

<b>3</b><b>)過濾器</b>

IStdoutFilter.java

/** 标準輸出過濾接口 */ 

public interface IStdoutFilter&lt;T&gt; { 

     * @brief 過濾操作 

     * @param stdout 

     *            标準輸出的内容 

     * @return true:過濾;false:保留 

    boolean filter(T stdout); 

     * @brief 處理操作 

     * @return 處理後的内容 

    T handle(); 

AbstractLineFilter.java

/** 

 * @brief 抽象的行過濾器 

 * @details 以行的方式周遊标準輸出,都進行一次過濾判斷 

 */ 

public abstract class AbstractLineFilter implements IStdoutFilter&lt;String&gt; { 

    /** 行内容 */ 

    protected String line; 

     * @brief 行過濾操作 

     * @param line 

     *            标準輸出的某行内容 

    protected abstract boolean lineFilter(String line); 

    public boolean filter(String stdout) { 

        this.line = stdout; 

        return lineFilter(stdout); 

    public String handle() { 

        return line; // 預設傳回原行 

PsLineFilter.java(應該加個單例==)

 * ps指令的行過濾及處理成pid的實作 

public final class PsLineFilter extends AbstractLineFilter { 

    protected boolean lineFilter(String line) { 

        // 過濾空行及标題行 

        if (null == line || "".endsWith(line) || line.startsWith("USER")) { 

            return line.trim().split("\\s+")[1]; // 擷取PID列 

        } catch (Exception e) { // null和越界異常 

            return line; 

<b>三、後記</b>

         這個東西貌似還得動手改源碼麼T^T。小弟告罄了,不想碰那個東西==。

         附件工程,随便看看了……

         ps:Doxygen簡單生成了個chm幫助檔案,在docs目錄下。

<a target="_blank" href="http://blog.51cto.com/attachment/201203/200837177.png"></a>

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

<a href="http://down.51cto.com/data/2360120" target="_blank">附件:http://down.51cto.com/data/2360120</a>

     本文轉自winorlose2000 51CTO部落格,原文連結:http://blog.51cto.com/vaero/815264,如需轉載請自行聯系原作者