- OpenGL ES 3.x游戲開發(上卷)
- 吳亞峰
- 3218字
- 2019-01-05 00:53:39
2.4 文件I/O
I/O即輸入與輸出,幾乎所有文件操作的工作都離不開I/O。對于Android游戲開發來說,I/O操作尤為重要,游戲中的地圖數據、人物圖片資源等的流暢讀取是保證游戲可玩性的重要方面。因此,在開發時,不同的場合必須選用合適的I/O操作方式,才能保證滿足用戶的需求。
Android中文件I/O分為3種方式:SD卡文件讀取、手機中文件夾的訪問和應用程序中assets文件的讀取,本節將結合簡單的小案例對這幾種文件I/O操作進行介紹。
2.4.1 訪問SD卡中的文件
伴隨著游戲品質的不斷提高,游戲數據占用的存儲空間也成幾何級數增加,以往幾十KB、幾MB的手機游戲已經發展為幾十MB、幾百MB甚至幾個GB的大型手機游戲。為了適應存儲需求的增長,SD卡也一直在進行更新換代,4GB、8GB、16GB大小的SD卡已隨處可見。
Android的設計者自然不會忽略這一點,在Android平臺上可以輕松地對手機SD卡中的文件進行讀取和操作。下面通過一個案例來詳細講解在Android開發中如何訪問SD卡中的文件。
1.案例的運行效果
運行本節案例Sample2_5,在輸入框中輸入要讀取的文件名,然后單擊“打開文件”按鈕。若SD卡中存在該文本文件,則在文本區域顯示出文件的內容,反之則提示沒有找到指定文件。本案例的運行效果如圖2-15和圖2-16所示。

▲圖2-15 成功加載SD卡中的文件

▲圖2-16 未找到指定的文件
提示
運行本案例時要注意SD卡中文本文件的編碼格式,在Android系統中一般要采用UTF-8編碼才能保證沒有亂碼出現。編碼格式為UTF-8的文本文件,見隨書中源代碼/第2章目錄下的AndroidIO.txt,讀者可以將該文件導入到手機或模擬器的SD卡中再對本案例進行測試。
2.案例的開發
介紹完本案例的運行效果以及文本文件編碼的注意事項后,接下來將對本案例的主控制類Sample2_5_Activity進行開發,其代碼如下。
代碼位置:見隨書中源代碼/第2章/Sample2_5/src/com/bn/pp5目錄下的Sample2_5_Activity.java。
1 package com/bn/pp5; 2 import java.io.File; //引入相關包 3 public class Sample2_5_Activity extends Activity { //創建Activity 4 @Override 5 public void onCreate(Bundle savedInstanceState) { //重寫onCreate方法 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.main); //跳轉到主界面 8 Button ok=(Button)this.findViewById(R.id.Button01); //獲取打開按鈕的引用 9 ok.setOnClickListener( //為打開按鈕添加監聽器 10 new OnClickListener(){ 11 public void onClick(View v) { 12 EditText et1=(EditText)findViewById(R.id.EditText01); 13 String nr=loadText(et1.getText().toString().trim()); 14 EditText et2=(EditText)findViewById(R.id.EditText02); 15 et2.setText(nr); //設置顯示框內容 16 }}); } 17 public String loadText(String name){ //加載SD卡文件方法 18 String nr=null; //內容字符串 19 try{ 20 File f=new File("/sdcard/"+name); //創建對應文件 21 byte[] buff=new byte[(int) f.length()]; //創建響應大小的byte數組 22 FileInputStream fis=new FileInputStream(f); 23 fis.read(buff); //讀入文件 24 fis.close(); //關閉輸入流 25 nr=new String(buff, "utf-8"); //轉碼生成字符串 26 nr=nr.replaceAll("\\r\\n", "\n"); //替換換行符 27 }catch (Exception e) { 28 Toast.makeText(getBaseContext(), //Toast提示用戶 29 "對不起,沒有找到指定文件。", 30 Toast.LENGTH_LONG).show(); 31 } 32 return nr; //返回內容字符串 33 }}
? 第8-16行是為打開文件的按鈕添加監聽器,監聽器的功能為首先獲取文本框中用戶輸入的文件名,然后調用從SD卡加載文本文件的方法,獲取SD卡中的對應文件內容并送入EditText控件顯示。
? 第17-32行為加載SD卡中文本文件的方法,其入口參數為要加載文件的文件名。工作過程為首先用Java標準的I/O操作來加載指定文本文件中的數據,然后將數據編碼為字符串,并替換換行符等空白字符,最后返回加載的內容。
提示
通過上面的小案例可以看出,Android中對SD卡文件的I/O實現與標準Java相同,因此,開發成本很低,易于上手。
2.4.2 訪問手機中的文件夾
前一小節中是將文件存放到SD卡中,其實文件也可以存放在手機內部存儲(ROM,相當于PC的硬盤)中。不過在Android系統中,其為每個應用程序在手機的ROM中都分配了一個私有的目錄,命名規則為“/data/data/<應用程序的包名>”。例如,應用程序的包名為“com.bn”,則系統分配的私有目錄為“/data/data/com.bn”。
應用程序開發時,若需要在ROM中存放文件,一般應該存放到系統分配的私有目錄中。啟動Eclipse,進入其中的DDMS面板,打開File Explorer,即可查看手機ROM中的文件組織情況,如圖2-17所示。

