2013年7月30日 星期二

Java NIO - Buffer基本function介紹flip()、rewind()、clear()、compact()、mark()、reset()


參考:Java NIO Buffer
搭配此範例圖參考:

Write data to a Buffer:
  1. Write data from a Channel into a Buffer:
    int bytesRead = inChannel.read(buf); //read into buffer.
  2. 自行使用put()之類的方式寫入
    buf.put(127);
Reading data from a Buffer:
  1. Read data from the buffer into a channel.
     
    //read from buffer into channel.
    int bytesWritten = inChannel.write(buf);
    
  2. 自行使用get()之類的方式讀取:
     
    byte aByte = buf.get();
    
  • flip()
    將Butter由write mode轉變為read moode,並將
    (1)limit設為當前position位置
    (2)position reset to 0
  • rewind()
    將position設定回0,但limit保持不變,因此可重新read所有的資料

  • clear()與compact():
  • clear()
    將position設定回0,limit設定至capacity,然而Buffer中資料並不是真的被清除,只是將這些mark改變,讓我們可以有足夠的空間重新寫入資料。但是請注意,雖然資料沒有被清除,但是因為mark被重置了,因此unread的資料將無法再次被讀取,若目前尚有未讀取資料但又需要在讀取之前write data,則可使用compact()代替clear()
  • compact()

  • mark()與reset():
  • mark():使用mark()則可將目前的position做標記(mark)
  • reset():將position 設定回之前所設定的標記(mark)處
buffer.mark();
//call buffer.get() a couple of times, e.g. during parsing.
buffer.reset();  //set position back to mark.

equals()與compareTo():


  • equals():滿足以下條件則2個buffer代表equals
    (1)type相同(char、byte、int etc..)
    (2)剩餘的【未讀】buffer大小(byte)相同,
    (3)剩餘的【未讀】buffer內容皆相同
    如上所述,equals()只比較剩餘【未讀】的buffer內容,並非整個buffer的內容
  • compareTo():與equals()相同,只比較剩餘的【未讀】buffer內容,通常做為排序使用;符合以下的情況代表【smaller】:
    (1)
    (2)所有的內容皆相同,但擁有較少的內容(?)

2013年7月27日 星期六

Java NIO - ByteBuffer基本屬性介紹 capacity、limit、position


參考:Java NIO Buffer

Once you need to read the data, you need to switch the buffer from writing mode into reading mode using the flip() method call

Once you have read all the data, you need to clear the buffer, to make it ready for writing again. You can do this in two ways: By calling  or by calling compact().

  1. clear() :clears the whole buffer
  2. compact():only clears the data which you have already read. Any unread data is moved to the beginning of the buffer, and data will now be written into the buffer after the unread data.
ByteBuffer實際上操作方式為:先write再read

三個ByteBuffer屬性(可參考下面的圖來理解):
The meaning of position and limit depends on whether the Buffer is in read or write mode. Capacity always means the same, no matter the buffer mode.




  1. capacity
    代表ByteBuffer的容量大小,一但滿了除非將他清空或是read,不然無法繼續寫入資料
  2. position
    1. write mode:起始時position index為0,隨者每一次寫入資料position位置都會變動,如寫了1byte則postion會index+1,index最多不能超過capacity -1
              read mode:當使用flip()將操作模式由write改為read,position會自動被reset至0,然               後隨者每一次read資料量不同position位置將會自動前進
  1. limit
  2.          write mode:代表共可寫入多少資料量,在此模式中香等於capacity      
             read mode:代表共有多少資料量可讀取,當使用flip()將操作模式由write改為read,
             limit會被設置於position的目前所在位置
建立一個ByteBuffer使用allocate:
ByteBuffer buf = ByteBuffer.allocate(48);
其中的int參數就是代表欲建立的capacity大小。


相關function使用方式待續....

2013年7月20日 星期六

Java IO: Exception Handling - 不安全的IO Exception處理(2) - 使用try-with-resources

參考:Try-with-resources in Java 7  、 try-with-resources

請注意這個機制只有java 7後才適用

上篇,我們這次來看看java7的新機制(同樣是參考文章後的心得形式,若需詳細請直接連入上方參考文章連結)。


先假設下面的code:
 

public class TryWithResource implements AutoCloseable {

 private FileInputStream _input;

 public void doIt() throws WrapperException, FileNotFoundException {
  _input = new FileInputStream("datasheet/file.txt");
  throw new WrapperException("doIt has an Exception!!!");
 }

 @Override
 public void close() throws IOException, WrapperException {
  _input.close();
  throw new WrapperException("close has an Exception!!!");
 }
 }

以下列方式呼叫使用:
 
 public static void withoutTryWithResources() throws WrapperException {
  TryWithResource tt = new TryWithResource();
  try {
   tt.doIt();
  } catch (Exception ex) {
   throw new WrapperException(ex);
  } finally {
   try {
    tt.close();
   } catch (IOException e) {
    throw new WrapperException(e);
   }
  }
 }
 public static void main(String[] args) {
  try {
   withoutTryWithResources();
  } catch (WrapperException wex) {
   wex.printStackTrace();
  }
 }

會有以下結果:
Exception in thread "main" java.lang.IndexOutOfBoundsException
exception.WrapperException: close has an Exception!!!
 at exception.failsave.TryWithResource.close(TryWithResource.java:12)
 at exception.failsave.TryWithResource.withoutTryWithResources(TryWithResource.java:32)
 at exception.failsave.TryWithResource.main(TryWithResource.java:49)

(上述的exception 行號每人都會不一樣)
執行流程如下:

  1. doIt()
  2. throw new WrapperException("doIt...")
  3. first catch(Exception ex) block
  4. finally block
  5. close()
  6. throw new WrapperException("close...")

最始祖的exception為應該為doIt()中出現的,但如同前一篇所說,始祖exception會被吃掉,因此,到了步驟6原先的始祖exception(步驟2)就消失了,這造成我們難以在第一時間發現最根本的問題點,因此我們可以使用java7的新機制,修改如下:

 
 public static void withResources() throws WrapperException {
  try (TryWithResource tt = new TryWithResource()) {
   tt.doIt();
  } catch (Exception e) {
   throw new WrapperException(e);
  }
 }

 public static void main(String[] args) {
  try {
//   withoutTryWithResources();
   withResources();
  } catch (WrapperException wex) {
   wex.printStackTrace();
  }
 }

而這次的exception 顯示為:
exception.WrapperException: exception.WrapperException: doIt has an Exception!!!
 at exception.failsave.TryWithResource.withResources(TryWithResource.java:43)
 at exception.failsave.TryWithResource.main(TryWithResource.java:50)
Caused by: exception.WrapperException: doIt has an Exception!!!
 at exception.failsave.TryWithResource.doIt(TryWithResource.java:15)
 at exception.failsave.TryWithResource.withResources(TryWithResource.java:41)
 ... 1 more
 Suppressed: exception.WrapperException: close has an Exception!!!
  at exception.failsave.TryWithResource.close(TryWithResource.java:21)
  at exception.failsave.TryWithResource.withResources(TryWithResource.java:42)
  ... 1 more

(再一次提醒,exception中的行號每人不同)
上述流程為:
  1. doIt()
  2. throw new WrapperException("doIt...")
  3. immediately call close() 
  4. close()
  5. Suppressed:throw new WrapperException("close...")  
  6. throw WrapperException("doIt...") (step 2)

不僅少了很多恐怖的try catch跟finally還很清楚地馬上就知道始祖exception問題處。
注意其中的Suppressed: exception.WrapperException: close has an Exception!!!(步驟5)
由於使用了try-with-resources機制,因此後面發生在close()中的exception就會被suppress(壓制)。

而try-with-resources機制也可以同時使用多個:
 
private static void printFileJava7() throws IOException {

    try(  FileInputStream     input         = new FileInputStream("file.txt");
          BufferedInputStream bufferedInput = new BufferedInputStream(input)
    ) {

        int data = bufferedInput.read();
        while(data != -1){
            System.out.print((char) data);
    data = bufferedInput.read();
        }
    }
}

如果發生了問題,則destructor (就是呼叫close())的順序為反向的:

  1. bufferedInput.close()
  2. input.close()

另外還有一個限制,使用在try-with-resources都必須在try(...)中宣告,而不能使用下列方式:
 
private static void printFileJava7() throws IOException {
   FileInputStream     input = null;
   BufferedInputStream bufferedInput = null;
    try(  input         = new FileInputStream("file.txt");
          bufferedInput = new BufferedInputStream(input)
    ) {

        int data = bufferedInput.read();
        while(data != -1){
            System.out.print((char) data);
    data = bufferedInput.read();
        }
    }
}


因此只要implement AutoCloseable 這個interface,除了原生java的class以外,自己實作的class也同樣可以享受try-with-resources 的好處呢!

以後解決這種惱人的io問題總算有解了!!exception handling真是個很重要的課題阿!!該去買文章作者的kindle電子書了Java Exception Handling
話說好便宜阿作者真是佛心來者...

Java IO: Exception Handling - 不安全的IO Exception處理

參考:Fail Safe Exception Handling

其實這篇等於是把參考文章直接以自己說法再寫一次當作心得筆記,想看詳細的話看上述參考文章比較妥當


處理IO時常常發生個惱人的問題,假設下列code:
 
public class WrapperException extends Exception {

 public WrapperException(IOException e) {
  super(e);
 }
}

 
InputStream input = null;

  try{

    input = new FileInputStream("myFile.txt");

    //do something with the stream

  } catch(IOException e){
    throw new WrapperException(e);
  } finally {
    try{
     input.close();
    } catch(IOException e){
       throw new WrapperException(e);
    }
  }

假設"myFile.txt"這個檔案並不存在,那麼上述Code執行如下:

  1. throw java.io.FileNotFoundException() (at FileInputStream() constructor)
  2. catch(IOException e) block,rethrow WrapperException
  3. finally block
  4. 由於input未初始化成功,因此再次被finally中的try catch捕捉,然而這次拋出的為NullPointerException
因此上述的exception顯示如下:

Exception in thread "main" java.lang.NullPointerException
 at exception.FailSaveException.failSave(FailSaveException.java:30)
 at exception.FailSaveException.main(FailSaveException.java:15)

原先造成exception的始祖,最該被拋出的FileNotFoundException()就這樣默默的被吃掉了....
解決方式是:在finally的input.clode中加入null check:
if(input != null) 
   input.close();

然而讓我們假設另一個情況:
  假設檔案存在,input順利被建立,但在try catch中處理內容時因為某些原因再度造成io exception,此時順序會變為:

  1. throw some IOException in first try catch block
  2. first try catch catch it ,rethrow WrapperException
  3. finally block
  4. rethrow WrapperException again.....if input.close() also error
然而要是執行到步驟3時,input.close()又再次因為某些不知明原因,再度throw IOException,則就會多了步驟4,再一次的,最關鍵的始祖IOException又被吃掉(步驟1)!!
(上述假設無example code,推測是因為很難馬上弄出個簡單的input.close()也出問題的範例)
而為了解決上述問題,讓我們這些苦命developer可以在第一時間就發現始祖exception,Java 7帶來了一個叫 try-with resource的機制。

詳情請見下篇介紹XD

2013年7月19日 星期五

hsqldb - 如何擴展沒有的FUNCTION以適應其他sql ?

參考:src_jrt_routines

寫Test的時候遇到一個問題,我想在我的SQL Query中使用 LEN()這個mssql function:

 
  SELECT LEN(str) FROM TABLE where name='123'

然而由於寫Test時使用的並非mssql而是使用hsqldb,hsqldb並不支援LEN(),
而是使用LENGTH(),如果將我的SQL Query改為 LENGTH(),在正式上線時又會遇到
MS-SQL並沒有LENGTH()的問題....因此該怎麼做比較好呢??

原來hsqldb有擴展function的機制,就是為了預防這個問題的產生:

  1. create一個擴展的java class如下(注意function一定得為static,詳細使用對應的參數型別請參考上述參考頁):
  2.  
    package util.extend.mssql;
    
    public class MSSql {
     public static int len(String str) {
      return str.length();
     }
    }
    
  3. 並於實際執行有用到LEN的SQL Query的地方之前,使用下列SQL Query
     
          CREATE FUNCTION  len(str VARCHAR(20000))
            RETURNS INT 
     LANGUAGE JAVA DETERMINISTIC NO SQL
     EXTERNAL NAME 'CLASSPATH:util.extend.mssql.MSSql.len'
    

  4. 以上若完成,執行test,應該就能成功囉
