分類
Android Uncategorized

Android Looper + Handler + Message + MessageQueue

本篇紀錄如何在Android中使用 Handler + Looper + Message + Queue。
為了讓App保持快速的回應性(避免產生ANR),長期任務不可執行在 UI thread上,這些長期任務包括,網路存取,讀寫檔案,資料庫存取,圖形處理等等。
在Android中使用Thread和在一般的Java中使用方式相當類似,但有一些同步化問題必須去解決,如 Race Condition, Blocking等等
為了方便解決這些問題,Android定義訊息傳送機制來確保訊息傳送的正確性以及安全性,Android訊息傳送機制包含4個角色,各為Looper,Handler,Message,MessageQueue。
 
Message :
訊息,由一組描述和一些資料物件所組成,可藉由Handler傳送到MessageQueue中,並由Looper取出再回傳給同一個Handler,當需要傳遞參數時,可夾帶在Message中。
建立Message的方式盡量使用Message.obtain(), 或是Handler.obtainMessage(),因為訊息會被放到緩衝池以重複使用。
 
MessageQueue :
訊息佇列,透過Looper建立,內容則是訊息組成,結構為無限制的連結串列,一個Looper只能有一個MessageQueue,
訊息會藉由Handler插入,並由Looper取出傳送到相對應的Handler。
 
Looper :
管理MessageQueue,會將存放於MessageQueue中的訊息依序送出到Handler上,讓Handler作進一步的處理,
Handler必須和Looper連結,否則無法傳送或接收訊息,而Thread也必須和Looper連結,才能讓MessageQueue和Thread相關連。
簡單的說Looper是Thread和MessageQueue的協調者。一個Thread只能有一個Looper。
 
Handler :
訊息處理者,在Android訊息傳送機制中,Handler是最常使用的類別,Handler可建立訊息,插入訊息到訊息佇列中,處理訊息。
一個Looper可以有多個Handler,Looper會自動將Message傳送給對應的Handler。
 
藉由以下的範例來說明彼此之間的互動,範例會盡量少用匿名內部類別來實作,而是把各個元件拆開為單一類別,比較容易了解
範例只有一個button和一個textview組成,當點擊button時,會去擷取 https://www.google.com.tw/ 的網頁資料
並顯示到textview上。
 
首先是 MainActivity,因為網路存取是長期任務,且android在Honeycomb SDK新增了NetworkOnMainThreadException,
若在Main thread(UI thread)進行網路操作,會丟出Exception。所以把網路操作放到CustomizeWorkerThread中執行,
第18行建立 mCustomizeWorkerThread 物件
第38行建立CustomizeWorkerThread物件並連結到參考,傳入this是為了把取得的網頁資料設定回MainActivity的textview
第39行呼叫start()方法以啟動mCustomizeWorkerThread的run方法。
第48行當按鈕按下時,呼叫sendMessageToWorkerHandler方法,該方法會建立message插入到MessageQueue
第55行取得mCustomizeWorkerThread的Handler,
第56行建立Message物件,其中數字2為Message的what欄位數值,當Handler處理Message時可以依此數值判斷行為。
第57行利用Handler將Message物件插入到MessageQueue中
第69行當activity結束時也告知Looper停止處理訊息

