Wednesday, December 22, 2010

開發之氣死人之路II - Second issue in Android

繼上一篇︰Android開發之氣死人之路I後,
這是我第2起案例,一模一樣的程式碼
跑在Android手機上的結果不同
This is my second case , programming is the same, but result in two different status.


Difficult way to develop Android program.

狀況︰
我希望將訊息通知設在上方的"進行中",而非下方的通知欄位。
用了相同method函式請求,卻產出不同結果。

下一篇(完)︰Android開發者應有的認知

Friday, December 17, 2010

Android開發之氣死人之路I

一模一樣的程式碼,在手機裡執行反應居然不一樣



狀況︰
長按後的結果不同。

原因︰
每個按鈕我都有做setOnClickListener和setOnLongClickListener,
後來發現由於電容面板的問題,
有些硬體執行完onLongClickListener後,
會雞婆的幫你執行onClickListener。

實測手機︰
Nexus One VS. Vibo A688

下一篇︰開發之氣死人之路II

Monday, December 13, 2010

進程與生命週期(Process and lifecycles)

以下文章來至Application Fundamentals :
The Android system tries to maintain an application process for as long as possible, but eventually

it will need to remove old processes when memory runs low.
只要環境允許,Android系統都會嚐試著保留著應用程式的進程。但是,如果記憶體很少時,它們還是會被砍殺的。
To determine which processes to keep and which to kill, Android places each process

into an "importance hierarchy" based on the components running in it and the state of those components.
為了能夠決定哪些進程要保留、哪些進程又要砍殺,Android將每個進程放入一個叫「層級優先權」的地方做管理了。這裡面記錄著哪些元件是執行中、哪些元件是在背景執行


Processes with the lowest importance are eliminated first, then those with the next lowest, and so on. 
如果是那種低重要性的(非被使用),會被第1個踢出去,系統會繼續找誰第二、誰第三、以此類推。

There are

five levels
 in the hierarchy. The following list presents them in order of importance:

1.A foreground process
2.A visible process
3.A service process
4.A background process
5.An empty process
以上這五種進程,由高至低,排序了其進程在系統裡的優先權。

底下詳細說明這5種進程。
什麼是前景進程 (foreground process)?

  • It is running an activity that the user is interacting with (the Activity object's onResume() method has been called). - Activity正在被使用者使用的當下(onResume()事件發生時)
  • It hosts a service that's bound to the activity that the user is interacting with. - 一個activity有互動的Service
  • It has a Service object that's executing one of its lifecycle callbacks (onCreate(), onStart(), or onDestroy()). - 有一個Service物件,而且這個Service物件還執行callback回呼函式(像Service裡的onCreate()、onStart()或onDestroy()之類的)
  • It has a BroadcastReceiver object that's executing its onReceive() method. - 有一個廣播監聽物件,而且該廣播還執行了onReceive這個callback回呼函式。
Only a few foreground processes will exist at any given time. They are killed only as a last resort.
因為在同樣的時間裡,只會有少數的前景進程存在,所以它們被排到最最最最最後面,才有機會被系統砍殺。





什麼是可用進程 (visible process)?

A visible process is one that

doesn't have any foreground components, but still can affect what the user sees on screen.
可用進程沒會有任何的前景元件執行(像Activity),但是呢,它卻仍會影響使用者所看的螢幕畫面。

  • It hosts an activity that is not in the foreground, but is still visible to the user (its onPause() method has been called). This may occur, for example, if the foreground activity is a dialog that allows the previous activity to be seen behind it. - 像是我們叫出了一個畫面裡的訊息對話視窗,此時原程式的onPause()被呼叫,我們稱此時該進程為可用進程
  • It hosts a service that's bound to a visible activity. - 一個綁到可用activity中的Service

A visible process is considered extremely important and will not be killed unless doing so is required to keep all foreground processes running.
可用進程也是非常重要的,系統會盡量的讓所有這些前景進程保持運作中,除非非不得己,才會砍掉。


什麼是Service進程 (service process)?
  Although service processes are not directly tied to anything the user sees, they are generally doing things that

the user cares about (such as playing an mp3 in the background or downloading data on the network), so the system keeps them running unless there's not enough memory to retain them along with all foreground and visible processes.
雖然Service進程不是對使用者所觀看到的畫面產生直接影響的元件,但是它們一般都被拿來做一些使用者在乎的事,像是在背景播mp3啦、從網路下載檔案啦…,所以,系統在記憶體不足以前,也會如同前景進程和可用進程一樣努力的保持著它的作業。


什麼是背景進程 (background process)?
A background process is

one holding an activity that's not currently visible to the user (the Activity object's
onStop() method has been called).
背景進程是一個使用者沒有在使用狀態的activity(此時Activity的onStop()會被系統呼叫)
因為通常系統如果呼叫到程式的onStop時,幾乎可以說該應用程式已經不是使用者當下想執行了。所以Android可以很大膽的先處理掉這種進程,這麼做,一點都不影響使用者的使用體驗。


什麼是空進程 (empty process)?
An empty process is one that

doesn't hold any active application components.
The only reason to keep such a process around is
as a cache to improve startup time the next time a component needs to run in it.
 
空進程是一個沒有執行任何啟動中的應用程式元件的進程。它存在唯一的理由就是保存cache,好讓下一次啟動元件時需要的元件,可以被快速的被找到和執行。
The system often kills these processes in order to balance overall system resources between process caches and the underlying kernel caches.
這種進程最容易被砍殺,為了讓系統保持良好的循環平衡。

何謂Service

Service文件裡提到︰
A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use.
Service是應用程式的其中一種元件,它意謂著當一隻APK在不需要與使用者互動時還要運作,因而存在。

Each service class must have a corresponding <service> declaration in its package's AndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().
Service在使用時,必須在AndroidManifest.xml裡宣告

使用Service的方式有2:
1.Context.startService()
A facility for the application to tell the system about something it wants to be doing in the background.
Service是一個讓應用程式告訴系統想在背景做些什麼事的工具。
2.Context.bindService()
A facility for an application to expose some of its functionality to other applications.
Service是一個讓應用程式展示它內部函式功能給其它應用程式的媒介。


Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work.
Service就像其它應用程式的物件一樣,在該程序Process的主執行緒運作著。
所以,當我們要用到像高度的CPU運算(像是播放MP3)或是一些會卡住的運算(像網路運算),那麼就應該建立出一個自己的執行緒,好讓工作能順利完成。

The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.
IntentService是一個標準實作Service的類別,它呢,擁有一個獨立的執行緒,靠這個類別,就能完成上述所提到的「獨立完成作業」。


Processes and Threads一文中提到︰

 When the first of an application's components needs to be run, Android starts a Linux process for it with a single thread of execution.