這機制真是太方便了!!!

p.s話說回來完全找不到相關的中文討論阿....是說寫Test的人真的很少還是高手都直接找英文了呢....0rz...網路上查到的方式Create alias也是過時的方式,害我花了不少時間找...

2013年7月14日 星期日

android - layout (暫時存放)

[Android]Android Layout元件

[Android] layout_weight的妙用-讓View的大小以百分比率顯示(proportionate size)

mssql - 刪除或編輯現有資料時出現 "沒有刪除任何資料列。 嘗試刪除資料列3時發生問題....."


參考:資料重複無法修改或刪除
今天使用ms-sql時遇到個問題,刪除某些現有的資料列與編輯,都會出現下列錯誤:

沒有刪除任何資料列。
嘗試刪除資料列3時發生問題。
錯誤來源:Microsoft VisualStudio.DataTools。
錯誤訊息:已更新或刪除的資料列值不會使資料列成為唯一,而是會修改多個資料列(2個資料列)。

解答是:應該要設定PK,這樣在編輯/刪除全部相同的資料列時,資料庫才不會因為無法識別修改而產生錯誤

2013年7月13日 星期六

android - 如何在目標頁面有redirect的情形下判斷頁面是否確實loading finished?

參考:How can I know that my WebView is loaded 100%?

遇到一個需求:需要在頁面讀取完成後通知使用者(使用Alert或是Toast)

一開始只是很簡單的implement WebViewClinet中的:
@Override
public void onPageFinished(WebView view, String url)

onPageFinished()可以在Page 讀取完畢後獲得通知,但發現一個問題,若這個頁面進行多次(1次以上)的redirect,那麼,每redirect一次,就會notify一次onPageFinished(),這似乎無法達成我想要的動作。

假設我的頁面的流程如下:
  1. 開啟 http://DNS/app/test.jsp
  2. redirect to http://DNS/app/afterTest.jsp       
直接以Logcat Log進行流程如下:
  1. onPageStarted() http://DNS/app/test.jsp
  2. shouldOverrideUrlLoading()  http://DNS/app/afterTest.jsp 
  3. onPageFinished() http://DNS/app/afterTest.jsp
  4. onPageStarted()  http://DNS/app/afterTest.jsp
  5. onPageFinished() http://DNS/app/afterTest.jsp
因此原先只有直接implement  onPageFinished()的作法會導致還沒進行到最後的afterTest.jsp就直接通知使用者;網路上找到建議作法如下,我自己也順利成功:

 
boolean loadingFinished = true;
boolean redirect = false;

mWebView.setWebViewClient(new WebViewClient() {

   @Override
   public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
       if (!loadingFinished) {
          redirect = true;
       }

   loadingFinished = false;
   webView.loadUrl(urlNewString);
   return true;
   }

   @Override
   public void onPageStarted(WebView view, String url) {
        loadingFinished = false;
        //SHOW LOADING IF IT ISNT ALREADY VISIBLE  
    }

   @Override
   public void onPageFinished(WebView view, String url) {
       if(!redirect){
          loadingFinished = true;
       }

       if(loadingFinished && !redirect){
         //HIDE LOADING IT HAS FINISHED
       } else{
          redirect = false; 
       }

    }
});



利用redirect前會先notify shouldOverrideUrlLoading()來判定是否為redirect,若是,則在onPageFinished()時就不代表結束;而在每一次onPageFinished()時reset redirect flag;因此,上述範例的流程的flag變化如下:

loadingFinished=true, redirect=false
  1. onPageStarted() http://DNS/app/test.jsp 
    loadingFinished=false  , redirect=false
  2. shouldOverrideUrlLoading()  http://DNS/app/afterTest.jsp
    loadingFinished=false  , redirect=true
  3. onPageFinished() http://DNS/app/afterTest.jsp
    loadingFinished=false  , redirect=false
  4. onPageStarted()  http://DNS/app/afterTest.jsp
    loadingFinished=false  , redirect=false
  5. onPageFinished() http://DNS/app/afterTest.jsp
    loadingFinished=true  , redirect=false
解決^^

android - 使用WebViewClient時一直 onReceivedError() :-1,發生網路錯誤


檢查希望執行的url並沒有任何問題,後來發現是AndroidManifest.xml中忘記開啟permission:

<uses-permission android:name="android.permission.INTERNET" />

加上後OK順利運作

android - R.java沒有自動產生

就算使用project->clean依然無自動產生,
這時需要檢查res下的.xml或是AndroidManifest.xml的配置是否有錯誤,例如<activity>下的android:name的package是否有錯誤

AndroidManifest.xml的錯誤會顯示於Console中

2013年7月12日 星期五

android - eclipse debug時出現 INSTALL_PARSE_FAILED_NO_CERTIFICATES

[2013-07-12 19:35:48 - XXXProject] Installation error: INSTALL_PARSE_FAILED_NO_CERTIFICATES
[2013-07-12 19:35:48 - XXXProject] Please check logcat output for more details.
[2013-07-12 19:35:48 - XXXProject] Launch canceled!

若有include 3rd的.jar 有可能是因為.jar中有與現有專案重複名稱的檔案存在(ex: resource file)

2013年7月10日 星期三

android初體驗 - 如何將lib打包為.jar後還能存取到其中的activity與resource


繼上一篇的使用Lib
前情提要:
最近因為案子需要開始接觸android,完全沒碰過android的我只能硬者頭皮上架,第一個就遇到問題:
因為我的lib有activity,所以如果直接做成.jar供Client呼叫,如下方式:

 
Intent intent = new Intent(v.getContext(),  LibMainActivity.class);
intent.putExtra("userid", "breeze");
intent.putExtra("facextra", "server_1");
startActivity(intent);

會在執行時出現 classdefnotfound exception!
後來找了一下文章,官方提到以下面的方式使用lib:(看友方(?)的使用文件也是一樣方式)
Setting up a Library Project

但很明顯的..這個等於完全將我的垃圾Code 曝露給其他人看到了啊!!(雖然說直接給jar照樣能反組...但至少沒那麼蠢....0rz..)

然後後來看到facebook sdk也是一樣這樣做法,心想可能真的就得如此做了吧,再加上隔天就要交差實在找不到解法的情況下,第二天只好這樣子交給廠商了,結果......

第二天廠商表明他們使用U3D開發,不是純android project,所以無法這樣include Lib,但是因為我真的不懂要怎麼弄成jar還能順利將activity成功啟動...因此他就自己做掉了,厚者臉皮詢問下他跟我說只要將R.id.xxx的resource拿掉改成如下方式:

 
 public static int getLayoutId(Context ctx,String paramString){
  return ctx.getResources().getIdentifier(paramString, "layout", ctx.getPackageName());
 }
 
就可以了,我照者試做,的確把所有的R.id.xxx的引用點改掉,原先為:

 
TextView text = (TextView) findViewById(R.id.noticeText);

改為:
 
  int rid = ResourceUtil.getId(LibMainActivity.this,"noticeText");
  TextView text = (TextView) findViewById(rid);

的確可以運作成功,可是!!!打包成.jar一樣會有錯誤阿!!!
找了半天終於找到解答了,簡單說明就是(明明打了很多廢話還簡單說明...)

  1. 將所有R.id.xxx的引用方式改為上述使用
  2. 所有資源檔的名字不能重複:例如預設的activity_main.xml就最好不要使用,因為使用的client也有可能很偷懶用了預設名稱
  3. 將所有lib會使用到的activity在Client的AndroidManifest.xml中也一併宣告,請參考下方的Declaring library components in the manifest file
  4. lib打包成jar時,除了選擇src以外其他都不能選擇
  5. 將打包好的jar置放(貼)於client project的 libs/ 底下
  6. 將所有會引用到的resource一併手動複製貼到client端的resource對應位置(資料夾下),例如res/layout/...
所以這麼一來雖然每次release時都要給出lib.jar與resource,但的確感覺比直接給出整個lib project要來的好得多了。
詳細圖文請參考Android将Activity打成jar包供第三方调用(解决资源文件不能打包的问题)

p.s剛剛才發現這篇文章是2013/7/6發布的耶!!!今天是2013/7/10,我真要感謝他剛好在我有問題前幾天發文...

2013年7月8日 星期一

android - import Library 後無法成功invoke Lib中的Activity


  1. 出現NoSuchFieldError: xxxxxR$.id
    尋找 res/下自動產生的<libpackage>\R.java 發現少了幾個id,搞半天才知道原來是因為Lib中的最主要起始Activity我使用eclipse預設的MainActivity,連帶layout下的activity_main.xml名字也是預設的;然後我使用Lib的project也都用預設名稱....所以無法自動產生activity_main.xml中定義的任何id。
         因此將Lib中的MainActivity、activity_main.xml更名,重新clean後就可以產
  1. Console出現 Found (n) versions of android-support-v4.jar in the dependency list,
    ....All versions of the libraries must be the same at this time.

    將lib中的 libs\android-support-v4.jar 砍掉,把使用專案的android-support-v4.jar貼過去,因為必須要同樣的才能使用
  2. 記得重新加入Lib匯入方式參考:匯入SDK LIB方式   FB的也可以看一下

2013年7月6日 星期六

dbunit初體驗 - 從現有XML DATA中過濾出需要的資料

之前使用的QueryDataSet可以下SQL Query來SELECT出想要的資料(請見此篇),
然而QueryDataSet好像只能接受從connection資料來建立...這不符合我的需求,找到一個好方法,就是使用RowFilterTable 搭配IRowFilter,直接來看code:


 public static ITable filterXmlDataCol(
   String xmlDataFile, String tableName,IDatabaseConnection conn
   , final String expectColNames[] , final Object expectColVals[]) throws Exception {
  
  IDataSet databaseDataSet = getDataSetFromXmlFile(xmlDataFile);
  ITable actualTable = databaseDataSet.getTable(tableName);
  
  IRowFilter rowFilter = new IRowFilter() {
   @Override
   public boolean accept(IRowValueProvider rowValueProvider) {
    try {
     for(int i=0 ; i< expectColNames.length ; ++i){
      Object columnValue = rowValueProvider.getColumnValue(expectColNames[i]);
      if (!columnValue.equals(expectColVals[i]))
       return false;
     }
     return true;
    } catch (DataSetException ex) {
     ex.printStackTrace();
    }
    return false;
   }
  };
  RowFilterTable filteredRowsTable = new RowFilterTable(
    actualTable, rowFilter);
  return filteredRowsTable;
 }

使用方式:
現有XML DATA:


  
  
    

    
    
    
    

我只希望以XMLDATA建立出的ITable有條件:product == "17公司" and name=="tables"的資料,那麼就照以下方式呼叫:
   ITable expect = DBUnitUtilities.filterXmlDataCol("datasheet\\FactoryInfo.xml","FactoryInfo"
     , _dbunitConnection,new String[]{"name","product"},new String[]{"17公司","tales"} );


這樣就可以過濾囉~~

debug一下其實可以發現RowFilterTable並不是真的將資料給"過濾"掉了,只是將原先的Table給wrap後,然後帶入自行建立的IRowFilter(上述line 8~23),並在每一次巡迴column資料時以自定義的@ovverride accept()回傳true(資料保留) 或 false(資料移除)
回傳true的row index就會被保留在filteredRowIndexes這個List中囉。


p.s:
星期一就要交差幾乎會開天窗的我居然還在研究test.....只希望我這個堅持是對的....畢竟我真的不想再寫垃圾Code穩定度又差又無法擴充又不敢修改的爛東西了阿.........0rz...


參考:Filtering data sets and comparing results using DBUnit

2013年7月3日 星期三

android - 新增Activity在與原先不同的package時,出現R cannot be resolved to a variable 錯誤...

參考:Android: Including multiple Java Packages to Manifest

假設我有兩個Activity,分別在不同的package:
1.com.ex.package1.PActivity1
2.com.ex.package2.PActivity2

該怎麼做才能將上述錯誤消除呢?

修改AndroidManifest.xml

   
  
//....


    
 
    

首先注意的是:

  1.  <manifest> 中的package屬性,必須指定到兩個分歧package的上一層
  2. 每一個<activity>中的android:name屬性,都必須以 . 起始之後再跟者分歧的package name
  3. 在每一個Activity上加入 import com.ex.R;
重新clean rebuild後應該可以消除error了