2012年10月29日 星期一

Android - Tasks and Back Stack Updated 2013-04-18

       在Android系統中,透過Intent與Intent-Filter的設置,可以讓某程式去呼叫其他程式的Activity來使用,在使用完該Activity後,可以輕易的使用上一頁的按鈕來回到原本呼叫的Activity,例如:在原本的Activity使用Intent來Send E-mail,此時系統便會跳出可以發送Email的程式(Activity)以供選擇,選擇完後,便會跳出所選擇的程式來發送E-mail,當發送完成,按下上一頁鈕後,系統又回到之前程式的Activity畫面,達成無縫的操作方式(因為主程式與發送Email的程式為不同的應用程式)。

        Task是一個Activity的集合為了能夠正確完成工作的集合,上述就是一個例子,而back stack(或只簡稱stack) 就是放置這些Activity並且排列的地方。在按下程式的launcher icon後(不管是按下Home鍵後點選桌面捷徑,還是點擊程式列表中的icon),Android會查看在Task群中是否有該App所建立的Task,如果有,則會將該Task帶到foreground,並啟動Foreground Activity與回復其狀態,而其他的Task則會到background,如果Task中並沒有該App所建立的Task,則會重新建立一個新的Task,並啟動該程式的Main Activity。

        而Back Stack的存放過程如下圖,一開始就只有Activity 1,所以建立起新Task,並將Activity 1放Back Stack中,而當該Activity 1啟動Activity 2後,Activity 1便進入Pause,Activity 2則放到Back Stack的前面,並且為Foreground Activity,如果Activity 2再啟動Activity 3,則會進行一樣的動作,Activity 3便成為Foreground Activity,其餘皆進入Pause狀態,如果這時Activity 3按下上一頁鈕,則Activity 3被Destroy,接著Activity 2便成Foreground Activity並回覆狀態,如果到最後Activity 1也被Destroy,則該Task和Back Stack就不再存在。Back Stack有兩個重要的特性便是:先進後出及不被重新排序整理,也就是進來的順序是固定,不會更改。




        有以上概念可以得知,在不同的Task其實會放置相同的Activity在Back Stack中,例如:兩個不同Task的Activity都呼叫相同發送Email的Activity,則這兩個Back Stack都會存放啟動相同的Activity狀態,也就是發送Email的Activity會被啟動多次,並存放到不同的Back Stack。

        Activity會在進入Background(排入Back Stage)或者因為記憶體不足,而Android自動將App的Process刪除,以取得更多的記憶體時,會呼叫onSaveInstanceState(Bundle outState)進行狀態的儲存,但是這個事件並不一定會呼叫,例如:按上一頁要Destroy Activity時,所以也就是為何Activity從Back Stage回到Foreground時,之前Activity的狀態仍會存在,所以如果有額外的資訊想要保留,以便能夠讓Activity re-initial後回復或使用所儲存的資訊,則可以Override onSaveInstanceState(Bundle outState)進行改寫,將所欲保留的資訊進行儲存的動作,但是如果要儲存持續性的資料,不建議寫在onSaveInstanceState(Bundle outState),而可以在onPause()寫入持續性的資料。

        上述說明如果要使用額外儲存在onSaveInstanceState(Bundle outState)的資料,必須是Activity re-initial時,是因為只有在re-initial時才會重新執行onCreate(Bundle savedInstanceState)或者執行onRestoreInstanceState(Bundle savedInstanceState),而這些所帶入的Bundle就是onSaveInstanceState(Bundle outState)所使用的Bundle,會re-initial的情況有翻轉螢幕,或因為記憶體不足Android自動將App的Process刪除,而導致使用者回到Activity時發生等情況。

       如果不要畫面翻轉時讓Activity re-initial,則可以在AndroidManifest.xml設定Acitivity的configChanges屬性,例如:android:configChanges="keyboardHidden|orientation|screenSize",此時便不會re-initial,而會執行onConfigurationChanged(Configuration newConfig)。

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel - 林靖傑
日期:2013/04/18

2012年10月25日 星期四

解決無法開啟Android API 16 (4.1.x) 的AVD

如果透過AVD Manager來開啟使用Android API 16所建立的API,
以下解析度(Skin)都可以正常使用:

