2014年12月2日 星期二

[Android Sample Code] 利用Sensor物件監測三軸加速度

講解待補

package com.example.sensortest;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity implements SensorEventListener{
 private TextView resulFromAccelerometertTextView;
 private SensorManager mSensorManager;
 private Sensor mSensor;
 private static final int factor = 10;
   
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViews();
        setUpAcceleratorSensor();
    }

    @Override  
    protected void onResume() {    
     super.onResume();    
     setUpAcceleratorSensor();
     mSensorManager.registerListener(this, mSensor, SensorManager.SENSOR_DELAY_NORMAL); 
    }
    
    @Override
    protected void onPause() { 
     super.onPause();  
     mSensorManager.unregisterListener(this);
    }

    private void findViews(){
     resulFromAccelerometertTextView = (TextView) findViewById(R.id.resulFromAccelerometertTextView);
    }
    private void setUpAcceleratorSensor(){
     mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
     if((mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)) != null);
     else
      Toast.makeText(this, "No Sensor Device Exist", Toast.LENGTH_LONG).show();
   }
   
 @Override
 public void onSensorChanged(SensorEvent event) {
  Sensor mySensor = event.sensor;
     if (mySensor.getType() == Sensor.TYPE_ACCELEROMETER) {
      if(event.values[0]/factor > 1 || event.values[1]/factor > 1 || event.values[2]/factor > 1){ 
       resulFromAccelerometertTextView.setText("Detect your montion\n");
       resulFromAccelerometertTextView.append("X axis acceleration:" + event.values[0] + "\n");
       resulFromAccelerometertTextView.append("Y axis acceleration:" + event.values[1] + "\n");
       resulFromAccelerometertTextView.append("Z axis acceleration:" + event.values[2] + "\n");
      }
     }
 }

 @Override
 public void onAccuracyChanged(Sensor sensor, int accuracy) {
 }
}

App的執行結果如下圖Fig. 1所示:

Fig. 1. 程式執行結果

2014年12月1日 星期一

[Android Sample Code] Activities之間的切換與資料傳遞 (文字與圖片)

一個可與使用者進行互動的畫面程序在Android裡稱為Activity,由於一個畫面代表一個Activity,所以一個App會有一至多個Activities,若想將目前執行中的Activity暫停,然後啟動其它Activities則是得透過Intents來達成,更正確的來說每個Activity都是被某一Intent所啟動,另外Activities之間的資料傳遞也可以也可以靠Intent來達成。本範例實作兩個Activity,Fig. 1顯示MainActivity的介面,只有一個按鈕物件用來提示使用者進行操作,當按下按鈕後Activity會透過Intent傳遞文字與圖片給SecMainActivity,MainActivity的程式碼顯示於下,(待補後續講解)


MainActivity.java:
package com.example.activitiestest;

import java.io.ByteArrayOutputStream;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends ActionBarActivity {
 private Button button;
 private Intent intent ;
 public final static String EXTRA_STRING_MSG = "EXTRA_STRING_MSG";
 public final static String EXTRA_BITMAP_BYTE_STREAM = "EXTRA_BITMAP_BYTE_STREAM";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViews();
        createIntent();
        setActionListener();
    }
    
    private void findViews(){
     button = (Button) findViewById(R.id.button);
    }
    
    private void createIntent(){
     Bitmap resImage;
     ByteArrayOutputStream outImageByteArrayOutputStream = new ByteArrayOutputStream(); 
     byte [] outImageByteArray;
     
     intent = new Intent(this,SecMainActivity.class);
        intent.putExtra(EXTRA_STRING_MSG, "Hello, another activity");
        resImage = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        resImage.compress(Bitmap.CompressFormat.JPEG, 100, outImageByteArrayOutputStream);
        outImageByteArray = outImageByteArrayOutputStream.toByteArray();
        intent.putExtra(EXTRA_BITMAP_BYTE_STREAM, outImageByteArray);
    }
    
    private void setActionListener(){
      button.setOnClickListener(new OnClickListener() {  
          @Override  
          public void onClick(View v) {   
           startActivity(intent);
          }
         });
    }
}

