- 浏览: 16483902 次
- 性别:
- 来自: 济南
最新评论
-
wu1236:
ef0793cd94337324b6fefc4c9474af5 ...
Android ApiDemos示例解析(87):Media->MediaPlayer -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
本博客文章都为转载,没有任何版权! -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
VPLEX - EMC的RAC -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
qTip2 Show -
77219634:
0127bf2236bee4dd1f632ce430f1af1 ...
SecureCRT中文乱码、复制粘贴乱码解决办法(修改版)
上課講義之 1: 高煥堂講解 Intent-based Programming
Android的4種嫡系組件(即Activity、Service、IntentReceiver和ContentProvider)之間如何互相溝通呢?這4種嫡系組件都是由Android啟動的,並不是組件之間透過直接呼叫而啟動的。就像我們打手機去車行叫計程車,而不是直接到街道上叫車。我們送給行一個簡訊一通電話,表明我們的「意圖」(Intent),當車行經理接到此意圖,就依據你的意圖的內含條件而去挑選最合適的計程車,然後派遣它去接你。
「意圖」(Intent)本身是定義為一個類別(Class),一個Intent物件表達一個目的(Goal)或期望(Expectation),敘述其所期望的服務或動作、與動作有關的資料等。Android則根據此Intent物件之敘述,負責配對,找出相配的組件,然後將 Intent物件傳遞給所找到的組件,Android的媒婆任務就完成了。
因此,Intent物件扮演著媒體仲介的角色,提供「Client組件 à Android à Server組件」之間互相溝通的相關資訊,實現了Client組件與Server組件之間『不知而亦能用』之效果,這又稱為疏結合(Loosely-coupled)效果。其創造了Server組件抽換的自由度,這又稱為PnP(Plug and Play)。
茲以下圖為例,Activity主要是提供UI畫面來與User進行互動,兩個Activity之間的直接互動較少。其它如ContentProvider則常是為Activity等提供服務的。所以Activity發出Intent物件委託Android挑選到適當的ContentProvider物件(並且將Intent物件傳遞給ContentProvider物件)之後,通常會透過ContentProvider介面而呼叫ContentProvider的各項服務或功能。
在此圖所示的範例裡,當我們在一個訂單列表畫面(如Activity-1),點選某個訂單之後,希望能夠呈現出此訂單的採購細項畫面(如Activity-2)。此時,Activity-1需要發出一個 Intent物件,這個Intent物件說明了意圖:包括“查找”(Get)動作、訂單ID等資料,然後呼叫Activity父類別的startActivity (Intent intent)函數,將此Intent物件傳送給Android。而Android會根據此Intent物件中的敘述,與AndroidManifest.xml所敘述的各嫡系類別之條件相比較,找出與此Intent敘述相配的組件(如Activity-2),然後Android將該Intent物件遞交給它,於是Activity-2會根據此Intent物件之敘述而執行相應的動作。
上課講義摘錄之2: 高煥堂講解 ContentProvider & 範例
1. 何謂Android的嫡系組件
Android有4項一等公民(或稱為嫡系親屬),包括:Activity、ContentProvider、IntentReceiver與Service。它們都必須宣告於AndroidManifest.xml檔案裡,如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest p xmlns:android="http://schemas.android.com/apk/res/android"><p>package="com.misoo.SQ03"></p> <p><uses-permission p xmlns:android="http://schemas.android.com/apk/res/android"><p>android:name="android.permission.INTERNET"></p> <p></p></uses-permission></p> <p><application android:label="@string/app_name" android:icon="@drawable/icon"></application></p> <p><<b>provider</b> android:name="DataProvider"</p> <p>android:authorities="com.misoo.provider.SQ03"></p> <p><b>provider</b>></p> <p><<b>activity</b> android:name=".ac01" android:label="@string/app_name"></p> <p><intent-filter></intent-filter></p> <p><action android:name="android.intent.action.MAIN"></action></p> <p><category android:name="android.intent.category.LAUNCHER"></category></p> <p></p> <p><b>activity</b>></p> <p><<b>activity</b> android:name=".DispActivity" android:label="DispActivity"></p> <p><b>activity</b>></p> <p></p> <p></p></manifest>
這讓Android知道我們城市裡定義了多少個嫡系組件類別;Android可以在啟動時就將它們執行起來,成為共享的(Shared)服務組件。這些嫡系服務組件間的溝通,通常是透過「意圖」(Intent)物件來請Android轉達給對方,Android則會依據意圖而找出最佳的配對。配對成功,就展開相互的溝通與服務了。
2. 什麼是ContentProvider嫡系組件
---- 以SQLite為例
在Android裡,SQLite資料庫是最典型的ContentProvider,負責儲存各式各樣的內容。除了資料庫之外,還有許多其他種類的ContentProvider。在這裡並不是要介紹這些ContentProvider,而是要透過SQLite認識ContentProvider介面,然後將舶來Linter組件,配上這種ContentProvider介面,讓它搖身一變成為Android的嫡系組件。
2.1 一般(即非嫡系)SQLite的範例
沒有透過ContentProvider介面來使用SQLite,就是對SQLite的「非嫡系」用法。此時,應用程式透過JDBC介面和SQL語句來與SQLite溝通,以存取資料庫裡的內容。先認識這種傳統用法。此範例將從SQLite讀取資料。首先建立一個程式專案,其含有兩個Java程式檔:ac01.java和DataProvider.java。其中,ac01.java 是典型的Activity類別,負責UI畫面的顯示工作,而DataProvider則負責與SQLite溝通。其詳細程式碼為:
/* ----- ac01.java 程式碼 ------*/
package com.misoo.pklx;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.app.ListActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class ac01 extends ListActivity {
private static final String[] PROJECTION = new String[] { "stud_no", "stud_name" };
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DataProvider dp = new DataProvider(this);
Cursor cur = dp.query(PROJECTION, null, null, null);
ArrayList> coll
= new ArrayList>();
Map<string object> item; </string>
cur.moveToFirst();
while(!cur.isAfterLast()) {
item = new HashMap<string object>();</string>
item.put("c1", cur.getString(0) + ", " + cur.getString(1));
coll.add(item);
cur.moveToNext();
}
dp.close();
this.setListAdapter(new SimpleAdapter(this, coll,
android.R.layout.simple_list_item_1, new String[] { "c1" },
new int[] {android.R.id.text1}));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) {
finish();
}}
指令:
DataProvider dp = new DataProvider(this);
這和一般類別之用法是一樣的。ac01物件指名要誕生一個DataProvider的物件。然後呼叫它,如下指令:
Cursor cur = dp.query(PROJECTION, null, null, null);
這要求SQLite從資料庫查詢出某些資料。詳細的DataProvider.java程式碼如下:
/* ----- DataProvider.java 程式碼 ------*/
package com.misoo.pklx;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
public class DataProvider {
private static final String DATABASE_NAME = "StudDB";
private static final String TABLE_NAME = "Student";
private final int DB_MODE = Context.MODE_PRIVATE;
private SQLiteDatabase db=null;
public DataProvider(Context ctx) {
try { db = ctx.openOrCreateDatabase(DATABASE_NAME, DB_MODE, null); }
catch (Exception e) { Log.e("ERROR", e.toString()); return; }
try { db.execSQL("drop table "+ TABLE_NAME); }
catch (Exception e) { Log.e("ERROR", e.toString()); }
db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + "stud_no" + " TEXT,"
+ "stud_name" + " TEXT" + ");");
String sql_1 = "insert into "+ TABLE_NAME +
" (stud_no, stud_name) values('S101', 'Lily');";
String sql_2 = "insert into " + TABLE_NAME +
" (stud_no, stud_name) values('S102', 'Linda');";
String sql_3 = "insert into " + TABLE_NAME +
" (stud_no, stud_name) values('S103', 'Bruce');";
try { db.execSQL(sql_1); db.execSQL(sql_2); db.execSQL(sql_3); }
catch (SQLException e) { Log.e("ERROR", e.toString()); return; }
}
public Cursor query(String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor cur = db.query(TABLE_NAME, projection, null, null, null, null, null);
return cur;
}
public void close(){ db.close(); }
}
這種用法屬於非嫡系的用法:在ac01.java程式碼裡,其指令:
DataProvider dp = new DataProvider(this);
明確指定由DataProvider物件來提供服務。反之,嫡系用法則是透過意圖(Intent)來請Android代為配對,進而找出適當的ContentProvider物件來為aco1物件提供服務。
2.2 嫡系SQLite的範例
剛才的範例裡,我們直接使用DataProvider類別的介面來與SQLite溝通。本節的範例,將替DataProvider配上ContentProvider介面,讓ac01物件能透過ContentProvider新介面來溝通。此範例也是從SQLite資料庫讀取3筆資料;請仔細看看其程式碼的微妙差異:
/* ----- ac01.java 程式碼 ------*/
package com.misoo.pkrr;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.SimpleAdapter;
public class ac01 extends ListActivity {
public static int g_variable;
public static final String AUTHORITY = "com.misoo.provider.rx09-02";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ "/Student");
private static final String[] PROJECTION
= new String[]{ "stud_no", "stud_name"};
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent.getData() == null) intent.setData(CONTENT_URI);
Cursor cur = getContentResolver().query(getIntent().getData(),
PROJECTION, null, null, null);
ArrayList> coll = new ArrayList>();
Map<string object> item;</string>
cur.moveToFirst();
while (!cur.isAfterLast()) {
item = new HashMap<string object>();</string>
item.put("c1", cur.getString(0) + ", " + cur.getString(1));
coll.add(item);
cur.moveToNext();
}
this.setListAdapter(new SimpleAdapter(this, coll,
android.R.layout.simple_list_item_1, new String[] { "c1" },
new int[] { android.R.id.text1 }));
}
@Override
protected void onListItemClick(ListView l, View v, int position, long id) { finish();}
}
指令:
Cursor cur = getContentResolver().query(getIntent().getData(),
PROJECTION, null, null, null);
要求Android代為尋找適合的ContentProvider來提供服務,並不刻意指定由DataProvider物件來擔任。只要合乎ConentProvider介面,且符合意圖條件的物件皆可以來為ac01物件提供服務。於是,ac01程式碼就不再直接呼叫DataProvider類別的函數了,而是呼叫ContentProvider介面所提供的函數。再來仔細看看DataProvider類別與ContentProvider介面的搭配情形:
/* ----- DataProvider.java 程式碼 ------*/
package com.misoo.pkrr;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.util.Log;
public class DataProvider extends ContentProvider {
private static final String DATABASE_NAME = "StudNewDB";
private static final int DATABASE_VERSION = 2;
private static final String TABLE_NAME = "StudTable";
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION); }
@Override public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + TABLE_NAME + " (" + "stud_no"
+ " TEXT," + "stud_name" + " TEXT" + ");");
String sql_1 = "insert into " + TABLE_NAME
+ " (stud_no, stud_name) values('S1001', 'Pam');";
String sql_2 = "insert into " + TABLE_NAME
+ " (stud_no, stud_name) values('S1002', 'Steve');";
String sql_3 = "insert into " + TABLE_NAME
+ " (stud_no, stud_name) values('S1003', 'John');";
try { db.execSQL(sql_1); db.execSQL(sql_2); db.execSQL(sql_3); }
catch (SQLException e) { Log.e("ERROR", e.toString()); }
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}
// ---------------------------------------------------------------------------------
private DatabaseHelper mOpenHelper;
@Override public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext()); return true; }
@Override public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = db.query(TABLE_NAME, projection, null, null, null, null, null);
return c;
}
@Override public String getType(Uri uri) { return null; }
@Override public Uri insert(Uri uri, ContentValues initialValues) { return uri; }
@Override public int delete(Uri uri, String where, String[] whereArgs) { return 0; }
@Override public int update(Uri uri, ContentValues values, String where,
String[] whereArgs)
{ return 0; }
}
類別定義:
public class DataProvider extends ContentProvider {
// …..…..
}
DataProvider類別繼承ContentProvider父類別,也繼承了它的介面定義。ContentProvider介面定義了多個函數,主要包括:
l query()函數---- 它查詢出合乎某條件的資料。
l insert()函數---- 它將存入一筆新資料。
l delete()函數---- 它刪除合乎某條件的資料。
l update()函數---- 更新某些筆資料的內容。
在這個DataProvider類別裡,撰寫了query()函數內的指令,來實現query()介面,這個query()函數實際呼叫SQLite資料庫的功能。也就是說,ac01等應用程式透過ContentProvider介面間接呼叫到DataProvider的query()函數,然後此query()函數才使用SQLite的服務。
由於此範例的DataProvider已經是ContentProvider嫡系身份了,必須由Android來啟動它,而不是有ac01等應用程式來直接啟動它,所以必須在AndroidManifest.xml文檔裡給Android一些指示,如下:
/* ----- AndroidManifest.xml 文檔 ------*/
<?xml version="1.0" encoding="utf-8"?>
<manifest p xmlns:android="http://schemas.android.com/apk/res/android"><p>package="com.misoo.pkrr"</p> <p>android:versionCode="1"</p> <p>android:versionName="1.0.0"></p> <p><application android:label="@string/app_name" android:icon="@drawable/icon"></application></p> <p><activity p android:name=".ac01"><p>android:label="@string/app_name"></p> <p><intent-filter></intent-filter></p> <p><action android:name="android.intent.action.MAIN"></action></p> <p><category android:name="android.intent.category.LAUNCHER"></category></p> <p></p> <p></p></activity></p> <p><b><provider android:name="DataProvider" mce_keep="true" b></provider></b></p> <p><b>android:authorities="com.misoo.provider.rx09-02"></b></p> <p><b></b></p> <p></p> <p></p></manifest>
這特別說明DataProvider是一個ContentProvider,於是Android就會來啟動它。
上課講義摘錄之3: 實際演練Android模擬器之操作
實際演練Android模擬器之操作
l Android的嫡系組件(first-class citizen)
Activity:敘述User使用此AP時會進行的一連串活動。
Intent Receiver:用以接收外來的事件通知(Notification)。
Service:非UI的幕後服務程式。
Content Provider:將資料儲存於檔案系統或資料庫(如SQLite或 Linter)裡。
l Android的角色
Android是在Windows或Linux上執行一個ARM-CPU模擬器,並在此模擬器上執行
Linux2.6.23. Android是一個應用框架(Application Framework),執行於上述的模擬
環境裡。
l 從Windows XP環境進入Android裡的Linux環境
使用XP環境的命令列模式,進入:\android-sdk-windows-1.0_r1\tools\打入命令:
adb shell 就會出現#號,就進入Linux地盤了。
l adb是什麼
adb是Android裡的一個管理程式,稱為Android Debug Bridge。儲存於
c:\android-sdk-windows-1.0_r1\tools\裡的一個.exe程式。必需在命令列模式
裡執行。它能安裝.apk檔案、將檔案拷貝到模擬器裡等等。
l 如何載入Android的 *.apk呢?
Step-1: 啟動Android的模擬器(以mouse點選c:\android-sdk-windows-1.0_r1\tools\ 裡
的android圖像)。
Step-2: 拷貝*.apk檔案到c:\android-sdk-windows-1.0_r1\tools\裡。
Step-3: 使用命令列模式,進入\tools\,然後執行 adb install *.apk。
此.apk就被存入Linux的\data\app\裡,並出現於模擬器畫面的.apk裡了。
(PS. Andorid應用程式編譯之後會產出一個.apk檔案,它是一個壓縮檔。)
l 如何移除*.apk呢?
使用命令列模式,進入c:\android-sdk-windows-1.0_r1\tools\,然後,執行
adb shell rm *.apk。或者,執行adb shell打開一個Linux shell,再進入\data\app\,
執行#rm *.apk。
l 清除模擬器裡的資料(Wipe your emulator data)
隨著程式的執行,常常會留下一些資料在模擬器裡,如果你想清除掉它們,
可進入c:\android-sdk-windows-1.0_r1\tools\裡,打入命令:emulator -wipe-data
來啟動模擬器。
l Kill-Server
如果發現 Eclipse與模擬器溝通不良(例如出現有* daemon not running. starting it
now * 的訊息時),可以關掉Eclipse,進入c:\android-sdk-windows-1.0_r1\tools\裡,
打入命令:adb kill-server,再啟動Eclipse。
l adb功能
adb(Android Debug Bridge)是Android提供的的Debug工具,它可以管理設備或手機
模擬器的狀態、更新模擬器中的應用程式碼、執行設備shell命令等。例如:adb
install 、adb shell、#cd /data/app、#rm app.apk等。
---- 進入設備或模擬器的shell:adb shell就可以進入模擬器的shell環境中,這是
Linux Shell,可以執行各種Linux的命令,格式為:adb shell [command]
例如:
adb shell dmesg會列印出Linux的debug訊息。
---- 複製一個檔或目錄到模擬器上:adb push
---- 從模擬器上複製一個檔或目錄:adb pull 例如:adb pull /data/data/kk.xml
上課講義摘錄之4:Android與Cross Compiler之關係
高煥堂談:Android與Cross Compiler之關係
---- 在Ubuntu/Linux/X86 環境裡使用2007q3-51交叉編譯C程式,然後放入Android裡執行。
l 何謂Cross compiler(交叉編譯器)?
Cross Compiler主要在資源較豐富的電腦上執行,而編譯出能在別的電腦上執行的目的碼(Object Code)。例如,當我們想寫個C程式,讓它能在Android手機裡跑。Android手機的ARM-CPU及記憶體容量都很小,我們無法在資源有限的Android/ARM裡進行編輯及編譯C程式。可行的方法是:在X86 PC環境裏編輯C程式,然後使用Cross Compiler去編譯出適合ARM-CPU裡執行的目的碼。這稱為Cross Compiler。
l 安裝ARM GNU/Linux 交叉編譯器
在Ubuntu裡安裝交叉編譯器的步驟是:
Step-1. 在Ubuntu畫面上,直接上網:
Step-2. 選取2007q3-51版,並下載:
Step-3. 這會自動安裝於 /home/tom/arm-2007q3/裡。
Step-4. 這樣,交叉編譯器就安裝完成了。
l 使用Cross Compiler編譯C函數,放入Android裡執行。
可先將.h和.c程式碼存於自訂的Proj_01檔案夾裡,如下:
l 開始進行交叉編譯C程式碼
接下來,對HalfAdder.c和 com_misoo_gx05_NativeJniAdder.c兩個程式檔,進行編譯,
將 產生.o的目的程式(Object Code)檔。
*** 編譯HalfAdder.c程式 ***
*** 編譯com_misoo_gx05_NativeJniAdder.c程式 ***
從畫面可看到他已經產出了兩個ARM-based的 .o 目的程式檔了。
l 連結出可在ARM上執行的 .so程式檔
對HalfAdder.o和 com_misoo_gx05_NativeJniAdder.o兩個目的程式檔,
進行連結而產生.so的共享程式檔案,使用下述命令:
l 將libNativeJniAdder.so共享程式檔拷貝並放置到Android模擬器裡
例如,在Windows環境。
Step-1. 先將.so檔案拷貝到c:/android-sdk-windows-1.0_r1/tools/裡。
Step-2. 啟動模擬器。
Step-3. 進入c:/android-sdk-windows-1.0_r1/tools/,並使用adb push命令
將.so檔案,存入模擬器的/system/lib/裡。
l 撰寫主程式去呼叫這libNativeJniAdder.so共享程式
在Android的Java程式可輕鬆地透過JNI去呼叫此.so程式庫。
也可以再利用Cross Compiler編譯一個C主函數(main())去呼叫它。
~ END ~
上課講義摘錄之5: 認識Android Application
認識Android 應用程式(Application)
---------------------------------------------------------------------------------------------------------------
PS. 別忘了....
1. 高煥堂 11月台北 Android 教育訓練課程
12~1月上海 Android 教育訓練課程
2. 下載 高煥堂 第1本Android書籍的免費e-book
-----------------------------------------------------------------------------------------
整個應用程式都定義於AndroidManifest.xml裡,其宣告了其進入點(Entry Point)、通訊層級(Communication Layer)、授權(Permission),以及各個Activity和意圖(Intent)等。其中,有4種基礎組件,我們稱之為Android的嫡系組件。
l Activity: Android 應用程式的UI(User Interface)基本組件。
l Intent receiver: 可隨時被啟動來處理Intent,並執行其任務。
l Service: 非UI功能的幕後處理組件。
l Content provider: 跨程式的共享資料之儲存者。
如何添增圖片(Image)資源
圖片資源就直接將圖片檔(例如ok.jpg)拷貝到/res/drawable檔案夾裡。此時,Eclipse的Android插件(Android Plug-In)會自動將一個新的ID值添加到R.java裡。所以R.java檔案裡會多加了一行指令如下:
在應用程式碼將就由此ID值來取得這個圖片檔,並顯示或處理它。
如何定義XML畫面佈局(Layout)
剛才已經新增了一個圖片資源檔。此時,在定義畫面佈局的XML檔案裡,就可以引用它了。畫面佈局的XML檔都擺在/res/layout檔案夾裡,其中Eclipse的Android插件已經誕生一個main.xml在那裡了。現在,你可利用Eclipse的File>New>File菜單選項來誕生新的畫面佈局XML檔案,例如:button_layout.xml。然後,以main.xml內容為底稿,將之拷貝到新的button_layout.xml裡。
將<textview>部分更改為<imagebutton>如下:</imagebutton></textview>
在畫面佈局XML檔案裡,使用@drawable/就能輕鬆地引用/res檔案夾裡的資源了,例如上圖的android:src="@drawable/ok"。此外,layout_width和layout_height 則說明這個ImageButton顯示出來的大小(Size)。button_layout.xml也成為一項新的資源。所以在R.java裡也會自動產生新的一行,如下:
同樣地,在應用程式碼裡也能隨時引用這個資源了,例如將ac01.java裡的R.layout.main更改為R.layout.button_layout,如下:
此應用程式執行時,就引用到button_layout.xml資源而顯示於畫面上,如下:
~~ END ~~
上課講義摘錄之6:注意Android是應用框架,不是一般OS平台
在上一節課程裡,特別強調應用框架的神祕力量,必須由Android的設計人角度去看它。不一定要成為框架設計人,但從他的角度和觀點能深刻觀察框架的魅力的源頭。在本課程的第一本教科書<<google android>>裡(第1.5節),就提到常見的迷思:</google>
1.5 框架與OS之關係:常見的迷思
1.5.1 迷思
許多人從空間角度去想像OS與應用框架之間的關係。的確,OS(如Linux或Windows)像木板床,應用框架像彈簧床墊,其擺在木版床上。而應用程式則像睡在床墊上的人。這個觀點是對的(如圖1-4所示)。 然而,許多人順勢推論他們之間的互動關係如下圖:
圖1-5 常見的迷思
乍看之下,似乎蠻合理的,其實是個迷思。請你換個角度,採取另一個觀點,如下圖,更容易體會框架的角色和涵意,此新觀點如下圖1-6所示。
回想一下,您寫傳統程式時,主控權掌握在程式手中,其決定如何呼叫庫存函數﹔就像棒球比賽的「投手」一樣。反之,使用框架時,您的程式則擔任「捕手」之角色。盼您在使用框架時,能有這種心理準備(Mindset) 。
圖1-6 較合理的觀點
上課講義摘錄之7: Android裡的類別繼承及物件組合
在Android裡定義了如下的類別繼承(Class Inheritance)體系:
還有如下的物件組合(Object Composition)關係:
ViewGroup的子孫類別(如下圖的LinearLayout),也自然繼承了上圖的組合關係:
同樣地,View的子孫類別也具有同樣的繼承,可推導出如下之組合關係:
這些是Android已經提供的基類(Base Class)。
在這裡,話插一下,我在北京程序員雜誌上寫的<<基類與愚公移山>>一文裡,我稱之為『畚箕』。Android應用程式的開發者就如同挑畚箕的人,在中華歷史上,有個家喻戶曉的偉大人物就是『愚公』,它是挑畚箕的人,想把泰山的土一擔一擔挑去填北海。現在,我就來扮演愚公的角色,挑一擔(寫個Android應用程式)給你看看,但是請你不要叫我愚公就是。此外,我這個超級愚公還可以一根扁擔挑3個畚箕呢!!
首先建立一個Android Project:
我這個愚公希望手機畫面出現如下:
在畫面上輸入一個字串,並按下<ok>時,就在畫面title區輸出了該字串:</ok>
現在開始寫程式了,拿著一根扁擔(Layout)和兩三個畚箕(一個EditText、和兩個Button)。
程式碼如下:
package com.misoo.pkaz;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
private Button btn, btn2;
private EditText et;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.show_layout();
}
public void show_layout(){
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
et = new EditText(this);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(FP, WC);
layout.addView(et, param);
btn = new Button(this);
LinearLayout.LayoutParams param2 =
new LinearLayout.LayoutParams(WC, WC);
param2.topMargin = 5;
btn.setText("OK");
btn.setBackgroundResource(R.drawable.x_blue3);
btn.setOnClickListener(this);
layout.addView(btn, param2);
btn2 = new Button(this);
btn2.setText("Exit");
btn2.setTextColor(Color.RED);
btn2.setBackgroundResource(R.drawable.x_gray3);
btn2.setOnClickListener(this);
layout.addView(btn2, param2);
setContentView(layout);
}
public void onClick(View v) {
if(v == btn)
setTitle(et.getText());
else if(v == btn2)
finish();
}
}
透過layout扁擔的addView()函數就將畚箕一個一個挑起來了。
by Tom
講義摘錄之8:如何從DDMS發出簡(短)訊給應用程式?
1. 如何切換到DDMS?
途徑-1:
從Eclipse/Android應用程式編輯畫面:
按下右上角的:
就出現:
再選取<ddms>就開啟了。</ddms>
途徑-2:
按下 <ctrl><f8>組合鍵,就開啟了。</f8></ctrl>
DDMS開啟的畫面:
2. 如何返回Eclipse/Android程式編輯畫面:
途徑-1:
再按下剛才的小窗戶:
再選取Java就行了。
途徑-2:
按下 <ctrl><f8>組合鍵,就返了。</f8></ctrl>
3. 現在請回到Eclipse/Android程式編輯畫面,編輯一個應用程式:
編寫 ac01.java程式碼:
package com.misoo.pksms;
import android.app.Activity;
import android.os.Bundle;
public class ac01 extends Activity {
private static ac01 appRef = null;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
appRef = this;
setContentView(R.layout.main);
}
public static ac01 getApp(){
return appRef;
}
public void call_back(String str){
setTitle(str);
}
}
編寫 mySMSReceiver.java程式碼:
package com.misoo.pkzz;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class mySMSReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
ac01 app = ac01.getApp();
app.call_back("hi, how are you?");
}
}
4. 現在執行這個程式,出現你所熟悉的畫面:
5. 切換到DDMS。
6. 選取com.misoo.pksms,看到綠色小蟲亮了,如下:
7. 選擇<sms>,輸入任意電話號碼,也輸入短訊內容。</sms>
8. 按下<sms>,就送出簡訊給com.misoo.pksms程式了,於是此程式畫面出現簡訊了:</sms>
返回Eclipse/Android程式編輯畫面。
~~~~ by top articles ~~~
講義摘錄之10:佈局(Layout)在Android軟體開發上的重要角色
如果以SaaS(Software as a Service)的觀念來看Layout,會更清楚它的角色。如果以舞台劇的一『幕』來比喻Layout,會更傳神。一幕就是一個劇情的片段,此片段有其獨特的演出意涵,也是有頭有尾的完整片段。從SaaS的觀念來說,一個Layout是一個有其獨特目標的服務(Service)片段,或稱為小服務。這些小服務可以組合成為更完整更大的服務(或劇本)。
依據SaaS的概念,Service是有幕後的軟體類別或模組所提供、支撐或實現的。那麼,Layout背後的軟體模組是甚麼呢?答案是:Activity類別。有關於這些Layout的事件的處理程序都寫在Activity類別裡。不同的Activity類別可由不同的開發者負責開發。所以Activity可視為開發的分工單位。不同的開發者發揮其獨特的專業能力而開發出獨特的Activity類別,提供一些獨特的服務(即Layout)。
Activity類別像樹枝,Layout像樹葉,而Android應用程式的用戶就像在樹葉上跑來跑去的金龜蟲。那麼,Android應用程式的角色就是將一系列來自不同Activity的Layout串聯起來。也就是那隻金龜蟲的行動軌跡了。
那麼,Android應用程式又如何串聯這些Layout呢? 在Android平台裡,有個Intent類別。Android應用程式藉由Intent物件來與Android應用框架(Application Framework)核心進行溝通,請求Android框架核心來物色合適的Activity來提供其Layout,來進行服務。至於一個Activity到底提供甚麼目的的服務呢? 則寫在AndroidManifest.xml的Activity裡的Filter裡,基於Filter的條件而找到適當的服務,及其背後的Activity類別。該Activity再透過Layout而展現其服務。
一般而言,一個應用程式常包含多個獨立的服務流程,通稱為Use Case。而每一個Use Case都是由許多Layout提供的服務所組成。而Layout又是由Activity類別的多個函數來聯合服務。因此,傳統的UML和OOAD裡大家所熟悉的用例(Use Case)圖和類別(Class)圖,只要加上一個Layout的角色就能全部派上用場了。
當我們將Layout視為服務時,SOA(Service-Oriented Architecture)和SaaS(Software as a Service)的兩項新的軟體開發技術,也能派上用場了。因之,Layout扮演一個極為重要的角色,連結了最新的軟體開發技術與Android最新的軟體開發平台,將為Android軟體開發團隊帶來可靠的開發技術和工具,也將為Android應用軟體帶來極高的品質和可靠度。
講義摘錄之11:Android的類別繼承與委託之範例
1.1 類別繼承的副作用
繼承和委託皆能達到物件再用之目的,各有所長各有所短,相輔相成才是完美的。如下述類別:
在New Collegiate字典上對「正方形」的定義是:
「正方形是一種4邊等長的長方形」
所以使用繼承如下:
然而,上述的實作繼承卻有些缺點,宜避免之。例如,Square從Rectangle繼承了setLength()和setWidth()函數,但這兩個函數對於Square而言是無意義且有害的。因之,上述的繼承關係是不良的。至於如何改善上述的繼承關係呢?可改用委託:
當Square之物件接到外界傳來的area()訊息時,就委託Rectangle之物件代為處理。雖然委託會令程式複雜些,但勉強使用繼承,後遺症將更大。因為副作用可能會延續到Rectangle的各子孫類別!
1.2 Android的繼承與委託之例
茲以Android裡的MediaPlayer類別為例。
1.2.1 操作情境:
1. 此程式開始執行後,出現畫面如下:
2. 按下<play>,就開始播放MP3音樂。</play>
3. 若按下,就結束播放音樂。
4. 若按下<exit>,程式就結束了。</exit>
1.2.2 範例程式(1):採單純繼承方法
1.2.2.1 撰寫步驟:
Step-1: 建立Android專案:Px01。
Step-2: 撰寫Activity的子類別:ac01,其程式碼如下:
/* ac01.java */
package com.misoo.pkzz;
import android.app.Activity;
import android.graphics.Color;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
private MediaPlayer mPlayer;
private myButton btn, btn2, btn3;
public TextView tv;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
btn = new myButton(this);
btn.setId(101);
btn.setText("play");
btn.setOnClickListener(this);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(btn.get_width(),
btn.get_height());
param.topMargin = 10;
layout.addView(btn, param);
btn2 = new myButton(this);
btn2.setId(102);
btn2.setText("stop");
btn2.setOnClickListener(this);
layout.addView(btn2, param);
btn3 = new myButton(this);
btn3.setId(103);
btn3.setText("exit");
btn3.setOnClickListener(this);
layout.addView(btn3, param);
tv = new TextView(this);
tv.setTextColor(Color.WHITE);
tv.setText("Ready");
LinearLayout.LayoutParams param2 =
new LinearLayout.LayoutParams(FP, WC);
param2.topMargin = 10;
layout.addView(tv, param2);
setContentView(layout);
//---------------------------------------
myMediaPlayer my_player = new myMediaPlayer();
mPlayer = my_player.create(this, R.raw.test_cbr);
}
public void onClick(View v) {
switch(v.getId()){
case 101:
mPlayer.start();
break;
case 102:
mPlayer.stop();
break;
case 103:
finish();
break;
}
}
}
Step-3: 撰寫myMediaPlayer類別,其程式碼如下:
/* myMediaPlayer.java */
package com.misoo.pkzz;
import android.content.Context;
import android.media.MediaPlayer;
public class myMediaPlayer extends MediaPlayer {
public static MediaPlayer create(Context context){
return MediaPlayer.create(context, R.raw.test_cbr);
}
}
Step-4: 撰寫Button的子類別:myButton,其程式碼如下:
/* myButton.java */
package com.misoo.pkcc;
import android.content.Context;
import android.widget.Button;
public class myButton extends Button {
public myButton(Context ctx){
super(ctx);
super.setBackgroundResource(R.drawable.heart);
}
public int get_width(){
return 80;
}
public int get_height(){
return 50;
}
}
Step-5: 執行之。
1.2.2.2 說明:
在ac01類別的指令:
myMediaPlayer my_player = new myMediaPlayer();
mPlayer = my_player.create(this, R.raw.test_cbr);
所傳回來的是MediaPlayer類別之物件參考(即mPlayer的值),而不是myMediaPlayer類別的物件參考。既然是MediaPlayer類別之物件參考,則外界的類別或函數(如ac01類別)就能透過mPlayer參考(即使用MediaPlayer介面)來使用此新誕生的MediaPlayer類別之物件了,如下圖:
雖然在my_player物件裡也有一個MediaPlayer的小物件,但是mPlayer並不是參考到它,而是參考到由create()函數所誕生的心物件。
1.2.3 範例程式(2):採繼承與委託混合型
1.2.3.1 撰寫步驟:
Step-1: 建立Android專案:Px02。
Step-2: 撰寫Activity的子類別:ac01,其程式碼如下:
/* ac01.java */
package com.misoo.pkzz;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
private myMediaPlayer my_player;
private myButton btn, btn2, btn3;
public TextView tv;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
btn = new myButton(this);
btn.setId(101);
btn.setText("play");
btn.setOnClickListener(this);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(btn.get_width(),
btn.get_height());
param.topMargin = 10;
layout.addView(btn, param);
btn2 = new myButton(this);
btn2.setId(102);
btn2.setText("stop");
btn2.setOnClickListener(this);
layout.addView(btn2, param);
btn3 = new myButton(this);
btn3.setId(103);
btn3.setText("exit");
btn3.setOnClickListener(this);
layout.addView(btn3, param);
tv = new TextView(this);
tv.setTextColor(Color.WHITE);
tv.setText("Ready");
LinearLayout.LayoutParams param2 =
new LinearLayout.LayoutParams(FP, WC);
param2.topMargin = 10;
layout.addView(tv, param2);
setContentView(layout);
//---------------------------------------
my_player = myMediaPlayer.create(this);
}
public void onClick(View v) {
switch(v.getId()){
case 101:
my_player.start();
break;
case 102:
my_player.stop();
break;
case 103:
finish();
break;
}
}
}
Step-3: 撰寫myMediaPlayer類別,其程式碼如下:
/* myMediaPlayer.java */
package com.misoo.pkzz;
import android.app.Activity;
import android.content.Context;
import android.media.MediaPlayer;
import android.util.Log;
public class myMediaPlayer extends MediaPlayer {
private static MediaPlayer mSuper;
private static Context ctx;
public static myMediaPlayer create(Context context){
ctx = context;
mSuper = MediaPlayer.create(context, R.raw.test_cbr);
return new myMediaPlayer();
}
public void start(){
try{
mSuper.start();
((ac01)ctx).tv.setText("Playing audio...");
((Activity)ctx).setTitle("MP3 Music");
} catch (Exception e) {
Log.e("StartPlay", "error: " + e.getMessage(), e);
}
}
public void stop(){
if (mSuper != null) {
((ac01)ctx).tv.setText("Stop");
mSuper.stop();
mSuper.release();
mSuper = null;
}
}
}
Step-4: 撰寫Button的子類別:myButton,其程式碼如下:
/* myButton.java */
package com.misoo.pkcc;
import android.content.Context;
import android.widget.Button;
public class myButton extends Button {
public myButton(Context ctx){
super(ctx);
super.setBackgroundResource(R.drawable.heart);
}
public int get_width(){
return 80;
}
public int get_height(){
return 50;
}
}
Step-5: 執行之。
1.2.3.2 說明:
在ac01物件裡的my_player是參考到myMediaPlayer的物件,此物件裡mSuper再參考到MediaPlayer的物件,如下圖:
在ac01類別裡的指令:
my_player.start();
是呼叫到myMediaPlayer子類別的start()函數,然後才委託呼叫MediaPlayer的start()函數。此時,可看到「繼承的副作用」了,就是:myMediaPlayer繼承了眾多函數,並無法透過my_palyer去呼叫之,甚至可能是有害的。因此宜改用下一小節的單純委託方法。
1.2.4 範例程式(3):採單純委託方法
1.2.4.1 撰寫步驟:
Step-1: 建立Android專案:Px03。
Step-2: 撰寫Activity的子類別:ac01,其程式碼如下:
/* ac01.java */
package com.misoo.pkcc;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
public class ac01 extends Activity implements OnClickListener {
private final int WC = LinearLayout.LayoutParams.WRAP_CONTENT;
private final int FP = LinearLayout.LayoutParams.FILL_PARENT;
private mp3Player mp3_player;
private myButton btn, btn2, btn3;
public TextView tv;
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
mp3_player = new mp3Player(this);
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
btn = new myButton(this);
btn.setId(101);
btn.setText("play");
btn.setOnClickListener(this);
LinearLayout.LayoutParams param =
new LinearLayout.LayoutParams(btn.get_width(),
btn.get_height());
param.topMar
相关推荐
《高焕堂Android课堂讲义》是一份深入浅出的Android技术学习资料,由知名IT讲师高焕堂精心编撰。这份讲义以其独特的视角和深入的架构解析,深受Android初学者的喜爱。以下将对其中的主要知识点进行详细的阐述。 ...
高焕堂Android讲义高焕堂Android讲义
高焕堂android培训讲义PDF 第一部分 内容包含: 01 android基础框架30问 02 掌握android架构的知识体系 03 从减法设计看android架构 04 从代码认识框架和接口 05 如何动手做框架 06 android的多层框架体系
《高焕堂讲义---Android讲义》是一份详尽的教育资源,由知名IT专家高焕堂编写,专注于Android开发领域的知识传授。...通过深入研读并实践讲义中的知识点,你将能够构建出高质量、用户友好的Android应用程序。
《高焕堂 Android 讲义》是一份深入探讨Android开发技术的专业文档,由知名讲师高焕堂编写。这份讲义全面覆盖了Android平台的基础知识、核心概念以及实战技巧,是学习Android开发的重要参考资料。 首先,讲义可能从...
《高焕堂Android讲义》是一份深度剖析Android操作系统的宝贵资料,专为渴望深入了解这一全球最广泛应用的移动平台的学习者而设计。这份讲义详细阐述了Android开发的基础与核心概念,涵盖了从系统架构到应用开发的...
高焕堂老师的Android三层架构讲义深入探讨了这一主题,旨在帮助开发者构建更加规范、模块化的应用程序。以下是基于讲义内容的详细解释: 1. **表现层(Presentation Layer)** 表现层是用户与应用交互的部分,主要...
《Android-框架&软硬整合讲义》是台湾Android技术服务中心主任高焕堂先生的一份珍贵教学资料,主要探讨了Android系统的框架结构以及如何进行软硬整合。这份讲义不仅包含了理论知识,还可能附带了源代码示例,使得...
A01_Android的软硬整合潮流 A02_Android四大组件 A03_Android的进程和IPC机制 A04_Android的线程模式 A05_Android的软硬整合流程 A06_认识Android的UI架构 A07_SurfaceView与UI多线程与2D特效 A08_DB与...
《高焕堂Android讲义》是一份深入浅出的Android技术学习资料,由知名IT专家高焕堂编著。这份讲义涵盖了Android开发的基础到高级主题,是Android开发者或者对移动应用开发感兴趣的学习者的重要参考资料。 一、...
《高焕堂讲义 Android》是一份专注于Android开发的学习资料,由知名IT专家高焕堂编撰。这份讲义深入浅出地介绍了Android平台的基本概念、开发环境搭建、应用程序架构、用户界面设计、数据存储、网络通信、多线程处理...
Android应用框架原理与程序开发_高焕堂(简中版).pdf 高焕堂设计招式之美(6个pdf文档) 高焕堂(33个word文档) 总之,涉及内容挺多的,包括框架、底层、模式。和android有关系。 想跟高焕堂学习的别吝啬分数了,资源...
在Android系统中,Service是四大组件之一,它在后台运行,不提供用户界面,主要用于执行长时间的任务或与其他组件交互。高焕堂先生深入探讨的"Android底层结构-SDK Service线程精讲篇"主题,旨在解析Service的工作...
"高焕堂Android应用软件架构设计"可能涵盖了关于如何构建高效、可复用且易于理解的Android应用程序的深入知识。高焕堂,作为在IT业界有影响力的专家,他的见解对于开发者来说极具价值。 首先,我们来谈谈Android...
Android讲义(台湾高人高焕堂)-完整文字版