分類
Uncategorized

The Simple Logging Facade for Java (SLF4J) 使用紀錄

SLF4J主要是提供”方便使用”log的工具,照著以下步驟即可在專案中使用
Step1.
到官網(https://www.slf4j.org/)下載slf4j-api-xxx.jar以及slf4j-simple-xxx.jar
Step2.
把以上的jar檔import到專案中
Step3.
使用方式,首先設定Logger,e.g.,

private Logger mLogger = LoggerFactory.getLogger(SLF4JTest.class);
//SLF4JTest.class通常會代換為使用該mLogger的class

接著印出訊息,e.g.,

        mLogger.info("in test");
        mLogger.debug("in test");
        mLogger.error("in test");
        mLogger.trace("in test");
        mLogger.warn("in test");

最後是簡單的使用測試案例。e.g.,

public class SLF4JTest extends TestCase
{
    private Logger mLogger = LoggerFactory.getLogger(SLF4JTest.class);
    public SLF4JTest(String name) {
        super(name);
    }
    public void test()
    {
        mLogger.info("in test");
        mLogger.debug("in test");
        mLogger.error("in test");
        mLogger.trace("in test");
        mLogger.warn("in test");
    }
}

其他詳細的使用方式請參考官網。

分類
Uncategorized

android 取得裝置資訊(android.os.Build.class.getFields())

source code
note:不一定所有的值都取的到

    public static void getDeviceInfo(){
        Field[] deviceInfo = android.os.Build.class.getFields();
        for (Field one : deviceInfo) {
            try {
                Log.d(TAG, "Name:" + one.getName() + " Value:" + one.get(one));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            }
        }
    }

output

06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:ASUSCID Value:ASUS
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:ASUSSKU Value:WW
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:AUTO_START Value:true
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:BOARD Value:msm8952
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:BOOTLOADER Value:unknown
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:BRAND Value:asus
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:CHARACTERISTICS Value:tablet
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:COUNTRYCODE Value:TW
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:CPU_ABI Value:arm64-v8a
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:CPU_ABI2 Value:
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:CTA Value:false
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:DEVICE Value:ASUS_P00I
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:DISPLAY Value:NRD90M.WW_ASUS_P00I-V5.4.6-20170815
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:FINGERPRINT Value:asus/WW_ASUS_P00I/ASUS_P00I:7.0/NRD90M/WW_ASUS_P00I-V5.4.6-20170815:user/release-keys
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:HARDWARE Value:qcom
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:HOST Value:mec7
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:ID Value:NRD90M
06-07 10:27:56.327 24184 24184 D DeviceUtility: Name:ISASUSVERMAX Value:false
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:IS_DEBUGGABLE Value:false
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:IS_EMULATOR Value:false
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:MANUFACTURER Value:asus
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:MODEL Value:ASUS_P00I
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:PERMISSIONS_REVIEW_REQUIRED Value:false
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:PRODUCT Value:WW_ASUS_P00I
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:RADIO Value:unknown
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:SERIAL Value:GCNPCX1079367R5
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:SUPPORTED_32_BIT_ABIS Value:[Ljava.lang.String;@3f04769
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:SUPPORTED_64_BIT_ABIS Value:[Ljava.lang.String;@74695ee
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:SUPPORTED_ABIS Value:[Ljava.lang.String;@ed08a8f
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:TAGS Value:release-keys
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:TIME Value:1502780830000
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:TYPE Value:user
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:UNKNOWN Value:unknown
06-07 10:27:56.328 24184 24184 D DeviceUtility: Name:USER Value:android
分類
Uncategorized

加入 Android SDK 路徑到環境變量 (ubuntu12.04 LTS)

目的 :

以後要使用 android adb 以及相關工具不必指定絕對路徑。

環境:

Ubuntu 12.04 LTS
zsh or bash
android-sdk_r20-linux

步驟:

1.將 android sdk path 加入環境變數

使用 bash 的情況
使用編輯工具修改 ~/.bashrc, 加入以下指令

export ANDROID_HOME=$HOME/android_sdk_path
export PATH=$PATH:$ANDROID_HOME/tools:$PATH:ANDROID_HOME/platform-tools

第1行的 /android_sdk_path 為本機端 android sdk 路徑


使用 zsh 的情況
使用編輯工具修改 ~/.zshrc, 加入以下指令

export PATH=$PATH:$ANDROID_SDK_HOME/tools:$ANDROID_SDK_HOME/platform-tools
export ANDROID_SDK_HOME=/android_sdk_path

第2行 /android_sdk_path 為本機端 android sdk 路徑

2.更新環境變數

source ~/.bashrc
or
source ~/.zshrc

 

3.測試結果

開啟一個新的 console 並輸入 adb 會出現以下提示訊息

$ adb
Android Debug Bridge version 1.0.32
Revision 09a0d98bebce-android
 -a                            - directs adb to listen on all interfaces for a connection
 -d                            - directs command to the only connected USB device
                                 returns an error if more than one USB device is present.
 -e                            - directs command to the only running emulator.
                                 returns an error if more than one emulator is running.
 -s <specific device>          - directs command to the device or emulator with the given
                                 serial number or qualifier. Overrides ANDROID_SERIAL
                                 environment variable.
 -p <product name or path>     - simple product name like 'sooner', or
                                 a relative/absolute path to a product
                                 out directory like 'out/target/product/sooner'.
                                 If -p is not specified, the ANDROID_PRODUCT_OUT
                                 environment variable is used, which must
                                 be an absolute path.
 ...

 

分類
Uncategorized

D-link DWA-131 install on Ubuntu 12.04 LTS

目的:

讓 DWA-131 可以在 ubuntu 12.04 LTS 上正常使用。
DWA-131 全名為 DWA-131 Wireless N NANO USB 無線網路卡。雖然官網在系統支援的部份沒有提到 Linux。

但有提供 linux driver 可以下載。來試著安裝看看。我的環境為 ubuntu 12.04 LTS。

步驟:

首先下載並解壓縮官網提供的 driver 會產生rtl8188C_8192C_usb_linux_v3.4.4_4749.20120730 資料夾,進到資料夾中沒有 readme,只好先 make 看看。
make 過程會出現一點小錯,大致是一些編譯參數不正確的問題,最後編譯過了,但使用 ifconfig 等等指令還是找不到無線網卡,還好找到這篇(http://askubuntu.com/questions/693500/dlink-dwa-131-wireless-adapter-not-working-on-ubuntu-15-10)。
就照著 Try this – https://github.com/Mange/rtl8192eu-linux-driver 的解法,成功編譯並啟動無線網卡。
 

分類
Android Uncategorized

在 Eclipse 使用 Android Manager 更新 android sdk 25之後無法使用 adb 問題

開啟 eclipse 啟動 android manager 並更新到 android adk 25.0.2 之後,使用 adb 會出現以下錯誤訊息

platform-tools ./adb devices
zsh: 可執行檔格式錯誤: ./adb

root cause:unknow
workaround : 到 android repository 下載並解壓縮 platform-tools.zip (經過測試只有platform-tools_r20-linux.zip 可用),替換掉原本無法使用的platform-tools 資料夾即可。
 

分類
Uncategorized

隱私權政策網址

隱私權政策

非常歡迎您使用本APP,為了讓您能夠安心的使用各項服務與資訊,特此向您說明本APP的隱私權保護政策,以保障您的權益,請您詳閱下列內容:
一、隱私權保護政策的適用範圍
隱私權保護政策內容,包括本APP如何處理在您使用網站服務時收集到的個人識別資料。隱私權保護政策不適用於本APP以外的相關連結網站,也不適用於非本APP所委託或參與管理的人員。
二、個人資料的蒐集、處理及利用方式
當您洽辦本APP業務或參與本APP活動時,我們將視業務或活動性質請您提供必要的個人資料,並在該特定目的範圍內處理及利用您的個人資料;非經您書面同意,本APP不會將個人資料用於其他用途。
本APP在您使用服務信箱、問卷調查等互動性功能時,會保留您所提供的姓名、電子郵件地址、聯絡方式及使用時間等。
於一般瀏覽時,伺服器會自行記錄相關行徑,包括您使用連線設備的IP位址、使用時間、使用的瀏覽器、瀏覽及點選資料記錄等,做為我們增進網站服務的參考依據,此記錄為內部應用,決不對外公佈。
為提供精確的服務,我們會將收集的問卷調查內容進行統計與分析,分析結果之統計數據或說明文字呈現,除供內部研究外,我們會視需要公佈統計數據及說明文字,但不涉及特定個人之資料。
三、資料之保護
本APP主機均設有防火牆、防毒系統等相關的各項資訊安全設備及必要的安全防護措施,加以保護網站及您的個人資料採用嚴格的保護措施,只由經過授權的人員才能接觸您的個人資料,相關處理人員皆簽有保密合約,如有違反保密義務者,將會受到相關的法律處分。
如因業務需要有必要委託其他單位提供服務時,本APP亦會嚴格要求其遵守保密義務,並且採取必要檢查程序以確定其將確實遵守。
四、網站對外的相關連結
本APP的網頁提供其他網站的網路連結,您也可經由本APP所提供的連結,點選進入其他網站。但該連結網站不適用本APP的隱私權保護政策,您必須參考該連結網站中的隱私權保護政策。
五、與第三人共用個人資料之政策
本APP絕不會提供、交換、出租或出售任何您的個人資料給其他個人、團體、私人企業或公務機關,但有法律依據或合約義務者,不在此限。
前項但書之情形包括不限於:
*經由您書面同意。
*法律明文規定。
*為免除您生命、身體、自由或財產上之危險。
*與公務機關或學術研究機構合作,基於公共利益為統計或學術研究而有必要,且資料經過提供者處理或蒐集著依其揭露方式無從識別特定之當事人。
*當您在網站的行為,違反服務條款或可能損害或妨礙網站與其他使用者權益或導致任何人遭受損害時,經網站管理單位研析揭露您的個人資料是為了辨識、聯絡或採取法律行動所必要者。
*有利於您的權益。
*本APP委託廠商協助蒐集、處理或利用您的個人資料時,將對委外廠商或個人善盡監督管理之責。
六、Cookie之使用
為了提供您最佳的服務,本APP會在您的電腦中放置並取用我們的Cookie,若您不願接受Cookie的寫入,您可在您使用的瀏覽器功能項中設定隱私權等級為高,即可拒絕Cookie的寫入,但可能會導至網站某些功能無法正常執行 。
七、隱私權保護政策之修正
本APP隱私權保護政策將因應需求隨時進行修正,修正後的條款將刊登於網站上。
分類
Uncategorized

Refactoring for LogoFlow.java

LogoFlow 是極簡黑記帳本(SimpleBlackNote app)的起始 Activity,當初寫的時候並沒有 refactoring 以及 readable code 的概念。
剛好可以拿來練習重構。
以下為原始程式碼,可以看到其中充滿許多 bad smell。e.g. large class(過大的類別) ,duplicate code(重複的程式碼),long method(過長的函式) ,
commit(過多的註釋),大型的if-else條件式,不適當的封裝等等 。

package com.foxx.main;
import java.util.Random;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.MotionEvent;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.google.ads.AdRequest;
import com.google.ads.AdSize;
import com.google.ads.AdView;
import com.tlyerfoxx.sbcnote.R;
@SuppressLint("NewApi")
public class LogoFlow extends Activity implements Runnable{
	private ImageView mLogo;
	private Handler mHandler;
	private float mLogoActIndex;
	private boolean mIsSleeping;
	private ValueAnimator mScaleX;
	private ValueAnimator mScaleY;
	private ValueAnimator mRotationY;
	private ValueAnimator mRotationX;
	private AnimatorSet mScalexy;
	public void onCreate(Bundle savedInstanceState){
		super.onCreate(savedInstanceState);
		setContentView(R.layout.logoflow);
//		showAdmob();
		mLogo = (ImageView)findViewById(R.id.imageView1);
//		useThreadFadeIn();
		Random r = new Random();
		useAnimfadein2(r.nextInt(4));
	}
	public void run() {
		// TODO Auto-generated method stub
		while(!mIsSleeping){
			try {
				Thread.sleep(30);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			Message m = new Message();
            mHandler.sendMessage(m);
		}
	}
	private void useThreadFadeIn(){
		mLogo.setAlpha(0.0f);
		mHandler = new Handler(){
			public void handleMessage(Message msg) {
				Log.v("Trace Log", "revice maessage");
				if(mLogo.getAlpha()<1.0f){
					mLogoActIndex+=0.01f;
					mLogo.setAlpha(mLogoActIndex);
					Log.v("Trace Log", "test2");
				}
				else if(mLogo.getAlpha()>=1.0f){
					LogoFlow.this.finish(); // close activity
					Log.v("Trace Log", "test3");
					mIsSleeping = true;// stop Thread
					Intent goAct = new Intent();// new a Intent
					goAct.setClass(LogoFlow.this, Table_Flow.class); // set another activity
					startActivity(goAct); // start another Activity
					System.exit(0);// stop program
				}
				Log.v("Trace Log", ""+mLogo.getAlpha());
                super.handleMessage(msg);
            }
		};
		Thread th = new Thread(this);
		th.start();
	}
	void useAnimfadein(){
		ValueAnimator fadein = ObjectAnimator.ofFloat(mLogo, "alpha", 0f, 1f);
        fadein.setDuration(2000);
        ValueAnimator standby = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 1f);
        standby.setDuration(1000);
        ValueAnimator fadeout = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 0f);
        fadeout.setDuration(2000);
        AnimatorSet bouncer = new AnimatorSet();
        bouncer.playSequentially(fadein,standby,fadeout);
        bouncer.start();
        bouncer.addListener(new AnimatorListener() {
			@Override
			public void onAnimationStart(Animator animation) {
				// TODO Auto-generated method stub
			}
			@Override
			public void onAnimationRepeat(Animator animation) {
				// TODO Auto-generated method stub
			}
			@Override
			public void onAnimationEnd(Animator animation) {
				// TODO Auto-generated method stub
				LogoFlow.this.finish(); // close activity
				mIsSleeping = true;// stop Thread
				Intent goAct = new Intent();// new a Intent
				goAct.setClass(LogoFlow.this, CopyRightFlow.class); // set another activity
				startActivity(goAct); // start another Activity
				System.exit(0);// stop program
			}
			@Override
			public void onAnimationCancel(Animator animation) {
				// TODO Auto-generated method stub
			}
		});
	}
	void useAnimfadein2(int type){
		if(type==0){
			mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 0.1f, 1f);
			mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 0.1f, 1f);
			mScaleX.setDuration(2500);
			mScaleY.setDuration(1250);
			mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
			mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
			mRotationY.setDuration(2500);
	        mRotationX.setDuration(1250);
	        mScalexy = new AnimatorSet();
			mScalexy.playTogether(mScaleX,mScaleY,mRotationY,mRotationX);
		}
		else if(type==1){
			mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 2.0f, 1f);
			mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 2.0f, 1f);
			mScaleX.setDuration(2500);
			mScaleY.setDuration(1250);
			mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
			mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
			mRotationY.setDuration(2500);
	        mRotationX.setDuration(1250);
	        mScalexy = new AnimatorSet();
			mScalexy.playTogether(mScaleX,mScaleY,mRotationY,mRotationX);
		}
		else if(type==2){
			mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
			mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
			mScaleX.setDuration(2500);
			mScaleY.setDuration(1250);
			mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 720f);
			mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 0f);
			mRotationY.setDuration(2500);
	        mRotationX.setDuration(1250);
	        mScalexy = new AnimatorSet();
			mScalexy.playTogether(mScaleX,mScaleY,mRotationY,mRotationX);
		}
		else if(type==3){
			mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
			mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
			mScaleX.setDuration(2500);
			mScaleY.setDuration(1250);
			mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 0f);
			mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 720f);
			mRotationY.setDuration(2500);
	        mRotationX.setDuration(1250);
	        mScalexy = new AnimatorSet();
			mScalexy.playTogether(mScaleX,mScaleY,mRotationY,mRotationX);
		}
        ValueAnimator standby = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 1f);
        standby.setDuration(1000);
        ValueAnimator fadeout = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 0f);
        fadeout.setDuration(1000);
        AnimatorSet bouncer = new AnimatorSet();
        bouncer.playSequentially(mScalexy,standby,fadeout);
        bouncer.start();
        bouncer.addListener(new AnimatorListener() {
			@Override
			public void onAnimationStart(Animator animation) {
				// TODO Auto-generated method stub
			}
			@Override
			public void onAnimationRepeat(Animator animation) {
				// TODO Auto-generated method stub
			}
			@Override
			public void onAnimationEnd(Animator animation) {
				// TODO Auto-generated method stub
				LogoFlow.this.finish(); // close activity
				mIsSleeping = true;// stop Thread
				Intent goAct = new Intent();// new a Intent
				goAct.setClass(LogoFlow.this, Table_Flow.class); // set another activity CopyRightFlow.class
				startActivity(goAct); // start another Activity
				overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);//�ϥιL��,��1�ӰѼƬO�U1�ӳ������i�J,��2�ӰѼƬO�������������}
				System.exit(0);// stop program
			}
			@Override
			public void onAnimationCancel(Animator animation) {
				// TODO Auto-generated method stub
			}
		});
	}
	void showAdmob(){
		 AdView adView;
//   	  Create the adView
    	adView = new AdView(this, AdSize.BANNER, "a150189a3abeb1a");
    	LinearLayout layout = (LinearLayout)findViewById(R.id.AdLayout);
    	layout.addView(adView);
    	adView.loadAd(new AdRequest());
	}
	public boolean onTouchEvent(MotionEvent event){
//		FP.p("in onTouchEvent");
		LogoFlow.this.finish(); // close activity
		mIsSleeping = true;// stop Thread
		Intent goAct = new Intent();// new a Intent
		goAct.setClass(LogoFlow.this, Table_Flow.class); // set another activity CopyRightFlow.class
		startActivity(goAct); // start another Activity
		overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);//�ϥιL��,��1�ӰѼƬO�U1�ӳ������i�J,��2�ӰѼƬO�������������}
		System.exit(0);// stop program
		switch(event.getAction()){
		case MotionEvent.ACTION_DOWN:
//			dx = (int)event.getX();
			break;
		case MotionEvent.ACTION_UP:
			break;
		}
			return true;
	}
}