Fig. 1. MainActivity Layout.
在Eclipse下建立SecMainActivity的步驟如Fig.2、3和4所示:
Fig. 2. 建立新的Activity (1)
 Fig. 3. 建立新的Activity (2)

 Fig. 4. 建立新的Activity (3)

SecMainActivity的介面圖示於Fig. 5,分別有一個TextView、ImageView和Button,其中TextView和ImageView用來顯示接收到的文字和圖片,Button則是用來返回先前的MainActivity,

SecMainActivity.java:
package com.example.activitiestest;

import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

public class SecMainActivity extends ActionBarActivity {
 private Button returnButton;
 private TextView receivedTextView;
 private ImageView receivedImageView;
 Intent intent;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_sec_main);
  findViews();
  handleReceivedInfoToUI();
  setActionListener();
 }
 
 private void findViews(){
  returnButton = (Button) findViewById(R.id.returnButton);
  receivedTextView = (TextView) findViewById(R.id.receivedTextView);
  receivedImageView = (ImageView) findViewById(R.id.receivedImageView);  
 }
 
 private void handleReceivedInfoToUI(){
  intent = getIntent();
  receivedTextView.setText(intent.getStringExtra(MainActivity.EXTRA_STRING_MSG));
  byte [] receivedImageByteArray = intent.getByteArrayExtra(MainActivity.EXTRA_BITMAP_BYTE_STREAM);
  Bitmap bmp = BitmapFactory.decodeByteArray(receivedImageByteArray, 0, receivedImageByteArray.length);
  receivedImageView.setImageBitmap(bmp);
 }
 
 private void setActionListener(){
  returnButton.setOnClickListener( new OnClickListener(){
   @Override
   public void onClick(View v){
    finish();
   }
  });
 }
}


Fig. 5. SecMainActivity Layout.
當在MainActivity按下Call Second Activity後的執行結果如下:
Fig. 6. 按下Call Second  Activity後的執行結果

之後按下Return to MainActivity後則會再返回Fig. 1。

延伸閱讀:

2014年11月24日 星期一

[Android] Android 裡的4大元件簡介

開發Android程式除了需要會寫Java之外,也必須對Android的Application Component有所瞭解,亦即Activities、Intents and Intent Filters、Services和Content Providers,以下對這些元件的用途做個基本的簡介:

1. Activities

一個可與使用者進行互動的畫面在Android裡稱為Activity,換句話說它是一個負責服務使用者的UI執行緒 (Thread),在此UI執行緒下可以再建立許多Non UI子執行緒去處理各種任務,這樣的好處是可以避免UI的響應 (response)延遲過久。由於一個畫面代表一個Activity,所以一個App會有一至多個Activities,Activities之間的溝通或是暫停目前執行中的Activity改啟動其它Activities則是得透過Intents來達成。

2. Intents and Intent Filters

Intents總共有三個用途:

(1). 啟動Activity

在Activity裡可以透過呼叫startActivity()或是startActivityForResult()等方法(method)並傳入Intent來啟動其它Activity,Android系統則會根據Intent所提供的資訊來決定要啟動哪個Activity。傳入的Intent又可以分成Explicit Intent或是 Implicit Intent,前者必須詳述想啟動的Activity的類別名稱,後者只需指定一些動做條件不需明確知道該啟動哪個Activity,如果程式內設定的動做條件有許多Activity符合的話,則系統會跳出視窗供使用者選擇要啟動哪一個Activity。


(2). 啟動Service


(3). 傳遞廣播