By default, all components of the application run in that process and thread.
However, you can arrange for components to run
in other processes
, and you can spawn additional threads for any process.

當應用程式的元件(指Activity、Service、BroadcastReceiver和ContentProvider)要啟動的時候,Android會去開啟Linux裡進程的單一執行緒,開始做運算處理。
預設來說,應用程式中的所有元件,都會在"那個"進程和執行緒運作。
所以,如果要制定元件在某個特定進程運作時,程式設計師可以自行決定要在哪個另外的進程或新增的執行緒下去執行應用程式。

參考網站
1.書生雜記
2.程式搖滾

Process與Thread

一個應用程式,會開啟一個Process
一個Process,可以被程式設計師開出多個Thread

同一個process內的Threads可以共享Code、Data Section及OS Resources

一個Thread 就是一個小型的 Process ,
若我們把 Process 分為兩個部份-----Threads 和 Resources,
Threads 就是這個 Process 的動態執行者(Dynamic Object)

附上Process和Thread的差別
           
The major difference between threads and processes is 
1.Threads share the address space of the process that created it; 
processes have their own address.

2.Threads have direct access to the data segment of its process; 
processes have their own copy of the data segment 
of the parent process. 

3.Threads can directly communicate with other threads of its process; 
processes must use interprocess communication to communicate with sibling processes. 

4.Threads have almost no overhead; processes have considerable overhead.

5.New threads are easily created; new processes require duplication of the parent process.

6.Threads can exercise considerable control over threads of the same process; 
processes can only exercise control over child processes. 

7.Changes to the main thread (cancellation, priority change, etc.) 
may affect the behavior of the other threads of the process; 
changes to the parent process does not affect child processes. 

何謂Task和Back Stack

撰寫時間︰2010/12/13 12:38
修改時間︰2012/07/10 10:38
修改次數︰2

一、前言
假設今天我們的APP裡要寫一個新功能︰「撰寫e-mail連絡開發者」。
該怎麼做?
我們開發人員要寫一個全新的頁面才能做到嗎?

這個答案是否定的。

Android提供了好用的Intent,
我們只要簡單的使用它,
就可以叫出系統發送e-mail的畫面,
當使用者撰寫好並發送出去後,
畫面又回到原本觸發發送e-mail的畫面。

這件事很奇怪,
發送e-mail的頁面不是我們程式撰寫的,
但是為什麼使用體驗就好像是APP裡寫出來的東西?
原來,
這一切是因為Android系統背後,藏了一個Stack(堆疊)機制。

二、文章開始
一個APP通常來說,都擁有數個Activity。
每個Activity都能夠執行個自的任務,
甚至是啟動另一個Activity。
舉例來說,當使用者開啟了e-mail軟體,
他們看到條列式的e-mail列表清單,
當使用者點擊了列表清單,
一個新的Activity又會開出來,
顯示那些e-mail的內容。

使用者在看e-mail清單列表,
又看了裡面的e-mail內容,
一次一次的舉動,
每個Activity頁面都被存進了任務容器(collection of activities),
我們稱這個容器為背景堆疊(back stack)。

當一個Activity啟動了另一個Activity之後,
新的Activity就被放進堆疊的最頂層,
而之前的Activity則被保留在堆疊中,
但是屬於暫停的狀態。

一旦Activity遭暫停(stop),
系統就會自動將該頁面的使用狀態儲存起來。
當使用者從新頁面按下返回按鈕後,
新的頁面被消毀(destroy),
而舊的頁面在此時又會被恢復(resume)。
也因此,堆疊的特性是「後進先出」。
圖1. 這張圖表顯示出每一個新的Activity是如何被放進背景堆疊中。當使用者按下返回鍵,當下的Activity就會被消毀(destroy),然後會將前一個Activity恢復(resume)給使用者。

======底下文章為2010/12/13撰寫======
To the user, it will seem as if the map viewer is part of the same application as your activity, even though it's defined in another application and runs in that application's process. Android maintains this user experience by

keeping both activities in the same task. Simply put, a task is what the user experiences as an "application." It's a group of related activities, arranged in a stack.
Task是使用者在使用Application時的User Experiences。如果今天我們的APK功能要開啟Google map,也許我們程式會做連結直接開啟MAP。但這個MAP卻不是我們寫的。但從我們的程式到展開MAP卻感覺是一體的。那是因為Google想要照顧這部份的使用者經驗。
Task寫在Stack,也就是堆疊裡。

The root activity in the stack is the one that began the task — typically, it's an activity the user selected in the application launcher.

The activity at the top of the stack is one that's currently running — the one that is the focus for user actions. When one activity starts another, the new activity is pushed on the stack; it becomes the running activity. The previous activity remains in the stack. When the user presses the BACK key, the current activity is popped from the stack, and the previous one resumes as the running activity.
Task裡放的,就是我們在使用一隻應用程式時,所有和這隻應用程式相關的Activity的存放空間。
程式會依我們最先開啟的Activity,在堆疊裡,開出一個Task(任務)空間,然後呢,它被放在Task的最頂層。如果此時又開了一個新的Activity,就會把前一個Activity往下推,而成為你眼前看到的畫面。之前那個Activity還是在堆疊裡,所以如果此時你又按了[返回]鍵,目前的新畫面,會"彈出"這個堆疊,然後把之前的Activity,利用onResume()的方式,呼叫回來你面前。

A task is a stack of activities, not a class or an element in the manifest file.
Task是一個Activity的堆疊,而非類別或任何manifest檔裡你看的見的物件。

Sunday, December 12, 2010

IntentService的功用

盧育聖的網站中,教了大家很清楚的IntentService的概念,但看了還不是很懂。
因為,這要從Thread講起(我接下來要說的,大家可以參考高煥堂-物件導向技術與執行緒模式一書找到更完整的解說)。
Android在傳遞訊息時,主要用到Looper和Message,每一個執行緒,都有一個Looper。
主畫面(UI)的Thread本身就有Looper去Handler(處理)各種訊息,但是,程式裡new出來的副Thread,當中的Looper必須被new出來,因為android沒有替副Thread做Looper。
想像一下,Looper是運轉中的果汁機,Message是柳丁,Android傳遞訊息的原理就是將Message丟進Looper,再由Handler(老闆娘)去處理看是要賣還是要自己喝。
在知道上面Thread、Looper、Message和Handler的概念後,我們再回到盧育聖的網站裡看。
IntentService繼承Service,我們先前知道,Service的幾個良好的特性︰
1.系統當發現資源不足,而砍殺現有程序時,Service是最後幾名被砍的,不會馬上被砍。
2.Service不用畫面就能做事情

所以,當我們用Intent去開啟IntentService時,就可以很放心的將要在背後做的事,放在IntentService去做,不用再擔心會被系統砍掉!