package com.foxx.threads;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity implements OnClickListener
{
    private static final String TAG = "MainActivity";
    private Button mStartThreadButton;
    private TextView mResultTextView;
    private CustomizeWorkerThread mCustomizeWorkerThread;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUIComponents();
        startWorkerThread();
    }
    private void initUIComponents()
    {
        mStartThreadButton = (Button) findViewById(R.id.startThread);
        mStartThreadButton.setOnClickListener(this);
        mResultTextView = (TextView) findViewById(R.id.textview);
    }
    private void startWorkerThread()
    {
        mCustomizeWorkerThread = new CustomizeWorkerThread(this);
        mCustomizeWorkerThread.start();
    }
    @Override
    public void onClick(View view)
    {
        int uiId = view.getId();
        switch (uiId) {
            case R.id.startThread:
                sendMessageToWorkerHandler();
                break;
        }
    }
    private void sendMessageToWorkerHandler()
    {
        Handler handler = mCustomizeWorkerThread.getHandler();
        if(handler != null){
            Message message = handler.obtainMessage(2);
            handler.sendMessage(message);
        }
    }
    public TextView getResultTextView()
    {
        return mResultTextView;
    }
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        mCustomizeWorkerThread.getHandler().getLooper().quit();
    }
}

 
以下為CustomizeWorkerThread的內容,
第10行為了取代匿名內部類別,另外建立了CustomizeWorkerHandler,該Handler負責處理從Looper傳送過來的Message
第18行的run方法相當重要,在run方法中連結了Looper,Thread,Handler三者。
第20行的Looper.prepare方法會建立Looper以及在該Looper中的MessageQueue,並連結到當前的Thread(CustomizeWorkerThread)
第21行建立CustomizeWorkerHandler物件並連結Looper
第22行Looper開始循序處理MessageQueue,為無限迴圈。當Message插入到MessageQueue中,Looper就會擷取該Message並發送到相關的Handler。
第25行回傳Handler讓外部可以藉由取得的Handler插入訊息
第31行即為CustomizeWorkerHandler,繼承 Handler
第41行必須複寫HandleMessage方法,當Looper傳送Message時,該方法即會被呼叫(callback)
第43行根據Message的what數值判斷行為
第45行就是網路存取的行為,HttpPageData.getPageData 會回傳google的網頁資料
第46行把取得的網頁資料設定到MainActivity的textview中

package com.foxx.threads;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
public class CustomizeWorkerThread extends Thread
{
    private static final String TAG = "WorkerThread";
    private CustomizeWorkerHandler mHandler;
    private MainActivity mMainActivity;
    public CustomizeWorkerThread(MainActivity activity) {
        mMainActivity = activity;
    }
    @Override
    public void run()
    {
        Looper.prepare();
        mHandler = new CustomizeWorkerHandler(mMainActivity);
        Looper.loop();
    }
    public CustomizeWorkerHandler getHandler()
    {
        return mHandler;
    }
}
class CustomizeWorkerHandler extends Handler
{
    private static final String TAG = "WorkerHandler";
    private MainActivity mMainActivity;
    public CustomizeWorkerHandler(MainActivity activity) {
        mMainActivity = activity;
    }
    public void handleMessage(Message message)
    {
        switch (message.what) {
            case 2:
                String result = HttpPageData.getPageData("https://www.google.com.tw/");
                setResultToMainActivity(result);
                break;
        }
    }
    private void setResultToMainActivity(final String result)
    {
        mMainActivity.runOnUiThread(new Runnable() {
            @Override
            public void run()
            {
                mMainActivity.getResultTextView().setText(result);
            }
        });
    }
}

 
接著簡單描述整個訊息傳送機制的互動過程,

建立訊息傳送機制

在 Activity 中會持有CustomWorkThread的類別成員(mCustomizeWorkerThread),
並在onCreate方法中建立實體,然後呼叫start方法,start方法會啟動CustomizeWorkerThread中的run方法,
該run方法會將worker thread,Looper, Handler 三者連結起來,到目前為止是整個傳遞訊息機制的建立,

發送訊息

在Activity中為了發送訊息到MessageQueue中,必須先建立訊息再藉由取得mCustomerWorkThread的Handler將訊息發送到MessageQueue中。

處理訊息

當訊息已經被發送到MessageQueue中,Looper便會自動擷取訊息,並將訊息發送到相對應的Handler,
相對應的意思為哪個Handler發送訊息,哪個Handler就會接收到訊息。
Handler藉由handleMessage方法來處理訊息。


以下為HttpPageData.java

