2011年9月26日

Android使用AnimationListener建立小程式起動動畫


目標
使用setAnimationListener製作開啟動畫。

實作
每個動畫類別都可以設置它的動畫監聽器,這個監聽器可以監聽其動畫開始、重複執行、動畫結束三個部分,而這邊我們使用的是動畫結束部分來實作啟動動畫。

Layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
    <ImageView
       android:id="@+id/img"
       android:layout_width="fill_parent"
       android:layout_height="fill_parent"
       android:src="@drawable/icon"
       android:visibility="invisible" 
       />
<TextView 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
</LinearLayout>

Activity.java
public class AppLoadingAnimationActivity extends Activity {
   private ImageView imageView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       
        imageView = (ImageView) findViewById(R.id.img);
       
        // 開啟動畫
        AlphaAnimation alphaAnimation = new AlphaAnimation((float) 0.1, 1);
        alphaAnimation.setDuration(2000);
        alphaAnimation.setAnimationListener(new AnimationListener() {
          @Override
          public void onAnimationStart(Animation animation) {
          }
         
          @Override
          public void onAnimationRepeat(Animation animation) {
          }
         
          @Override
          public void onAnimationEnd(Animation animation) {
              imageView.setVisibility(View.GONE);
          }
        });
       
        imageView.setAnimation(alphaAnimation);
        imageView.setVisibility(View.VISIBLE);
    }
}

如此一來就可以完成一個簡單的啟動動畫。

備註 : 如果你的應用是非常需要loading的請不要使用這個方法,請使用handler,確實的將結束訊號傳送並接受,這樣才能正確的結束啟動動畫。

SQL 交易



是所有資料庫系統的一個基本概念。 一次交易的要點就是它把多個步驟捆綁成了一個單一的不成功則成仁的操作。 其它並發的交易是看不到在這些步驟之間的中間狀態的,並且如果發生了一些問題, 導致該交易無法完成,那麼所有這些步驟都完全不會影響資料庫。
比如,假設一個銀行的資料庫包含各種客戶帳戶的餘額,以及每個分行的總餘額。 假設我們要記錄一次從 Alice 的帳戶到 Bob 的帳戶的金額為 $100.00 的支付動作。那麼,完成這個任務的簡單到極點的 SQL 命令象下面這樣

UPDATE accounts SET balance = balance - 100.00
WHERE name = 'Alice';
UPDATE branches SET balance = balance - 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Alice');
UPDATE accounts SET balance = balance + 100.00
WHERE name = 'Bob';
UPDATE branches SET balance = balance + 100.00
WHERE name = (SELECT branch_name FROM accounts WHERE name = 'Bob');



這些命令的細節在這兒並不重要;重要的是這裡牽涉到了好幾個獨立的更新來完成這個相當簡單的操作。 我們的銀行官員會希望要麼所有這些更新都生效,要麼全部不起作用。 我們當然不希望一次系統崩潰就導致 Bob 收到 100 塊不是 Alice 支付的錢, 也不希望 Alice 老是不花錢從 Bob 那裡拿到物品。我們需要保證:如果在操作的過程中出了差錯, 那麼所有這些步驟都不會發生效果。把這些更新組合成一個交易就給予我們這樣的保證。 交易被認為是原子的:從其它交易的角度來看,它要麼是全部發生,要麼完全不發生。
我們還需要保證:一旦一個交易完成並且得到資料庫系統的認可, 那麼它必須被真正永久地儲存,並且不會在隨後的崩潰中消失。 比如,如果我們記錄到了一個 Bob 撤單的動作, 那麼我們不希望僅僅在他走出銀行大門之後的一次崩潰就會導致對他的帳戶的扣減動作消失。 一個交易型資料庫保證一個交易所做的所有更新在交易發出完成響應之前都記錄到永久的儲存中(也就是磁盤)。
交易型資料庫的另外一個重要的性質和原子更新的概念關係密切: 當多個交易並發地執行的時候,那麼每個交易都不應看到其它交易所做的未完成的變化。 比如,如果一個交易正忙著計算所有分行的餘額總和, 那麼它不應該包括來自 Alice 的分行的扣帳和來自 Bob 分行的入帳,反之亦然。 所以交易必須是黑白分明的,不僅僅體現在它們在資料庫上產生的永久影響出發,而且體現在它們運轉時的自身的可讀性上。 一個打開的交易做的更新在它完成之前是其它交易無法看到的,而到提交的時候所有更新同時可見。
 PostgreSQL 裡,一個交易是透過把 SQL 命令用 BEGIN  COMMIT 命令包圍實現的。 因此我們的銀行交易實際上看起來像下面這樣

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
-- 等等
COMMIT;



