新建一個android工程:D_session;它有一個activity:D_sessionActivity;package名:com.mysession
二.測試工程:
新建一個
測試工程:D_sessionTest, 類型是android test project;
1. menifest文件:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.mysession.test" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" /> <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.mysession" /> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <uses-library android:name="android.test.runner" /> </application> </manifest> |
2. 一個base activity 來定義各種模擬測試者的動作和判斷測試結果,各個測試類都繼承該類:
package com.mysession.test; import android.app.Activity; import android.app.Instrumentation; import android.app.Instrumentation.ActivityMonitor; import android.content.Intent; import android.test.InstrumentationTestCase; import android.test.TouchUtils; import android.widget.Button; import android.widget.TextView; import com.mysession.D_sessionActivity; public class SessionActivityTest extends InstrumentationTestCase { private Instrumentation mInstrumentation; private ActivityMonitor mSessionMonitor; private Activity mCurrentActivity, mSessionActivity; private String TextNotEqual = "text not equal."; private static final String PackageName = "com.mysession"; @Override protected void setUp() throws Exception { // 初始化 super.setUp(); if (mInstrumentation == null) { mInstrumentation = getInstrumentation(); } mSessionActivity = null; } @Override protected void tearDown() throws Exception { super.tearDown(); //釋放資源 closeActivity(mSessionActivity); mCurrentActivity = null; } private void closeActivity(Activity activity) { if(activity != null){ activity.finish(); activity = null; } } public void openSessionActivity() { // 打開session activity try { setUp(); } catch (Exception e) { e.printStackTrace(); } mSessionMonitor = mInstrumentation.addMonitor( D_sessionActivity.class.getName(), null, false); Intent intent = new Intent(Intent.ACTION_MAIN); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName(PackageName, D_sessionActivity.class.getName()); mInstrumentation.startActivitySync(intent); mSessionActivity = getInstrumentation().waitForMonitor(mSessionMonitor); assertNotNull(mSessionActivity); mCurrentActivity = mSessionActivity; } //判斷text是否正確 public void assertTextEqual(int resId, String strText) { TextView textView = (TextView) mCurrentActivity.findViewById(resId); assertNotNull(textView); assertEquals(TextNotEqual, strText, textView.getText().toString()); }; // 模擬按鈕點擊事件 public void clickButton(int resId){ Button button = (Button) mCurrentActivity.findViewById(resId); assertNotNull(button); TouchUtils.clickView(this, button); } } |
3. 各個測試類:
測試類一:
package com.mysession.test.cases; import com.mysession.R; import com.mysession.test.SessionActivityTest; public class MyCase1 extends SessionActivityTest { public void testCase1() { openSessionActivity(); assertTextEqual(R.id.etUrl, "http://172.20.230.5/iportal/samples/jsapi/mobile.html"); } public void testCase3() { openSessionActivity(); clickButton(R.id.btnLoad); clickButton(R.id.btnHistory); } } |
測試類二:
package com.mysession.test.cases; import com.mysession.R; import com.mysession.test.SessionActivityTest; public class MyCase2 extends SessionActivityTest{ public void testCase2() { openSessionActivity(); clickButton(R.id.btnLoad); } } |
三. 有些動作(如點擊menu)需要通過包robotium-solo-1.8.0.jar來完成。
所以要在Build Path->Configure Build Path…中導入:robotium-solo-1.8.0.jar
程序中:
private Solo solo;
solo = new Solo(getInstrumentation(),getActivity);
就可以使用solo了, 如:
solo.clickOnMenuItem(text);
solo.goBack();
目前有很多Web UI自動化
測試框架,如WatiN,Selinimu,WebDriver等,這些框架都可以操作Web中的控件,模擬用戶輸入,點擊等操作,實現Web自動化測試。其實這些工具的原理都一樣,都是通過調用IE COM接口和HTMLDOM 對IE瀏覽器以及WEB測試對象的操作。 本文介紹脫離這些
自動化測試框架,通過AutoIT直接使用IE COM接口結合HTML DOM對IE瀏覽器以及WEB對象進行自動化測試的方法。
1.IE常用操作
首先新建一個IE COM對象,配置IE窗口屬性,模擬用戶同時跳轉至相應的頁面同時進行相應操作。訪問頁面時,需要等待頁面加載完成后再進行操作。這里我們可以使用IE COM的BUSY屬性檢查瀏覽器是否處于加載狀態,再進行相應的操作。示例代碼如下:
2.利用DOM操作測試對象
現在已經會使用IE COM組件來對IE瀏覽器進行自動化的操作,但是對于瀏覽器頁面中的測試對象IE COM是無法對其進行操作的,這個時候就需要使用HTML DOM來對其進行操作。
2.1 HTML DOM簡介
HTML DOM是HTML Document Object Model(文檔對象模型)的縮寫,它將網頁中的各個元素都看作一個個對象,從而使網頁中的元素也可以被計算機語言獲取或者編輯。 常用DOM 屬性如下:
className.同一樣式規則的元素用相同的類名。可以通過className快速過濾出一組類似的元素。
document.用于指向包含當前元素的文檔對象。
id.當前元素的標識。如果文檔中包含多個相同id的元素,則返回一個數組。
innerHTML.用于指向當前元素的開始標記和結束標記之間的所有文本和HTML標簽。
innerText.用于指向當前元素的開始標記和結束標記之間的所有文本和HTML標簽。
offsetHeight, offsetWidth.元素的高度和寬度。
offsetLeft, offsetTop.當前元素相同對于父親元素的左邊位置和頂部位置。
outerHTML.當前元素的開始標記和結束標記之間的所有文本和HTML標簽。
outerText.當前元素的開始標記和結束標記之間的所有文本,但不包括HTML標簽。
parentElement.當前元素的父親元素。
sourceIndex.元素在document.all集合中的索引(index)。
style.元素的樣式表單屬性。
tagName.當前元素的標簽名。
title.在IE中,代表元素的tool tip文本
常用DOM方法如下:
click().模擬用戶對當前元素的鼠標點擊。
contains(element).用于判斷當前元素是否包含指定的元素。
getAttribute(attributeName, caseSensitive).返回當前元素所包含的某個屬性,參數attributeName為屬性名、caseSensitive表示是否大小寫敏感。
setAttribute(attributeName, value, caseSenstive). 設置當前元素的屬性。
常用DOM 集合如下:
All[].當前元素中包含的所有HTML元素的數組。
children[].當前元素包含的子元素。
2.2 種方法對比
2.2.1getElementByID
getElementByID( )方法可根據指定的id屬性值得到對象。 首先需要分析頁面,在Chome瀏覽器中選擇相應的網頁元素點擊右鍵選擇"審查元素"(或使用IE Develop Toolbar或者firebug等插件亦可),即可得到頁面控件的ID等信息。

通過getElementByID方法獲取百度搜索框及搜索按鈕對象,并對其進行輸入及點擊操作,從而完成搜索操作。示例代碼如下:
2.2.2getElementsByName
getElementsByName( )方法可返回帶有指定名稱的對象的集合。
同樣獲取網頁元素name后,即可通過getElementsByName方法獲取定位對象,并對其進行操作(與getElementByID返回的單個對象不同,getElementsByName返回的是一個元素的集合,需要通過遍歷對象才能對其進行操作):
2.2.3getElementsByTagName
getElementsByTagName( )方法通過查找整個HTML文檔中的任何HTML元素,傳回指定名稱的元素集合。 因此也可使用getElementsByTagName獲取TAG名,通過得到相同類型的元素及在遍歷中進行判斷控件類型并進行操作:

2.3 利用FORM名來獲取對象元素
使用FORM名來獲取對象元素會大大簡化我們的腳本。首先查看百度的搜索框對應的FORM名,得到FORM名為f:
通過如下簡單的腳本,同樣可以達到相同的效果:
2.4 訪問Web頁面的Script腳本變量
通過DOM還可以直接訪問Web頁面中的JavaScript或者VBScript中的變量。首先打開百度的源文件:
可以看到在百度源文件的JavaScript腳本中定義了一個變量為k,并且賦值為d.f.wd(實際上就是百度搜索框對象)。那么可以直接使用parentWindow來訪問Web頁面Script中的變量k,對百度搜索框進行自動測試:
3 總結
本文主要介紹了利用IE的COM以及HTML DOM來自動化IE瀏覽器,以及對瀏覽器的一些控件對象進行自動化的操作,包括IE瀏覽器常用操作、利用DOM操作測試對象、利用FORM名來獲取對象元素、訪問Web頁面的Script腳本變量等。 直接操作IE COM來實現Web自動化,不僅有助于有助于理解Web頁面自動化測試框架的運行原理,還能脫離這些自動化測試框架自己快速建立一個輕量型的自動化測試程序,從而真正的提高測試效率。
原文鏈接:http://lovesoo.org/web-automation-testing-principle.html
測試是一個很重要的問題,我覺得:一個程序員行不行,關鍵看他會不會調試。程序的測試都是從小范圍入手然后向四面八方入手,一點點的向外擴展,直至最后程序整體運行良好。這只是在下的一點愚見。以前進行調試時總是在程序中寫個main方法,然后進行測試,以前學過一個Junit單元測試,沒想起來用,今天看見老師的代碼中有這個
單元測試,所以下來了看了看以前的代碼并上網查了查有關Junit的東西,在此簡單的說一下。以MyElipse 8.5 進行說明。
1.新建一個project,然后右擊選擇properties-----Java Build Path-----Libraries---AddLibrary---Junit,將Junit插件添加到該項目中。
2.在project中建立一個JDemo.java 和
Test.java,JDemo中由一個簡單的方法add(),Test要繼承TestCase,代碼如下:
JDemo.java package com.bx.testjunit; public class JDemo { int a; int b; int result; public int add(int a,int b){ result = a + b; return result; } } Test.java package com.bx.testjunit; import junit.framework.TestCase; import org.junit.After; import org.junit.Before; public class Test extends TestCase{ @Before public void setUp() throws Exception { System.out.println("Test :setUp"); } @After public void tearDown() throws Exception { System.out.println("Test :tearDown"); } public void test(){ JDemo a = new JDemo(); assertEquals(6,a.add(3, 3)); } } |
3.運行,右擊Junit Test,在控制臺上會出來一個Junit選項卡,如果出現綠顏色的bar則說明程序正確,如果是紅顏色的bar則說明程序錯誤,需進行檢錯。
上面的程序用的是Junit3,必須繼承TestCase
下面看一個Junit4的例子
package com.bx.service; import org.junit.Test; public class HelloWorld { @Test public void test(){ System.out.println("HelloWord :test"); } } |
Junit4不需要繼承TestCase,只需使用@Test標注的expected屬性。
1、項目進度
1.1測試執行階段
寫清楚當前階段預計在什么時候結束(在可控的情況下,如果不可控或者不可預測,說明風險在哪里)
寫清楚當前測試了哪個模塊,還有哪些模塊沒有測試(一般都是先測試優先級高的模塊)
寫清楚下個星期的測試計劃是什么
2、測試情況
2.1 再介紹一下本周的測試模塊
2.2 寫清楚當前模塊的質量情況:open了多少bug,fix了多少,close了多少。bug的趨勢是什么(后期應當收斂,在前期如果未收斂,解釋未收斂的原因)
2.3 本周遇到的問題是什么(風險是什么)
3、貼圖
3.1 相關圖片,有需要解釋的進行解釋(如reopen的bug,高級別的bug)
1.概述
一個軟件設計的好壞,我想很大程度上取決于它的整體架構,而這個整體架構其實就是你對整個宏觀商業業務的抽象框架,當代表業務邏輯的高層抽象層結構 合理時,你底層的具體實現需要考慮的就僅僅是一些算法和一些具體的業務實現了。當你需要再開發另一個相近的項目時,你以前的抽象層說不定還可以再次利用 。面對對象的設計,復用的重點其實應該是抽象層的復用,而不是具體某一個代碼塊的復用。
說到了抽象,我就不能不提到曾讓我頭痛的Java接口和Java抽象類了,這也是本文我想說的重點。
既然面向對象設計的重點在于抽象,那Java接口和Java抽象類就有它存在的必然性了。
Java接口(interface)和Java抽象類(abstract class)代表的就是抽象類型,就是我們需要提出的抽象層的具體表現。OOP面向對象的編程,如果要提高程序的復用率,增加程序 的可維護性,可擴展性,就必須是面向接口的編程,面向抽象的編程,正確地使用接口、抽象類這些有用的抽象類型作為你結構層次上的頂層。
Java接口和Java抽象類有太多相似的地方,又有太多特別的地方,究竟在什么地方,才是它們的最佳位置呢?把它們比較一下,你就可以發現了。
- Java接口和Java抽象類最大的一個區別,就在于Java抽象類可以提供某些方法的部分實現,而Java接口不可以(就是interface中只能定義方法,而不能有方法的實現,而在abstract class中則可以既有方法的具體實現,又有沒有具體實現的抽象方法),這大概就是Java抽象類唯一的優點吧,但這個優點非常有用。如果向一個抽象類里加入一個新的具體方法時,那么它所有的子類都一下子都得到了這個新方法,而Java接口做不到這一點,如果向一個Java接口里加入一個 新方法,所有實現這個接口的類就無法成功通過編譯了,因為你必須讓每一個類都再實現這個方法才行,這顯然是Java接口的缺點。這個在我的另外一篇博客mapreduce 新舊API 區別中有提到類似的問題,在新的mapreduce api中更傾向于使用抽象類,而不是接口,因為這更容易擴展。原因就是上面劃線部分所說的。
- 一個抽象類的實現只能由這個抽象類的子類給出,也就是說,這個實現處在抽象類所定義出的繼承的等級結構中,而由于Java語言的單繼承性,所以抽象類作為類型定義工具的效能大打折扣。在這一點上,Java接口的優勢就出來了,任何一個實現了一個Java接口所規定的方法的類都可以具有這個接口的類型,而一個類可以實現任意多個Java接口,從而這個類就有了多種類型。(使用抽象類,那么繼承這個抽象類的子類類型就比較單一,因為子類只能單繼承抽象類;而子類能夠同時實現多個接口,因為類型就比較多。接口和抽象類都可以定義對象,但是只能用他們的具體實現類來進行實例化。)
- 從第2點不難看出,Java接口是定義混合類型的理想工具,混合類表明一個類不僅僅具有某個主類型的行為,而且具有其他的次要行為。
- 結合1、2點中抽象類和Java接口的各自優勢,具精典的設計模式就出來了:聲明類型的工作仍然由Java接口承擔,但是同時給出一個Java 抽象類,且實現了這個接口,而其他同屬于這個抽象類型的具體類可以選擇實現這個Java接口,也可以選擇繼承這個抽象類,也就是說在層次結構中,Java 接口在最上面,然后緊跟著抽象類,這下兩個的最大優點都能發揮到極至了。這個模式就是“缺省適配模式”。在Java語言API中用了這種模式,而且全都遵循一定的命名規范:Abstract +接口名。(A extends AbstractB implements interfaceC,那么A即可以選擇實現(@Override)接口interfaceC中的方法,也可以選擇不實現;A即可以選擇實現(@Override)抽象類AbstractB中的方法,也可以選擇不實現)
Java接口和Java抽象類的存在就是為了用于具體類的實現和繼承的,如果你準備寫一個具體類去繼承另一個具體類的話,那你的設計就有很大問題了。Java抽象類就是為了繼承而存在的,它的抽象方法就是為了強制子類必須去實現的。
使用Java接口和抽象Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。而不要用具體Java類進行變量的類型聲明、參數是類型聲明、方法的返還類型說明,以及數據類型的轉換等。
2.實例
下面給出一個具體的接口Action,代碼如下所示:
- package org.springframework.webflow.execution;
- public interface Action {
- public Event execute(RequestContext context) throws Exception;
- }
在這個接口中,定義了一個沒有具體實現的方法,方法名叫做execute(),返回類型是Event。如前面第一條所述,接口中的方法都是沒有實現的。這些方法的具體實現是在實現(implements)這個接口的類中給出的。再來看一個實現Action接口的抽象類AbstractAction,代碼如下。
- package org.springframework.webflow.action;
-
- import org.apache.commons.logging.Log;
- import org.apache.commons.logging.LogFactory;
- import org.springframework.beans.factory.BeanInitializationException;
- import org.springframework.beans.factory.InitializingBean;
- import org.springframework.util.ClassUtils;
- import org.springframework.webflow.core.collection.AttributeMap;
- import org.springframework.webflow.execution.Action;
- import org.springframework.webflow.execution.Event;
- import org.springframework.webflow.execution.RequestContext;
-
- public abstract class AbstractAction implements Action, InitializingBean {
-
- protected final Log logger = LogFactory.getLog(getClass());
-
- public EventFactorySupport getEventFactorySupport() {
- return new EventFactorySupport();
- }
-
- public void afterPropertiesSet() throws Exception {
- try {
- initAction();
- } catch (Exception ex) {
- throw new BeanInitializationException("Initialization of this Action failed: " + ex.getMessage(), ex);
- }
- }
-
- protected void initAction() throws Exception {
- }
-
- protected Event success() {
- return getEventFactorySupport().success(this);
- }
-
- protected Event success(Object result) {
- return getEventFactorySupport().success(this, result);
- }
-
- protected Event error() {
- return getEventFactorySupport().error(this);
- }
-
- protected Event error(Exception e) {
- return getEventFactorySupport().error(this, e);
- }
-
- protected Event yes() {
- return getEventFactorySupport().yes(this);
- }
-
- protected Event no() {
- return getEventFactorySupport().no(this);
- }
-
- protected Event result(boolean booleanResult) {
- return getEventFactorySupport().event(this, booleanResult);
- }
-
- protected Event result(String eventId) {
- return getEventFactorySupport().event(this, eventId);
- }
-
- protected Event result(String eventId, AttributeMap resultAttributes) {
- return getEventFactorySupport().event(this, eventId, resultAttributes);
- }
-
- protected Event result(String eventId, String resultAttributeName, Object resultAttributeValue) {
- return getEventFactorySupport().event(this, eventId, resultAttributeName, resultAttributeValue);
- }
-
- public final Event execute(RequestContext context) throws Exception {
- Event result = doPreExecute(context);
- if (result == null) {
- result = doExecute(context);
- doPostExecute(context);
- } else {
- if (logger.isInfoEnabled()) {
- logger.info("Action execution disallowed; pre-execution result is '" + result.getId() + "'");
- }
- }
- return result;
- }
-
- protected String getActionNameForLogging() {
- return ClassUtils.getShortName(getClass());
- }
-
- protected Event doPreExecute(RequestContext context) throws Exception {
- return null;
- }
-
- //抽象方法
- protected abstract Event doExecute(RequestContext context) throws Exception;
-
- protected void doPostExecute(RequestContext context) throws Exception {
- }
- }
在抽象類AbstractAction中,既有具體實現的方法,又有沒有具體實現的抽象方法- //抽象方法
- protected abstract Event doExecute(RequestContext context) throws Exception;
需要注意的是在抽象類中,如果方法沒有具體實現(就是方法后面沒有{}),那么必須加上abstract來聲明這個方法,而接口中不需要使用abstract來聲明(抽象類之所以被稱為抽象類,就是因為它包含有抽象方法。含有抽象方法的類叫做抽象類)。
最近突然發現忘了
數據庫鎖和數據庫隔離級別,時常弄混它們之間的關系。為此特此寫下此博客,以方便自己復習,同時也可以幫助博友。
數據庫鎖
數據庫鎖就是事務T在對某個數據對象(例如表、記錄等)操作之前,先向系統發出請求,對其加鎖。加鎖后事務T就對該數據對象有了一定的控制,在事務T釋放它的鎖之前,其它的事務不能更新此數據對象。
數據庫鎖是實現并發控制的重要技術,但是“鎖”會帶來系統額外的開銷。所以需要注意選擇封鎖粒度時必須同時考慮開銷和并發度兩個因素,進行權衡,以求得最優的效果。
鎖的類型主要有兩種類型排它鎖(也叫獨占鎖)和共享鎖,當然還有很多教程增加了一種--更新鎖。
共享(S)鎖:多個事務可封鎖一個共享頁;任何事務都不能修改該頁; 通常是該頁被讀取完畢,S鎖立即被釋放。在執行select語句的時候需要給操作對象(表或者一些記錄)加上共享鎖,但加鎖之前需要檢查是否有排他鎖,如果沒有,則可以加共享鎖(一個對象上可以加n個共享鎖),否則不行。共享鎖通常在執行完select語句之后被釋放,當然也有可能是在事務結束(包括正常結束和異常結束)的時候被釋放,主要取決與數據庫所設置的事務隔離級別。
排它(X)鎖:僅允許一個事務封鎖此頁;其他任何事務必須等到X鎖被釋放才能對該頁進行訪問;X鎖一直到事務結束才能被釋放。執行insert、update、delete語句的時候需要給操作的對象加排他鎖(我感覺在執行insert的時候應該是在表級加排他鎖),在加排他鎖之前必須確認該對象上沒有其他任何鎖,一旦加上排他鎖之后,就不能再給這個對象加其他任何鎖。排他鎖的釋放通常是在事務結束的時候(當然也有例外,就是在數據庫事務隔離級別被設置成Read Uncommitted(讀未提交數據)的時候,這種情況下排他鎖會在執行完更新操作之后就釋放,而不是在事務結束的時候)。
更新(U)鎖:用來預定要對此頁施加X鎖,它允許其他事務讀,但不允許再施加U鎖或X鎖;當被讀取的頁將要被更新時,則升級為X鎖;U鎖一直到事務結束時才能被釋放。數據庫是支持在一個事務中進行自動鎖升級的,例如,在某個事務中先執行select語句,后執行update語句,這兩條語句操作了同一個對象,并且假定共享鎖是在事務結束的時候被釋放的。如果數據庫不支持自動鎖升級,那么當update語句請求排他鎖的時候將不能成功。因為之前select語句的共享鎖沒有被釋放,那么事務就進入了無限等待,即死鎖。有了自動鎖升級,在執行update語句的時候就可以將之前加的共享鎖升級為排他鎖,但有個前提,就是這個共享鎖必須是本事務自己加的,而且在操作對象上沒有在加其他任何鎖,否則共享鎖是不能被升級為排他鎖的,必須等待其他鎖的釋放。
因為通常在執行更新操作的時候要先查詢,也就是我們通常會在update語句和delete語句中加where子句。那么,有的數據庫系統可能會在執行查詢的時候先給操作對象加共享鎖,然后在更新的時候加排他鎖,但這么做會有問題,也就是如果兩個事務同時要更新一個對象,都先給這個對象加了共享鎖,當要更新的時候,都請求升級鎖,但由于這個對象上存在對方事務加的共享鎖。。所以無法升級。這樣兩個事務就在等待對方釋放共享鎖,進入死鎖狀態。更新鎖就是為了解決這個問題,即在執行查詢操作的時候加的不是共享鎖而是更新鎖(一個對象上只能有一個更新鎖和n個共享鎖),當要更新的時候,再將更新鎖升級為排他鎖,升級前提是這個對象上只有本事務加的更新鎖,沒有其他任何鎖了。其實,,我想,如果在執行查詢的時候就給事務加排他鎖不也能解決死鎖問題嗎,但這樣似乎會減弱系統的并發性能。
數據庫的事務隔離級別
在數據庫操作中,為了有效保證并發讀取數據的正確性,提出的事務隔離級別。
在沒有數據庫的事務隔離級別時會出現如下問題:
更新丟失
兩個事務都同時更新一行數據,但是第二個事務卻中途失敗退出,導致對數據的兩個修改都失效了。這是因為系統沒有執行任何的鎖操作,因此并發事務并沒有被隔離開來。
臟讀
一個事務開始讀取了某行數據,但是另外一個事務已經更新了此數據但沒有能夠及時提交。這是相當危險的,因為很可能所有的操作都被回滾。
不可重復讀
不可重復讀(Non-repeatable Reads):一個事務對同一行數據重復讀取兩次,但是卻得到了不同的結果。
包括以下情況:
(1) 事務T1讀取某一數據后,事務T2對其做了修改,當事務T1再次讀該數據時得到與前一次不同的值。
(2) 幻讀(Phantom Reads):事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺少了第一次查詢中出現的數據(這里并不要求兩次查詢的SQL語句相同)。這是因為在兩次查詢過程中有另外一個事務插入數據造成的。
為了避免上面出現的幾種情況,在標準SQL規范中,定義了4個事務隔離級別,不同的隔離級別對事務的處理不同。
未授權讀取
也稱為讀未提交(Read Uncommitted):允許臟讀取,但不允許更新丟失。如果一個事務已經開始寫數據,則另外一個事務則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。
授權讀取
也稱為讀提交(Read Committed):允許不可重復讀取,但不允許臟讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。
可重復讀取(Repeatable Read)
可重復讀取(Repeatable Read):禁止不可重復讀取和臟讀取,但是有時可能出現幻影數據。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
序列化(Serializable)
序列化(Serializable):提供嚴格的事務隔離。它要求事務序列化執行,事務只能一個接著一個地執行,但不能并發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。
隔離級別越高,越能保證數據的完整性和一致性,但是對并發性能的影響也越大。對于多數應用程序,可以優先考慮把數據庫系統的隔離級別設為Read Committed。它能夠避免臟讀取,而且具有較好的并發性能。盡管它會導致不可重復讀、虛讀和第二類丟失更新這些并發問題,在可能出現這類問題的個別場合,可以由應用程序采用悲觀鎖或樂觀鎖來控制。
1.前言
作為現在App里必不可少的用戶分享需要,社交化分享顯然是我們開發app里較為常用的。
最近因為公司App有社交化分享的需要,就特此研究了會,拿出來與大家分享。
想要集成社交會分享,我們可以使用
ShareSDK - 優點功能豐富,缺點體積較大
百度分享SDK - 缺點功能相對ShareSDK較少,優點體積較小
這是現在較為常用的兩種社交化分享工具。
使用哪一種,就看個人的app的需要來決定了。
今天我主要說的是ShareSDK的簡單集成和使用。
2.1. 拿自己的Appkey 去下載ShareSDK
下載地址:http://sharesdk.cn/Download
2.2. 申請分享工具的Appkey
例如: 新浪微博、
騰訊微博、豆瓣應用、人人網、QQ空間
去各大社交網站的開發者平臺,進行注冊申請即可。
獲取如下:
//新浪微博:
//App Key:2258477553
//App Secret:1e2f275afc375109e456f550fb3918e8
//騰訊微博:
//App key:2620460989
//App secret:58c55f572d5ae35e0c355f4c0ee11283
3.集成ShareSDK
3.1.注冊使用ShareSDK
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//注冊ShareSDK
[ShareSDK registerApp:@"1983bf0916db”];
return YES;
}
3.2.添加要集成的分享平臺
注意: 新浪微博需要提供回調地址才行
回調地址去新浪開發者平臺獲取
如圖:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //注冊ShareSDK [ShareSDK registerApp:@"1983bf0916db”]; //添加新浪微博應用 [ShareSDK connectSinaWeiboWithAppKey:@"2620460989" appSecret:@"58c55f572d5ae35e0c355f4c0ee11283" redirectUri:@"http://weibo.cn/ext/share?ru=http%3A%2F%2F16kxs.com%2Fwap%2FBook%2FShow.aspx%3Fid%3D7983%26lmid%3D0%26uid%3D0%26ups%3D0&rt=%E9%83%BD%E5%B8%82%E7%89%A7%E9%AC%BC%E4%BA%BA&st=1301645308&appkey=2620460989”]; //添加騰訊微博應用 [ShareSDK connectTencentWeiboWithAppKey:@"801307650" appSecret:@"ae36f4ee3946e1cbb98d6965b0b2ff5c" redirectUri:@"http://www.sharesdk.cn"]; //添加豆瓣應用 [ShareSDK connectDoubanWithAppKey:@"07d08fbfc1210e931771af3f43632bb9" appSecret:@"e32896161e72be91" redirectUri:@"http://dev.kumoway.com/braininference/infos.php"]; //添加人人網應用 [ShareSDK connectRenRenWithAppKey:@"fc5b8aed373c4c27a05b712acba0f8c3" appSecret:@"f29df781abdd4f49beca5a2194676ca4"]; //添加Facebook應用 [ShareSDK connectFacebookWithAppKey:@"107704292745179" appSecret:@"38053202e1a5fe26c80c753071f0b573"]; } |
3.3.彈出分享View
a.初始化默認分享內容
NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"123" ofType:@"png"]; //構建優秀的SDK //構造分享內容 id<ISSContent> publishContent = [ShareSDK content:@"iOS社交化分享測試內容。" defaultContent:@"默認分享內容,沒內容時顯示" image:[ShareSDK imageWithPath:imagePath] title:@"ShareSDK" url:@"http://www.sharesdk.cn" description:@"這是一條測試信息" mediaType:SSPublishContentMediaTypeNews]; |
b.彈出分享View
[ShareSDK showShareActionSheet:nil shareList:nil content:publishContent statusBarTips:YES authOptions:nil shareOptions: nil result:^(ShareType type, SSResponseState state, id<ISSPlatformShareInfo> statusInfo, id<ICMErrorInfo> error, BOOL end) { if (state == SSResponseStateSuccess) { NSLog(@"分享成功"); } else if (state == SSResponseStateFail) { NSLog(@"分享失敗,錯誤碼:%d,錯誤描述:%@", [error errorCode], [error errorDescription]); } }]; |
效果圖:
對于
JAVA中變量的初始化是一個很基礎的問題,其中的一些問題也是易被
學習者所忽略。當在編寫代碼的時候碰到時,常被這些問題引發的錯誤,感覺莫名其妙。而且現在許多大公司的
面試題,對于這方面的考查也是屢試不爽。以下是對java變量初始化的時機的分析。
【java變量執行初始化的步驟】
java是一門強類型語言,因此java語言規定每個變量必須先聲明,然后才能使用,聲明變量時必須指定該變量的數據類型。首先看下面這條語句的執行過程:
int a = 5;
實際上面這條語句會被拆分成兩個過程執行:
(1)int a ;//創建java對象時根據該語句為變量分配內存空間;
(2)a = 5;//賦值操作會最終被提取到構造器中執行初始化操作。
實際上在java中,在使用new操作符創建一個類的實例對象的時候,開始分配空間并將成員變量初始化為默認的數值,在這里并不是指將變量初始化為在變量定義處的初始值,對于基本類型變量,默認值空值是0或false,對于引用類型變量,默認空值為null。這一切都是在構造函數執行之前,變量的真正初始化是在構造函數中執行。
【java中變量初始化的時機】
1、程序可以在3個地方對實例變量進行初始化:
(1)定義實例變量的時候指定初始值;
(2)非靜態初始化代碼塊中對實例變量指定初值;
(3)構造器中對實例變量指定初值。
雖然程序在3個地方都可對實例變量指定初始值,但(1)、(2)處的賦值操作最終會被提取到構造器中執行。且(1)、(2)的執行順序與他們在源程序中出現的順序相同。可以通過JDK的javap工具來看程序的執行過程:
2、java中類變量初始化的時機:
(1)定義類變量的時候指定初始值;
(2)靜態初始化代碼塊中對類變量指定初值。
這里類變量初始化的時機為兩處,而沒有在構造器中這一說。這也很容易理解,因為類變量是類加載的時候執行的初始化,且只執行一次,而調用構造器是對象實例化執行的,每實例化一次對象,執行一次。
第一個正式的universal項目差不多快要結束,總結一下,分享給大家。因為可能我的比較具有代表性,如何從壓根不懂開始做起。(分享的另外一個目的也是希望大家提提建議,畢竟只有互相交流中才能更快成長)
-----------------------------------------------
做項目前:
零面向對象實際項目經驗,更不用說透徹理解design pattern
零iOS實際項目經驗
只懂一點點的C和匯編代碼,其它如
java、ruby、php、HTML5一點不沾邊,可以說是不屬于
互聯網的業余玩家
項目的情況:
做項目過程中,客戶需求變化極其頻繁和巨大,對代碼結構的robust是一大挑戰。雖然本人特別討厭需求變動,但是在外,身不由己
Universal項目,即是iPhone + iPad 的一個項目
基本上這個項目涉及到了iOS的方方面面,麻雀雖小,但是五臟俱全
關于程序結構設計
嘗試用面向對象的方式去設計結構,但設計的時候流于形式,根據現有的一些best practice依葫蘆畫瓢,但實際上只有實踐了才知道,比如:
1. 屬性: 什么時候用和為什么用屬性、如何保持屬性私有、self.的使用,屬性的內存釋放;
2. 成員變量和屬性的區別
3. 方法:什么時候用類方法和對象方法
4. 好的設計真的是“增之一分則太長,減之一分則太短”;好的設計關系到以后重構的方便性
5. 解耦設計:對象之間如何通訊,如何傳值,如何回傳,如何用好notification、delegate、KVO;如何保持對象的純潔(不受玷污)
6. MVC中的M和C分離,一直覺得自己做的項目是小項目,而且一直認為過于注重結構,會增加代碼量,但是實際上項目不分大小,好的設計:
能隨時應對客戶的需求變化
能自己看得懂自己寫的代碼(改的多了,都看不懂自己的代碼了,這是最悲催)
回歸
測試,一旦客戶需求改變,亂糟糟的代碼更加亂,這樣回歸基本上是需要全部。好的設計可以把客戶需求改變帶來的回歸測試降低到最低
關于面向對象設計
之前從沒有面向對象設計的經驗,所以第一次從這種角度去解決問題。“實踐出真理”,無論你平時看多少書,如果沒有實踐過,真的是無法體會面向對象設計的:
一切從面向對象出發設計:類、對象、(私有)方法、(私有)屬性
所謂面向對象,就是根據現實世界中客觀存在的事物(即對象)出發來構造軟件系統
只有真正從面向對象去設計,幾個月甚至幾年后,你才能復盤你的代碼。以前一直覺得代碼復盤如同圍棋復盤絕對是天才才有的本領,現在才明白,其實關鍵是:你要清楚的知道你的代碼用在了哪里,為什么用
從面向對象出發,不要覺得一個功能很簡單一個方法就搞定,盡量用面向對象去考慮。這是做項目過程中犯的最大的錯誤
關于ARC
我是項目做了1個月后,才決定把項目從MRC轉到ARC,現在回頭看看,當初真實明智,因為在第一個月,內存管理上的問題和處理讓我很頭疼也很花時間。關于ARC
沒有想象中的會比MRC性能差,ARC不是JAVA的垃圾回收,性能其實與MRC基本一致
ARC中沒有明確的release操作,這時更需要注意內存管理,比如在一個Controller中使用Gyro sensor的時候,這種操作是絕對不能賦值給局部變量的:[[CMMotionManager alloc]init]
雖然ARC似乎能為你做很多事,但是有些事情自己解決還是自己解決,比如當不需要用Gyro sensor時,_motionManager = nil(此時如果不設置,則startDeviceMotionUpdatesToQueue中的更新會一直進行);
總之,對于ARC,難得糊涂中要“時刻保持覺醒”
關于Perfomrance設計
Coding真的是一點都來不得馬虎,以前一直覺得iOS性能強大,無須擔心性能,但是項目做下來,一大痛苦之處就是性能不夠:
應用程序、UIViewController和UIView的生命周期的認識如果不十分清楚,就很容易造成性能瓶頸
大量的UIView插入移除操作會導致性能問題
UITableView和UIScrollView導致滑動不順暢的best practice
關于知識點
成為一名優秀Programmer需要豐富的經驗和知識面,但是知識永遠是
學習不完的,所以要抓核心和基本,個人覺得以下幾個知識點是iOS開發必須的。至于有些比如CoreText、CoreImage等,其實等到需要用時再去學習也來得及。
內存管理,MRC和ARC
多線程,iOS下有多種多線程實現方式,什么都應該了解一下,但是除了dispatch需要精通,其它只需要看懂 (dispatch效率最高,使用最方便)
數據庫,無論是Core Data,FMDB還是基本的Sqlite,萬事不離其中,掌握Sqlite和
SQL基本語法是必須的
UIViewController、UITableViewController 和應用程序的生命周期
看似簡單但是很有深度的View之間的轉場處理,因為涉及到大量生命周期,如presentModalViewController, presentViewController, pushViewController, addSubview, removeFromSuperview, self.view....
網絡處理相關的,如何請求JSON數據,如何HTTP GET和POST
旋轉處理,特別是iOS4、iOS5、iOS6的不同處理
Debug的能力
基本的設計模式:MVC、delegate、notification、target-action
面向對象的核心思想,例如:不要以用戶無法使用或不感興趣的東西擾亂類的公有接口、類之間應該零耦合、把不相關的信息放在另一個類中
不重復造輪子
這個也不例外,iOS下的開源framework都太多了,基本上你需要的都能在Github或者Stackoverflow上找得到,所以平時:
不要做井底之蛙,平時多了解開源的框架
框架適合就行,就像爭論AFNetwork和ASIHttpNetwork更棒沒有意義的。寫程序的有兩類人,一類人追求技術極致,一類人技術只是實現產品的一種手段,我就是后面這個
關于開源框架的學習
這世界好的開源框架太多了,給我10年都看不完,所以需要選擇,就像讀書不在于都多,而在于讀精,個人推薦如下。
Three20 (其實我是不推薦的,因為它過時了,但是因為淘寶客戶端用到)
AFNetwork
MBProgressHUD
SDWebImage
關于Continuous Improvement
Six sigma中提到了持續改進,我們的能力提高也是這樣。通過讀好的開源框架是最好的進步方式。如何讀開源框架,我們讀開源框架的目的:
其中的花式寫法我們只是了解,不是我們的目的
了解作者寫框架的思路
對比自己現有的,求改進
關于設計模式
做項目前,把GOF的23種設計模式都看了一遍,項目做下了,體會到:
單看設計模式的書,純粹是無用;
設計模式的核心在于平時的有意無意的使用,因為它本身來源于實際;
能熟背23種設計模式固然是件好事,但是不能也不見得是壞事(反正我是記不住的)
欲速則不達
代碼之間往往只查一兩個字符,但性能和結局多半千差萬別,因為項目緊,壓力大,又是第一個項目,所以寫代碼的時候,追求:"meet requirement,先滿足功能,再考慮代碼結構",但是實際:
需求無論大小,代碼結構設計是必須的而且是第一位的,因為這關系到未來的改動,未來自己能否看懂;
欲速則不達,真是一個真理
關于Best Practice的重要
iOS已經很成熟了,基本上,所有問題都能找到答案,所有的需求都有現成的framework,或者只需要稍許改改。但是也正因為“萬能的internet”,所以很多答案或者很多framework都是有問題的,所以適時總結很重要:
把常用的代碼或者容易錯的代碼寫到Xcode的snippet中
要有自己的library,不是自己擺酷,而是知識需要積累,有些開發中經常會遇到的
用好的framework。不流行的框架要注意是否用了私有方法(蘋果 will reject it)
best practice,比如如何自定義TableCell,如何自定義Navigation bar
不玩花的,不玩偏門的,寫代碼就是規規矩矩,一切按照蘋果的best practice寫
面向對象的思想有很多概要,平時要時刻提醒自己
關于HTML5
iOS原生與HTML5 WEB APP天生就是一對敵人,做HTML5的可以不懂iOS開發,但是做iOS開發必須懂點HTML5:
iOS應用中一些“高度變化”或“性能要求不高”或“上線緊迫”的地方會用到UIWebView
iOS原生與UIWebView的之間交互其實也可以很棒,甚至JSP交互
HTML5是“可能”的未來,世界都在談論
HTML5看似只有一個知識點,但是其實要求比iOS原生開都高:一個典型的移動HTML5頁面 = JSP + HTML + CSS + JQuery + backbone.js。或者學習PhoneGap也是不錯的注意。
關于未來:
如何讓自己在最短的時間內成為優秀,這是每天都在思考的,因為對比別人_大學+工作下來的多年工作經驗,我是不懼任何優勢的,但是既然入行,就必須做優秀。所以選擇值得做的事尤其重要:
看書沒用,實踐和Coding是提高能力的唯一途徑;
做實際項目比自己玩玩靠譜十萬倍
壓力下工作成長更快,所以不斷挑戰自己,人的潛力是無限的
番茄工作法則比較適合我(每次集中做半個小時)
1. Bugzilla
2. Bugfree
3. TestDirector (Quality Center)
4. ClearQuest
5. JIRA
6. Mantis
7. Bugzero
8. BugTracker
9. URTracker
10.KisTracker
11.TestLink
12、JTrac
13、BugNet
14、BugOnline
15、eTraxis
一、Bugzilla(免費,跨平臺)
Bugzilla是一開源Bug Tracking System,是專門為Unix定制開發的。但是在windows平臺下依然可以成功安裝使用.
Testopia是一款和Bugzilla集成到一起的
test case management系統.
它的強大功能表現在以下幾個方面:
1. 強大的檢索功能
2. 用戶可配置的通過Email公布Bug變更
3. 歷史變更記錄
4. 通過跟蹤和描述處理Bug
5. 附件管理
6. 完備的產品分類方案和細致的安全策略
7. 安全的審核機制
10. 友好的網絡用戶界面
11. 豐富多樣的配置設定
12. 版本間向下兼容
二、BugFree(免費)
BugFree是借鑒
微軟的研發流程和Bug管理理念,使用PHP+MySQL獨立寫出的一個Bug管理系統。簡單實用、免費并且開放源代碼(遵循GNU GPL)。
三、Quality Center(商業,前身Mercury TestDirector ,跨平臺)
HP Quality Center; 提供了基于 Web 的系統,可在廣泛的應用環境下自動執行軟件質量
測試和管理。儀表盤技術使您可以了解驗證功能和將業務流程自動化,并確定生產中阻礙業務成果的瓶頸。HP Quality Center 使 IT 團隊能夠在開發流程完成前就參與應用程序測試。這樣將縮短發布時間表,同時確保最高水平的質量。
企業級的軟件質量解決方案。
IBM Rational ClearQuest 是一款強大的軟件開發測試工具。集成并自動化軟件及系統開發的業務過程。V7.0 提供增強的需求跟蹤、構建跟蹤、企業測試管理,及部署跟蹤的功能。這提供了從開發到部署的完整的審計跟蹤,并擴展了跨生命周期的可追溯性。軟件增強了開發流程并使之自動化,同時還提高了軟件生命周期的可理解性、可預測性和可控制性。
五、JIRA(商業)
JIRA是集項目計劃、任務分配、
需求管理、錯誤跟蹤于一體的商業軟件。
JIRA功能全面,界面友好,安裝簡單,配置靈活,權限管理以及可擴展性方面都十分出色。
JIRA創建的默認問題類型包括New Feature、Bug、Task和Improvement四種,還可以自己定義,所以它也一是過程管理系統。
Jira融合了
項目管理、任務管理和缺陷管理,許多著名的開源項目都采用了JIRA。
JIRA 是目前比較流行的基于Java架構的管理系統,由于Atlassian公司對很多開源項目實行免費提供缺陷跟蹤服務,因此在開源領域,其認知度比其他的產品要高得多,而且易用性也好一些。同時,開源則是其另一特色,在用戶購買其軟件的同時,也就將源代碼也購置進來,方便做二次開發。
六、Mantis(開源)
Mantis是一個基于PHP技術的輕量級的缺陷跟蹤系統,其功能與前面提及的JIRA系統類似,都是以Web操作的形式提供項目管理及缺陷跟蹤服務。在功能上可能沒有JIRA那么專業,界面也沒有JIRA漂亮,但在實用性上足以滿足中小型項目的管理及跟蹤。更重要的是其開源,不需要負擔任何費用。不過目前的版本還存在一些問題,期待在今后的版本中能夠得以完善。
七、Bugzero 免費開源,跨平臺)
BUGZERO? 是一個多功能,基于網絡 (web-based) 并在瀏覽器 (browser) 下運行的 Bug缺陷管理和跟蹤系統 (bug tracking system),可用來記錄,跟蹤,并歸類處理軟件開發過程出現的 Bug 和硬件系統中存在的缺陷(defect)。 BUGZERO 也是一個完整的服務管理軟件,包括集成服務臺熱線流程管理(Help Desk),可用來記錄各種日常事務,變更請求,和問題報告,及追蹤和處理各種客戶訊問,反饋,和意見。
BUGZERO 提供了一個可靠的中央數據庫使公司內部團隊成員以及外部客戶能在任何地點任何時間進行協調和信息交流,并且使任何記錄都有據可查。它使你省時省力。BUGZERO 不但使用方便,而且功能齊全,變通性好,能夠靈活設置各種實際
工作流程,滿足你特定的業務和產品環境下的需求。這種靈活、易用的缺陷跟蹤流程不僅增強了項目開發的質量,同時也提高了整個機構的生產效率。
八、BugTracker(免費開源,跨平臺)
Bugtracker是一個完整的bug/issue管理系統.它使用Java Servlet作為web前臺,MySQL數據庫作為后臺。
九、JTrac(開源)
JTrac是一個開源且可高度配置的問題追蹤的Web應用程序。它是一個一般性問題,跟蹤網絡應用程序,可方便地實現定制,增加自定義字段和下拉式。其特點包括可定制的工作流程,實地一級的權限,電子郵件集成,文件附件和詳細的歷史觀點。
十、BugNet(開源)
BugNet是一個不錯的開源bug跟蹤和項目管理系統
十一、BugOnline(開源)
BugOnline 是一個開源的BUG管理系統。其功能強大,易于使用。
基于asp.net2.0 ,sql server 2005(包括Express 版),Ajax等技術.
BugOnline 的一些特性:
在線消息及E-mail自動通知功能。有新Bug及Bug分配給用戶同時將自動通知用戶。
優秀的人員分配,工作量統計功能。
居于項目角色的權限管理,工作規劃,流程化。
Bug狀態統計,便于掌控項目進度。
基于SSL的數據傳輸,確保數據不被截取,保證安全性。(也可設定為非SSL)
強大的報表功能。
十二、eTraxis(開源)
eTraxis是基于網頁的免費bug跟蹤系統。主要特點是:完全自定義模板,先進的過濾器, LDAP支持,電子郵件通知,訂閱報刊,提醒,靈活的權限管理,圖形化的項目指標等。
十三、URTracker(商業)
URTracker 是一款通用的流程化的問題跟蹤管理軟件。它可以:
幫助您集中管理各種類型的問題、工作任務、人員交互等事務;
跟蹤每個事務的處理過程;
控制問題處理的流程;
提供一個有效的協作平臺,簡化團隊成員的交互,提升溝通效率;
積累知識信息;
幫助您進行統計和分析;
十四、KisTracker
KisTracker一款bug跟蹤管理軟件(原名叫:iTracker),功能強大,易于使用,是基于WEB方式的協同工作軟件.
它用于幫助公司和團隊跟蹤工作中的問題,管理和記錄這些問題的處理過程.可以應用于:
1.產品BUG跟蹤
2.任務跟蹤
3.服務跟蹤
4.各種處理問題跟蹤
十五、TestLink(開源)
TestLink用于進行測試過程中的管理,通過使用TestLink提供的功能,
可以將測試過程從測試需求、測試設計、到測試執行完整的管理起來,
同時,它還提供了好多種測試結果的統計和分析,使我們能夠簡單的開始測試工作和分析測試結果。
TestLink 是sourceforge的開放源代碼項目之一。作為基于web的測試管理系統,TestLink的主要功能包括:測試需求管理 測試用例管理測試用例對測試需求的覆蓋管理 測試計劃的制定 測試用例的執行 大量測試數據的度量和統計功能。