package com.foxx.threads;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
public class HttpPageData
{
    /**
     * 取得網頁資料
     * @return : 回傳string型態的網頁資料
     */
    public static String getPageData(String httpUrl){
        java.net.CookieManager cm = new java.net.CookieManager();
        java.net.CookieHandler.setDefault(cm);
        URL u = null;
        InputStream in = null;
        InputStreamReader r = null;
        BufferedReader br = null;
        StringBuffer message = null;
        try {
           u = new URL(httpUrl);
           in = u.openStream();
           r = new InputStreamReader(in, "BIG5");//UTF-8
           br = new BufferedReader(r);
           String tempstr = null;
           message = new StringBuffer();
           while ((tempstr = br.readLine()) != null) {
               message.append(tempstr);
           }
        } catch (Exception e) {
           e.getStackTrace();
           System.out.println(e.getMessage());
        } finally {
           try {
              u = null;
              in.close();
              r.close();
              br.close();
           } catch (Exception e) {
           }
    }
        return message.toString();
    }
}

 

分類
Android

Create Test Case for android project(Step by Step)

官網教學,參考 https://developer.android.com/training/testing.html
這裡為紀錄詳細步驟。
Target Class : RoundAmountSaving也就是要測試的Android app(被測目標),該app能幫你計算本金和,你只需要輸入目前儲蓄金額,年利率,以及儲蓄月數
它就能算出儲蓄多久可得到多少錢。
裡面的元件組成相當簡單,只有幾個EditText,Button,一個簡單的計算公式。

package com.foxx.round_amount_saving;
import com.foxx_round_amount_saving.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class RoundAmountSaving extends Activity
{
    private static final int PERCENT = 100;
    private static final int MONTH_COUNT = 12;
    private EditText mInputLend;
    private EditText mInputRate;
    private EditText mInputNumber;
    private Button mSubmit;
    private TextView mAmount;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initLayoutComponents();
    }
    private void initLayoutComponents()
    {
        mInputLend = (EditText) findViewById(R.id.input_lend);
        mInputNumber = (EditText) findViewById(R.id.input_number);
        mInputRate = (EditText) findViewById(R.id.input_rate);
        mSubmit = (Button) findViewById(R.id.submit);
        mSubmit.setOnClickListener(getClickListenerForSubmit());
        mAmount = (TextView) findViewById(R.id.amount);
    }
    private OnClickListener getClickListenerForSubmit()
    {
        return new OnClickListener() {
            @Override
            public void onClick(View v)
            {
                mAmount.setText("" + countAmount());
            }
        };
    }
    private double countAmount()
    {
        int lend = Integer.parseInt(mInputLend.getText().toString());
        double rateOfMonth = Double.parseDouble(mInputRate.getText().toString()) / MONTH_COUNT
                / PERCENT;
        int number = Integer.parseInt(mInputNumber.getText().toString());
        return lend * (Math.pow(rateOfMonth + 1, number));
    }
}

 
開始建立Android Test Project

1. Eclipse -> File -> New -> Other -> type : android test project -> next

 

2. type : RoundAmountSavingTest in project name -> next

 

3. select test target : RoundAmountSaving -> Finish

Note : RoundAmountSaving 是 project Name
完成後會產生android test project,名稱為 RoundAmountSavingTest,其中有設定好的package,接著開始來寫 test case
 

4.建立 junit test case

Eclipse -> File -> New -> Other -> type : junit test case -> next
 

5. 選擇 New Junit 3 test,輸入Name,並選擇Class under test : com.foxx.roundamountsaving.RoundAmountSaving -> Finish

 

6.修改RoundAmountSavingTest.java

由於RoundAmountSaving繼承Activity,為了測試Activity我們必須讓Test Case繼承ActivityInstrumentationTestCase2並呼叫父類別建構式

package com.foxx.roundamountsaving.test;
import android.test.ActivityInstrumentationTestCase2;
import com.foxx.roundamountsaving.RoundAmountSaving;
public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving>
{
    public RoundAmountSavingTest(){
        super(RoundAmountSaving.class);
    }
}

 
接著override setUp方法,並在方法中設定相關內容,包含測試類別的實體

package com.foxx.roundamountsaving.test;
import android.test.ActivityInstrumentationTestCase2;
import com.foxx.roundamountsaving.RoundAmountSaving;
public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving>
{
    private RoundAmountSaving mRoundAmountSaving;
    public RoundAmountSavingTest(){
        super(RoundAmountSaving.class);
    }
    @Override
    protected void setUp() throws Exception
    {
        super.setUp();
        setActivityInitialTouchMode(true);
        mRoundAmountSaving = getActivity();
    }
}

 
可以開始寫測試方法了,為了確保測試資料的完整性,先測試mRoundAmountSaving不可為null

public void testRoundAmountSavingNotNull(){
        assertNotNull(mRoundAmountSaving);
}

 
Run test!!!
加入RoundAmountSaving上的UI元件,測試這些元件的layout或是功能性,
預設情況在38~42行會出現 id cannot be resolved or is not a field 的錯誤,Eclipse 並不會自動修復,我們必須手動import RoundAmountSaving的R(第11行)

package com.foxx.roundamountsaving.test;
import android.test.ActivityInstrumentationTestCase2;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.foxx.roundamountsaving.RoundAmountSaving;
import com.foxx.roundamountsaving.R;
public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving>
{
    private RoundAmountSaving mRoundAmountSaving;
    private EditText mInputLend;
    private EditText mInputRate;
    private EditText mInputNumber;
    private Button mSubmit;
    private TextView mAmount;
    public RoundAmountSavingTest(){
        super(RoundAmountSaving.class);
    }
    @Override
    protected void setUp() throws Exception
    {
        super.setUp();
        setActivityInitialTouchMode(true);
        mRoundAmountSaving = getActivity();
        initLayoutComponents();
    }
    public void testRoundAmountSavingNotNull(){
        assertNotNull(mRoundAmountSaving);
    }
    private void initLayoutComponents()
    {
        mInputLend = (EditText) mRoundAmountSaving.findViewById(R.id.input_lend);
        mInputNumber = (EditText) mRoundAmountSaving.findViewById(R.id.input_number);
        mInputRate = (EditText) mRoundAmountSaving.findViewById(R.id.input_rate);
        mSubmit = (Button) mRoundAmountSaving.findViewById(R.id.submit);
        mAmount = (TextView) mRoundAmountSaving.findViewById(R.id.amount);
    }
}

 
加入UI元件不可為null的測試

public void testLayoutComponentsNotNull()
{
        assertNotNull(mInputLend);
        assertNotNull(mInputNumber);
        assertNotNull(mInputRate);
        assertNotNull(mSubmit);
}

 
Run test~
加入測試UI元件layout的測試,assertEquals的第2個參數必須參考RoundAmountSaving的UI xml的設定

public void testAmountLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mAmount.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.WRAP_CONTENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testInputNumberLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mInputNumber.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testInputRateLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mInputRate.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testInputLendLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mInputLend.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testSubmitLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mSubmit.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }

 
Run test~
為了測試UI元件的功能性,加入requestFocusLayoutComponent和setValueToLayoutComponent2個方法,
requestFocusLayoutComponent可讓UI元件取得焦點,取得焦點後再呼叫setValueToLayoutComponent這個方法可以在edittext中設定數值

private void requestFocusLayoutComponent(final View component)
    {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run()
            {
                component.requestFocus();
            }
        });
    }
private void setValueToLayoutComponent(String value)
    {
        getInstrumentation().waitForIdleSync();
        getInstrumentation().sendStringSync(value);
        getInstrumentation().waitForIdleSync();
    }

 
設定數值到各UI元件

private void inputTestValueInInputNumber()
    {
        requestFocusLayoutComponent(mInputNumber);
        setValueToLayoutComponent("20");
    }
    private void inputTestValueInInputRate()
    {
        requestFocusLayoutComponent(mInputRate);
        setValueToLayoutComponent("10");
    }
    private void inputTestValueInInputLend()
    {
        requestFocusLayoutComponent(mInputLend);
        setValueToLayoutComponent("100");
    }

 
 
