最近開發功能的時候monkey總是能跑出一個bug,
java.lang.RuntimeException: Could not read input channel file descriptors from parcel.
百思不得其解,認為是系統上面的bug,實時證明自己還是太年輕.現在開始分析一下這個bug產生的原因.
一.為什么會產生句柄泄露?
眾所周知Android是linux內核,也就是可以理解linux下,一切資源都是句柄,每個進程都有自己的句柄上限,而超過了這個句柄上線,就會發生異常.一般android的App都是在單個進程下運行的,FD的句柄上限是1024,這個在后面的會說明.一切的重點都在proc(Processes,虛擬的目錄,是系統內存的映射。可直接訪問這個目錄來獲取系統信息。 )這個文件夾下的內容.
這里有參考The proc File System.
The Linux kernel has two primary functions: to control access to physical devices on the computer and to schedule when and how processes interact with these devices. The /proc/ directory — also called the proc file system — contains a hierarchy of special files which represent the current state of the kernel — allowing applications and users to peer into the kernel's view of the system.
Within the /proc/ directory, one can find a wealth of information detailing the system hardware and any processes currently running. In addition, some of the files within the /proc/ directory tree can be manipulated by users and applications to communicate configuration changes to the kernel.
Linux系統上的/proc目錄是一種文件系統,即proc文件系統。與其它常見的文件系統不同的是,/proc是一種偽文件系統(也即虛擬文件系統),存儲的是當前內核運行狀態的一系列特殊文件,用戶可以通過這些文件查看有關系統硬件及當前正在運行進程的信息,甚至可以通過更改其中某些文件來改變內核的運行狀態。
二.查看進程的句柄信息
我們通過proc這個虛擬文件系統,可以獲取到當前App的進程,然后查看具體的進程信息,當前App是否存在句柄泄露的問題.具體方法如下.
public class MainActivity extends Activity {
private PageView mPageView;
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
exeCommand("ps");
}
private void exeCommand(String command){
Runtime runtime = Runtime.getRuntime();
try {
Process proc = runtime.exec(command);
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
這樣我們看到在Android Studio的logcat中打印出了當前所有的進程號.
然后我們繼續修改我們的代碼.
exeCommand("ps | grep com.behanga");
com.behanga是我測試App的名字.
這是我們可以看到該App的進程號是16023,也就是說對應在proc中對應的文件夾為/proc/16023/,在這個文件夾下我們看到當前App的信息,好我們繼續往下查看.
這時我們打開terminal,輸入
adb shell
進入adb shell,然后輸入
cd /proc/16023 && ls -al
現在展示在我們面前的就是當前進程下信息.
我們現在可以查看一下當前進程一些限制.
cat limits
這里我們看到Max open fils的限制為1024,硬件限制為4096,也就是說我們當前打開文件句柄的最大數為1024.
這時我們繼續往下看,因為是要分析file descriptors的問題,那么自然要進入fd目錄下一看究竟.
cd fd && ls -al
當前展示了所在進程下所有已經被打開的文件句柄.我們可以在當前文件夾下統計所有的句柄總數.
ls | wc -l
這時打印出所有的句柄總數為64.這時我們已經完成對當前進程的一些句柄信息查看.
三.模擬句柄泄露情況
如果我們要模擬句柄泄漏的情況,我們可以在App中啟動一個HandlerThread,每次在onCreate的時候HandlerThread.start(),但是在onDestory()時,并不調用HandlerThread.quit()方法.重復進入.然后在terminal中統計FD的總數,查看是否有變化,如果FD的總數一直在增加,就可以確認你當前App中已經出現了句柄泄露的情況.
四.如何避免句柄泄露的發生
提高對編碼意識,另外就是monkey的測試,很多時候會發現一些黑盒測試無法發現的問題.