WVGA800(Default)
HVGA
QVGA
WQVGA400
WQVGA432
WSVGA
WVGA854

其實以上都是預設使用512MB的預設解析度(Skin),
這些解析度就算直接修改記憶體大小到1024MB, 基本上都是可以正常開啟!

但是以下這幾個解析度(Skin), 預設使用1024MB記憶體:

WXGA720
WXGA800
WXGA800-7in

就算修改其記憶體到512MB或256MB還是會出現以下錯誤

Failed to allocate memory: 8
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

如果要使用上述那三種Skin進行設定的話, 可以使用以下方式, 在命令模式打上:

注意: 記憶體一定要512MB!

<Skin ID>為上述三種Skin,
<AVD_ID>為你自己所建立的AVD名稱!

emulator -skin <Skin ID> -memory 512 -avd <AVD_ID>
ex: emulator -skin WXGA800-7in -memory 512 -avd AVD_ID

如此便可以正常開啟!

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel - 林靖傑
日期:2012/10/25

2012年10月12日 星期五

PHP的Exception與錯誤處理

        會寫這篇文章,主要是為了讓學生能夠對於PHP 5的錯誤處理機制有更進一步的認識,而非單純依靠程式產生的方式來處理,甚至不處理自己程式的錯誤。

在PHP 5之前,PHP對於錯誤處理(Error)採用以下的方法:
<?php
  //自訂錯誤處理函數,PHP預設錯誤常數,錯誤訊息,錯誤發生的檔案,錯誤發生的行號
  function custom_error($errno, $errstr, $errfile, $errline){
     //如果回傳true,不要讓PHP來正常處理錯誤(不拋出)
     return true;
     //如果回傳false,則代表讓PHP來正常處理錯誤(拋出)
     //return false;
  }

  //設定錯誤需要報告的等級
  error_reporting(E_ERROR|E_USER_ERROR|E_WARNING|E_USER_WARNING|E_NOTICE|E_USER_NOTICE);
  //如果不設定就讓PHP來正常處理錯誤(拋出)
  set_error_handler("custom_error");

  //因為customer_error傳回true則
  echo $ary['err'].'<br />';
  echo 'after error'.'<br />';
?>

        error_reporting函數所代表的,是那些錯誤等級需要PHP將其處理與呈現,如果不加上這一句,代表所有錯誤等級皆會處理,也就是錯誤等級E_ALL,錯誤等級列表如下:http://tw2.php.net/manual/en/errorfunc.constants.php
如果要針對錯誤進行相關處理,可以藉由set_error_handler來指定錯誤處理函數,而錯誤處理函數的帶入參數為:自訂錯誤處理函數、PHP預設錯誤常數、錯誤訊息、錯誤發生的檔案、錯誤發生的行號。

        上述的範例中,「$ary['err']」這一行一定會錯,所以如果錯誤處理函數回傳為true,代表不要讓PHP來正常處理錯誤(不拋出),如果回傳false,則代表讓PHP來正常處理錯誤(拋出),當然也可以在錯誤處理函數裡針對不同等級的錯誤來進行相關處理,如下範例:
<?php
  function custom_error($errno, $errstr, $errfile, $errline){
     //
     $stop_flag = false;
     //可以依照錯誤的等級個別指定處理方式
     switch ($errno){
      case E_USER_ERROR:
         echo 'E_USER_ERROR<br/>';
         $stop_flag = true;
         break;
      case E_USER_WARNING:
         echo 'E_USER_WARNING<br/>';
         $stop_flag = true;
         break;
      case E_USER_NOTICE:
         echo 'E_USER_NOTICE<br/>';
         $stop_flag = true;
         break;
      case E_ERROR:
         echo 'E_ERROR<br/>';
         $stop_flag = true;
         break;
      case E_WARNING:
         echo 'E_WARNING<br/>';
         $stop_flag = true;
         break;
      case E_NOTICE:
         echo 'E_NOTICE<br/>';
         $stop_flag = true;
         break;
      default:
         echo 'other error<br/>';
         $stop_flag = true;
     }
     return $stop_flag;
  }

  //
  error_reporting(E_ERROR|E_USER_ERROR|E_WARNING|E_USER_WARNING|E_NOTICE|E_USER_NOTICE);
  set_error_handler("custom_error");

  //
  echo $ary['err'].'<br />';
  echo 'after error'.'<br />';