廣播的概念就像學生時代幹壞(ㄔㄨㄣˇ)事被發現時,訓導主任會用麥克風廣播:「XXX請至訓導處一趟」。發出廣播的時候全校的人都會聽到,但只有做賊心虛的XXX會乖乖的到訓導處報到或是選擇翻牆落跑。用Intent傳遞廣播 (Broadcast)的概念就像是這種狀況,寫程式時可以讓Activity發送廣播給其它Activity,也可以註冊一個事件去監聽特定的廣播,雖然Activities之間無法知道對方的資料,不過透過Intent發廣播可以達成將資料傳遞給其它Activity、或是接收來自其它Activity的資料。其實系統一直會有廣播發出 (例如剩餘電量),但就像前面所提,只有XXX會做出反應去訓導處報到,因此在Activity裡註冊一個事件去監聽廣播的同時,也必須用Intent Filters過濾出Activity想接收的特定廣播是什麼,例如等待系統發出剩餘電量的廣播並在接收到訊息後做處理 (像是根據剩餘電量多寡再決定是否通知使用者充電)。

3. Services

Service是沒有UI介面的背景執行程序 (知道想幹壞事該怎麼做了吧),例如背景執行的音樂播放程序或是檔案下載程序,另外也可以透過Service來達成Inter-Process Communication (IPC)。Service可以被Activities和Intents啟動。


4. Content Providers

Content Providers負責管理結構化的資料 ,並提供給應用程式做存取,由於各個應用程式之間無法知道彼此之間的資料,所以透過Content Provider可以達到應用程式之間的資料共享,例如通訊錄、相簿…等。

[Job Experience] OEM/ODM UEFI/BIOS工程師工作內容 簡介

介紹OEM/ODM UEFI BIOS工程師的工作內容之前,先分享當年面試時幾位系統廠主管講的話:

A公司主管: 當我們需要H.264 Decoder時,我們不會去花時間去研究和改code而是直接用買的,因為自己改code的performance未必會比用買的好,其實系統廠主要做的事情就是做系統整合。
B公司主管:Embedded System bootloader這塊是屬於比較乏味的部分。
C公司主管:比起一般寫driver的韌體工程師,做BIOS才更有價值,因為要懂得東西很多,厲害的公司才有辦法讓BIOS的開機速度比別人快。