▲圖2-17 查看手機內部存儲文件
下面給出一個瀏覽手機ROM中文件夾的小案例,通過單擊文件夾的名稱,可以打開相應的文件夾,顯示其中的文件列表。運行本案例,其運行效果如圖2-18、圖2-19和圖2-20所示。

▲圖2-18 程序初始運行

▲圖2-19 進入dev目錄

▲圖2-20 單擊出錯
提示
圖2-18為程序一開始運行,顯示“/”目錄下內容的情況。圖2-19為單擊進入了dev目錄,圖2-20為單擊了不允許進入或不是文件夾的條目。
了解了本案例的運行效果之后,接下來對本案例中唯一的類—Sample2_6_Activity進行開發,具體代碼如下。
代碼位置:見隨書中源代碼/第2章/Sample2_6/src/com/bn/pp6目錄下的Sample2_6_Activity.java。
1 package com.bn.pp6; //聲明包 2 import java.io.File; //引入相關類 3 ……//此處省略了部分類的引入代碼,讀者可自行查看隨書的源代碼 4 import android.widget.AdapterView.OnItemClickListener; //引入相關類 5 public class Sample2_6_Activity extends Activity { //創建Activity 6 String currPath; //當前路徑字符串 7 String rootPath = "/"; //根目錄路徑 8 TextView currDirTV; //顯示當前路徑的TextView引用 9 @Override 10 public void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 setContentView(R.layout.main); //跳轉到主界面 13 final ListView lv=(ListView)this.findViewById(R.id.lv); //獲取ListView 14 Button back = (Button) this.findViewById(R.id.back); //獲取返回按鈕 15 final File[] files = getFiles(rootPath); //調用getFiles方法獲取根目錄下文件列表 16 currDirTV = (TextView) this.findViewById(R.id.currDirTV); //獲取ListView 17 currPath = rootPath; 18 currDirTV.setText("當前路徑:" + currPath); //設置當前路徑 19 initListView(files, lv); //初始化顯示列表 20 back.setOnClickListener //返回按鈕監聽器 21 (new OnClickListener() { 22 @Override 23 public void onClick(View v) { 24 if (! currPath.equals(rootPath)) { //若當前路徑不是根目錄,返回到上一層目錄 25 File f = new File(currPath); //獲取當前路徑下的文件列表 26 f = f.getParentFile(); //獲取當前路徑的上層路徑 27 currPath = f.getPath(); //更改當前路徑 28 currDirTV.setText("當前路徑:" + currPath); //設置當前路徑 29 initListView(getFiles(currPath), lv); //初始化顯示列表 30 }}}); } 31 //獲取當前目錄下的文件列表的方法 32 public File[] getFiles(String filePath) { 33 File[] files = new File(filePath).listFiles(); //獲取當前目錄下的文件列表 34 return files; //返回文件列表 35 } 36 ……//此處省略了初始化ListView的方法,讀者可以自行查閱隨書中的源代碼 37 }
? 第9-30行為案例整體功能的實現代碼。程序運行時,首先調用initListView方法初始化文件顯示列表,顯示根目錄下的文件情況。然后給“返回上一層”按鈕添加了監聽器,監聽器功能為若存在上一層目錄,則刷新文件顯示列表為上一層目錄下的情況。
? 第32-35行為獲取當前路徑下所有文件列表的方法。該方法中首先需要獲得當前路徑對應的File對象,之后返回其下對應的文件列表。
提示
由于篇幅所限,初始化文件列表的代碼省略,有需要的讀者請自行查閱中的源代碼進行學習。
2.4.3 讀取assets文件夾下的內容
Android還能將應用程序所需的數據文件打包到apk文件中,省去了使用者安裝應用程序后還需要下載數據包的情況。但是要注意的是,打包到apk文件中的數據文件并不是可以放在任何位置,一般應該放到項目中的assets文件夾下。
提示
對于特別大(如50MB)的數據文件,在實際開發中一般還是與apk安裝文件分開的。對于不大的數據文件,就非常適合打包到apk包中的assets文件夾下。
下面將通過一個小案例來說明如何編程訪問assets文件夾下的內容,其主要功能是完成對應用程序內部的assets文件夾下文件的讀取。操作過程為首先在案例初始界面輸入文件名,然后單擊打開按鈕。若存在該文件,則在下方顯示出文件內容;反之,則彈出Toast提示用戶重新輸入。案例運行效果如圖2-21和圖2-22所示。