這裡還有asynctask和IntentService的差別。倒也可以順便看看。

總之,IntentService幫我們處理掉煩人的Looper、Message和Handler啦!

Service一些手札記錄

以下文章來至於Android guide

it's started by calling Context.startService() and stopped by calling Context.stopService(). It can stop itself by calling Service.stopSelf() or Service.stopSelfResult()
靠Context的method:startService()來啟動,
並靠stopService()關閉
另外,Service也能自行自殺:stopSelf()(果然具有服務精神啊!)


Only one stopService() call is needed to stop the service, no matter how many times startService() was called.
只需要呼叫一次stopService()就能關閉Service了。但是startService()愛叫幾次都行!(服務生真是周到!)

The two modes are not entirely separate. You can bind to a service that was started with startService().
開起服務生後,還能綁架他。
For example, a background music service could be started by calling startService() with an Intent object that identifies the music to play.
當叫服務生播音樂後,
Only later, possibly when the user wants to exercise some control over the player or get information about the current song, would an activity establish a connection to the service by calling bindService().
客人想要看歌曲資訊,也許這時後我們能呼叫綁架服務生。
In cases like this, stopService() will not actually stop the service until the last binding is closed. 
這時候,就算要關閉服務生,也因為被我們綁架了,他也逃不了了,除非我們解繩。

另外,服務生因為不是最重要的,所以生命短暫︰
他只有
void onCreate()  活吧!
void onStart(Intent intent)  做事了!
void onDestroy() 去死吧!
However, onStart() is called only for services started by startService().

當然,如果服務生被綁架了,事情就沒有那麼單純了!
快來看看會發生什麼事!?
 會產生3個Callback
IBinder onBind(Intent intent)
boolean onUnbind(Intent intent)
void onRebind(Intent intent)

The onBind() callback is passed the Intent object that was passed to bindService and onUnbind() is handed the intent that was passed to unbindService().
onBind()函式被拿來傳送Intent(意圖),
If the service permits the binding, onBind() returns the communications channel that clients use to interact with the service.
onBind()會回傳管道上和Service互動的資訊

keep in mind that any service, no matter how it's started, can potentially allow clients to bind to it, so any service may receive onBind() and onUnbind() calls.

因為任何的Service都有可能被綁架,所以你會看到實作Service時,都會有onBind()的出現
不管你是用bindService還是startService,總之,Service都有可能接到onBind()呼叫啦!

Tuesday, December 7, 2010

在Ubuntu10.04增加Android2.3的adb環境變數

1.終端機裡打上︰
sudo gedit '/etc/environment'
2.最後一行加上
:/media/Data/android-sdk-linux_86/platform-tools
3.重開機

adb工具在android2.3好像移到了platform-tools目錄(待查證)
(查證結果︰沒錯,果真被移至此
所以環境變數必須重新設定,才能繼續在任何目錄使用adb工具)

Monday, November 29, 2010

[作品集1]統一發票愛我

這隻是我第1隻上傳至Android Market的APK
但不是我第1隻APK
好難懂~~哈哈

v1.0.8版功能有
3種語音模式 - 美女主播 / 大奶搖搖 / 開心大媽,減少你枯燥的對獎心情
大鍵盤 - 連男生大拇指都能用(因為我量過)
懶人輸入法 - 直接打XX告訴你發票最後一碼沒中,節省你的時間
3種對獎邏輯 - 從右到左對,末三碼對,不管怎麼對,你都能對

感謝mobile01網友的熱列支持!

Wednesday, November 24, 2010

將Log輸出成txt的方法

Linux下,將檔案產生到根目錄︰
$adb logcat -d > ~/logcat.txt

如果是要顯示當前程式︰
$adb shell logcat -d > ~/logcat

如果還要顯示特定標籤
$adb shell logcat -d -s tag > ~/logcat
▲tag是我自訂的tag的標籤名稱

Windows下︰
adb logcat > %userprofile%\Desktop\logcat.txt

▲ 「>」是指令,不是終端機的目錄符號!

Monday, November 22, 2010

何謂Callback

搞了半天,原來Callback就是Android裡Receiver的技術名稱

我在這個網站中,覺得它的舉例我能理解
1.如果想省下等待的時間,這時CallBack function是可以考慮的
2.「為了不多做check事件的動作,但又必須在事件發生後執行程式。」
3.CallBack function並不是直接讓你的程式呼叫的,它是等著被呼叫的function

Thursday, November 18, 2010

Monday, October 18, 2010

dip和px

dip是絕對像素,在各種型號的大小不變
PX則會自動縮放大小
===========
對不起,以上的資料誤導大家了
在API文件中︰
the application must ensure that it declares its UI dimensions using density-independent pixels (dp) and scales any absolute pixel values (px)
也就是說︰dpi是根據螢幕密度去動態調整元件大小
而px是絕對大小

Sunday, October 3, 2010

kill-server後老是?????no permission

已經照著Android的教學去設定Ubuntu 的51-android.rules後
在$下達adb kill-server
再進到#下達adb start-server→adb devices
仍然是?????no permission的原因是:

Eclipse要先關掉,如果有接另一臺Android裝置也要拔掉
然後,程式才有可能成功的kill-server和start-server

Friday, October 1, 2010

onKeyDown()

如果要讓onKeyDown的返回鍵按下返回後,出現視窗並等待使用者的指令
則必須return true;
否則程式仍會跳回前一頁

Tuesday, September 21, 2010

1.5版以後不能用Widget直接update

在1.5時,各大書籍和SDKGUIDE都教我們在Widget template裡的<updateperiodmillis>設定時間來更新Widget,
但在1.6以上,Google把這項功能關閉了,強制將更新時間改為30mins以上。
所以,如果要更新,就必須改用AlarmManager,
並且,將<updateperiodmillis>設為0(事後證實,改為0後,若Service被關閉,Widget不會重開Service,所以不能改為0)。
免得像我一樣花了1天的時間在找為什麼一直出現NullPointerException><"(後來這個原因只是我程式邏輯中的問題)

Monday, September 13, 2010

有的沒的

在onCreate()建立的實體,在該Activity中,不會消失

BroadcastReceiver的兩種註冊方式

1.在主Activity的onCreate()裡registerReceiver();
2.在Androidmanifest.xml裡寫<receiver>標籤

註︰如果要自訂Broadcast的Action訊息,只能用第1種方法

Wednesday, August 25, 2010

simpleCursorAdapter和mutiple choice的問題

原本想說,simpleCursorAdapter的建構子是這樣︰
SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to)
那我只要將c.query()的回傳值Cursor放入,並指定欄位from和接收並顯示的位置to,就可以資料庫的資料一筆一筆顯示出來,
本來我資料庫還包括了_open這個boolean值


