2015年5月7日 星期四

【Android】 多執行緒-Handler和Thread的關係

有個網站寫多執行緒寫得很有趣,值得看一看。
http://j796160836.pixnet.net/blog/post/29895257


多執行緒的部分,有幾個名詞
  • Runnable 工作包 (要做的事情)
  • Thread 執行緒
  • Handler
  • Message

--------------------------------------------------------------------------
Runnable
就是像是專案管理裡的工作包,說穿了就是要做的事情啦,像是這樣
   private Runnable r1=new Runnable () {
        public void run() {
            //.............................
            //做了很多事
        }
    };
這裡的 r1 就是一個runnable
--------------------------------------------------------------------------
Thread
在Android的世界裡,Thread分成二種
1.  單次型 (Java原有的)
2.  常駐型 (Android特有的)


--------------------------------------------------------------------------
1.  單次型
意指就是給他一件艱巨的任務
他做完就會關閉了

寫法實在有夠簡單
 
   Thread t1=
new Thread(r1);
   t1.start();

這裡的 r1 是一個runnable
--------------------------------------------------------------------------

2.  常駐型
就是做完事情他不會自動關閉,而是變成一個idle (閒置) 的狀態
閒置意思就是他沒事幹啦~  要給他事情做

        mThread = new HandlerThread("name");
        //Worker待命,等待其工作 (開啟Thread)
        mThread.start();
這樣子就可以建立且執行Thread了
連Runnable也不想打的話可以合併起來

     Thread XXX = new Thread(new Runnable()
     {
           publicvoid run()
           {
                // ......工作
           }
     }).start();

要給他事情做要這樣寫
 mThreadHandler.post(r1);


有沒有發現,單次型的Thread就是把事情定義下來然後呼叫start()
開始跑,跑完關閉

而常駐型的話,反而是讓你先start()
然後post(r1)   給他事情做,做完就閒置

請務必記得不用這個Thread的時候要把他關閉


        if (mThread != null)
            mThread.quit();
大部分的情況,常駐型的Thread
在onCreate()裡面建立Thread
在onDestory()裡面關閉Thread


--------------------------------------------------------------------------
Handler
那甚麼是Handler呢?
你可以想成是一個服務的窗口
給他事情做的地方

寫法有二種

1.  mUI_Handler.post(r2);
2.  mUI_Handler.sendEmptyMessage(MSG_UPLOAD_OK);
第一種就是直接給他一個Runnable,讓他去執行
第二種就是傳一個Message給他


Handler的建立
 private Handler mUI_Handler = new Handler();
這樣會建立一個基於Main Thread (UI Thread)的Handler

--------------------------------------------------------------------------
Message

這東西不複雜,剛剛不是還在講Runnable嘛?
Message就是要一言以蔽之,用一個值  (一句話)
代表一堆事情(Runnable)

先看看Handler的變形吧

Handler的建立
 private Handler mUI_Handler = new Handler();
這樣會建立一個基於Main Thread (UI Thread)的Handler
以下是他的變形
     private Handler mUI_Handler new Handler()
     {
           @Override
           public void handleMessage(Message msg)
           {
                switch (msg.what)
                {
                     case MSG_UPLOAD_OK:
                           // ..............
                           break;
                }
           }
     };
這樣會建立一個基於Main Thread (UI Thread)的Handler
有一個窗口,有一個地方可以處理Message的地方  ( 就是handleMessage() )
這裡用一個switch case的格式表示
msg.what  就是你訊息的內容
MSG_UPLOAD_OK  這就是你的訊息了(自己自訂)
像這樣
privatestaticfinalint MSG_UPLOAD_OK= 0x00000001;
說穿了就是個int而已

要使用的時候就這樣
 mUI_Handler.sendEmptyMessage(MSG_UPLOAD_OK);

可以使用帶參數的寫法
 mUI_Handler.obtainMessage(MSG_UPLOAD_OK, arg1, arg2).sendToTarget();
或是
 mUI_Handler.obtainMessage(MSG_UPLOAD_OK, obj1).sendToTarget();
這樣可以帶二個int去,或是直接帶object給他(收到了之後再去轉型...)


【Android】 Unable to execute dex: GC overhead limit exceeded

通常 apk 使用資源過大, 會遇到
Unable to execute dex: GC overhead limit exceeded
我的解決方式如下:
1. 到 Eclipse 的資料夾底下
2. 找到檔案 eclipse.ini
3. 編輯這個檔,修改紅色的地方

-startup
plugins/org.eclipse.equinox.launcher_1.3.0.v20140415-2008.jar
--launcher.library
plugins/org.eclipse.equinox.launcher.win32.win32.x86_64_1.1.200.v20140603-1326
-product
org.eclipse.epp.package.standard.product
--launcher.defaultAction
openFile
--launcher.XXMaxPermSize
512M
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
512m
--launcher.defaultAction
openFile
--launcher.appendVmargs
-vmargs
-Dosgi.requiredJavaVersion=1.7
-Xms512m
-Xmx1024m