以下將一步一步進行重構。
首先從適當的封裝開始,觀察類別屬性以及函式是否為useless,或是可以縮小存取權限以避免讓外界知道不必要的細節。
可以發現第77行的useThreadFadeIn,第119行的useAnimfadein,第282的showAdmob 這3個函式都沒有使用,沒有使用函式就是多餘的函式。
直接刪除吧。
刪完沒有使用的函式之後,logoActIndex 變數也沒有使用,另外刪掉多餘的 import 以及註解。
最後為類別屬性加上適當的重新命名之後。整個LogoFlow類別會從324行縮短到 175行。e.g.

package com.foxx.main;
import java.util.Random;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.MotionEvent;
import android.widget.ImageView;
import com.tlyerfoxx.sbcnote.R;
@SuppressLint("NewApi")
public class LogoFlow extends Activity implements Runnable
{
    private ImageView mLogo;
    private Handler mHandler;
    private boolean mIsSleeping;
    private ValueAnimator mScaleX;
    private ValueAnimator mScaleY;
    private ValueAnimator mRotationY;
    private ValueAnimator mRotationX;
    private AnimatorSet mScalexy;
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.logoflow);
        mLogo = (ImageView) findViewById(R.id.imageView1);
        Random r = new Random();
        useAnimfadein2(r.nextInt(4));
    }
    public void run()
    {
        while (!mIsSleeping) {
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message m = new Message();
            mHandler.sendMessage(m);
        }
    }
    void useAnimfadein2(int type)
    {
        if (type == 0) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 0.1f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 0.1f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        } else if (type == 1) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 2.0f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 2.0f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        } else if (type == 2) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 720f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 0f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        } else if (type == 3) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 0f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 720f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        }
        ValueAnimator standby = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 1f);
        standby.setDuration(1000);
        ValueAnimator fadeout = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 0f);
        fadeout.setDuration(1000);
        AnimatorSet bouncer = new AnimatorSet();
        bouncer.playSequentially(mScalexy, standby, fadeout);
        bouncer.start();
        bouncer.addListener(new AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation)
            {
            }
            @Override
            public void onAnimationRepeat(Animator animation)
            {
            }
            @Override
            public void onAnimationEnd(Animator animation)
            {
                LogoFlow.this.finish();
                mIsSleeping = true;// stop Thread
                Intent goAct = new Intent();
                goAct.setClass(LogoFlow.this, Table_Flow.class);
                startActivity(goAct);
                overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
                System.exit(0);// stop program
            }
            @Override
            public void onAnimationCancel(Animator animation)
            {
            }
        });
    }
    public boolean onTouchEvent(MotionEvent event)
    {
        LogoFlow.this.finish();
        mIsSleeping = true;// stop Thread
        Intent goAct = new Intent();
        goAct.setClass(LogoFlow.this, Table_Flow.class);
        startActivity(goAct);
        overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
        System.exit(0);// stop program
        return true;
    }
}