回正題,會走BIOS主要是受C公司主管的影響,因為如果以走底層來說,學得越多接觸的越廣自然是好事一件,我想對於想走韌體方面的工作的人來說,主要原因不外乎是覺得寫driver驅動硬體很有趣或是比起純軟體,軟硬都會能讓自己變的更有價值,但是我認為想踏入這行的人必須先瞭解產業的概況,以免工作後才驚覺落差與自己的期望甚大,尤其對一綁就是三年的研替來說更是重要。所以才在此分享OEM/ODM UEFI/BIOS的工作內容。
基本上絕大部分系統廠(華碩、技嘉、微星...等公司稱之)的UEFI/BIOS都是用買的,賣UEFI/BIOS code的公司叫做BIOS Vendor (AMI, Phoenix, Insight...等公司),只有極少數的公司才會開發自家的UEFI/BIOS (或是說只有極少數的公司有這能力和資源,例如DELL)。通常進系統廠擔任UEFI/BIOS工程師公司都會先讓你進行一些基本的Training,然後開始看從Vendor那買來的BIOS code,所以你首先要做的就是瞭解BIOS code的架構以及跑code的流程,這樣你才會知道要如何改code,在看code的過程中當然得搭配UEFI、眾多晶片組以及一些工業標準的技術文件交替看才有辦法看得懂Vendor的code,所以要摸到非常熟悉UEFI/BIOS勢必需要數年以上的時間。再來,一年會開數個案子到數十個案子不等,開案時PM就會定好相關的時程,所以UEFI/BIOS的工作SOP就是要在打板回來前根據硬體上的設計準備好你的UEFI/BIOS,這部分你要做的就是套用相關UEFI/BIOS 功能模組到你的案子、包入device OpRom、Intel 提供的相關資源以及更改一些專案裡硬體參數設定...等事項,打板回來做完開機測試後,你還得做一些基本的測試(例如基本的UEFI/BIOS功能測試、灌OS、ACPI S345...等測項),以及之後諸多更繁雜的主機板測試 (這點其實依各公司的工作分配而定,分工明確的公司就不需做太多的測試),這也是開始爆出一堆issue的時候,這時你就得開始釐清是硬體設計問題還是你做的UEFI/BIOS有問題,在釐清你的UEFI/BIOS的過程中(複製和釐清issue也是個累死人的過程),你還得確認你或你同事改的code、Vendor code和 Intel提供的東西哪裡有問題;還是其它的原因造成的 (例如device OpRom或是特定晶片組的問題),然後開始解問題和re回報問題給相關廠商請他們解 (系統廠是較無力的,通常90%以上的問題都是得靠相關的廠商來解),因為相關的時程都已經被PM定好了,所以責任制就會這樣找上門,另外講到這你應該也能瞭解到UEFI/BIOS工程師在porting、追蹤廠商解issue的進度和做測試及驗證所佔的比重是非常重的,寫新的程式碼的機會其實偏少且多是以改改幾行程式碼為主,而且PM一直開案的話每天的事情會都很多、多到讓你感覺總是在處理雜務。再來提一下這行的轉職概況,基本上要轉職就只能轉去那幾家手指數的完的系統廠或是外商,外商的話就像是去Intel當FAE或是到BIOS Vendor裡工作,整體而言出入較窄。現在回頭看A公司主官說的話,系統廠的UEFI/BIOS主要也是在做整合,但也像C公司主管說的,可以讓你有機會學的很廣、懂很多底層的東西,不過待久了寫程式的能力可能會退化不少,而且依系統廠的工作生態,勢必會讓人感到分身乏術,另外個人認為這份工作其實蠻讓人感到乏味的,雖然UEFI/BIOS不是什麼熱門的職缺,但一直都很缺人(理由自己想),如果看完這篇勸世文後還是對這行很有興趣的話,那我還是說聲歡迎來到煉獄,不過請盡量選擇進BIOS Vendor,對於未來的發展和實力的培養才會比較好。




2014年11月17日 星期一

[Android Sample Code] 將 Non-UI Thread 的資料傳遞給 UI Thread - 使用runOnUiThread

在Android系統裡,Activity Manager 和 Window Manager system services會負責監控程式的響應(response)時間,如果由使用者觸發的事件 (例如按下按鈕)超過太久沒有響應的話 (Android限制使用者觸發的事件得在5秒內完成;BroadcastReceiver則是限制在10秒內得完成工作),Android會觸發Application Not Responding (ANR)對話框詢問是否要終止程式:

Fig. 1 ANR Dialog

