編輯:關於Android編程
按照是否知道源代碼:
1.黑盒測試:不知道源代碼
2.白盒測試:知道源代碼
按照測試的粒度:
1.方法測試 function test
2.單元測試 unit test
3.集成測試 integration test
4.系統測試 system test
按照測試的暴力程度:
1.冒煙測試 smoke test
2.壓力測試 pressure test
monkey程序由Android系統自帶,使用Java語言寫成,在Android SDK中的存放路徑是:/sdk/tools/lib/monkey.jar
monkey測試的原理就是利用socket通訊的方式來模擬用戶的按鍵輸入,觸摸屏輸入,手勢輸入等。
monkey常用指令:monkey -p packagename count;
例子:monkey -p com.android.email 300;
首先我們在命令行中輸入adb shell,掛載到Linux空間,然後我們輸入monkey命令,會顯示可選參數:
然後我們可以在後面加上count參數,例如monkey 2000
模擬器結果:由於我們使用monkey測試,當點擊某個應用某些功能時程序崩潰,出現以下結果。
單元測試(unit test),是指對軟件中的最小可測試單元進行檢查和驗證。
Android中單元測試的步驟:
1.寫一個類繼承AndroidTestCase
public class TestCalc extends AndroidTestCase{ //需要寫一個測試方法 public void testAdd(){ //想測試 計算器相加的方法 Calc calc = new Calc(); int result = calc.add(5, 6); //斷言 assertEquals(11,result); } }
2.Androidmanifest中application節點下配置一個uses-library節點:
3.androidmanifest中manifest節點下配置一個instrumentation:
注意:如果不知道如何配置androidmanifest可以右擊新建一個android test project 的測試工程,會自動配置。
1.可以按照tag ,packagename ,pid添加過濾器。
2.日志可以使用Log類來打印,有五種級別:e,w,i,d,v,如圖:
3.一般公司開發打log需要封裝一個工具類,通過一個開關來控制log什麼時候打印。
4.logcat可以幫助我們分析程序運行的狀況,幫助我們找到程序運行過程中出現的錯誤信息。
添加日志過濾器:
a) 點擊“+”按鈕
b) 填寫過濾條件,第一行是自己起的名字,第二行是Tag的名稱。這邊按照打印輸出這個標簽來過濾日志。
整體布局是一個LinearLayout,最下面一行是相對布局。
上圖UI布局文件代碼如下:
1.找到相關的控件對象
et_name = (EditText) findViewById(R.id.et_username); et_userpassword = (EditText) findViewById(R.id.et_userpassword); cb_ischeck = (CheckBox) findViewById(R.id.cb_ischeck);
2.定義登錄按鈕的點擊事件
//寫按鈕的點擊事件 public void login(View v){ }
3.當按鈕發生點擊時,需要獲取用戶輸入的用戶名,密碼,是否記住密碼
//獲取用戶名和密碼 String name = et_name.getText().toString().trim(); String pwd = et_userpassword.getText().toString().trim();
4.驗證用戶名密碼格式是否正確,是否為null,如果為空,提示給用戶
if (TextUtils.isEmpty(name)||TextUtils.isEmpty(pwd)) { Toast.makeText(MainActivity.this, "用戶名或密碼不能為空", 1).show(); }else { //進行登錄的邏輯 System.out.println("連接服務器 進行登錄 等我們講到 第四天 網絡 編程 在說"); }
不為空,提交用戶名密碼到服務器(默認登錄成功)。
5.登錄成功,判斷是否記住密碼,記住,需要保存用戶名密碼到本地。
if (cb_ischeck.isChecked()) { //把用戶名和密碼的數據給我存起來 boolean result = UserInfoUtils.saveInfo(name, pwd); if (result) { Toast.makeText(MainActivity.this, "保存成功", 1).show(); }else{ Toast.makeText(MainActivity.this, "保存失敗", 1).show(); } }else { Toast.makeText(MainActivity.this, "請勾選cb", 1).show(); }
在上述代碼中,UserInforUtils是一個工具類,在該工具類中定義了方法saveInfo用來保存當前登錄用戶的用戶名和密碼。
// 保存用戶名和密碼的業務方法 public static boolean saveInfo(String username, String pwd) { try { String result = username + "##" + pwd; // 創建file類指定我們要把數據存儲的位置 File file = new File("/data/data/com.gaogao.login/info.txt"); // 創建一個文件輸出流 FileOutputStream fos = new FileOutputStream(file); fos.write(result.getBytes()); fos.close(); return true; } catch (Exception e) { e.printStackTrace(); return false; } }
6.下次再進入該界面時,需要回顯用戶名密碼
//讀取 data/data 下 info.txt信息 Mapmaps = UserInfoUtils.readInfo(); if (maps!=null) { //把name 和 pwd 取出來 String name = maps.get("name"); String pwd = maps.get("pwd"); //把name 和pwd 顯示到editText控件上 et_name.setText(name); et_userpassword.setText(pwd); }
7.方法readInfo具體實現如下:
// 讀取用戶的信息 public static MapreadInfo() { // 定義map try { Map maps = new HashMap (); File file = new File("/data/data/com.gaogao.login/info.txt"); FileInputStream fis = new FileInputStream(file); BufferedReader bufr = new BufferedReader( new InputStreamReader(fis)); String content = bufr.readLine(); // 讀取數據 // 切割字符串 封裝到map集合中 String[] splits = content.split("##"); String name = splits[0]; String pwd = splits[1]; // 把name 和 pwd 放入map中 maps.put("name", name); maps.put("pwd", pwd); fis.close(); return maps; } catch (Exception e) { e.printStackTrace(); return null; } }
在Android的API中對Context的描述是這樣的:
Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc
1.Context描述的是一個應用程序環境的信息,即上下文。
2.Context是一個抽象類,Android提供了該抽象類的具體實現類。
3.通過Context我們可以獲取應用程序的資源和類,包括一些應用級別的操作,如啟動一個Activity,發送廣播,接受Intent信息等。
Context相關類的繼承關系:
從上圖可以看出Activity也是Context類,所以我們調用Toast彈出吐司的時候,第一個參數需要傳入上下文,我們可以使用this。
6. Android存儲方式
在Android中,分為內部存儲空間和外部存儲空間。我們通過DDMS中的File Explorer功能可以很容易的查看手機的文件體統。如下圖:
vcrWu/rJz6Ostry74dTayta7+mRhdGEvZGF0YcS/wrzPwsPmyfqzydLU06bTw7D8w/vD/MP7tcTOxLz+vNCjrNXiuPbOxLz+vNC05rfFtcTKx7nY09rV4rj206bTw7XEyv2+3aOs0rK+zcrH06bTw7XExNqyv7TmtKK/1bzkoaPSu7Wp06bTw7G70LbU2MHLo6y4w8S/wry+zbvh19S2r8m+s/2jrMjnz8LNvKO6PGJyIC8+DQo8aW1nIGFsdD0="這裡寫圖片描述" src="/uploadfile/Collfiles/20160827/20160827093247575.png" title="\" />
外部存儲空間指的的是外部存儲設備sd卡,該目錄為/mnt/sdcard如下圖:
6.1. 將數據存儲到內部存儲空間
public static boolean saveUserInfo(Context context, String username, String pwd) {
String result = username + "##" + pwd;
// 把當前數據保存到當前應用包中
String path = context.getFilesDir().getPath();
File file = new File(path, "info.txt");
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(result.getBytes());
fos.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
第4行,通過上下文Context獲取到內部存儲空間文件夾,該目錄在應用程序包名裡面的files文件夾中。
6.2. 將數據存儲到Sd卡
要操作sd卡,需要加入權限:
獲取sd卡路徑:
//通過Environment獲取sdcard目錄路徑
String path = Environment.getExternalStorageDirectory().getPath();
sdcard狀態問題:
在手機設置頁面,點擊進入存儲設置,可以將sd卡卸載和掛載。
if(!Environment.getExternalStorageState().
equals(Environment.MEDIA_MOUNTED)){
Toast.makeText(getApplicationContext(), "sdcard不存在或未掛載",
0).show();
return;
}
獲取sdcard的大小:
File file = Environment.getExternalStorageDirectory();
//獲取總空間
long totalSpace = file.getTotalSpace();
//格式化大小,該方法在api level 9以後才出現
String totalSpaceSize = Formatter.formatFileSize(MainActivity.this,totalSpace);
獲取sdcard的可用空間:
long usableSpace = file.getUsableSpace();
String usableSpace_str = Formatter.formatFileSize(this, usableSpace);
7. 文件權限
1.使用Context對象獲取一個私有目錄的文件寫入流
FileOutputStream fileOutputStream = context.openFileOutput("userinfo.txt", Context.MODE_PRIVATE);
第1行,通過Context.openFileOutput()方法獲取一個私有目錄的文件寫入流。其中參數的含義如下:
參數
含義
Name
有目錄下的文件名稱
Mode
文件模式 private append read write 用來控制文件的權限
使用context對象獲取一個私有目錄的文件讀取流:
FileInputStream fileInputStream= context.openFileInput("userinfo.txt")
Linux中一個文件的權限由10位標示,如圖:
第1位代表文件的類型: d:文件夾 l:快捷方式 -:文件
第2-4位:文件所屬用戶對本文件的權限
第5-7: 文件所屬用戶組對本文件的權限
第8-10:其他用戶對本文件的權限
8. SharedPreference存儲數據
首先看下API文檔中對SharedPreference的介紹:
The SharedPreferences class provides a general framework that allows you to save and retrieve persistent key-value pairs of primitive data types. You can use SharedPreferences to save any primitive data: booleans, floats, ints, longs, and strings. This data will persist across user sessions (even if your application is killed).
SharedPreference這個類提供了常用的框架來允許用戶保存和提取持久化的私有的數據,這些數據是key-value(鍵值對)的形式。
1.sharedPreference文件是以xml的方式存儲在/data/data/包名/shared_pres目錄下
2.使用sharedPreference一般用於存放標記性或設置性的數據。
3.存儲數據:
a) 使用Context獲取SharedPreferenced對象
SharedPreferences sharedPreferences = context.getSharedPreferences("userinfo.txt", Context.MODE_PRIVATE);
b) 使用SharedPreference對象獲取一個Editor對象
Editor editor = sharedPreferences.edit();
c) 向Editor對象中put鍵值對的數據
editor.putString("username", username);
editor.putString("password", password);
d) 提交Editor對象
editor.commit();
4.讀取數據:
使用Context獲取SharedPreference對象 :
SharedPreferences sharedPreferences = context.getSharedPreferences ("userinfo.txt", Context.MODE_PRIVATE);
SharedPreference對象獲取存放的鍵值:
String username = sharedPreferences.getString("username", "admin");
String password = sharedPreferences.getString("password", "admin");
方法第二個參數表示如果沒有獲取到返回的默認值。
9. XML的生成和解析
9.1. StringBuffer生成XML
模擬備份短信到本地文件,將短信的信息轉化成xml文件存到sdcard。
定義短信實體:
public class Sms {
private String address;
private String body;
private String date;
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public String getBody() {
return body;
}
public void setBody(String body) {
this.body = body;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
准備備份的短信信息:
List smsLists = smsLists = new ArrayList();
for (int i = 0; i < 4; i++) {
Sms sms = new Sms();
sms.setAddress("beijing" + i);
sms.setBody("nihao" + i);
sms.setDate("201" + i);
// 要把sms對象加入到 集合中
smsLists.add(sms);
}
生成xml文件,並保存:
public void click(View v) {
StringBuffer sb = new StringBuffer();
//組拼xml的聲明
sb.append("");
//添加節點
sb.append("");
for (Sms sms : smsLists) {
//添加節點
sb.append("");
sb.append("");
sb.append(sms.getAddress());
sb.append("");
sb.append("");
sb.append(sms.getBody());
sb.append("");
sb.append("");
sb.append(sms.getDate());
sb.append(" ");
sb.append(" ");
}
sb.append(" ");
try {
File file = new
File(Environment.getExternalStorageDirectory().getPath(),
"smsbackup.xml");
FileOutputStream fos = new FileOutputStream(file);
fos.write(sb.toString().getBytes());
fos.close();
Toast.makeText(MainActivity.this, "備份成功", 1).show();
} catch (Exception e) {
e.printStackTrace();
}
}
9.2. XmlSerializer生成Xml
public void click(View v) {
try {
XmlSerializer serializer = Xml.newSerializer();
File file = new File(Environment.getExternalStorageDirectory()
.getPath(), "smsbackup1.xml");
FileOutputStream fos = new FileOutputStream(file);
serializer.setOutput(fos, "utf-8");
serializer.startDocument("utf-8", true);
serializer.startTag(null, "smss");
for (Sms sms : smsLists) {
serializer.startTag(null, "sms");
serializer.startTag(null, "address");
serializer.text(sms.getAddress());
serializer.endTag(null, "address");
serializer.startTag(null, "body");
serializer.text(sms.getBody());
serializer.endTag(null, "body");
serializer.startTag(null, "date");
serializer.text(sms.getDate());
serializer.endTag(null, "date");
serializer.endTag(null, "sms");
}
serializer.endTag(null, "smss");
serializer.endDocument();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
第3行,初始化xml的序列化器對象;
第7行,設置序列化器的參數,生成xml的編碼;
第8行,startDocument用來開始文檔,有startDocument也有endDocument;
第9行,startTag用來寫入開始標簽,一個startTag對應一個endTag;
第10-22行,遍歷集合,生成各個節點,第13行的text方法,作用是寫入標簽裡面的文本。
運行結果:
在SD卡上可以看到我們的備份文件,我們將該備份文件導出在浏覽器中打開驗證是否正確:
導出smsbackup1.xml文件,使用浏覽器打開,能正確顯示數據如下:
9.3. XmlPullParser解析Xml
利用一些開源的api,我們可以實現一些自己的應用,比如,有些公司會提供免費的天氣api,一般返回的數據都是xml格式。現在需要解析這些xml:
北京
25°
3
300
鄭州
20°
4
300
長春
10°
4
100
沈陽
20°
1
50
定義天氣的實體bean:
public class Channel {
private String id;
private String city;
private String temp;
private String wind;
private String pm250;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getTemp() {
return temp;
}
public void setTemp(String temp) {
this.temp = temp;
}
public String getWind() {
return wind;
}
public void setWind(String wind) {
this.wind = wind;
}
public String getPm250() {
return pm250;
}
public void setPm250(String pm250) {
this.pm250 = pm250;
}
@Override
public String toString() {
return "Channel [id=" + id + "城市" + city + "溫度" + temp
+ "風" + wind + " pm250=" + pm250 + "]";
}
}
解析xml文件:
public static List parseXml(InputStream in) {
List weatherLists = null;
Channel channel = null;
//初始化 xmlpullparser解析器;
XmlPullParser parser = Xml.newPullParser();
try {
//設置解析xml的輸入流,以UTF-8格式;
parser.setInput(in, "utf-8");
//獲取到解析的事件類型,根據類型做具體的操作;
int type = parser.getEventType();
//while語句,條件是如果是文檔結尾就不循環,如果沒有到文檔結尾,就繼續循環,文檔結束標記為XmlPullParser.END_DOCUMENT;
while (type != XmlPullParser.END_DOCUMENT) {
//switch語句,就是用來分別針對解析到的事件類型做相應的處理,parser.nextText()獲取標簽中的文本內容。
switch (type) {
case XmlPullParser.START_TAG:
if ("weather".equals(parser.getName())) {
weatherLists = new ArrayList();
} else if ("channel".equals(parser.getName())) {
channel = new Channel();
String id = parser.getAttributeValue(0);
channel.setId(id);
} else if ("city".equals(parser.getName())) {
String city = parser.nextText();
channel.setCity(city);
} else if ("temp".equals(parser.getName())) {
String temp = parser.nextText();
channel.setTemp(temp);
} else if ("wind".equals(parser.getName())) {
String wind = parser.nextText();
channel.setWind(wind);
} else if ("pm250".equals(parser.getName())) {
String pm250 = parser.nextText();
channel.setPm250(pm250);
}
break;
case XmlPullParser.END_TAG:
if ("channel".equals(parser.getName())) {
weatherLists.add(channel);
}
break;
}
//重新給type賦值,parser.next()會解析下一個事件。
type = parser.next();
}
return weatherLists;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
事件類型
含義
XmlPullParser.START_TAG
開始標簽 如
XmlPullParser.END_TAG
結束標簽 如
一、概述現在大多數的電商APP的詳情頁長得幾乎都差不多,幾乎都是上面一個商品的圖片,當你滑動的時候,會有Tab懸浮在上面,這樣做用戶體驗確實不錯,如果Tab滑上去,用戶可
layout<?xml version=1.0?>-<LinearLayout android:paddingTop=@dimen/ac
首先,我們看一下什麼是serializer,serializer就是串行化,又名序列化。它可並不只是簡單的把對象保存在存儲器上,它可以使我們在流中傳輸對象,使對象變的可以
工作幾年發現自己沒留下啥東西,天天開發開發,沒總結過。 這次想總結下。故而寫這個系列的博客。希望對廣大的開發者有所幫助。OK 首先先分析下 框架的作用,以及框架所應擁有的