為的是要讓我的mutiple choice能控制是否被選取
結果...結果...
G前輩幫我花了一些時間去找原始碼,發現...
simpleCursorAdapter只收Text,不收其它值
simpleCursorAdapter的to位置,可以收android.R.id.text1
讓我們要顯示出來的文字可以在multiple-choice的xml標籤中顯示。

Sunday, August 22, 2010

ProgressDialog提示等待視窗

  哼,只不過是一個小視窗告知使用者︰你必須等待。這麼簡單的小東西我小鰻一下就能搞定!
  結果我一搞就搞了一個下午才搞懂。
  在SDK範例大全中,看到很多的ProgressDialog都有跟著執行緒做視窗的結束,起先我以為ProgressDialog就是要跟著執行緒了,研究到最後,才發現…其實不用。
  仔細看看程式碼,書裡寫的是︰
// 顯示Progress對話方塊
      myDialog = ProgressDialog.show
                 (
                   要顯示視窗的Context,
                   標題的文字,
                   視窗內文,
                   boolean值
                 );
      
      new Thread()
      { 
        public void run()
        { 
          try
          { 
            /* 在這裡寫上要背景執行的程式片段 */
            /* 為了明顯看見效果,以暫停3秒作為示範 */
            sleep(3000);
          }
          catch (Exception e)
          {
            e.printStackTrace();
          }
          finally
          {
            myDialog.dismiss();    // 卸載所建立的myDialog物件
          }
        }
      }.start(); //開始執行執行緒
ok,很清楚的知道,書裡在ProgressDialog的實作下,跟了一個Thread,好讓程式能夠在3秒以內完成並結束。

  但是,Thread這一段就算不寫也沒關係,反正只要用任何方法,讓
myDialog.dismiss();
能夠被執行到,視窗自然就會消失,畫面返回主程式了。

  小鰻真正在實作時,除了摸索Thread倒底要不要寫這個問題以外,還搞了一個小烏龍。
  ProgressDialog本身是能夠接受New出實體的,於是,我在
myDialog = ProgressDialog.show
                 (
                   要顯示視窗的Context,
                   標題的文字,
                   視窗內文,
                   boolean值
                 );
的上面,又加上一段
myDialog = new ProgressDialog(this);
也難怪我在執行myDialog.dismiss()時,怎麼樣也關不掉視窗!

Monday, August 16, 2010

我的隨手新聞RSS解析

話說︰
  小鰻最近在忙一個專案,其中有一段剛好跟RSS有關,研究了3、4天的SDK範例大全和另一本開發實務的書,在這裡稍微把概念記錄下來。
  android有提供幾種方式去抓取RSS資料來源,最為人知的就是使用SAXParser分析器了。
  RSS說簡單了,也只不過是一個xml檔(也有可能是其他的副檔名)。它有一定的規範與格式,所以在解析各大新聞臺的RSS時,也變得有跡可尋。

  以下是常看到的RSSxml檔案格式
  
<itme>
    <title>新聞的標題</title>
    <link>新聞的連結網址</link>
    <description>新聞內容簡述</description>
    <pubDate>發佈新聞的時間</pubDate>
</item>
  所以啦,所謂的RSS解析器,說白一點,就是在這些標籤裡,把字取出來啦!
  通常各大網站都會提供RSS新聞的連結,像yam新聞的RSS網址是這樣子的︰
http://c.yam.com/news/o5link/r.c?http://n.yam.com/RSS/Rss_life.xml
所以,到時候程式裡一定要有個地方能夠存這個網址。
------------------------------------------------------

RSS解析器裡會有4個工人分工合作著
(1)XMLReader - 閱讀工人
(2)MyAdapter(繼承自BaseAdapter) - 版面配置工人
(3)MyHandler(繼承自DefaultHandler) - 文字分配工人
(4)一個具物件導向特性的類別News - 模版工人

要能夠解析一份RSS,Android交給了4個工人幫忙
(1)XMLReader
第1個工人既然叫閱讀工人,我們就來看看這個工人是怎麼讀書的吧︰
path = "http://c.yam.com/news/o5link/r.c?http://n.yam.com/RSS/Rss_life.xml";
URL url = null;

try{
    url = new URL(path);//呼叫網址進來
    SAXParserFactory spf = SAXParserFactory.newInstance();//先蓋一個工廠
    SAXParser sp = spf.newSAXParser();//工廠有一個知識不太高的解析工人
    XMLReader xr = sp.getXMLReader();//也有一個閱讀工人
    MyHandler myHandler = new MyHandler();//用到了我們之後建立的分配工人
    xr.setContentHandler(myHandler);//將閱讀工人和分配工人做結合
    xr.parse(new InputSource(url.openStream()));//閱讀工人用parse去開啟一個InputStream放資料
    
    data = myHandler.getParsedData();//getParsedData()方法會在myHandler裡看到
}catch(Exception e){
    log.i("message","wrong!");
    }
原來光一個閱讀工人,就已經做了一些的前置動作了。

(2)MyAdapter
其實這個版面配置工人還真的是可有可無,要不是主編要求取出來的"新聞發佈時間"不能和"新聞標題"一樣大,還真的是不用請版面配置工人出來,用原本的android預設配置就好
這個配置工人,android希望我們繼承自BaseAdapter,而且,還規範了我們必須實作以下4個method
//建構子
public MyAdapter(Context context,List lt){
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount(){
    return 數量;
    }
@Override
public int getItem(int position){
    return position;
    }
@Override
public int getItemId(){
    return position;
    }
@Override
public View getView(int position,View convertView,ViewGroup par){
    ViewHolder holder;
    if(convertView == null){
    convertView = mInflater.inflate(R.layout.news_row, null);
    holder = new ViewHolder();
    holder.text = (TextView) convertView.findViewById(R.id.text);
    convertView.setTag(holder);
    }else{
    holder = (ViewHolder) convertView.getTag();
    }
    return convertView;
  }

class ViewHolder{//為自己訂的版面的xml做一個類別
    TextView text;
    }
}
最後面這段「為自己訂的版面的xml做一個類別」是什麼意思呢?
其實配置工人通常會帶一個小老婆出現︰
news_row.xml
他們其實是很親密的,正所謂孟不離焦啊!
一個負責外表(xml),另一個是對應的骨架(class ViewHolder)

(3)MyHandler
既然叫Handler,當然什麼事都要幫你搞定囉!
它也有幾個一定要我們覆寫的method
public class MyHandler extends DefaultHandler{
    private List<News> li;

    public List getParsedData(){//這個是我們自己寫的,沒有一定要覆寫
    return li;
    }
    