最後加入測試計算的正確性,expectedCountAmountResult 為預期的計算結果,先設定數值(inutTestValueInInputXXX)到各UI元件中
再呼叫TouchUtils.clickView模擬mSubmit被按下,最後從mAmount取得計算結果和expectedCountAmountResult比較是否相等
相等的話代表算法正確

public void testCountAmount()
    {
        final double expectedCountAmountResult = 118.05448347663095;
        inputTestValueInInputLend();
        inputTestValueInInputRate();
        inputTestValueInInputNumber();
        TouchUtils.clickView(this, mSubmit);
        String result = (String) mAmount.getText();
        assertEquals(result, "" + expectedCountAmountResult);
    }

 
Run test~
其實在測試算法還可以加入更多的測試條件,如邊界值,特殊值等等
final RoundAmountSavingTest.java

package com.foxx.roundamountsaving.test;
import android.test.ActivityInstrumentationTestCase2;
import android.test.TouchUtils;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import com.foxx.roundamountsaving.RoundAmountSaving;
import com.foxx.roundamountsaving.R;
public class RoundAmountSavingTest extends ActivityInstrumentationTestCase2<RoundAmountSaving>
{
    private RoundAmountSaving mRoundAmountSaving;
    private EditText mInputLend;
    private EditText mInputRate;
    private EditText mInputNumber;
    private Button mSubmit;
    private TextView mAmount;
    public RoundAmountSavingTest(){
        super(RoundAmountSaving.class);
    }
    @Override
    protected void setUp() throws Exception
    {
        super.setUp();
        setActivityInitialTouchMode(true);
        mRoundAmountSaving = getActivity();
        initLayoutComponents();
    }
    public void testRoundAmountSavingNotNull(){
        assertNotNull(mRoundAmountSaving);
    }
    public void testLayoutComponentsNotNull()
    {
        assertNotNull(mInputLend);
        assertNotNull(mInputNumber);
        assertNotNull(mInputRate);
        assertNotNull(mSubmit);
    }
    private void initLayoutComponents()
    {
        mInputLend = (EditText) mRoundAmountSaving.findViewById(R.id.input_lend);
        mInputNumber = (EditText) mRoundAmountSaving.findViewById(R.id.input_number);
        mInputRate = (EditText) mRoundAmountSaving.findViewById(R.id.input_rate);
        mSubmit = (Button) mRoundAmountSaving.findViewById(R.id.submit);
        mAmount = (TextView) mRoundAmountSaving.findViewById(R.id.amount);
    }
    private void requestFocusLayoutComponent(final View component)
    {
        getInstrumentation().runOnMainSync(new Runnable() {
            @Override
            public void run()
            {
                component.requestFocus();
            }
        });
    }
    private void setValueToLayoutComponent(String value)
    {
        getInstrumentation().waitForIdleSync();
        getInstrumentation().sendStringSync(value);
        getInstrumentation().waitForIdleSync();
    }
    public void testCountAmount()
    {
        final double expectedCountAmountResult = 118.05448347663095;
        inputTestValueInInputLend();
        inputTestValueInInputRate();
        inputTestValueInInputNumber();
        TouchUtils.clickView(this, mSubmit);
        String result = (String) mAmount.getText();
        assertEquals(result, "" + expectedCountAmountResult);
    }
    private void inputTestValueInInputNumber()
    {
        requestFocusLayoutComponent(mInputNumber);
        setValueToLayoutComponent("20");
    }
    private void inputTestValueInInputRate()
    {
        requestFocusLayoutComponent(mInputRate);
        setValueToLayoutComponent("10");
    }
    private void inputTestValueInInputLend()
    {
        requestFocusLayoutComponent(mInputLend);
        setValueToLayoutComponent("100");
    }
    public void testAmountLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mAmount.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.WRAP_CONTENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testInputNumberLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mInputNumber.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testInputRateLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mInputRate.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testInputLendLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mInputLend.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
    public void testSubmitLayoutValid()
    {
        final ViewGroup.LayoutParams layoutParams = mSubmit.getLayoutParams();
        assertNotNull(layoutParams);
        assertEquals(layoutParams.width, WindowManager.LayoutParams.MATCH_PARENT);
        assertEquals(layoutParams.height, WindowManager.LayoutParams.WRAP_CONTENT);
    }
}
分類
Android Uncategorized