?>

         而PHP 5之後,開始加入Exception的例外處理模式,也就是其他語言中所熟知的Try…Catch..語法,但是在目前的PHP 5裡尚未加入Finally的語法,如果要在PHP裡讓Exception可以抓到錯誤,則必須拋出Exception,如下面範例中的「throw new Exception("custom exception!");」,但是針對之前我們所介紹的錯誤,卻是無法進行抓取:

<?php
  //try...catch 用法
  try{
      throw new Exception("custom exception!");
  } catch (Exception $e) {
      echo "例外的訊息字串 : " . $e->getMessage()."<br />";
      echo "產生例外的檔案 : " . $e->getFile()."<br />";
      echo "產生例外的行號 : " . $e->getLine()."<br />";
      echo "產生例外的程式碼 : " . $e->getCode()."<br />";
      echo "例外的堆疊追蹤陣列 : ";
      print_r($e->getTrace());
      echo "<br />";
      echo "例外的堆疊追蹤字串 : " . $e->getTraceAsString()."<br />";
      echo "例外的表示字串 : " . $e->__toString()."<br />";
  }
  echo 'after exception...'.'<br/>';


  //但是error無法使用
  try{
      echo $ary['err'];
  } catch (Exception $e) {
      echo $e->getMessage()."<br />";
  }
?>

        所以我們可以在錯誤處理函數裡拋出ErrorException,這樣不管是怎樣的錯誤等級,都可以使用Exception來進行處理,當然也可以在錯誤處理函數裡區分錯誤等級,不一定所有錯誤等級都要拋出ErrorException。
<?php
  function custom_error($errno, $errstr, $errfile, $errline){
     //發生Error就直接拋出Exception,以便後續可以使用try~catch處理
     //錯誤訊息,內部代碼,PHP預設錯誤常數,錯誤發生的檔案,錯誤發生的行號
     throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
  }

  //
  error_reporting(E_ERROR|E_USER_ERROR|E_WARNING|E_USER_WARNING|E_NOTICE|E_USER_NOTICE);
  set_error_handler("custom_error");

  //
  try{
   echo $ary['err'];
  } catch (Exception $e) {
   echo $e->getMessage().'<br />';
  }
  echo 'after error exception...'."<br />";
?>

        當然可以使用PHP 5之後的物件導向的類別,將其包裝成可重覆使用的物件,以減少程式碼的產生,也可以在該類別中加上記錄的函數,將其記錄到所指定的文件裡,以下範例是每天會產生出一個檔案來記錄
<?php
  class error_handler{
    public function error_handler(){
   
    }
   
    public static function custom_error($errno, $errstr, $errfile, $errline){
         throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
    }
   
    public static function record(Exception $e){
     //先指定時區
     date_default_timezone_set('Asia/Taipei');
   
     //格式化錯誤訊息
     $err_str = sprintf("錯誤發生日期: %s[錯誤訊息: %s]\r\n 錯誤檔案: %s\r\n 錯誤行號 : %s\r\n 錯誤堆疊追蹤字串: %s\r\n-------------\r\n",
       date("Y-m-d H:m:s"), $e->getMessage(), $e->getFile(), $e->getLine(), $e->getTraceAsString());
   
     //在檔案內記錄
     error_log($err_str, 3, "C:\\error_log\\".date("Ymd").".log");
    }
  }
?>


<?php require_once 'error_handler.php';?>
<?php
  $error_handler = set_error_handler("error_handler::custom_error");
  try{
   echo $ary['err'];
  } catch (Exception $e) {
   error_handler::record($e);
   echo '系統發生錯誤!';
  }
?>

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel - 林靖傑
日期:2012/10/12

Java SE 7 - Thread - ForkJoinPool 2013-02-23修正

將工作從一個切割成多個,而每個產生的工作又可以再切割成多個工作並行處理,例如:

A工作分成A1及A2,A1及A2又分成A11、A12及A21、A22,這時A、A1及A2會一直等待,當A11及A12都完成工作,將結果傳給A1後,A1就停止等待,將結果進行處理並傳回給A,而A21及A22也都完成工作,將結果傳給A2後,A2就停止等待,並將結果處理並傳回給A,最後A都收到A1及A2的結果後,將最後結果整合並傳回最終結果。

每個工作會分配Thread來執行,如果不需要工作在執行完成後傳回結果,可以使用RecursiveAction,如果需要在工作重後傳回結果,則可以使用RecursiveTask。

例如:以下是將MapReduce計算某一篇文章出現的字母次數的範例進行重寫:

 /*
 * Powered By Samuel - 林靖傑2013/02/23
 * 轉貼請說明出處!
 *
 * */
public class WordCountFork {
   
 public static void main(String[] args) {
     ForkJoinPool mainPool = new ForkJoinPool();
     //取出文字檔內容
     String Content = "";
     char[] TextData = new char[1024];
     int length = -1;

     try (Reader input = new BufferedReader(new FileReader("WordFile.txt"));
            Writer SW = new StringWriter()){
            while ((length = input.read(TextData)) != -1){
                      SW.write(TextData, 0, length);
            }
            Content = SW.toString();
     } catch (FileNotFoundException e) {
             e.printStackTrace();
     } catch (IOException e) {
             e.printStackTrace();
     }

     //Step 1: 執行
     int[] WordAry = mainPool.invoke(new LineAnaylze(Content));

     //取出結果
     for (int i = 0 ; i < WordAry.length; i ++){
            String Alphabet = Character.toString ((char) (65 + i));
            System.out.println(
                Alphabet + ":" + String.valueOf(WordAry[i]));
     }
  }
}

//Step 2: 建立RecursiveTask來傳回Task結果
//如果不需帶入值, 可以使用RecursiveAction
class LineAnaylze extends RecursiveTask<int[]> {
    private String Content = "";
    public LineAnaylze(String content){
             Content = content;
    }

    @Override
    protected int[] compute() {
         //Step 2: 如果字數大於50, 則各切割成一半成小工作執行
         if (Content.length() > 50){
              int half = Content.length() / 2;
              //先建立Task, 每一個工作都是一個Task, 所以會一直分化執行
              LineAnaylze half1 = new LineAnaylze(Content.substring(0, (half - 1)));
              LineAnaylze half2 = new LineAnaylze(Content.substring(half));
              //讓ForkJoinPool分配Thread執行Task
              half1.fork();
              half2.fork();
              //等待兩個工作完成
              int[] WordPair1 = half1.join();
              int[] WordPair2 = half2.join();

              //將兩個Task結果整合傳回
              for (int i = 0; i < WordPair1.length; i ++){
                     WordPair1[i] = WordPair1[i] + WordPair2[i];
              }
               return WordPair1;
         } else {
              //不足50個字直接執行後傳回
              return countWord(Content);
         }
    }

    //計算字數函式
    private int[] countWord(String line){
          int[] WordAry = new int[26];
          for (int i = 0 ;i < line.length(); i ++){
                 String Alphabet = String.valueOf(line.charAt(i)).toUpperCase().trim();
                 if (!Alphabet.equals("")){
                      int WordCount = 0;
                      int Ascii = (int)Alphabet.charAt(0);
                      if (Ascii >= 65 && Ascii <= 90){
                           WordCount = WordAry[Ascii - 65];
                           WordAry[Ascii - 65] = ++WordCount;
                      }
                 }
          }

          return WordAry;
    }
}

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel
日期:2013/02/23

2012年10月9日 星期二

Android 安裝 busybox 完善 Linux 命令



Android的開發人員都知道Android在連接電腦選擇充電模式並開啟USB偵錯後便可以藉由adb shell命令來登入Android系統打Linux指令操作但是並不是大部份在Linux所支援的指令都可以在Android系統上執行所以就必須安裝busybox這個工具busybox包含大部份在Linux會用到的指令以下為下載地址


==>選擇版本後再選擇busybox-armv6l版本的busybox

在安裝前手機必須要先root否則無法使用

安裝步驟(Nexus 7操作)
1.      先將busybox使用adb pushsdcard
>adb push busybox /mnt/sdcard/busybox

2.      登入shell模式
>adb shell

3.      切換到root
>su

4.      /system可以讀寫
>mount -o rw,remount /dev/block/mtdblock3 /system

5.      因為預設沒有cp命令必須要安裝busybox後才有故先使用以下語法複製到/system/xbin假設原本下載的檔名改成busybox
>cat /mnt/sdcard/busybox > /system/xbin/busybox

6.      更改busybox權限
>chmod 755 /system/xbin/busybox

7.      安裝busybox
>busybox --install /system/xbin

8.      移除sdcard中的busybox
>rm /mnt/sdcard/busybox

9.      /system/xbin設定成唯讀
>mount -o ro,remount /dev/block/mtdblock3 /system

以後如果要執行命令便可以直接打上命令即可例如cp如果命令在Android系統上就已經支援則可以在命令前面加上busybox便可以使用busybox所提供的命令例如ls命令在android系統本身即有夾帶故可以使用busybox ls為了避免需要一直打上busybox故可以使用alias例如

alias ls='busybox ls --color=never'
alias ll='busybox ls -la --color=never'

加上--color=never主要是因為在windowscmd模式下因沒有辦法呈現顏色,會導致亂碼,故強制使其不要加上顏色

※如果想要在Android手機或平板上直接使用命令操作Android系統,則可以安裝Terminal Emulator,Market上就可以找到。

※如果嫌以上安裝方式太麻煩,可以試試Busybox Installer,Market上就可以找到,但是手機或平板一樣要允許root。

※很多軟體其實都會需要事先安裝busybox,以便執行安裝在Android系統裡的軟體功能。

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel
日期:2012/10/10

Java SE 7 - Exception - Suppressed 架構觀念

有時在catch或finally區段裡會再進行try…catch…相關動作,以確保所執行的程式碼如果再發生例外時應該做的對應處理。如下:

try{
    throw new Exception("Ex 1");
} catch (Exception ex){
    try{
       Do Something….
       throw new Exception("Ex 2");
    } catch (Exception ex2){
       Do Something…
    }
    throw ex;
}


try{
    throw new Exception("Ex 1");
} catch (Exception ex){
    Do Something….
    throw ex;
} finally {
    try{
       Do Something….
       throw new Exception("Ex 2");
    } catch (Exception ex2){
       Do Something…
    }
}

通常上述範例只會呈現Ex 1訊息如下
Exception in thread "main" java.lang.Exception: Ex 1
    ……


如果要完整呈現Ex 2的錯誤則必須直接列出:
try{
    throw new Exception("Ex 1");
} catch (Exception ex){
    try{
       Do Something….
       throw new Exception("Ex 2");
    } catch (Exception ex2){
       ex2.printStackTrace();
    }
    throw ex;
}

但會呈現兩則訊息
java.lang.Exception: Ex 2
    ......
Exception in thread "main" java.lang.Exception: Ex 1
    ......

如果連Ex 2都拋出
try{
    throw new Exception("Ex 1");
} catch (Exception ex){
    Do Something….
    throw ex;
} finally {
    try{
       Do Something….
       throw new Exception("Ex 2");
    } catch (Exception ex2){
       throw ex2;
    }
}
則只會呈現ex2
Exception in thread "main" java.lang.Exception: Ex 2
      …...

SE 7多出了Exception Suppressed抑制功能,讓我們先看寫法與結果:
try{
    throw new Exception("Ex 1");
} catch (Exception ex){
    try{
       throw new Exception("Ex 2");
    } catch (Exception ex2){
       ex.addSuppressed(ex2);
    }
    throw ex;
}

Exception outex = null;
try{
   throw new Exception("Ex 1");
} catch (Exception ex){
   outex = ex;
   throw ex;
} finally {
   try{
      throw new Exception("Ex 2");
   } catch (Exception ex2){
      outex.addSuppressed(ex2);
   }
}
這個時候Ex 2會被包在Ex 1裡呈現,可以讓我們很清楚的知道錯誤的結構與內容:
Exception in thread "main" java.lang.Exception: Ex 1
    ......
    Suppressed: java.lang.Exception: Ex 2
        ......
        ... 1 more

這樣的寫法在實作java.lang.AutoCloseable Inteface的類別中,Java編譯器會將其轉成這樣的寫法:

public static void main(String[] args) throws Exception {
    test();
}
static class ResourceA implements AutoCloseable {
    public void read() throws Exception{
        throw new Exception("ResourceA read exception");
    }
    @Override
    public void close() throws Exception {
        throw new Exception("ResourceA close exception");
    }
};
public static void test() throws Exception{
    try (ResourceA a = new ResourceA()) {
       a.read();
    } catch (Exception e) {
       throw e;
    }
}

呈現訊息如下:
Exception in thread "main" java.lang.Exception: ResourceA read exception
    ......
    Suppressed: java.lang.Exception: ResourceA close exception
        ......
        ... 1 more
待續...

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel
日期:2012/10/06


Java SE 7 - Exception - try with resources 自動嘗試

在SE 7之前如果要確保資源的正確關閉,通常都會將資源關閉寫在finally如下:

public static String readFile(String name) throws FileNotFoundException {
     StringBuilder builder = new StringBuilder();
     Scanner scanner = null;
     try {
         scanner = new Scanner(new FileInputStream(name));
         while (scanner.hasNext()) {
             builder.append(scanner.nextLine());
             builder.append('\n');
         }
     } catch(FileNotFoundException ex) {
         ex.printStackTrace();
         throw ex;
     } finally {
         if(scanner != null) {
             scanner.close();
         }
     }
     return builder.toString();
}

在SE7後,只要有實作java.lang.AutoCloseable Inteface就可以使用以下寫法:

public static String readFile(String name) throws FileNotFoundException {
    StringBuilder builder = new StringBuilder();
    try(Scanner scanner = new Scanner(new FileInputStream(name))) {
        while (scanner.hasNext()) {
            builder.append(scanner.nextLine());
            builder.append('\n');
        }
    } catch(FileNotFoundException ex) {
        ex.printStackTrace();
        throw ex;
    }
    return builder.toString();
}

不管是否是正常執行還是異常中斷,java都會呼叫介面所定義的close方法。try裡面也可以使用多個物件,以分號區隔,越後面的物件會越早被關閉資源

以下為java.lang.AutoCloseable Inteface所需實作的規範
http://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html

上述範例中的Scanner在SE 7也是有實作java.lang.AutoCloseable Inteface
http://docs.oracle.com/javase/7/docs/api/java/util/Scanner.html


轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel
日期:2012/10/06

Java SE 7 - Exception - More-Precise-Rethrow

如果為此時有兩個Checked Exception,則必須分別Catch這兩個Check Exception,如下:

try {      
    if (filename.equals("")){
        throw new FileNotFoundException();
    } else {
       throw new EOFException();
    }   
} catch (EOFException ex) {
    System.out.println("End of file reached.");
} catch (FileNotFoundException ex) {
    ex.printStackTrace();
}

        在SE 7以前如果要使用以下語法會錯,但是SE 7後可以直接使用父類別Catch即可,但是這樣的寫法還是適用在:Catch後都是使用同樣的動作處理Exception。

try {      
     if (filename.equals("")){
         throw new FileNotFoundException();
     } else {
         throw new EOFException();
     }   
} catch (IOException ex) {
     System.out.println("End of file reached.");
}

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel
日期:2012/10/06

Java SE 7 - Exception - Multi-Catch 多重捕捉

        如果程式中有使用Checked Exception(受檢例外),則必須使用Try…Catch..進行包圍或者Throws該Exception,若想要特別判斷Unchecked Exception(非受檢例外)時也會進行這樣的動作如下:


try{
   do someting...
} catch (FileNotFoundException e){
   e.printStackTrace();
} catch (EOFException e){
   e.printStackTrace();
} catch (IOException e){
   e.printStackTrace();
} catch (Exception e){
   e.printStackTrace();
}


但是這些Exception都是處理同樣的事,SE 7可以將進行Multi-Catch如下:


try{
   do someting...
} catch (FileNotFoundException | EOFException | IOException | Exception e){
   e.printStackTrace();
}

通常在Catch Exception時,會將最上層的Exception放到最下面最後Catch,避免所有問題都在Exception就被Catch,Multi-Catch也是有同樣的情況,只是這時在右邊而非下面,如果為以下寫法語法會錯:


try{
   do someting...
} catch (Exception | IOException | FileNotFoundException | EOFException e){
   e.printStackTrace();
}

轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel
日期:2012/10/06