為了避免上述的狀況,過於花時的運算和事件等待應避免在UI 執行緒(Activity裡的Main Thread)執行,換句話說可以透過建立額外的Non UI 執行緒來處理,不過Android不允許Non UI 執行緒 (即UI 執行緒下的子執行緒)直接更新UI 執行緒的畫面,所以Non UI 執行緒必須得將自己的資料回傳給UI 執行緒由它負責處理,在這篇文章介紹一個簡單的方法,使用 - runOnUiThread。 runOnUiThread(Runnable action)是Activity Class裡的一個方法 (Method),當Non UI 執行緒呼叫此方法時,會將引數action回傳到UI 執行緒的Event Queue裡等待被執行,所以可以根據實際的設計需求實作特定的action,將Non UI 執行緒的資料傳遞給UI 執行緒也得以實踐。下面的範例程式碼實作了一個忽略時間誤差的簡單時鐘程式,此範例程式透過Non UI 執行緒取得系統時間後傳給UI 執行緒更新畫面。此範例的UI介面有兩個TextView,第一個TextView顯示"A Message form the Non-UI Threads:",用來提示此APP的功能,第二個msgTextView用來顯示Non UI執行緒傳給UI執行緒的資料。關於程式碼的部份,MainActivity class (APP執行時的UI執行緒)裡宣告了一個繼承Thread class的inner class - NonUIThread (APP執行時UI執行緒下的Non UI執行緒),第64和65行是它的成員物件,nonUIThreadName是Non UI執行緒的名字,用來識別是哪一個執行緒,第67到71行的default constructor設定"Unknown Non-UI Thread"為預設的名字,或是透過呼叫第73到76行的constructor賦與不同的名字,另一個成員calendar則是用來獲取裝置所設定的系統年分、日期和時間的日曆物件,第79到90行覆寫了Thread class的run方法,run方法會不斷的去呼叫sayHelloToUIThread方法,第83行的sleep方法設定了讓執行緒下次再繼續執行的時間(即1秒),第91到103行是sayHelloToUIThread方法的實做部分,此方法每隔一秒會被呼叫,並透過Calendar.getInstance()擷取出目前的時間,第93到96行將這些資訊轉換成字串(所有的JAVA calss均會繼承Object class,Object class裡有一個toString的方法,所以calendar.get()回傳的時間會靠此方法轉成字串),第97到102行就是本範例要講的重點,inner class - NonUIThread呼叫了MainActivity的runOnUiThread,傳入引數的Runnable物件一樣覆寫了run方法,run方法會呼叫MainActivity裡的receiveMsgFromNonUIThread方法並,傳入的引數toUIStr即為Non UI執行緒想要傳給UI執行緒的資料 ,第59到61行的receiveMsgFromNonUIThread方法實做則是再呼叫msgTextView的setText方法將目前的時間更新到UI執行緒上。MainActivity的onCreate方法建立了UI畫面和一個Non UI執行緒的物件,在onStart方法裡呼叫nonUIThread1.start()讓Non UI執行緒開始執行獲取系統時間並傳回給UI,Fig.2顯示了此範例APP的執行結果。


package com.example.threadcommunicationex1;

import java.util.Calendar;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;


public class MainActivity extends ActionBarActivity {
 private TextView msgTextView;
 private NonUIThread nonUIThread1;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getViewComponent();
        CreateThread(); 
    }
    
    @Override
    protected void onStart(){
     super.onStart();
     nonUIThread1.start();
    }
    
    private void getViewComponent()
    {
     msgTextView = (TextView) findViewById(R.id.msg);
     msgTextView.setText("");
    }
    
    private void CreateThread(){
     nonUIThread1 = new NonUIThread("Non-UI Thread 1");
    }
    
    private void receiveMsgFromNonUIThread(String msg){
     msgTextView.setText(msg);
    }
    
    class NonUIThread extends Thread{
     private String nonUIThreadName;
     private Calendar calendar;
     
     NonUIThread(){
      nonUIThreadName = "Unknown Non-UI Thread";
      //get a default instance of this class for general use
      calendar = Calendar.getInstance(); 
     }
     
     NonUIThread(String name){
      this();
      nonUIThreadName = name;  
     }
     
     @Override
     public void run(){
       while(true){
        sayHelloToUIThread();
        try {
          sleep(1000);
        } 
        catch (InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
     
     private void sayHelloToUIThread(){
       calendar = Calendar.getInstance();
       final String  toUIStr = "This is " + nonUIThreadName + 
        ", " + "current time is " + calendar.get(Calendar.HOUR_OF_DAY) 
        + ":" + calendar.get(Calendar.MINUTE) + ":" + 
        calendar.get(Calendar.SECOND) + "\n\n";
       runOnUiThread(new Runnable(){
         @Override
         public void run(){
          receiveMsgFromNonUIThread(toUIStr);
         }
       });
      }
    }   
}


程式執行的畫面如下:
Fig. 2. 程式執行結果

對JAVA執行緒不熟的話可以參考此網站的講解: