編輯:關於Android編程
前言: 周末真的是除了睡覺還是睡覺啊O(∩_∩)O~,打開博客,看到別人大牛寫的東西的時候,感覺差距好大啊,自己要學習的東西太多太多了,不管怎樣,現在還是加油吧,騷年~~
對Android Studio還不是很熟的,或者是ADT的深度中毒患者的可以去看看這篇文章
因為之前一直用的ADT,才轉到AS不久,所以對AS不是很熟悉,每次創建完工程後,as會自動的在src目錄中會自動創建兩個目錄:
沒用過單元測試的你是不是跟我一樣困惑,這兩貨是什麼鬼?
沒錯~這就是我們今天所研究的東西(單元測試)
那麼單元測試有什麼好處呢?
1、比如你做“登錄”模塊,但在用戶登錄之前,你需要干很多東西,走很久才走到你登錄模塊,這樣測試起來每次花在走前面流程的時間就浪費的很多了,有了單元測試,你可以直接跳過之前所有步驟,直接走登錄流程。
2、可以很容易的發現你程序中的bug,通過單元測試設置一個預期值,當跟你程序中的結果值不一致的時候,就說明你的邏輯代碼有問題。
那有的童鞋會說了,你說的第一點我也可以做到啊,我直接修改下代碼,讓app一啟動直接跳轉到我指定的頁面,不就可以繞開前面的邏輯了?我想說,你硬是要這樣的話,我也沒辦法啊,(^__^) ,單元測試不需要你改之前的邏輯,你只需要針對你需要測試的部分做測試就可以了,不會對你app造成影響。
下面說下src下androidTest跟src下的test的區別:
src/test不需要你連接設備,而src/androidTest需要連接設備測試。
一、先說一下src/tests
首先我們需要添加依賴:
dependencies { testCompile 'junit:junit:4.12' }
然後我們創建一個計算的工具類:
package com.cisetech.androidunit;
/**
* author:yinqingy
* date:2016-11-13 21:00
* blog:http://blog.csdn.net/vv_bug
* desc:
*/
public class Calculator {
public double sum(double a, double b){
return a+b;
}
public double substract(double a, double b){
return a-b;
}
public double divide(double a, double b){
return a/b;
}
public double multiply(double a, double b){
return a*b;
}
}
這個時候呢,既然是計算的工具類,你肯定需要驗證一下這些計算的到底對不對了。
接下來我們就針對Calculator 做一下測試,我們可以直接創建測試類,如:
as也為我們提供了快捷創建測試類的方法:
然後一路點擊ok就可以了:
然後as自動會為我們對應創建一個測試類:
package com.cisetech.androidunit;
import org.junit.Before;
import org.junit.Test;
/**
* author:yinqingy
* date:2016-11-13 21:08
* blog:http://blog.csdn.net/vv_bug
* desc:
*/
public class CalculatorTest {
@Before
public void setUp() throws Exception {
}
@Test
public void sum() throws Exception {
}
@Test
public void substract() throws Exception {
}
@Test
public void divide() throws Exception {
}
@Test
public void multiply() throws Exception {
}
}
@Before注解的意思是:在測試開始之前回調的方法( 比如說初始化我們的類)。
@Test注解的意思是:我們需要測試的方法(進行+-*/操作)。
了解用法之後,我們改改代碼:
package com.cisetech.androidunit;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
/**
* author:yinqingy
* date:2016-11-13 21:08
* blog:http://blog.csdn.net/vv_bug
* desc:
*/
public class CalculatorTest {
private Calculator mCalculator;
@Before
public void setUp() throws Exception {
mCalculator = new Calculator();
}
@Test
public void testSum() throws Exception {
assertEquals("1+5=6",6d, mCalculator.sum(1d, 5d), 0);
}
@Test
public void testSubstract() throws Exception {
assertEquals("5-4=1",2d, mCalculator.substract(5d, 4d), 0);
}
@Test
public void testDivide() throws Exception {
assertEquals("20/5=4",4d, mCalculator.divide(20d, 5d), 0);
}
@Test
public void testMultiply() throws Exception {
assertEquals("2*5=10",0, mCalculator.multiply(2d, 5d), 0);
}
}
assertEquals方法可以傳入一個預期的值、我們算出的值、預期的值跟算出值的誤差范圍。
終於到運行測試的時候了!右鍵點擊CalculatorTest類,選擇Run > CalculatorTest。也可以通過命令行運行測試,在工程目錄內輸入:
./gradlew test
通過結果我們可以看到,我們的testSum跟testDivide方法通過了測試,而另外兩個方法沒有通過測試:
錯誤很明顯哦
java.lang.AssertionError: 5-4=1
Expected :2.0
Actual :1.0
通過這個小實驗,是不是對junit有點感興趣了呢?我們也只是使用了其冰山一角啊,其它的一些用法,小伙伴們就自己去研究吧。
前面我們看了src/test,下面看看src/androidTest的用法(先看看我們的demo運行後的效果圖):
沒錯!!就是一個簡單的三級聯動的實現。
demo中是直接導的一個數據庫文件。
city.db數據庫文件結構(大致看一下哈):
city表結構:
district表結構:
province表結構:
看不懂的小伙伴也沒關系,我待會會解釋一下的,sqlite可視化工具我也會上傳在demo中O(∩_∩)O~
明確下我們的目標:
package com.cisetech.androidunit.dao;
import com.cisetech.androidunit.bean.AddressBean;
import java.util.List;
/**
* author:yinqingy
* date:2016-11-13 21:52
* blog:http://blog.csdn.net/vv_bug
* desc:地址數據訪問層
*/
public interface AddressDao {
/**
* 關閉數據庫
*/
void closeDb();
/**
* 根據城市Code找到所有的地區
* @param cityCode
* @return
* @throws Exception
*/
List getDistirctsByPcode(String cityCode) throws Exception;
/**
* 根據省份code找到所有的市
* @param pCode
* @return
* @throws Exception
*/
List getCitiesByPcode(String pCode) throws Exception;
/**
* 獲取所有的省份
* @return
* @throws Exception
*/
List getAllProvince() throws Exception;
}
然後寫好實現類:
package com.cisetech.androidunit.dao;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import com.cisetech.androidunit.R;
import com.cisetech.androidunit.bean.AddressBean;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
/**
* author:yinqingy
* date:2016-11-13 21:54
* blog:http://blog.csdn.net/vv_bug
* desc:地址數據訪問層實現層
*/
public class AddressDaoImp implements AddressDao {
public static final String tag = "AddressDao";
public static final String CITY_DB_NAME = "CITY_DB";
private SQLiteDatabase db;
public AddressDaoImp(Context context) {
File db_file = null;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {// 判斷sd卡是否有效
db_file = new File(Environment.getExternalStorageDirectory(),CITY_DB_NAME);
Log.i(tag, "拿到內存卡中的city——db");
}
if (db_file == null) {// 內存卡不存在的情況(放在手機緩存中)
Log.i(tag, "拿到手機內存中的city——db");
db_file = new File(context.getFilesDir(),CITY_DB_NAME);
}
if (db_file != null && db_file.length() > 0) {
db = SQLiteDatabase.openDatabase(db_file.getAbsolutePath(), null,
SQLiteDatabase.OPEN_READONLY);
}else{
try {
InputStream is = context.getResources().openRawResource(R.raw.city);
FileOutputStream fos = new FileOutputStream(db_file);
byte[] buffer = new byte[512];
int count = 0;
while ((count =is.read(buffer)) > 0) {
fos.write(buffer, 0, count);
fos.flush();
}
fos.close();
is.close();
Log.i(tag, "db拷貝完成");
db = SQLiteDatabase.openDatabase(db_file.getAbsolutePath(), null,
SQLiteDatabase.OPEN_READONLY);
} catch (Exception e) {
db_file.delete();//異常就刪除db文件
e.printStackTrace();
Log.i(tag, "db拷貝異常");
}
}
if (db == null) {
Log.i(tag, "獲取db異常~~~~");
}
}
@Override
public void closeDb() {
if (db != null) {
db.close();
db = null;
}
}
/**
* 根據城市code獲取所有的district
* @return
*/
public List getDistirctsByPcode(String cityCode) {
List cities = new ArrayList();
try {
Cursor cursor = db.rawQuery("select * from district where pcode=?",
new String[] { cityCode });
while (cursor.moveToNext()) {
AddressBean item = new AddressBean();
item.setCode(cursor.getString(cursor.getColumnIndex("code")));
byte bytes[] = cursor.getBlob(2);
String name = new String(bytes, "gbk");
if(!TextUtils.isEmpty(name)){
name=name.trim();
}
item.setName(name);
cities.add(item);
}
cursor.close();
} catch (Exception e) {
return cities;
}
return cities;
}
/**
* 根據省獲取city
*
* @return
*/
@Override
public List getCitiesByPcode(String pCode) throws Exception {
List cities = new ArrayList();
try {
Cursor cursor = db.rawQuery("select * from city where pcode=?",
new String[] { pCode });
while (cursor.moveToNext()) {
AddressBean item = new AddressBean();
item.setCode(cursor.getString(cursor.getColumnIndex("code")));
byte bytes[] = cursor.getBlob(2);
String name = new String(bytes, "gbk");
if(!TextUtils.isEmpty(name)){
name=name.trim();
}
item.setName(name);
cities.add(item);
}
cursor.close();
} catch (Exception e) {
return cities;
}
return cities;
}
/**
* 獲取所有的province
* @return
*/
@Override
public List getAllProvince() {
List provinces = new ArrayList();
try {
Cursor cursor = db.rawQuery("select * from province", null);
while (cursor.moveToNext()) {
AddressBean item = new AddressBean();
item.setCode(cursor.getString(cursor.getColumnIndex("code")));
byte bytes[] = cursor.getBlob(2);
String name = new String(bytes, "gbk");
if(!TextUtils.isEmpty(name)){
name=name.trim();
}
item.setName(name);
provinces.add(item);
}
cursor.close();
} catch (Exception e) {
return provinces;
}
return provinces;
}
}
在還沒有把寫ui布局邏輯的時候,我們先測試一下我們的daoimp的正確性:
我們直接在src/androidTest下創建一個AddressDaoImpTest的文件,然後把ExampleInstrumentedTest的代碼copy過去:
@Test
public void testDao() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
String name = appContext.getPackageName();
assertEquals("com.cisetech.androidunit", appContext.getPackageName());
AddressDaoImp dao=new AddressDaoImp(appContext);
List allProvince = dao.getAllProvince();
for (AddressBean bean:allProvince) {
Log.e("TAG","province--->"+bean.getName());
}
}
我們運行代碼,此時需要連上真機或者模擬器測試了(我們直接右擊方法名運行,同時你也可以選擇debug運行):
然後看到log裡面打印:
1-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->四川省
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->貴州省
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->雲南省
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->西藏
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->陝西省
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->甘肅省
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->青海省
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->寧夏
11-14 23:07:31.128 27711-27741/com.cisetech.androidunit E/TAG: province--->新疆
是不是so easy!!
同時你還可以進行省市區的測試,我就不再運行測試了,留給小伙伴自己研究哈!!~~
最後附上demo github鏈接:
https://github.com/913453448/AndroidUnit
Supporting Multiple ScreensAndroid runs on a variety of devices that offer different
前言最近做項目的時候遇到一個卡劵的效果,由於自己覺得用圖片來做的話可以會出現適配效果不好,再加上自己自定義view方面的知識比較薄弱,所以想試試用自定義View來實現。但
B. Little Pony and Harmony Chest time limit per test 4 seconds memory limi
書接上回,我們已經了解了一些關於適配的一些相關概念,接下來我們會了解一下,在設置布局時我們應該注意的地方。盡量不去設定具體的尺寸值。為了確保布局適應各種尺寸的屏幕,在保證
一、ArrayAdapter的介紹以及ListView的用法: Adap