使用JavaScript Framework所延伸瀏覽器Cache JavaScript File的問題

    在目前JavaScript Framework(如:ExtJQueryAjaxControlToolkit等)大量使用的時代,對開發人員而言,瀏覽器 Cache 網頁和JavaScript File(下文以JS File簡稱)不僅僅導致開發人員開發上的問題,更會使得使用者因為無法正確使用更新後的版本,而無法正常使用功能!本文將不再老生常談一些基本知識,如果大家有需要會再另寫文章。
    我們都知道,如果要使網頁或者JS File在每次Request時,都會自動向Server取回檔案,必須使用以下的metadata資訊:

<meta http-equiv="pragma" content="no-cache"/>
<meta http-equiv="cache-control" content="no-cache"/>
<meta http-equiv="expires" content="0"/>

cache-control為例,重點在於," Cache-Control: no-cache""Cache-Control: max-age=0"到底有甚麼區別?乍看之下好像沒有甚麼差異性,主要差異在於"max-age=0"它只會去跟Server驗證(可以參考http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3原文說明)是否有過期,如果ResponseDate Header判斷後不需要重新取回,那就會使用Cache,而"no-cache"則是每次request都會去重新取回檔案(可以參考http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.2.6原文說明),所以如果使用"max-age=0"不見得會更新你的資料!
    但是這一切如果是這樣那就太好了!那是不是只要設定no-cache就可以自動更新檔案?很不幸的,不同瀏覽器下所抓到的Request Header,其Cache-Control有時會呈現max-age=0的方式,所以會發現網頁或者JS File沒有被更新,IE 9下更測出明明有更新過檔案,但是Request HeaderStatus Code一直呈現304(代表直接使用Cache200代表會從伺服器抓取)!這樣的結果代表瀏覽器會不遵循直接加在head中的metadata資訊,Google Chrome直到最新版本仍會有不遵循Cache-Control的設定,隨便向Google查詢相關問題就會跑出一堆結果。
    那如果完全使用JavaScript Framework建構的AJAX網站情況是不是也一樣呢?如果完全使用JavaScript Framework建構,例如:Extjs,時常會使用autoLoad的方式來載入其它頁面( EX: autoLoad: { url: URLPath, scripts: true,nocache:true }),在有加metadataExtjsnocache屬性的情況下,有時會發現,不管如何大力按F5瀏覽器依舊使用Cache!所以在開發環境下,通常開發人員會強制讓瀏覽器直接重新下載JS File,以確保開發過程中的順利,但是如果現在角色是Client端這又該如何?歷史是不是一定又會重演?上述加metadata和加Extjsnocache屬性的方式好像又不管用,就算更新網頁,但是就是不會更新JS File
    有鍵於此,建議加上參數來讓瀏覽器自動從Server重新載入JS File

<script type="text/javascript" src="/JSFilePath.js?r=1234"></script>

但是這樣到最後不是一樣意思嗎!是否可以隨機產生亂數讓每一次的數值都可以不一樣,這時就需要使用隨機產生亂數和動態加載JS File,動態加載JS File在網路上有很多五花八門的使用方式,但是因為只需要簡單的載入JS File,故我們可以使用以下的方式動態加載JS File並且亂數產生數值,適用各種瀏覽器:

<script type="text/javascript>
   var DnyScript = document.createElement('script');
   DnyScript.setAttribute('type', 'text/javascript');
   DnyScript.setAttribute('src', '/JSFilePath.js?r=' +Math.floor(Math.random() * 1000 + 1));
   document.body.appendChild(DnyScript);
</script>

如此便可以讓瀏覽器強制更新JS File,並且可以像以往一樣自行決定JS FileHTML中的執行順序和各JS File間的執行順序,但是唯一的缺點就是每次的Request會需要額外的流量,但這就是正確性跟流量的取捨問題,不過可以藉由壓縮和混淆JS File的方式來減少JS File的大小。另外值得一提的是,使用此種方式對於JS File的保護會有一定程度上的幫助,這地方各位可以思考看看!
     如果本文有不足或錯誤之處,希望各方大老不吝指教,文中若有其他需要另開文章說明,各位不嫌棄本人的文筆及技術再煩請告知,在空閒之餘會另開文章發表拙見!


轉貼請註明出處,最好直接使用聯結轉貼!Thanks~
作者: Samuel-林靖傑
日期:2011/06/28