    @Override
    pubilc void starDocument() throws SAXExcption{
    li = new ArrayList<News>();//在程式解析之初,先建立一個ArrayList容器
    }

    @Override
    pubilc void endDocument() throws SAXExcption{
    }

    @Override
    pubilc void starElement(String namespaceURI, String localName, String qName, Attributes atts) thtows SAXException{
    news = new News();//在程式解析之初,先產生實體
    }

    @Override
    pubilc void endElement(String namespaceURI, String localName, String qName, Attributes atts) thtows SAXException{
    //更多程式碼
    }

    @Override  //取標籤內的字的方式
    pubilc void characters(char ch[], int start, int length){
    //更多程式碼    
    }
}
這段的程式跑法是︰
starElement()→characters()→endElement()→starElement()→characters()→endElement().....
將一個一個的標籤辨識出來並跑完



(4)News類別
既然叫模版工人,就是說從解析器解出來的字串,要有一個一個的對應去接收資料,好讓容器能歸納整理咩

所以,模版的程式碼超基本
public class News{
    private String title="";  //新聞的標題存在這裡
    private String link="";  //新聞的連結網址存在這裡
    private String desc="";  //新聞的大綱描述存在這裡
    private String date="";  //新聞的發佈時間存在這裡
//以下是一堆的getter和setter
//像︰
    public String getTitle(){
    return title;
    }
    public String setTitle(String title){
    this.title = title;
    }
    //更多程式碼    
}

最後最後,有一個超關鍵,如果你忽略了,可能都不知道為什麼會無法解析或上網咧!

請你在AndroidManifest.xml這個apk基本配面版中的<application>區塊外,做以下的新增
<uses-permission android:name="android.permission.INTERNET" />
因為android預設的apk連網功能是關閉的,必須透過上述的設定將上網權限打開。

好啦!大功告成。
程式不多不少,但是就是因為大家的配合,才產生出了好用的RSS隨手新聞。

下臺一鞠躬(笑)。

Thursday, August 12, 2010

intent和ActivityResult

一開始不懂為什麼Activity會有一個onActivityResult(),為什麼在A畫有等待接收B畫面的回傳值的功能,不是直接從Bintent過來就好了嗎?
產生這個問題後,馬上去問k前輩
他說
 一般來說,我們用intent是為了讓畫面能從A跳到B,但是如果我們又從Bintent到A,在Android中,會產生出2個A畫面,而且intent回去的A,是A2
但是,如果在B畫面setResult傳回A的onActivityResult(),此時雖然也是回A畫面,但是回的是最先開始的A1畫面,這樣解釋能理解嗎?

自訂Adapter的inflater方法

想要替Gallery或ListView去自制樣板時,通常會用到inflater指令。

然而這個Adapter會實作以下四個method︰