接著我們將目標轉向第61行的 useAnimfadein2 函式,因為它實在太長了,過長的函式通常不只做一件事,包含太多職責,讓函式的意圖不明。
先從名稱開始,useAnimfadein2大概可以猜出跟動畫淡入有關,而2是個數字命名法,是一個不好的命名習慣,因為你沒辦法從2得到訊息。
基本上也沒有人知道誰使用動畫淡入在誰身上,因此我們只能從函式內容看起。
從第64 到 125行應該是屬於動畫的參數設定,從參數上可以看出是mLogo使用動畫。
而第127行開始執行動畫,第129到159則是設定動畫的監聽器。
因此我們可以把 useAnimfadein2 更名為 runLogoAnimations 並拆分為3個函式 , setAnimationParameters , createAnimationListener。e.g.

    private void runLogoAnimations(int animationType)
    {
        AnimatorSet animation = new AnimatorSet();
        animation.playSequentially(setAnimationParameters(animationType));
        animation.start();
        animation.addListener(createAnimationListener());
    }
    private List<Animator> setAnimationParameters(int type)
    {
        List<Animator> animators = new ArrayList<Animator>();
        if (type == 0) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 0.1f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 0.1f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        } else if (type == 1) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 2.0f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 2.0f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        } else if (type == 2) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 720f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 0f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        } else if (type == 3) {
            mScaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
            mScaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
            mScaleX.setDuration(2500);
            mScaleY.setDuration(1250);
            mRotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 0f);
            mRotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 720f);
            mRotationY.setDuration(2500);
            mRotationX.setDuration(1250);
            mScalexy = new AnimatorSet();
            mScalexy.playTogether(mScaleX, mScaleY, mRotationY, mRotationX);
        }
        ValueAnimator standby = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 1f);
        standby.setDuration(1000);
        ValueAnimator fadeout = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 0f);
        fadeout.setDuration(1000);
        animators.add(mScalexy);
        animators.add(standby.setDuration(1000));
        animators.add(fadeout);
        return animators;
    }
    private AnimatorListener createAnimationListener()
    {
        return new AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation)
            {
            }
            @Override
            public void onAnimationRepeat(Animator animation)
            {
            }
            @Override
            public void onAnimationEnd(Animator animation)
            {
                LogoFlow.this.finish();
                mIsSleeping = true;// stop Thread
                Intent goAct = new Intent();
                goAct.setClass(LogoFlow.this, Table_Flow.class);
                startActivity(goAct);
                overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
                System.exit(0);// stop program
            }
            @Override
            public void onAnimationCancel(Animator animation)
            {
            }
        };
    }

