<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<String> 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)) > 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 + " &";
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->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) && (pidList.size() >= 1);
Log.d(TAG, "VNC服務開啟狀态:" + result);
return result;
/** 擷取IP位址 */
private String getLocalIpAddress() {
try {
// 周遊網絡接口
for (Enumeration<NetworkInterface> en = NetworkInterface
.getNetworkInterfaces(); en.hasMoreElements();) {
NetworkInterface intf = en.nextElement();
// 周遊IP位址
for (Enumeration<InetAddress> 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<String> mIStdoutFilter;
/** 設定标準輸出的過濾器 */
public void setFilter(IStdoutFilter<String> 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<String> getStdout() {
// ArrayList<String> lineArray = new ArrayList<String>();
// 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 指令&參數組成的數組
* @param workDir 指令工作目錄
* @param isStdout 是否輸出結果
* @return 輸出結果的集合
public ArrayList<String> execCommand(String[] cmd, String workDir,
boolean isStdout) {
ArrayList<String> 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<String>(); // 建立對象
handleStdout(lineArray, process);
return lineArray;
* 處理标準輸出内容
* @throws IOException
private void handleStdout(ArrayList<String> 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<T> {
* @brief 過濾操作
* @param stdout
* 标準輸出的内容
* @return true:過濾;false:保留
boolean filter(T stdout);
* @brief 處理操作
* @return 處理後的内容
T handle();
AbstractLineFilter.java
/**
* @brief 抽象的行過濾器
* @details 以行的方式周遊标準輸出,都進行一次過濾判斷
*/
public abstract class AbstractLineFilter implements IStdoutFilter<String> {
/** 行内容 */
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,如需轉載請自行聯系原作者