public class myAdapter extends baseAdapter
1.public int getCount() //決定了要跑出幾條清單
2.public Object getItem(int position)
3.public long getItemId(int position
4.public View getView(int position, View convertView, ViewGroup parent)//顯示的前置作業

Layout的顯現前置作業,通常都是在getView()裡去交待的
經實驗後,發現邏輯是
(1)進來跑一次的getView()→輸出position=0→out程式區塊
(2)重覆步驟1,但這次是跑position=1
(3)依此類推
★以上3個步驟,實驗後發現居然run了3遍這3個步驟


如︰
(1)Gallery經過setAdapter後的自製Adapter,會一次建立一個ImageView的實體,並取得資源,一經滑動到那個位置(Position),馬上重新執行一次getView(),去重新取得新的資源
(2)在ListView的setAdapter後的自製Adapter,也是一次一個getView()的方式去完成一格的list清單,重覆呼叫,直到整個ListView都將資料呈現出來為止

Wednesday, August 11, 2010

在Google Blogger使用程式碼標註「SyntaxHighlighter」


一、前言

資訊業版的部落格常常需要貼上程式語法,
但文字和程式碼黏成一塊,
對觀看的讀者來說實在是很吃力的一件事。

因此筆者在這邊推薦一套程式語法的「高亮」標註工具︰
SyntaxHighlighter。

一起來看看它的魅力吧!

二、文章開始


將SyntaxHighlighter語法貼進Blogger中

將下列程式碼複製到你的Blogger中的在[範本]→[網頁即時狀態]→[編輯HTML]→"<head>"標籤底下

    
    
    
    
註︰因為Blogger的一些BUG,在貼上上述的code並儲存時可能會遇到您的 HTML 不被接受: 缺少結尾的標記: SCRIPT 之類的警語,請修改成對應的語法。

攢寫SyntaxHighlighter語法

如何寫漂亮的程式框架和程式呢?
將下面這段寫在你的html頁面中
如果你的程式標註使用的是JAVA︰
程式碼寫在這裡...
 

如果你的程式標註使用的是C++︰
程式碼寫在這裡...
 

如果你的程式標註使用的是javascript︰
程式碼寫在這裡...
 

如果你的程式標註使用的是objective-c︰
程式碼寫在這裡...
 

如果你的程式標註使用的是shell︰
 
程式碼寫在這裡...

如果你的程式標註使用的是html︰
 
程式碼寫在這裡...

如果你的程式標註使用的是純文字︰
 
程式碼寫在這裡...

如果還想知道更多語法的用法,
可以參考SyntaxHighlighter官網

更換SyntaxHighlighter背景主題

可以參考官網教學
將上面

改成其它的.css主題檔。


其它有趣的用法


1.不要看到行數
 
程式碼寫在這裡...
執行結果如下
程式碼寫在這裡...

2.不要看到右邊的問號
 
程式碼寫在這裡...
執行結果如下
程式碼寫在這裡...

3.替特定行數畫下重點
 
程式碼第1行
程式碼第2行
程式碼第3行
執行結果如下
程式碼第1行
程式碼第2行
程式碼第3行

4.特殊符號表 

參考網址

Tuesday, July 20, 2010

周思博趣談軟體介面設計

【重點1】當「設計模型」越接近「使用者模型」,代表你的程式介面設計的越成功
使用者模型:使用者的心中所期待的程式使用方式
【重點2】如果程式模型越不單純,就可能不是使用者模型
【重點3】「請你做出選擇」盡可能是程式設計師的責任,而非使用者。
【重點4】善用「隱喻」可以幫助使用者的使用直覺性。
如:程式裡的放大鏡,使用者馬上能聯想到「放大」
【重點5】抄襲已成功的知名軟體設計,而非創新
也許程式設計師覺得自己設計出來的版面比別套成功的軟體好用,只因使用者已習慣舊有的控制方式,就算舊的程式設計的很難用,卻都會因"我習慣了"而變得好用。
【重點6】使用者看不下任何字,不管是有耐心還是沒耐心的使用者
也許我們以為字多=好,但是別想要使用者去翻說明書來使用!
【重點7】不要以為使用者手腳都很靈活
介面最忌諱設計的死板,我一定要"拉到底",才會出現最下面的畫面
【重點8】貼心 != 雞婆
常常我們以為這樣子設計使用者一定覺得方便,但有時候就是讓人不便【重點9】大眾常用到的功能,就是要優先陳列出來的功能


設計良好軟體大概需要六個步驟:

  1. 創造一些使用者
  2. 找出重要的活動
  3. 找出使用者模型:使用者期望如何完成這些活動
  4. 草擬出初版的設計
  5. 一直反覆把設計修改得更容易,直到虛構使用者能輕易使用為止
  6. 找真人來看著他們試用你的軟體。


Thursday, July 15, 2010

計算機概論

    CPU其實內部已經含有一些小指令集,我們所使用的軟體都要經過CPU內部的微指令集來達成才行。
    世界上常見到的兩種主要CPU種 類: 分別是精簡指令集(RISC)與複雜指令集(CISC)系統。
    CPU的種類
    精簡指令集(RISC)
    複雜指令集(CISC)
    優點
    指令的執行時間都很短
    完成的動作也很單純
    指令的執行效能較佳
    每個小指令可以執行一些較低階的硬體操作
    缺點
    複雜的事情,
    就要由多個指令來完成。
    指令數目多而且複雜
    使用的公司

    公司
    出產系列
    使用單位
    昇陽
    SPARC系列
    銀行金融體系的主要伺服器
    IBM

    Power Architecture(包括PowerPC)系列
    新力(Sony)公司出產的Play Station 3(PS3)
    ARM

    廠牌手機、PDA、導航系統
    AMD、Intel、VIA
    0/1 的單位我們稱為 bit。但 bit 實在太小了, 並且在儲存資料時每份簡單的資料都會使用到 8 個 bits 的大小來記錄,因此定義出 byte 這個單位,他們的關係為:
    1 Byte = 8 bits
    時脈就是CPU每秒鐘可以進行的工作次數。 所以時脈越高表示這顆CPU單位時間內可以作更多的事情。舉例來說,Intel的Core 2 Duo型號E8400的CPU時脈為3.0GHz, 表示這顆CPU在一秒內可以進行3.0x109次工作,每次工作都可以進行少數的指令運作之意。
    不同的CPU之間不能單純的以時脈來判斷 運算效能喔!這是因為每顆CPU的微指令集不相同,時脈目前僅能用來比較同款CPU的速度!
    主機板晶片組如何進行各個電腦元件之間的溝通?這個時候就需要用到所謂的I/O位址與IRQ囉!
    I/O位址有點類似每個裝置的門牌號碼,IRQ可以想成是各個門牌連接到郵件中心(CPU)的專門路徑囉!
    CMOS主要的功能為記錄主機板上面的重要參數, 包括系統時間、CPU電壓與頻率、各項設備的I/O位址與IRQ等,由於這些資料的記錄要花費電力,因此主機板上面才有電池。 BIOS為寫入到主機板上某一塊 flash 或 EEPROM 的程式,他可以在開機的時候執行,以載入CMOS當中的參數, 並嘗試呼叫儲存裝置中的開機程式,進一步進入作業系統當中。
    文字編碼系統
    當我們要寫入檔案的文字資料時,該文字資料會由編碼對照表將該文字轉成數字後,再存入檔案當中。 同樣的,當我們要將檔案內容的資料讀出時,也會經過編碼對照表將該數字轉成對應的文字後,再顯示到螢幕上。 現在你知道為何瀏覽器上面如果編碼寫錯時,會出現亂碼了嗎?這是因為編碼對照表寫錯, 導致對照的文字產生誤差之故啦!
    big5碼的中文字編碼對於某些資料庫系統來說是很有問題的,某些字碼例如『許、蓋、功』等字, 由於這幾個字的內部編碼會被誤判為單/雙引號,在寫入還不成問題,在讀出資料的對照表時, 常常就會變成亂碼。不只中文字,其他非英語系國家也常常會有這樣的問題出現啊!
    為了解決這個問題,由國際組織ISO/IEC跳出來制訂了所謂的Unicode編碼系 統, 我們常常稱呼的UTF8或萬國碼的編碼就是這個咚咚。因為這個編碼系統打破了所有國家的不同編碼, 因此目前網際網路社會大多朝向這個編碼系統在走,所以各位親愛的朋友啊,記得將你的編碼系統修訂一下喔!
    你要讓DVD影片能夠放映, 那就得要參考DVD光碟機的硬體資訊才行。
    作業系統(Operating System, OS)其實也是一組程式, 這組程式的重點在於管理電腦的所有活動以及驅動系統中的所有硬體。

Monday, July 12, 2010

Servlet initiate parameter

Servlet在建立時,有一個生命週期
而在建立一個Servlet的一開始,會先做一些初始化的動作,
而這些動作,是在web.xml裡去設定的...


如下圖,我們要在初始時,就將要印出來的table border設為2,而顏色設為藍色,web container會在建立Servlet的一開始,就在:ServerletConfig做設定
------------------------------------------------------------------


當我們要攢寫一個新的Servlet時,我們必須要繼承HttpServlet。但是我們的初始化設定在HttpServlet的父父類別(ServletConfig), 也就是在"阿公"這一代,就已經設定了。
---------------------------------------------------------------------
init()方法,在Servlet建立的初時,只會被呼叫1次,僅1次。


原來我們不是只在web.xml做<init-param>設定就好,還要在我們新寫的Servlet裡,呼叫阿公的method:init(),並使用getInitParameter去把web.xml裡的值叫出來做對應,才算正式的完成初始化。

---------------------------------------------------------------------------
初始化完就結束了?
這個答案是否定的,因為你還沒下參數來"使用"。
下了
out.println("<table border='" + borderSize + "' bordercolor='" + borderColor + " ' >");
才算真的完成格式的載入。


使用者註冊表單的整個架構圖

特殊的html標籤

段落
<ul>
//程式碼
</ul>


青春痘
<li>//青春痘裡要輸出的字</li>

將資料從1個servlet丟到另一個servlet

如果要將資料丟到別的程式,用到一個概念:
 所以 ,修改好的程式碼應該如下:
  request.setAttribute("error", error);
               RequestDispatcher view =
                request.getRequestDispatcher("tell_me_please.view");
        view.forward(request, response);


一定要在程式forward以前將資料setAttribute出去





Sunday, July 11, 2010

實體繼續導向

在一般的網頁中,如果要將第1個網頁跳轉到第2個網頁,也許我們會在doPost()下 response.sendRedirect("第2個網頁.view");指令。
但是,這在第2個網頁,會發生requests和response為null的冏境。
下面的圖示,TellMeServlet.java是第1個網頁,而HelloServlet.java是第2個網頁。
 原來在從who_are_you.html裡傳給TellMeServlet的request和reponse,在回傳給user的browser之後,再遇到response.sendRedirect("hello.view");時,這個時候的request和reponse都被清空了。
---------------------------------------------------------------------------------------------------------
解決的辦法當然是有,利用RequestDispatcher。


這樣子request和response物件就順利丟到第2個網頁了

Servlet Request









Saturday, July 10, 2010

HTTP Request method -GET和POST比較



HTML表單標籤

一個HTML表單(如:論壇裡註冊時你所輸入的註冊資訊),可能涵蓋了
●下拉式選單
●文字空格
●按鈕
因此我們必須知道HTML表單的標籤,才知道如何呼叫它們

2.新增首頁

要新增html或jsp首頁,需在web pages裡做new html的動作,新增一個index.html
然後,在web.xml的 <welcome-file>index.jsp</welcome-file>將jsp改成html
最後變成  <welcome-file>index.html</welcome-file>















1.web.xml

web.xml是替servlet做設定的功能


會分別加入 
1.<servlet></servlet>
再在裡面加入
(1) <servlet-name>替servlet取的名稱</servlet-name>
(2) <servlet-class>servlet的路徑</servlet-class>


2. <servlet-mapping></servlet-mapping>
在裡面再加入
(1) <servlet-name>servlet的名稱</servlet-name>
(2) <url-pattern>替這個servlet取上url</url-pattern>


以下是上述的實際輸出程式碼
    <servlet>
       <servlet-name>MyFirstServlet</servlet-name>
       <servlet-class>servlets.MyFirstServlet</servlet-class>
   </servlet>
   <servlet-mapping>
       <servlet-name>MyFirstServlet</servlet-name>
       <url-pattern>/my_first_servlet.view</url-pattern>
   </servlet-mapping>

Friday, July 9, 2010

MVC架構模式

Model:負責存放資料庫(交給EJB或Java Class負責)
View:負責畫面(交給JSP負責)
Controller:程式操作流程的程式碼(交給Servlet負責)
●接收HTML表單資料
●檢查表單資料格式
●再來要怎麼控制內部的程式碼

使用MVC的優點: 應用程式在擴充和修改時,不會因為程式碼都放在一起,而花費大量的時間。

Model2架構:使用MVC的另一種說法而已

Saturday, June 26, 2010

將自己設計xml的Layout轉成View:LayoutInflater

一般來講,我們用LayoutInflater做一件事:inflate

inflate這個方法總共有四種形式,目的都是把xml表述的layout轉化為View。其中只有一個我個人比較常用,View inflate(int resource, ViewGroup root),另外三個,其實目的和這個差不多。這裡簡單說一下它的用法,相信已經開始實踐的人都差不多用過了。
int resource,也就是resource/layout文件在R文件中對應的ID,這個必須指定。而ViewGroup root則可以是null,null時就只創建一個resource對應的View,不是null時,會將創建的view自動加為root的child。

問題就是為什麼要調用inflate(),而不是用setContentView()讓它自己去inflate?

setContentView()一旦調用, layout就會立刻被貼上UI。而inflate只會把Layout形成一個以view類實現成的對象。到時若有需要時再用 setContentView(view)把它貼上。

以上內容轉載至eoeAndroid論壇 

==========================================

一般來講,我們用LayoutInflater做一件事:inflate。 inflate這個方法總共有四種形式,目的都是把xml表述的layout轉化為View。 
This class is used to instantiate layout XML file into its corresponding View objects . It is never be used directly -- use getLayoutInflater() or getSystemService(String)getLayoutInflater() or getSystemService(String) to retrieve a standard LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are running on

1. Context.public abstract Object getSystemService (String name) :Return the handle to a system-level service by name. The class of the returned object varies by the requested name. 具體參見文檔。

2. 2種獲得LayoutInflater的方法
(1)通過SystemService獲得LayoutInflater inflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); (2)從給定的context中獲取