接著我們再從 setAnimationParameters 來看看,是否有可以改進的地方。
首先 setAnimationParameters 有個旗標參數,函式會根據旗標參數執行不同的行為。這也代表該方法做了不只一件事。
觀察 mSaleX , mScaleY,mRotationX , mRotationY , mScaleXY , 可以看到這四個類別屬性僅僅使用在setAnimationParameters函式內。
而不需要在類別的函式之間使用。因此可以將他們改為暫時變數,為了縮短生命週期。

    private List<Animator> setAnimationParameters(int type)
    {
        ValueAnimator scaleX;
        ValueAnimator scaleY;
        ValueAnimator rotationY;
        ValueAnimator rotationX;
        AnimatorSet scaleXY = null;
        if (type == 0) {
            scaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 0.1f, 1f);
            scaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 0.1f, 1f);
            scaleX.setDuration(2500);
            scaleY.setDuration(1250);
            rotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
            rotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
            rotationY.setDuration(2500);
            rotationX.setDuration(1250);
            scaleXY = new AnimatorSet();
            scaleXY.playTogether(scaleX, scaleY, rotationY, rotationX);
        } else if (type == 1) {
            scaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 2.0f, 1f);
            scaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 2.0f, 1f);
            scaleX.setDuration(2500);
            scaleY.setDuration(1250);
            rotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 360f);
            rotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 360f);
            rotationY.setDuration(2500);
            rotationX.setDuration(1250);
            scaleXY = new AnimatorSet();
            scaleXY.playTogether(scaleX, scaleY, rotationY, rotationX);
        } else if (type == 2) {
            scaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
            scaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
            scaleX.setDuration(2500);
            scaleY.setDuration(1250);
            rotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 720f);
            rotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 0f);
            rotationY.setDuration(2500);
            rotationX.setDuration(1250);
            scaleXY = new AnimatorSet();
            scaleXY.playTogether(scaleX, scaleY, rotationY, rotationX);
        } else if (type == 3) {
            scaleX = ObjectAnimator.ofFloat(mLogo, "scaleX", 1f, 1f);
            scaleY = ObjectAnimator.ofFloat(mLogo, "scaleY", 1f, 1f);
            scaleX.setDuration(2500);
            scaleY.setDuration(1250);
            rotationY = ObjectAnimator.ofFloat(mLogo, "rotationY", 0f, 0f);
            rotationX = ObjectAnimator.ofFloat(mLogo, "rotationX", 0f, 720f);
            rotationY.setDuration(2500);
            rotationX.setDuration(1250);
            scaleXY = new AnimatorSet();
            scaleXY.playTogether(scaleX, scaleY, rotationY, rotationX);
        }
        ValueAnimator standby = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 1f);
        standby.setDuration(1000);
        ValueAnimator fadeout = ObjectAnimator.ofFloat(mLogo, "alpha", 1f, 0f);
        fadeout.setDuration(1000);
        List<Animator> animators = new ArrayList<Animator>();
        animators.add(scaleXY);
        animators.add(standby.setDuration(1000));
        animators.add(fadeout);
        return animators;
    }

觀察其中的條件式(第9~61),可以發現每個條件式的執行區塊,都做了2件事,首先是設定XY的縮放動畫,再來設定旋轉動畫。
但不管是設定縮放還是設定旋轉,都是使用類似的參數。為了減少重複的程式碼,我們可以另外建立通用的函式 setAnimation。
並把 if-else 條件式內容提取出來以增加可讀性。e.g.

    private ValueAnimator setAnimation(float start, float end, String propertyName, int durationMS)
    {
        ValueAnimator animation;
        animation = ObjectAnimator.ofFloat(mLogo, propertyName, start, end);
        animation.setDuration(durationMS);
        return animation;
    }
    private AnimatorSet zoomInAndRotation360()
    {
        AnimatorSet animations;
        ValueAnimator scaleX = setAnimation(0.1f, 1f, "scaleX", 2500);
        ValueAnimator scaleY = setAnimation(0.1f, 1f, "scaleY", 1250);
        ValueAnimator rotationY = setAnimation(0f, 360f, "rotationY", 2500);
        ValueAnimator rotationX = setAnimation(0f, 360f, "rotationX", 1250);
        animations = new AnimatorSet();
        animations.playTogether(scaleX, scaleY, rotationY, rotationX);
        return animations;
    }
    private AnimatorSet zoomOutAndRotation360()
    {
        AnimatorSet animations;
        ValueAnimator scaleX = setAnimation(2.0f, 1f, "scaleX", 2500);
        ValueAnimator scaleY = setAnimation(2.0f, 1f, "scaleY", 1250);
        ValueAnimator rotationY = setAnimation(0f, 360f, "rotationY", 2500);
        ValueAnimator rotationX = setAnimation(0f, 360f, "rotationX", 1250);
        animations = new AnimatorSet();
        animations.playTogether(scaleX, scaleY, rotationY, rotationX);
        return animations;
    }
    private AnimatorSet rotationYAxis720()
    {
        AnimatorSet animations;
        ValueAnimator rotationY = setAnimation(0f, 720f, "rotationY", 2500);
        animations = new AnimatorSet();
        animations.playTogether(rotationY);
        return animations;
    }
    private AnimatorSet rotationXAxis720()
    {
        AnimatorSet animations;
        ValueAnimator rotationX = setAnimation(0f, 720f, "rotationX", 2500);
        animations = new AnimatorSet();
        animations.playTogether(rotationX);
        return animations;
    }
    private List<Animator> setAnimationParameters(int type)
    {
        AnimatorSet animations = null;
        if (type == 0) {
            animations = zoomInAndRotation360();
        } else if (type == 1) {
            animations = zoomOutAndRotation360();
        } else if (type == 2) {
            animations = rotationYAxis720();
        } else if (type == 3) {
            animations = rotationXAxis720();
        }
        ValueAnimator alphaNormal = setAnimation(1f, 1f, "alpha", 1000);
        ValueAnimator fadeOut = setAnimation(1f, 0f, "alpha", 1000);
        List<Animator> animators = new ArrayList<Animator>();
        animators.add(animations);
        animators.add(alphaNormal);
        animators.add(fadeOut);
        return animators;
    }

OK~ 原先的setAnimationParameters,被我們拆分為一小段一小段的函式,小段的函式除了可以增加重用的機會以外,而明確的函式名稱完整表達了其意圖。
而且經過重構可以了解正確的程式碼意圖。為了提昇可讀性,第52~64的 if-else 決定引入列舉(AnimationType)來取代原本的int。e.g.

    ...
    private enum AnimationType {
        ZOOMIN_AND_ROTATION360, ZOOMOUT_AND_ROTATION360, ROTATION_Y_AXIS720, ROTATION_X_AXIS720
    }
    private List<Animator> setAnimationParameters(int type)
    {
        AnimationType animationType = AnimationType.values()[type];
        AnimatorSet animations = null;
        if (animationType == AnimationType.ZOOMIN_AND_ROTATION360) {
            animations = zoomInAndRotation360();
        } else if (animationType == AnimationType.ZOOMOUT_AND_ROTATION360) {
            animations = zoomOutAndRotation360();
        } else if (animationType == AnimationType.ROTATION_Y_AXIS720) {
            animations = rotationYAxis720();
        } else if (animationType == AnimationType.ROTATION_X_AXIS720) {
            animations = rotationXAxis720();
        }
        ValueAnimator alphaNormal = setAnimation(1f, 1f, "alpha", 1000);
        ValueAnimator fadeOut = setAnimation(1f, 0f, "alpha", 1000);
        List<Animator> animators = new ArrayList<Animator>();
        animators.add(animations);
        animators.add(alphaNormal);
        animators.add(fadeOut);
        return animators;
    }
...

現在我們再來看看 mHandler 的用途,看起來它在 run 函式中呼叫了 sendMessage。讓 Logo 發生圖形的變化。
事實上讓 LogoFlow 繼承 Runnable interface 並不是一個好的作法。
簡單的說為了避免 ANR,一般的耗時任務都應該放到Worker Thread 而不可在 UI Thread 執行(詳細內容參考android worker thread)。
因此另外將圖形處理獨立到另一個 LogoAnimTask 來處理。e.g.

package com.foxx.main;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.os.AsyncTask;
import com.foxx.main.LogoFlow.AnimationType;
@SuppressLint("NewApi")
public class LogoAnimTask extends AsyncTask<Void, Void, Void>
{
    private ILogoFlow mLogoFlow;
    public LogoAnimTask(ILogoFlow logoFlow){
        mLogoFlow = logoFlow;
    }
    @Override
    protected void onPreExecute()
    {
        Random randomAnimationType = new Random();
        runLogoAnimations(randomAnimationType.nextInt(4));
    }
    @Override
    protected Void doInBackground(Void... params)
    {
        return null;
    }
    private void runLogoAnimations(int animationType)
    {
        AnimatorSet animation = new AnimatorSet();
        animation.playSequentially(setAnimationParameters(animationType));
        animation.start();
        animation.addListener(createAnimationListener());
    }
    private ValueAnimator setAnimation(float start, float end, String propertyName, int durationMS)
    {
        ValueAnimator animation;
        animation = ObjectAnimator.ofFloat(mLogoFlow.getLogo(), propertyName, start, end);
        animation.setDuration(durationMS);
        return animation;
    }
    private AnimatorSet zoomInAndRotation360()
    {
        AnimatorSet animations;
        ValueAnimator scaleX = setAnimation(0.1f, 1f, "scaleX", 2500);
        ValueAnimator scaleY = setAnimation(0.1f, 1f, "scaleY", 1250);
        ValueAnimator rotationY = setAnimation(0f, 360f, "rotationY", 2500);
        ValueAnimator rotationX = setAnimation(0f, 360f, "rotationX", 1250);
        animations = new AnimatorSet();
        animations.playTogether(scaleX, scaleY, rotationY, rotationX);
        return animations;
    }
    private AnimatorSet zoomOutAndRotation360()
    {
        AnimatorSet animations;
        ValueAnimator scaleX = setAnimation(2.0f, 1f, "scaleX", 2500);
        ValueAnimator scaleY = setAnimation(2.0f, 1f, "scaleY", 1250);
        ValueAnimator rotationY = setAnimation(0f, 360f, "rotationY", 2500);
        ValueAnimator rotationX = setAnimation(0f, 360f, "rotationX", 1250);
        animations = new AnimatorSet();
        animations.playTogether(scaleX, scaleY, rotationY, rotationX);
        return animations;
    }
    private AnimatorSet rotationYAxis720()
    {
        AnimatorSet animations;
        ValueAnimator rotationY = setAnimation(0f, 720f, "rotationY", 2500);
        animations = new AnimatorSet();
        animations.playTogether(rotationY);
        return animations;
    }
    private AnimatorSet rotationXAxis720()
    {
        AnimatorSet animations;
        ValueAnimator rotationX = setAnimation(0f, 720f, "rotationX", 2500);
        animations = new AnimatorSet();
        animations.playTogether(rotationX);
        return animations;
    }
    private List<Animator> setAnimationParameters(int type)
    {
        AnimationType animationType = AnimationType.values()[type];
        AnimatorSet animations = null;
        if (animationType == AnimationType.ZOOMIN_AND_ROTATION360) {
            animations = zoomInAndRotation360();
        } else if (animationType == AnimationType.ZOOMOUT_AND_ROTATION360) {
            animations = zoomOutAndRotation360();
        } else if (animationType == AnimationType.ROTATION_Y_AXIS720) {
            animations = rotationYAxis720();
        } else if (animationType == AnimationType.ROTATION_X_AXIS720) {
            animations = rotationXAxis720();
        }
        ValueAnimator alphaNormal = setAnimation(1f, 1f, "alpha", 1000);
        ValueAnimator fadeOut = setAnimation(1f, 0f, "alpha", 1000);
        List<Animator> animators = new ArrayList<Animator>();
        animators.add(animations);
        animators.add(alphaNormal);
        animators.add(fadeOut);
        return animators;
    }
    private AnimatorListener createAnimationListener()
    {
        return new AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation)
            {
            }
            @Override
            public void onAnimationRepeat(Animator animation)
            {
            }
            @Override
            public void onAnimationEnd(Animator animation)
            {
                mLogoFlow.finishLogo();
            }
            @Override
            public void onAnimationCancel(Animator animation)
            {
            }
        };
    }
}

另外建立ILogoFlow interface 並讓外界透過 interface 來交互,目的是讓模組透過抽象來相依。

public interface ILogoFlow
{
    public View getLogo();
    public void finishLogoFlow();
}

而原本的 LogoFlow 只剩下輕量化的內容。e.g.

package com.foxx.main;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import com.tlyerfoxx.sbcnote.R;
@SuppressLint("NewApi")
public class LogoFlow extends Activity implements ILogoFlow
{
    private ImageView mLogo;
    private LogoAnimTask mLogoAnimationtask;
    public enum AnimationType {
        ZOOMIN_AND_ROTATION360, ZOOMOUT_AND_ROTATION360, ROTATION_Y_AXIS720, ROTATION_X_AXIS720
    }
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.logoflow);
        mLogo = (ImageView) findViewById(R.id.imageView1);
        mLogoAnimationtask = new LogoAnimTask(this);
        mLogoAnimationtask.execute();
    }
    public boolean onTouchEvent(MotionEvent event)
    {
        finishLogoFlow();
        return true;
    }
    @Override
    public View getLogo()
    {
        return mLogo;
    }
    @Override
    public void finishLogoFlow()
    {
        LogoFlow.this.finish();
        Intent goAct = new Intent();
        goAct.setClass(LogoFlow.this, Table_Flow.class);
        startActivity(goAct);
        overridePendingTransition(R.anim.zoom_enter, R.anim.zoom_exit);
        System.exit(0);
    }
}

最後重新命名 LogoFlow 為 LogoActivity,ILogoFlow 為 ILogo。
 
 
 
 
 
 
 
 
 
 
 
 
 

分類
Uncategorized

分析 hello-jni 範例

這裡以hello-Jni為範例,紀錄如何呼叫原生方法,宣告原生方法,載入原生模組,在c/c++實作原生方法。
以下為 HelloJni.java

package com.example.hellojni;
import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle;
public class HelloJni extends Activity
{
    /* this is used to load the 'hello-jni' library on application
     * startup. The library has already been unpacked into
     * /data/data/com.example.hellojni/lib/libhello-jni.so at
     * installation time by the package manager.
     */
    static {
        System.loadLibrary("hello-jni");
    }
    /* A native method that is implemented by the
     * 'hello-jni' native library, which is packaged
     * with this application.
     */
    public native String  stringFromJNI();
    /* This is another native method declaration that is *not*
     * implemented by 'hello-jni'. This is simply to show that
     * you can declare as many native methods in your Java code
     * as you want, their implementation is searched in the
     * currently loaded native libraries only the first time
     * you call them.
     *
     * Trying to call this function will result in a
     * java.lang.UnsatisfiedLinkError exception !
     */
    public native String  unimplementedStringFromJNI();
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        /* Create a TextView and set its content.
         * the text is retrieved by calling a native
         * function.
         */
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }
}

第16~18行為載入原生模組的動作,注意使用 System.loadLibrary(“hello-jni”)指的是載入 java.library.path變數所包含路徑的 hello-jni,不必包含擴展名稱(lib)(.so),
而”hello-jni”必須與 Android.mk 裡的 LOCAL_MODULE 變數值(hello-jni)相同,另外要注意是原生模組的名稱要避免重複。
第24行宣告原生方法 stringFromJni()
第36行宣告原生方法unimplementedStringFromJni()
 
接著來看看 hello-jni.c 的內容。

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <string.h>
#include <jni.h>
/* This is a trivial JNI example where we use a native method
 * to return a new VM String. See the corresponding Java source
 * file located at:
 *
 *   apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
 */
jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

第26~30行即為原生方法stringFromJNI()的實作,注意

Java_com_example_hellojni_HelloJni_stringFromJNI

除了開頭的 Java_ 以外,原生方法的名稱必須完全對應於java層的位置以及名稱,e.g.
com_example_hellojni :路徑
HelloJni:類別名稱
stringFromJNI:方法名稱


 
而回傳值 jstring 對應於 java層的 String
參數部份java層不帶參數,而對應的原生方法必須帶入 JNIEnv* env , jobject thiz 2個參數。
JNIEnv* env 必須為原生方法的第一個參數,透過 env 可以使用虛擬機的許多方法(ref:http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html)。
注意幾點:
1.在 C 中,使用env的方式必須為 (*env)->method,
而 C++ 則是 env->method。
 
2.靜態方法和實體方法,在java層中如果是實體方法,JNI層第2個參數為 jobject thiz,
在java層中如果是靜態方法,JNI層第2個參數則為 jclass clazz。
 
 

分類
Uncategorized

CheckStyle 下載 編譯 執行

這裡紀錄測試後可以成功動作的部份。

使用Command Line執行

java -jar /path/of/checkstyle-x.x-all.jar -c /path/of/config.xml /path/of/files_you_want_to_check
 
ps:
以下路徑皆為完整路徑
/path/of/checkstyle-x.x-SNAPSHOT-all.jar 可以到官網下載。
/path/of/config.xml 為使用的config.xml 也可以到官網下載。
/path/of/files_you_want_to_check 為想要檢查的檔案,可以倒退到存放所有java source code 檔案位置,它會自動檢查裡面所有的檔案。


 

 

下載並編譯CheckStyle

這裡跟官網的步驟相同,需要注意的是如果想要產生checkstyle-X.X-SNAPSHOT-all.jar,必須使用mvn clean package -Passembly
Download and compile:
git clone https://github.com/checkstyle/checkstyle.git
cd checkstyle
mvn clean compile 
 
ps: mvn compile or package 無法產生checkstyle-X.X-SNAPSHOT-all.jar,只有使用mvn clean package -Passembly 才能產生


 

使用Maven 啟動 CheckStyle

Run validation with arguments:

cd /path/of/checkstyle_root_path
mvn exec:java -Dexec.mainClass=”com.puppycrawl.tools.checkstyle.Main” -Dexec.args=”-c /sun_checks.xml src/main/java ”
 

Run UI application for file :

cd /path/of/checkstyle_root_path
mvn exec:java -Dexec.mainClass=”com.puppycrawl.tools.checkstyle.gui.Main” -Dexec.args=”src/main/java/com/puppycrawl/tools/checkstyle/Checker.java”


 

Build all jars

cd /path/of/checkstyle_root_path
mvn clean package -Passembly
 
ps:會產生
/target/checkstyle-x.x-SNAPSHOT.jar
/target/checkstyle-x.x-SNAPSHOT-all.jar
/target/checkstyle-x.x-SNAPSHOT-bin.tar.gz
/target/checkstyle-x.x-SNAPSHOT-bin.zip
/target/checkstyle-x.x-SNAPSHOT-src.tar.gz
/target/checkstyle-x.x-SNAPSHOT-src.zip
/target/checkstyle-x.x-SNAPSHOT-tests.jar


 
 
 

分類
Android Uncategorized

查表法(表驅動法)(Table-Driven-Methods )

查表法(Table-Driven-Method)主要是藉由表格的維度和對應的數值來替代冗長的判斷式(if-else or switch)
查表法根據不同的查表方式,可分為三種:
直接存取(direct access):可從表格中直接讀取數值。
索引存取(indexed access):先透過索引取出key值,再從key值取出數值。
階梯存取(stair-step access):對於不同的數值範圍有效。
 


 
直接存取:
如果要查詢月份對應的天數,最直接的寫法 e.g.,

    private int getDaysOfMonth(int month)
    {
        if (month == 1) {
            return 31;
        } else if (month == 2) {
            return 28;
        } else if (month == 3) {
            return 31;
        } else if (month == 4) {
            return 30;
        } else if (month == 5) {
            return 31;
        } else if (month == 6) {
            return 30;
        } else if (month == 7) {
            return 31;
        } else if (month == 8) {
            return 31;
        } else if (month == 9) {
            return 30;
        } else if (month == 10) {
            return 31;
        } else if (month == 11) {
            return 30;
        } else if (month == 12) {
            return 31;
        } else {
            throw new IllegalArgumentException();
        }
    }

可以藉由以下表格來取代

private static final int[] DAYS_OF_MONTH = {
            31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};

取值如下,第7行為使用查表法的動作。

    @Test
    public void testGetDayOfMonth()
    {
        int[] months = {1,2,3,4,5,6,7,8,9,10,11,12};
        for(int month : months){
            int resultBySwitch = getDaysOfMonth(month);
            int resultByTable = DAYS_OF_MONTH[month - 1];
            assertEquals(resultBySwitch, resultByTable);
        }
    }

 


 
索引存取:
如果條件判斷式的變數型態不是int , 就無法使用陣列來建表,必須改用Map,而 Map 的 key 值即是索引值。e.g.

    private int getDayOfMonthByName(String nameOfMonth)
    {
        if (nameOfMonth.equals("Jan")) {
            return 31;
        } else if (nameOfMonth.equals("Feb")) {
            return 28;
        } else if (nameOfMonth.equals("Mar")) {
            return 31;
        } else if (nameOfMonth.equals("Apr")) {
            return 30;
        } else if (nameOfMonth.equals("May")) {
            return 31;
        } else if (nameOfMonth.equals("Jun")) {
            return 30;
        } else if (nameOfMonth.equals("Jul")) {
            return 31;
        } else if (nameOfMonth.equals("Aug")) {
            return 31;
        } else if (nameOfMonth.equals("Sep")) {
            return 30;
        } else if (nameOfMonth.equals("Oct")) {
            return 31;
        } else if (nameOfMonth.equals("Nov")) {
            return 30;
        } else if (nameOfMonth.equals("Dec")) {
            return 31;
        } else {
            throw new IllegalArgumentException();
        }
    }

使用Map取值,第1到10行為初始化Map , 第14行為使用Map取值。

        Map<String, Integer> DAY_OF_MONTH = new HashMap<String,Integer>();
        String[] nameOfMonth = new String[] {
                "Jan", "Feb", "Mar", "Apr", "May","Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
        };
        Integer[] numberDayOfMonth = {
                31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
        };
        for (int i = 0; i < nameOfMonth.length; ++i) {
            DAY_OF_MONTH.put(nameOfMonth[i], numberDayOfMonth[i]);
        }
        for(String name : nameOfMonth){
            int resultByMethod = getDayOfMonthByName(name);
            int resultByMap = DAY_OF_MONTH.get(name);
            assertEquals(resultByMethod, resultByMap);
        }

 


 
階梯存取:
假設現在要對不同分數給予等級的判斷, e.g.

    private String getNameOfGradeByMethod(int score)
    {
        if (score >= 90) {
            return "A";
        } else if (score >= 80 && score < 90) {
            return "B";
        } else if (score >= 70 && score < 80) {
            return "C";
        } else if (score >= 60 && score < 70) {
            return "D";
        } else if (score < 60) {
            return "F";
        } else {
            throw new IllegalArgumentException();
        }
    }

需要建立2個對應的table , 以及求值的函式(getNameOfGradeByTable)。 e.g.

    private static final String NAME_OF_GRADE_LEVEL[] = {
            "A", "B", "C", "D", "F"
    };
    private static final int NUMBER_OF_GRADE_LEVEL[] = {
            90, 80, 70, 60
    };
    private String getNameOfGradeByTable(int score)
    {
        int gradeLevel = 0;
        while (NAME_OF_GRADE_LEVEL[gradeLevel] != NAME_OF_GRADE_LEVEL[NAME_OF_GRADE_LEVEL.length - 1]) {
            if (score < NUMBER_OF_GRADE_LEVEL[gradeLevel]) {
                ++gradeLevel;
            } else {
                break;
            }
        }
        return NAME_OF_GRADE_LEVEL[gradeLevel];
    }

雖然整體看起來比判斷式來的長,但若是接下來需要增加更多的判斷如 60, 50, 40, 30, 20, 10。
對於查表法只要在 table 加入相對應數值即可。
也適合用於沒有規則變化的求值。


 
事實上查表法無法去除掉原判斷式的邏輯,它只是把邏輯搬移到表中。表格的複雜度會跟著原判斷式的邏輯成正比。
另外也需要提供空間儲存表格。
查表法可以減少程式碼的長度,但無法簡化程式碼。
對可讀性的幫助並不大,反而需要完全理解表格才能修改原功能或是增加新功能。
因此最適合的情況為冗長但判斷邏輯簡單的條件式。
更複雜的查表法範例 ref