如果在該交易的過程中,我們決定不做提交(可能是我們剛發現 Alice 的餘額是負數), 那麼我們可以發出 ROLLBACK 命令而不是 COMMIT 命令,那麼到目前為止我們的所有更新都會被取消。
PostgreSQL 實際上把每個 SQL 語句當做在一個交易中執行的來看待。 如果您沒有發出 BEGIN 命令,那麼每個獨立的語句都有一個隱含的 BEGIN 和(如果成功的話) COMMIT 語句包圍在周圍。 一組包圍在 BEGIN COMMIT 語句中間的語句有時候被稱做交易
注意一些客戶庫自動發出 BEGIN  COMMIT, 因此您可能不需要特意請求就可以獲取交易的效果。查看您使用的接口的文件。
我們可以透過使用 savepoints 的方法,在一個交易裡更加精細地控制其中的語句。 保存點允許您有選擇性地拋棄交易中的某些部分,而提交其它剩下的。 在用 SAVEPOINT 定義了一個保存點後,如果需要,您可以使用 ROLLBACK TO 回滾到該保存點。 則該交易在定義保存點到回滾到它之間的所有資料庫更改都被拋棄,但是在保存點之前的修改將被保留。
在回滾到一個保存點之後,這個保存點仍然保存著其定義,所以您可以回滾到這個位置好幾次。 當然,如果您確信您不需要再次回滾到一個保存點,那麼您可以釋放它,這樣系統可以釋放一些資源。 要記住:釋放或者回滾到一個保存點都會自動釋放在其後定義的所有保存點。
所有這些都發生在一個交易內部,所以所有這些都不可能被其它交易會話看到。 當且僅當您提交了這個交易,這些提交了的動作才能以一個但願的方式被其它會話看到, 而回滾的動作完全不會再被看到。
還記得我們的銀行資料庫嗎?假設我們從 Alice 的帳戶上消費 $100.00, 然後給 Bob 的帳戶進行貸記加款,稍後我們發現我們應該給 Wally 的賬號貸記加款。 那麼我們可以像下面這樣的保存點來做:

BEGIN;
UPDATE accounts SET balance = balance - 100.00
    WHERE name = 'Alice';
SAVEPOINT my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Bob';
-- 呀!加錯錢了,應該用 Wally 的賬號
ROLLBACK TO my_savepoint;
UPDATE accounts SET balance = balance + 100.00
    WHERE name = 'Wally';
COMMIT;



這個例子當然是實在太簡單了,但是透過使用保存點,我們可以對交易有大量的控制。 並且,ROLLBACK TO是除了交易全部回滾,重新來過之外的唯一可用的, 用於重新控制一個因錯誤而被系統置於退出狀態下的交易的方法。

(轉譯自 PostgreSQL 中國 製作的簡體中文版本)


Resin使用Restlet建構Restful應用



研究目的
使用rest的方式減少網址複雜度。

Servlet的限制
2003年末,Jetty Web容器的作者、Servlet規範的貢獻者:Greg Wilkins在其博客上對Servlet的問題進行了如下總計:
    *
沒有對協議與應用之間的關係進行清洗的劃分。
    *
由於在設計Servlet時存在對阻塞IO的假設,因此不能充分利用非阻塞NIO機制。
    *
所有的Servlet Web容器對於某些應用來講是過度設計的。

他提出構思新的API規範,使其能夠真實地脫離協議,並定義能夠暴露內容和中繼資料的contentlets。這些想法就是Restlet項目創建的靈感源泉。在
之後的文章中Greg Wilkins解釋了為什麼當前Servlet API限制非阻塞NIO API得到高效使用的詳細理由:這種傳統的用法針對每個HTTP請求都創建獨立的執行緒進行處理。並提出了他對下一代Servlet技術的設想

Restlet簡介
當複雜核心化模式日趨強大之時,物件導向設計範例已經不總是Web開發中的最佳選擇,Java開發者需要認識到這一點,並且在開發新的Web服務端或是AJAX Web用戶端時開始思考更加RESTfully的設計。Restlet這個開源專案為那些要採用REST結構體系來構建應用程式的Java開發者提供了一個具體的解決方案。它的非常簡單易用的功能和RESTfullyWeb框架,這使其成為了Web2.0開發中的又一利器。好吧,朋友們,下面就讓我們開始Restlet探索之旅吧!


REST架構概述
讓我們先從REST的視角審視一下典型的web架構。在下面的圖表中,埠代表了connector,而後者負責component之間的通訊(元件在圖中被表示為大盒子)。連結代表了用於實際通訊的特定協定(HTTPSMTP)



請注意,同一個component能夠具有任何數量的用戶端/服務端connector。例如,Web伺服器B就具有一個用於回應使用者代理元件(User Agent component)的服務端connector,和多個發送請求到其它服務端的用戶端connector

Componentvirtual hostsapplications
另外,為了支援前面所表述的標準REST軟體架構元素,Restlet框架也提供了一套類:它們極大地簡化了在單一JVM中部署多個應用的工作。其目的在於提供一種RESTful、可移植的、比現存的Servlet API更加靈活的框架。在下面的圖表中,我們將看到三種Restlet,它們用於管理上述複雜情況:Components能夠管理多個Virtual HostsApplicationsVirtual Hosts支援靈活的配置,例如同一個IP位址能夠分享多個功能變數名稱、使用同一個功能變數名稱實現跨越多個IP位址的負載均衡。最後,我們使用應用去管理一套相關的 RestletResourceRepresentations另外,應用確保了在不同Restlet實現、不同Virtual Hosts之上的可攜性和可配置性。這三種Restlet的協助為我們提供了眾多的功能:譬如訪問日誌、請求自動解碼、配置狀態頁設置等。




為了展示這些類,讓我們嘗試一個簡單的示例。首先,我們創建一個component,然後在其上添加一個HTTP服務端connector,並偵聽 8182埠。接著創建一個簡單的、具有追蹤功能的Restlet,將它放置到元件預設的Virtual Hosts上。這個預設的主機將捕捉那些沒有路由到指定Virtual Hosts的請求(詳見Component.hosts屬性)。在後面的一個示例中,我們還將介紹應用類的使用方法。請注意,目前你並不能在控制台輸出中看到任何的訪問日誌。

// Adds a new server connector in the map supporting the given protocol on the specified port.
component.getServers().add(Protocol.HTTP, 8182);

// Create a new tracing Restlet
Restlet restlet = new Restlet() {
    @Override
    public void handle(Request request, Response response) {
        // Print the requested URI path
        String message = "Resource URI  : " + request.getResourceRef()
                + '
' + "Root URI      : " + request.getRootRef()
                + '
' + "Routed part   : "
                + request.getResourceRef().getBaseRef() + '
'
                + "Remaining part: "
                + request.getResourceRef().getRemainingPart();
        response.setEntity(message, MediaType.TEXT_PLAIN);
    }
};

// Then attach it to the local host
// Attaches a target Restlet to this router with an empty URI pattern.
component.getDefaultHost().attach("/trace", restlet);

// Now, let's start the component!
// Note that the HTTP server connector is also automatically started.
component.start();



URI重寫和重定向
Restlet框架的另一個優點是對cool URI的內建支持。Jacob Nielsen他的AlertBox中給出了對URI設計的重要性的絕佳描述
首先介紹的工具是Redirector,它能夠將cool URI重寫為另一個URI,並接著進行相應的自動重定向。這裡支持一些重定向類型:通過用戶端/流覽器的外部重定向、類似代理行為的connector重定向。在下面的例子中,我們將基於Google為名為"mysite.org"的網站定義一個檢索服務。與URI相關的"/search"就是檢索服務,它通過"kwd"參數接收一些檢索關鍵字:

// Create an application
Application application = new Application(component.getContext()) {
    @Override
    public Restlet createRoot() {
        // Create a Redirector to Google search service
        String target =
           "http://www.google.com/search?q=site:mysite.org+{keywords}";
        return new Redirector(getContext(), target,
                Redirector.MODE_CLIENT_TEMPORARY);
    }
};

// Attach the application to the component's default host
Route route = component.getDefaultHost().attach("/search", application);

// While routing requests to the application, extract a query parameter
// For instance :
// http://localhost:8182/search?kwd=myKeyword1+myKeyword2
// will be routed to
// http://www.google.com/search?q=site:mysite.org+myKeyword1%20myKeyword2
route.extractQuery("keywords", "kwd",
true);


請注意,Redirector只需要三個參數。第一個參數是父級上下文,第二個參數定義了如何基於URI範本重寫URI。這裡的URI範本將被Template類處理。第三個參數定義了重定向類型:出於簡化的目的,我們選擇了用戶端重定向。

同時,當調用被傳遞給application時,我們使用了Route類從request中提取查詢參數“kwd”。如果發現參數,參數將被複製到request“keywords”屬性中,以便Redirector在格式化目標URI時使用。

路由器和分層URI
作為Redirector的補充,我們還具有另一個管理cool URI的工具:Router(路由器)。它們是一種特殊的Restlet,能夠使其它Restlet(例如FinderFilter)依附於它們,並基於URI範本進行自動委派調用(delegate call)。通常,你可以將Router設置為Application的根。

這裡,我們將解釋一下如何處理下面的URI範本:

   1. /docs/
用於顯示靜態檔
   2. /users/{user}
用於顯示使用者帳號
   3. /users/{user}/orders
用於顯示特定使用者的所有訂單
   4. /users/{user}/orders/{order}
用於顯示特定的訂單

實際上,這些URI包含了可變的部分(在大括弧中)並且沒有檔副檔名,這在傳統的web容器中很難處理。而現在,你只需要做的只是使用URI範本將目標Restlet附著到Router上。在Restlet框架運行時,與requestURI最為匹配的Route將接收調用,並調用它所附著的 Restlet。同時,request的屬性工作表也將自動更新為URI範本變數。





請看下面的具體實現代碼。在真實的應用中,你可能希望創建單獨的子類來代替我們這裡使用的匿名類:

// Create a component
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
component.getClients().add(Protocol.FILE);

// Create an application
Application application = new Application(component.getContext()) {
    @Override
    public Restlet createRoot() {
        // Create a root router
        Router router = new Router(getContext());

        // Attach a guard to secure access to the directory
        Guard guard = new Guard(getContext(),
                ChallengeScheme.HTTP_BASIC, "Restlet tutorial");
        guard.getSecrets().put("scott", "tiger".toCharArray());
        router.attach("/docs/", guard);

        // Create a directory able to expose a hierarchy of files
        Directory directory = new Directory(getContext(), ROOT_URI);
        guard.setNext(directory);

        // Create the account handler
        Restlet account = new Restlet() {
            @Override
            public void handle(Request request, Response response) {
                // Print the requested URI path
                String message = "Account of user \""
                        + request.getAttributes().get("user") + "\"";
                response.setEntity(message, MediaType.TEXT_PLAIN);
            }
        };

        // Create the orders handler
        Restlet orders = new Restlet(getContext()) {
            @Override
            public void handle(Request request, Response response) {
                // Print the user name of the requested orders
                String message = "Orders of user \""
                        + request.getAttributes().get("user") + "\"";
                response.setEntity(message, MediaType.TEXT_PLAIN);
            }
        };

        // Create the order handler
        Restlet order = new Restlet(getContext()) {
            @Override
            public void handle(Request request, Response response) {
                // Print the user name of the requested orders
                String message = "Order \""
                        + request.getAttributes().get("order")
                        + "\" for user \""
                        + request.getAttributes().get("user") + "\"";
                response.setEntity(message, MediaType.TEXT_PLAIN);
            }
        };

        // Attach the handlers to the root router
        router.attach("/users/{user}", account);
        router.attach("/users/{user}/orders", orders);
        router.attach("/users/{user}/orders/{order}", order);

        // Return the root router
        return router;
    }
};

// Attach the application to the component and start it
component.getDefaultHost().attach(application);
component.start();


請注意,變數的值是直接從URI中提取的,因此這是沒有精確解碼的。為了實現這樣的工作,請查看手冊中的decode(String)方法

實作
Resin配置方法 :
1.          首先下載Restlet1.1.9版本
2.          將裡面lib包中的 org.restlet.jar , com.noelios.restlet.jar , com.noelios.restlet.ext.servlet_2.5.jar放入專案目錄的/lib以及/resin ext-lib資料夾。
3.          然後修改resin/wapps/專案/WEB-INF/web.xml檔案,新增以下

<context-param> 
    <param-name>org.restlet.component</param-name> 
    <param-value>com.firstResource.RestComponent</param-value> 
</context-param>    

<servlet>
    <servlet-name>RestletServlet</servlet-name>
    <servlet-class>com.noelios.restlet.ext.servlet.ServerServlet 
    </servlet-class>
</servlet>

<servlet-mapping>
    <servlet-name>RestletServlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

4.          其中RestComponent就是所有Application 管理者,用來分配uri給相對的Appication
5.          建立RestComponent

public class RestComponent extends Component {
   public RestComponent() {
      getServers().add(Protocol.HTTP, 8080); 
      getDefaultHost().attach("/member",
             new MemberApp(getContext()));
      getDefaultHost().attach("/circle",
             new CircleApp(getContext()));
      getDefaultHost().attach("/calculate",
             new CalculateApp(getContext()));
   }
}

MemberApp(Application)

   @Override
   public synchronized Restlet createRoot() {
      // Create a router Restlet that defines routes.
      Router router = new Router(getContext());
      router.attachDefault(MemberProfileResource.class);
      router.attach("/{value}/photo", MemberPhotoResource.class);
      router.attach("/{value}/profile", MemberProfileResource.class);
      router.attach("/{value}/company", MemberCompanyResource.class);
     
      return router;
   }


Application可以在將近來的url再分派給底下的Resource類別實作。
/{value}/profile{}表示這個url的可變數,後面以/profile結尾

MemberProfileResource(Resource)

public class MemberProfileResource extends Resource {

   String value;
  
   public MemberProfileResource(Context context, Request request,
          Response response) {
      super(context, request, response);
      this.value = (String) getRequest().getAttributes().get("value");
      // This representation has only one type of representation.
      getVariants().add(new Variant(MediaType.TEXT_PLAIN));
   }

   /**
    * Returns a full representation for a given variant.
    */
   @Override
   public Representation getRepresentation(Variant variant) {
      byte[] ba_mrscid = BytesUtil.hexToBytes(value);
     
      Representation representation = new StringRepresentation(
             "test", MediaType.TEXT_PLAIN);
      return representation;
   }
}


可以使用getRequest().getAttributes().get("value");
取得url命名之變數,這裡是得到value的值,然後將其存入類別裡的成員變數中,最後Resource會在getRepresentation中實作回應的內容,這邊我們就使用value作為faceboss的會員hex_mrscid,透過MemberInfoKeeper取得會員資料。

2011.09.26 補充 :
如果中文顯示亂碼請在representation中加入下面程式碼
representation.setCharacterSet(CharacterSet.UTF_8);


取得url參數(parameter)方法
for (Parameter parameter : form) {
   if(parameter.getName().equals("value")){
   }
}




本文參考於cleverpig作為貢獻者完成了本文的翻譯和整理工作,在這裡為其說明在resin上坐實作並推廣,有興趣的人可以到restlet官方上參考英文文件。


ShareThis