▲圖2-21 程序運行效果圖

▲圖2-22 無法找到指定文件
了解了本案例的功能后,下面來介紹本案例的開發過程,具體步驟如下。
(1)首先將AndroidIO.txt文本文件復制到項目中的assets文件夾中,如圖2-23所示。

▲圖2-23 添加文本文件
(2)將AndroidIO.txt文本文件復制到項目中的assets文件夾后,接下來將開發本案例中唯一的類Sample2_7_Activity,其代碼如下。
代碼位置:見隨書中源代碼/第2章/Sample2_7/src/com/bn/pp7目錄下的Sample2_7_Activity.java。
1 package com.bn.pp7; 2 import java.io.ByteArrayOutputStream; //引入相關包 3 public class Sample2_7_Activity extends Activity { //創建Activity 4 @Override 5 public void onCreate(Bundle savedInstanceState) { //重寫onCreate方法 6 super.onCreate(savedInstanceState); 7 setContentView(R.layout.main); //跳轉至主界面 8 Button ok=(Button)this.findViewById(R.id.Button01); //獲取打開按鈕引用 9 ok.setOnClickListener( //為打開按鈕添加監聽器 10 new OnClickListener() { 11 public void onClick(View v) { 12 EditText et1=(EditText)findViewById(R.id.EditText01); 13 //調用loadText方法獲取對應文件名的文件 14 String nr=loadText(et1.getText().toString().trim()); 15 EditText et2=(EditText)findViewById(R.id.EditText02); 16 et2.setText(nr); //設置顯示框內容 17 }}); } 18 public String loadText(String name){ //加載assets文件方法 19 String nr=null; //內容字符串 20 try { //打開對應名稱文件的輸入流 21 InputStream is=this.getResources().getAssets().open(name); 22 int ch=0; 23 //創建字節數組輸出流 24 ByteArrayOutputStream baos=new ByteArrayOutputStream(); 25 while((ch=is.read())! =-1){baos.write(ch); }//讀取文件 26 byte[] buff=baos.toByteArray(); //轉化為字節數組 27 baos.close(); //關閉輸入輸出流 28 is.close(); //關閉輸入輸出流 29 nr=new String(buff, "utf-8"); //轉碼生成新字符串 30 nr=nr.replaceAll("\\r\\n", "\n"); //替換換行符等空白字符 31 } catch (Exception e) { //沒有找到對應文件,進行提示 32 Toast.makeText(getBaseContext(), "對不起,沒有找到指定文件。", 33 Toast.LENGTH_LONG).show(); 34 } 35 return nr; //返回內容字符串 36 }}
? 第8-17行首先獲取了打開按鈕的引用,然后給此按鈕添加了監聽器。監聽器的功能為讀取用戶輸入名稱的文件內容,并顯示到界面上。
? 第18-36行為從assets中加載指定名稱文本文件的方法,其首先獲取了指向指定文件的輸入流,然后從輸入流中讀取數據,接著將數據編碼為字符串,并替換換行符等空白字符,最后返回加載的內容。
提示
通過上述案例的介紹,相信讀者對Android平臺下文件的存儲和讀取已經有所掌握。在真正的開發過程中,開發人員應該考慮各方面的因素,在需要時采用最適合自己的存儲方式進行存儲。