(3)二者區別:實質是一樣的,請看源碼

  1. public static LayoutInflater from(Context context) {  
  2.     LayoutInflater LayoutInflater =  
  3.             (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
  4.     if (LayoutInflater == null) {  
  5.         throw new AssertionError("LayoutInflater not found.");  
  6.     }  
  7.     return LayoutInflater;  
 3. LayoutInflater.inflate()將Layout文件轉換為View,顧名思義,專門供Layout使用的Inflater。
雖然 Layout也是View的子類,但在android中如果想將xml中的Layout轉換為View放入.java代碼中操作,只能通過 Inflater,而不能通過findViewById(),這一段描述有誤,看如下代碼。看下面文檔寫的已經很清楚。
  1. <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:orientation="vertical"   
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="wrap_content"> 
  5.       
  6.     <LinearLayout android:id="@+id/placeslist_linearlayout"  
  7.         android:layout_width="fill_parent"  
  8.         android:layout_height="wrap_content"  
  9.         android:orientation="vertical"> 
  10.           
  11.   LinearLayout> 
  12. >
LinearLayout linearLayout = (LinearLayout) findViewById(R.id.placeslist_linearlayout); linearLayout.addView(place_type_text);這是可運行的,這上面的xml中,LinearLayout不再是 Layout的代表,而只是一個普通的View。

 4. findViewById有2中形式R.layout.xx 是引用res/layout/xx.xml的佈局文件(inflate方法),R.id.xx是引用佈局文件裡面的組件,組件的id是xx. ..(findViewById方法)。看看R.java配置文件吧,R對文件分類管理,多寫幾個layout.xml後你會發現,所有的組件id都能用 R.id.xx來查看,但是組件不在setContentView()裡面的layout中就無法使用,Activity.findViewById() 會出現NullPointException。

(1)Activity中的findViewById()
 (2)View中的findViewById()
 以上轉載至醋溜的部落格

Friday, June 25, 2010

消除ImageButton的邊框

若想讓圖形按鈕只顯示圖形而不要出現邊框,只要在xml裡將background設成背景顏色即可

Tuesday, June 22, 2010

Android的雜七雜八

1.mp.getCurrentPosition()事件最好不要在程式一開始就啟動(建議有按鈕或時間軸的觸發事件)→可能出現IlleagalArgumentException
2. mp.getCurrentPosition()因為回傳millisecond,數值太微小,很難跟動態歌詞的時間對到。
3.TextView.setVisibiliy()、TextView.setBackgroundColor()這種之類的method都要在onCreat()時就設定好,否則出錯

Sunday, June 20, 2010

在Android使用HttpPost時,出現socketException: permission undenied

這是一個經典錯誤,Socket不能對外連接,錯誤不會被報出,調測的時候,能看到Exception,這個Exception會有很多變體,所以不一一舉例,只要存在網絡互聯,該東東挺身而出,讓你的程序不得運行。原因是:需要訪問到網絡, 所以,在AndroidManifest.xml文件中,需要進行如下配置:


Saturday, June 19, 2010

幽靈程式

我還在開發的mp3播放程式,昨天去請老師幫忙找bug,遇到了一段相當奇怪的狀況

我要將歌詞從txt檔,用BufferedReader.readLine()將資料放進hashMap裡,
我使用String.indexof("a")==1做判斷式,如果第1位是a,那我就不要將這一行放入hashMap。

這個動態歌詞的txt檔一部份是這樣的
[ti:煙花易冷]
[ar:周杰倫]
[al:跨時代]
[by:253057646] 
[00:11.62]繁華聲 遁入空門 折煞了世人
[00:17.59]夢偏冷 輾轉一生 情債又幾本

我的目的只要將"[00:11.62]繁華聲 遁入空門 折煞了世人"這類型的完整歌詞放進hashMap,
怎麼說"a"都應該在第1位元吧,
但怪異的是Eclipse一直跳出判斷式,
可是相同的程式碼在Netbeans去執行卻沒問題。

老師幫我抓出String.indexof("a")的位置居然是==2,
[ar:周杰倫]的"[a"前面,居然隱藏了一個空字元!

更怪的來了,當我真的將[a改成==2後,進入判斷式,判斷式裡面使用一個switch去抓出每行歌詞的資料,
我一樣用String.indexof("[")
但是這時候"["又變成==0!

在一旁跟我一起開發mp3的同學,稱此所謂幽靈程式。

老兄,七月還沒到哩!

Looper和Handler,有如劉備需孔明,孔明也需劉備

之前一直在當柯南,試圖查明Message和Looper、Handler的這段三角關係,什麼時候要用Message,什麼時候還要再多加Looper,Handler又是要幹嘛的?後來看了高煥堂先生第3本著作:物件導向技術與執行緒模式(第三版),才終於知道Looper和Message是連體嬰的關係,
只有不斷的Looper,副執行緒才能不斷的丟出Message,
若沒有Looper,就只會丟僅一次的Message。

而且,Looper的相關功能通常都放在Thread的run()方法中,如下:
public subThread implements Runnalbe{
       public void run(){
            Looper.prepare();
               //程式區塊(譬如像在這裡寫handler.sendMessage(Message);指令)
        Looper.loop();
            }

這兩行method很關鍵,要重複丟出Message,程式碼就要被包含在這2個method當中。

註:如果啟動了Looper,當run()結束時,Thread也不會dead

Wednesday, June 16, 2010

在Eclipse按Ctrl沒有出現java原始碼

解決辦法

When you are coding in Eclipse, press CTRL and click on any core Java class name in your source. Eclipse will now show a screen saying you don't have the sources installed. However, in this screen there is a link saying "Attach source...". Click that link and import the src.zip file from your JDK installation directory (src.zip). This should do the trick

Tuesday, June 15, 2010

UniCode table

符號
代碼
[
'\u005B'
]
'\u005D'
.
'\u002E'

Friday, June 11, 2010

android裡的thread的朋友:Handler

android裡的thread
好像不能即時Toast和TextView.setText()
一直出現錯誤訊息(後來找到原因,你往下看就知道了)
只能跟在run()裡執行seekbar.setProgress()

很怪,若要用thread傳送即時訊息更新,好像要用到android的class:handler(花了一天得結果:這個推論沒有錯!但是更新和加總的動作不是副執行緒做,副執行緒只負責sleep(1000),副執行緒永遠不可能可以傳送資料去給主畫面)


Android為了考量安全性和執行效能,不允許副執行緒直接更改主畫面的資料
eoeAndroid網友ardwing在評論中提到:
Android的用戶界面更新不是一個執行緒安全的進程。但有多危險我也不清楚,就是知道不能在子執行緒上進行更新。也就是說一切更新必須黏在和OnCreate()一起的主執行緒上。但對於多工工作來說,特別是開發遊戲引擎上是一個障礙。 為了解決這問題,Android提供了一個處理程序和sendMessage的機制。使設計者可以把主執行緒看成一個用戶界面更新的大本營。而每個子執行緒則變成一個獨立運作、獨立判斷的小個體。這些小個體遇到一些用戶界面更新的需要,便透過 sendMessage向大本營發出更新要求,讓大本營,也就是主執行緒進行排期更新。而在主執行緒負責接收這些信息的地方就是處理器下跟帶(?)的程序。

高煥堂老師在他的教學網站說:
在每一個進程有一個主線程(Main Thread),它的主要任務是處理與UI有關的事件(Event),所以又常稱為UI線程。主線程可以誕生子線程(Child Thread),而且「經 常」需要誕生子線程。其主要原因是:主線程(UI線程)必須時時刻刻(標準為5秒內)去照顧UI的事件,以便快速回應使用者的要求。因此,UI線程沒空閒去執行較為費時的函數(或工作),只好誕生子線程去 執行較為費時的工作了。在Android平台裡,UI線程與其子線程之分工是很明確的:
l   子線程負責執行費時的工作。
l   主線程負責UI的操作或事件;子線程不可以插手有關UI的事。



以下是我寫的mp3播放程式中,想要即時更新已播放時間,
最後找到的辦法:
副執行緒負責每隔一秒傳message,好讓我主畫面去s=s+1

主執行緒:
1.需新增一個Handler
2.此類別需實作handleMessage()方法
protected void onCreate(Bundle savedInstanceState) {
hr = new Handler(){
             public void handleMessage(Message msg) {
                 switch(msg.what){
                 //檢查msg.what若為ABC,就執行s+1,
                 //並將值設成已播放時間
                 case ABC:
                     if(mp.isPlaying()==true){
                     s++;               
                     tv2.setText(convert());
                     }
                 break;
                 }                         
                 super.handleMessage(msg);
             }
        };

}




在跑迴圈的副執行緒:
1.new一個Message類別
2.裡面使用.what函式
3.執行Handler這個物件的sendMessage()方法,括弧裡夾雜Message物件變數
new Thread(new Runnable(){
            public void run(){
               
                while(true){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                //使用Handler和Message把資料丟給主UI去後續處理
                if(Playist.this.isFinishing()==true){
                    return;
                    }
                Message m = new Message();                               
                 m.what = ABC;               
                 Playist.this.hr.sendMessage(m);               
                }                       
            }
        }).start();



幾個原則:
1.副執行緒永遠不可能改變主畫面的內容,如:setText主畫面的TextView
2.副執行緒的功能只是做"時間管理",任何計算或迴圈還是得交回主執行緒去處理