Set unban ip in fail2ban

How to set unban ip in fail2ban??

 
1. Edit /etc/fail2ban/jail.local and find ignoreip

[DEFAULT]
# "ignoreip" can be an IP address, a CIDR mask or a DNS host
ignoreip = 127.0.0.1/8 xxx.xxx.xxx.xxx

 
2. Input your unban ip as xxx.xxx.xxx.xxx
 
Note:
1. Remember use space to identify different ip
2. fail2ban commands
 

分類
Android Uncategorized

jenkins build error : error while writing Main: Main.class (Permission denied)

Description:

Build java project in jenkins and show error message

[javac] /path_of_class.java:1: error while writing Main: /path_of_class.class (Permission denied)
[javac] public class Main
[javac]        ^
[javac] 1 error

 

Root caused:

permission of directory is not enough
 

Solution:

1.
Open Terminal and move to parent of director which at java project
2.
sudo chmod 777 director_name
 

分類
Android Uncategorized

如何簡單的從TextView, EditView取得數值

通常輸入數值都會使用EditText等等的文字框當作對象, 如何才能很快速的從這些view取得數值呢??
使用 getText().toString() 加上各個數值類別(Integer, Double, Float, Long, Byte, Short)的ParseXXX() 最快 如下

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class GetValueFromTextView extends Activity
{
    private static final int PERCENT = 100;
    private static final int MONTH_COUNT = 12;
    private EditText mInput_lend;
    private EditText mInputRate;
    private EditText mInputNumber;
    private Button mSubmit;
    private TextView mAmount;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        initComponents();
    }
    private void initComponents()
    {
        mInput_lend = (EditText) findViewById(R.id.input_lend);
        mInputNumber = (EditText) findViewById(R.id.input_number);
        mInputRate = (EditText) findViewById(R.id.input_rate);
        mSubmit = (Button) findViewById(R.id.submit);
        mSubmit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v)
            {
                mAmount.setText("" + countAmount());
            }
        });
        mAmount = (TextView) findViewById(R.id.amount);
    }
    private double countAmount()
    {
        int lend = Integer.parseInt(mInput_lend.getText().toString());
        double rateOfMonth = Double.parseDouble(mInputRate.getText().toString()) / MONTH_COUNT
                / PERCENT;
        int number = Integer.parseInt(mInputNumber.getText().toString());
        return lend * (Math.pow(rateOfMonth + 1, number));
    }
}

重點在 countAmount 方法中, 以下列方式取得string並轉換成數字

int lend = Integer.parseInt(mInput_lend.getText().toString());
double rateOfMonth = Double.parseDouble(mInputRate.getText().toString()) / MONTH_COUNT / PERCENT;
int number = Integer.parseInt(mInputNumber.getText().toString());

但要小心轉型溢位的問題
main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/main_tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/title"/>
    <TextView
        android:id="@+id/main_tv_lendHint"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lend"/>
    <EditText
        android:id="@+id/input_lend"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="number"/>
    <TextView
        android:id="@+id/main_tv_rateHint"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/rate"/>
    <EditText
        android:id="@+id/input_rate"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="numberDecimal"/>
    <TextView
        android:id="@+id/main_tv_number"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/rate"/>
    <EditText
        android:id="@+id/input_number"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:inputType="number"/>
    <Button
        android:id="@+id/submit"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/btn"/>
    <TextView
        android:id="@+id/amount"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</LinearLayout>

以及 strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Count Round-amount savings</string>
    <string name="title">Round-amount savings:</string>
    <string name="lend">capital(NT$):</string>
    <string name="rate">rate of year(%):</string>
    <string name="number">number of periods:</string>
    <string name="btn">count</string>
</resources>

 

分類
Android Uncategorized

取得 wifi scan ap result : ScanResult

在 android 手機中 -> 設定 -> Wi-Fi , 每隔一段時間會自動搜尋附近的wifi ap的訊息並顯示在列表上, 本篇介紹如何同步取得這些wifi ap的訊息
這些wifi ap的訊息是藉由ScanResult來顯示, ScanResult 類別相當簡單, 請參考官網
而每次搜尋wifi ap的動作都是以Broadcast的方式來達成, 所以我們只要建立一個專門來接收該動作的Broadcast Receiver 就能自動同步取得其搜尋結果(ScanResult)

package com.foxx.wifiapreceiver;
import java.util.List;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.util.Log;
public class WifiApReceiver extends BroadcastReceiver
{
    private static final String TAG = "WifiApResult";
    private Activity mActivity;
    public WifiApReceiver(Activity activity) {
        mActivity = activity;
        mActivity.registerReceiver(this, new IntentFilter(
                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
    }
    @Override
    public void onReceive(Context context, Intent intent)
    {
        getScanResults();
    }
    private void getScanResults()
    {
        List<ScanResult> scanResults = getWifiManager().getScanResults();
        Iterator<ScanResult> iter = scanResults.iterator();
        while (iter.hasNext()) {
            showAllFieldOfScanResult(iter.next());
        }
    }
    private WifiManager getWifiManager()
    {
        return (WifiManager) mActivity.getSystemService(Context.WIFI_SERVICE);
    }
    private void showAllFieldOfScanResult(ScanResult scanResult)
    {
        Log.d(TAG, "--------------------------------------");
        Log.d(TAG, "scanResult.SSID:" + scanResult.SSID);
        Log.d(TAG, "scanResult.BSSID:" + scanResult.BSSID);
        Log.d(TAG, "scanResult.capabilities:" + scanResult.capabilities);
        Log.d(TAG, "scanResult.frequency:" + scanResult.frequency);
        Log.d(TAG, "scanResult.level:" + scanResult.level);
        Log.d(TAG, "scanResult.BSSID:" + scanResult.describeContents());
    }
    public void unregisterReceiver()
    {
        try {
            mActivity.unregisterReceiver(this);
        } catch (Exception e) {
            Log.d(TAG, "execption in unregisterReceiver");
        }
    }
}

1. 在constructor註冊了receiver
2. OnReceiver 為 callback function , 只要有搜尋wifi ap的動作, OnReceiver就會被呼叫
 
在AndroidManifest.xml加入2個必要的permission

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

 
使用的方式相當簡單

package com.foxx.wifiapreceiver;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
    private WifiApReceiver mWifiReceiver;
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initWifiReceiver();
    }
    private void initWifiReceiver(){
        mWifiReceiver = new WifiApReceiver(this);
    }
    @Override
    protected void onStop()
    {
        mWifiReceiver.unregisterReceiver();
        super.onStop();
    }
}

只要注意在onStop要unregister receiver即可
 
 
 

分類
Android

Can't bind to local 8X00 for debugger

Description:

Debugger connected fail。
 

Solution:

開啟 eclipse 後 -> Window -> Preferences  -> Android -> DDMS
勾選 Use ADBHOST -> 127.0.0.1

分類
Android

eclipse android 實機装置顯示 ????????? unknow

Description:
接上實機後,Eclipse DDMS Devices 顯示 ??????????  ?? unknow
 
Solution:
開啟终端機輸入

sudo -s

輸入密碼取得  root 權限
移動到 SDK 中的 platfarm-tools 資料夾,其內要有adb檔案。
以下步驟可能要重複好幾次,直到顯示名稱OK
輸入

sudo ./adb kill-server

會開始Connection attempts
再輸入

sudo ./adb devices

如果装置有顯示名稱就OK了