【Android】在 eclipse 中設定 google play library

1. 安裝 Google Play services SDK

在安裝之前,請先確認 ADT 和 Android SDK 的版本是最新的,可以到 Help > Check for Updates 更新,或重新安裝。

參考官方說明,首先到 Eclipse 的 Android SDK Manager 最下面 Extras 尋找 Google Play services 並且安裝。


路徑不一定,之前安裝太久,忘了,花了幾十秒才想起來 >,<
我安裝在


C:\Users\myusername\AppData\Local\Android\sdk\extras\google\google_play_services\libproject\google-play-services_lib


2. 接著到 Eclipse 的 File > Import,選擇 Android > Existing Android Code into Workspace 匯入 /extras/google/google_play_services/libproject/google-play-services_lib 專案。

到這裡就完成了 Google Play services SDK 的安裝。當我們的專案要使用 Google Play services 時,直接在專案資料夾上點選滑鼠右鍵選擇 Properties,到 Android > Library 按下 Add 加入 google-play-services_lib,即可完成 Google Play services 的建置。


3.在AndroidManifest.xml 部署

<meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />

2015年5月6日 星期三

【Android】在 Android Studio 設定 Google Play Services


1. 在專案中打開 build.gradle 檔案

2. 在 dependencies 之中,增加最新版本的 play-services. 例如:

apply plugin: 'com.android.application'
...

dependencies {
    compile 'com.android.support:appcompat-v7:21.0.3'
    compile 'com.google.android.gms:play-services:7.3.0'
}

3.儲存,然後工具欄之中,點擊 Sync Project with Gradle Files

2015年5月5日 星期二

【Android】部分Android手機拍照後照片被旋轉的解決方案


轉載 http://www.baidufe.com/item/4bb733d9999c53cb8fed.html


在部分 Android手機上,使用Camera拍照以後,得到的照片會被自動旋轉(90°180°270°),這個情況很不符 合預期。仔細分析了一下,因為照片屬性中是存儲了旋轉資訊的,所以要解決這個問題,可以在onActivityResult方法中,獲取到照片資料後,讀 取它的旋轉資訊,如果不是0,說明這個照片已經被旋轉過了,那麼再使用android.graphics.Matrix將照片旋轉回去即可。



1、讀取圖片的旋轉屬性


/**
 * 讀取圖片的旋轉的角度
 *
 * @param path  女
 *           
 * @return 圖片的旋轉角度
 */
private int getBitmapDegree(String path) {
    int degree = 0;
    try {
        // 從指定路徑下讀取圖片,並獲取其EXIF資訊
        ExifInterface exifInterface = new ExifInterface(path);
        // 獲取圖片的旋轉資訊
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION,
                ExifInterface.ORIENTATION_NORMAL);
        switch (orientation) {
        case ExifInterface.ORIENTATION_ROTATE_90:
            degree = 90;
            break;
        case ExifInterface.ORIENTATION_ROTATE_180:
            degree = 180;
            break;
        case ExifInterface.ORIENTATION_ROTATE_270:
            degree = 270;
            break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return degree;
}
 


2、將圖片按照某個角度進行旋轉


/**
 * 將圖片按照某個角度進行旋轉
 *
 * @param bm
 *            需要旋轉的圖片
 * @param degree
 *            旋轉角度
 * @return 旋轉後的圖片
 */
public static Bitmap rotateBitmapByDegree(Bitmap bm, int degree) {
    Bitmap returnBm = null;
  
    // 根據旋轉角度,生成旋轉矩陣
    Matrix matrix = new Matrix();
    matrix.postRotate(degree);
    try {
        // 將原始圖片按照旋轉矩陣進行旋轉,並得到新的圖片
        returnBm = Bitmap.createBitmap(bm, 0, 0, bm.getWidth(), bm.getHeight(), matrix, true);
    } catch (OutOfMemoryError e) {
    }
    if (returnBm == null) {
        returnBm = bm;
    }
    if (bm != returnBm) {
        bm.recycle();
    }
    return returnBm;
}


【Android 】常用的檢查機制(IP, mail, 手機, 車牌)

在網站找了一些基本的驗證,不錯用,可以參考




import java.util.regex.Pattern;


public class Validator {
    private static final Pattern ASCII_PATTERN = Pattern
            .compile("[\\x00-\\x7F]*");

    public static boolean isPureASCII(String msg) {
        boolean result = false;
        if (ASCII_PATTERN.matcher(msg).matches()) {
            result = true;
        }
        return result;
    }

    /**
     * IP Address 檢查程式
     *
     */
    public static boolean isValidIPAddr(String msg) {
        boolean result = true;
        String[] tmp = msg.split(".");
        if (tmp.length <= 4) {
            for (int i = 0; i < tmp.length; i++) {
                if (Integer.parseInt(tmp[i]) > 255) {
                    result = false;
                }
            }
        }
        return result;
    }

    /**
     * 手機門號檢查程式
     *
     */
    public static final Pattern MSISDN_PATTERN = Pattern
            .compile("[+-]?\\d{10,12}");

    public static boolean isValidMSISDN(String msisdn) {
        boolean result = false;
        if (MSISDN_PATTERN.matcher(msisdn).matches()) {
            result = true;
        }
        return result;
    }

    public static final Pattern EMAIL_PATTERN = Pattern
            .compile("^\\w+\\.*\\w+@(\\w+\\.){1,5}[a-zA-Z]{2,3}$");

    /**
     * Email 格式檢查程式
     *
     */
    public static boolean isValidEmail(String email) {
        boolean result = false;
        if (EMAIL_PATTERN.matcher(email).matches()) {
            result = true;
        }
        return result;
    }

    public static final Pattern TWPID_PATTERN = Pattern
            .compile("[ABCDEFGHJKLMNPQRSTUVXYWZIO][12]\\d{8}");

    /**
     * 身分證字號檢查程式,身分證字號規則:
     * 字母(ABCDEFGHJKLMNPQRSTUVXYWZIO)對應一組數(10~35),
     * 令其十位數為X1,個位數為X2;( 如A:X1=1 , X2=0 );D表示2~9數字
     * Y = X1 + 9*X2 + 8*D1 + 7*D2 + 6*D3 + 5*D4 + 4*D5 + 3*D6 + 2*D7+ 1*D8 + D9
     * 如Y能被10整除,則表示該身分證號碼為正確,否則為錯誤。
     * 臺北市(A)、臺中市(B)、基隆市(C)、臺南市(D)、高雄市(E)、臺北縣(F)、
     * 宜蘭縣(G)、桃園縣(H)、嘉義市(I)、新竹縣(J)、苗栗縣(K)、臺中縣(L)、
     * 南投縣(M)、彰化縣(N)、新竹市(O)、雲林縣(P)、嘉義縣(Q)、臺南縣(R)、
     * 高雄縣(S)、屏東縣(T)、花蓮縣(U)、臺東縣(V)、金門縣(W)、澎湖縣(X)、
     * 陽明山(Y)、連江縣(Z)
     *
     */
    public static boolean isValidTWPID(String twpid) {
        boolean result = false;
        String pattern = "ABCDEFGHJKLMNPQRSTUVXYWZIO";
        if (TWPID_PATTERN.matcher(twpid.toUpperCase()).matches()) {
            int code = pattern.indexOf(twpid.toUpperCase().charAt(0)) + 10;
            int sum = 0;
            sum = (int) (code / 10) + 9 * (code % 10) + 8 * (twpid.charAt(1) - '0')
                    + 7 * (twpid.charAt(2) - '0') + 6 * (twpid.charAt(3) - '0')
                    + 5 * (twpid.charAt(4) - '0') + 4 * (twpid.charAt(5) - '0')
                    + 3 * (twpid.charAt(6) - '0') + 2 * (twpid.charAt(7) - '0')
                    + (twpid.charAt(8) - '0') + (twpid.charAt(9) - '0');
            if ((sum % 10) == 0) {
                result = true;
            }
        }
        return result;
    }

    public static final Pattern TWBID_PATTERN = Pattern
            .compile("^[0-9]{8}$");

    /**
     * 營利事業統一編號檢查程式
     * 可至 http://www.etax.nat.gov.tw/ 查詢營業登記資料
     *
     */
    public static boolean isValidTWBID(String twbid) {
        boolean result = false;
        String weight = "12121241";
        boolean type2 = false; //第七個數是否為七
        if (TWBID_PATTERN.matcher(twbid).matches()) {
            int tmp = 0, sum = 0;
            for (int i = 0; i < 8; i++) {
                tmp = (twbid.charAt(i) - '0') * (weight.charAt(i) - '0');
                sum += tmp / 10 + (tmp % 10); //取出十位數和個位數相加
                if (i == 6 && twbid.charAt(i) == '7') {
                    type2 = true;
                }
            }
            if (type2) {
                if ((sum % 10) == 0 || ((sum + 1) % 10) == 0) { //如果第七位數為7
                    result = true;
                }
            } else {
                if ((sum % 10) == 0) {
                    result = true;
                }
            }
        }
        return result;
    }

    /**
     * "關鍵字不可包含 ( @ ' % \ _ = ) 等字元\n";
     */
    public static boolean isValidQueryString(String sqlStr) {

        if (sqlStr == null || sqlStr.length() <= 0 || sqlStr.contains("@") ||
                sqlStr.contains("'") || sqlStr.contains("_") ||
                sqlStr.contains("\"") || sqlStr.contains("%") ||
                sqlStr.contains("=")) {

            return false;
        } else {
            return true;
        }
    }
}


2015年5月4日 星期一

【Android 】Notification 通知欄

研究了通知欄一段時間,有幾個網站寫的很好。

此文章有很全面的說明,收獲頗多
http://blog.csdn.net/vipzjyno1/article/details/25248021

看完,可以多瞭解觀念與實作
http://blog.csdn.net/xy_nyle/article/details/19853591
http://magiclen.org/android-notifications/