| Java? 語言是 Android 開發(fā)人員所選的工具。Android 運(yùn)行時使用自己的虛擬機(jī)Dalvik,這并不是多數(shù)程序開發(fā)人員使用的普通Java 虛擬機(jī)。Dalvik支持Java 編程語言的大部分功能——但并不是全部。在本文中,您將學(xué)習(xí)高級Java功能及其如何在Android中實現(xiàn)。這些功能包括并發(fā)性、聯(lián)網(wǎng)和數(shù)據(jù)庫訪問。 |
準(zhǔn)備開始
本文介紹幾種處理復(fù)雜情況的 Android SDK 工具。開發(fā) Android 應(yīng)用程序,需要最新版 Android SDK,這需要一個 Java Development Kit (JDK)。我使用的是 Android 2.2 和 JDK 1.6.0_17(這些工具的鏈接見 參考資料)。物理設(shè)備不是必須的;本文中的所有代碼在 SDK 附帶的 Android 模擬器上可以很好的運(yùn)行。本文假設(shè)您對 Android 編程比較熟悉,故不涉及 Android 基礎(chǔ)開發(fā),但是如果您有 Java 編程語言的知識,也是可以的。
并發(fā)性和聯(lián)網(wǎng)
Android 應(yīng)用程序一個最常見的任務(wù)就是檢索數(shù)據(jù)或通過網(wǎng)絡(luò)將數(shù)據(jù)發(fā)送到遠(yuǎn)程服務(wù)器。這一操作的結(jié)果通常是一些您想要展示給用戶的新數(shù)據(jù)。這意味著您需要修改用戶界面。大多數(shù)開發(fā)人員知道您將不會執(zhí)行一個潛在的長期運(yùn)行任務(wù),例如,在主 UI 線程上通過網(wǎng)絡(luò)訪問數(shù)據(jù)(特別使用一個網(wǎng)絡(luò)連接非常慢的手機(jī))。凍結(jié)您的應(yīng)用程序直至長期運(yùn)行任務(wù)完成。事實上,如果這個任務(wù)超過 5 秒,Android 操作系統(tǒng)將出現(xiàn)臭名昭著的 Application Not Responding 對話框,如圖1所示。
圖 1. Android 臭名昭著的 Application Not Responding 對話框
您不可能知道用戶網(wǎng)絡(luò)連接能有多慢。為了避免冒險,您必須在不同的線程上執(zhí)行任務(wù),或者至少不在主 UI 線程上執(zhí)行。許多 Android 應(yīng)用程序,但不是全部,需要處理多線程,由此引起并發(fā)。應(yīng)用程序經(jīng)常需要本地保存數(shù)據(jù),Android 數(shù)據(jù)庫是一個很好的選擇。這三個場景(不同線程,并發(fā)和本地保存數(shù)據(jù))在 Java 環(huán)境中有許多標(biāo)準(zhǔn) 方法可以用來處理。然而,正如您將要看到的,Android 提供不同的選擇。讓我們逐個看看,看看其優(yōu)點(diǎn)和缺點(diǎn)。
Android 網(wǎng)絡(luò)
通過網(wǎng)絡(luò)使用 Java 編程進(jìn)行調(diào)用是簡單的,我們熟悉的 java.net 包含幾個執(zhí)行此操作的類。這些類大多數(shù)在 Android 中都可用,事實上,您可以使用像 java.net.URL 和 java.net.URLConnection 這樣的類,就像您在其他 Java 應(yīng)用程序中那樣。然而,Android 包括 pache HttpClient 庫,這是在 Android 上連接網(wǎng)絡(luò)的首選方法。即使您使用常用 Java 類,Android 實現(xiàn)仍然使用 HttpClient。清單 1 顯示了一個使用這個必不可少的庫的示例。(所有源代碼見 下載。)
清單 1. 在 Android 上使用 Http Client 庫
private?ArrayList<Stock>?fetchStockData(Stock[]?oldStocks)? ?????throws?ClientProtocolException,?IOException{ ?????StringBuilder?sb?=?new?StringBuilder(); ?????for?(Stock?stock?:?oldStocks){ ?????????sb.append(stock.getSymbol()); ?????????sb.append('+'); ?????} ?????sb.deleteCharAt(sb.length()?-?1); ?????String?urlStr?=? ?????????"http://finance.yahoo.com/d/quotes.csv?f=sb2n&s="?+? ?????????????????sb.toString(); ?????HttpClient?client?=?new?DefaultHttpClient(); ?????HttpGet?request?=?new?HttpGet(urlStr.toString()); ?????HttpResponse?response?=?client.execute(request); ?????BufferedReader?reader?=?new?BufferedReader( ?????????????new?InputStreamReader(response.getEntity().getContent())); ?????String?line?=?reader.readLine(); ?????int?i?=?0; ?????ArrayList<Stock>?newStocks?=?new?ArrayList<Stock>(oldStocks.length); ?????while?(line?!=?null){ ?????????String[]?values?=?line.split(","); ?????????Stock?stock?=?new?Stock(oldStocks[i],?oldStocks[i].getId()); ?????????stock.setCurrentPrice(Double.parseDouble(values[1])); ?????????stock.setName(values[2]); ?????????newStocks.add(stock); ?????????line?=?reader.readLine(); ?????????i++; ?????} ?????return?newStocks; ?}? 在這段代碼中有一組 Stock 對象。這是基本的數(shù)據(jù)結(jié)構(gòu)對象,保存用戶擁有股票信息(比如,代號、價格等)以及更多的個人信息(比如,用戶付了多少錢)。您可以使用 HttpClient 類從 Yahoo Finance 檢索動態(tài)數(shù)據(jù)(例如,這支股票目前的價格)。HttpClient 包含一個 HttpUriRequest ,在本例中,您可以使用 HttpGet,這是 HttpUriRequest 的一個子類。類似地,當(dāng)您需要向遠(yuǎn)程服務(wù)器發(fā)送數(shù)據(jù)時,可以使用 HttpPost 類,當(dāng)您從客戶端得到 HttpResponse 時,您能接觸到響應(yīng)的潛在 InputStream、對其進(jìn)行緩沖、解析來獲取股票信息。
現(xiàn)在,您看到了如何通過網(wǎng)絡(luò)檢索數(shù)據(jù)、如何用這個數(shù)據(jù)來通過使用多線程智能地更新 Android UI。
Android 并發(fā)性實踐
如果您在應(yīng)用程序的主 UI 線程上運(yùn)行 清單 1 中的代碼,可能會出現(xiàn) Application Not Responding 對話框,具體視用戶網(wǎng)絡(luò)速度而定。因此必須確定生成一個線程來獲取數(shù)據(jù)。清單 2 顯示了一種解決方法。
清單 2. Na?ve 多線程(別這樣,這行不通!)
private?void?refreshStockData(){ ?????Runnable?task?=?new?Runnable(){ ?????????public?void?run()?{ ?????????????try?{ ?????????????????ArrayList<Stock>?newStocks?=? ?????????????????????fetchStockData(stocks.toArray( ???????????????????????????????????new?Stock[stocks.size()])); ?????????????????for?(int?i=0;i<stocks.size();i++){ ?????????????????????Stock?s?=?stocks.get(i); ?????????????????????s.setCurrentPrice( ???????????????????????????????????newStocks.get(i).getCurrentPrice()); ?????????????????????s.setName(newStocks.get(i).getName()); ?????????????????????refresh(); ?????????????????} ?????????????}?catch?(Exception?e)?{ ?????????????????Log.e("StockPortfolioViewStocks",? ?????????????????????????????"Exception?getting?stock?data",?e); ?????????????} ?????????} ?????}; ?????Thread?t?=?new?Thread(task); ?????t.start(); ?}? 清單 2 的標(biāo)題聲明這是 na?ve 代碼,確實是。在這個例子中,您將調(diào)用 清單 1 中的 fetchStockData 方法,將其封裝在 Runnable 對象中,并在一個新線程中執(zhí)行。在這個新線程中,您可以訪問 stocks,一個封裝 Activity(此類創(chuàng)建了 UI)的成員變量。顧名思義,這是 Stock 對象的一個數(shù)據(jù)結(jié)構(gòu)(本例中是 java.util.ArrayList)。換句話說,您在兩個線程之間共享數(shù)據(jù),主 UI 線程和衍生(spawned)線程(在 清單 2 中調(diào)用)。當(dāng)您修改了衍生線程中的共享數(shù)據(jù)時,通過在 Activity 對象上調(diào)用 refresh 方法來更新 UI。
如果您編寫了 Java Swing 應(yīng)用程序,您可能需要遵循一個像這樣的模式。然而,這在 Android 中將不能正常工作。衍生線程根本不能修改 UI。因此在不凍結(jié) UI ,但另一方面,在數(shù)據(jù)收到之后又允許您修改 UI 的情況下,您怎樣檢索數(shù)據(jù)?android.os.Handler 類允許您在線程之間協(xié)調(diào)和通信。清單 3 顯示了一個使用 Handler 的已更新 refreshStockData 方法。
清單 3. 實際工作的多線程 — 通過使用 Handler
private?void?refreshStockData(){ ?????final?ArrayList<Stock>?localStocks?=? ???????????new?ArrayList<Stock>(stocks.size()); ?????for?(Stock?stock?:?stocks){ ?????????localStocks.add(new?Stock(stock,?stock.getId())); ?????} ?????final?Handler?handler?=?new?Handler(){ ?????????@Override?????????public?void?handleMessage(Message?msg)?{ ?????????????for?(int?i=0;i<stocks.size();i++){ ?????????????????stocks.set(i,?localStocks.get(i)); ?????????????} ?????????????refresh(); ?????????} ?????}; ?????Runnable?task?=?new?Runnable(){ ?????????public?void?run()?{ ?????????????try?{ ?????????????????ArrayList<Stock>?newStocks?=? ?????????????????????fetchStockData(localStocks.toArray( ???????????????????????????????????new?Stock[localStocks.size()])); ?????????????????for?(int?i=0;i<localStocks.size();i++){ ?????????????????????Stock?ns?=?newStocks.get(i); ?????????????????????Stock?ls?=?localStocks.get(i); ?????????????????????ls.setName(ns.getName()); ?????????????????????ls.setCurrentPrice(ns.getCurrentPrice()); ?????????????????} ?????????????????handler.sendEmptyMessage(RESULT_OK); ?????????????}?catch?(Exception?e)?{ ?????????????????Log.e("StockPortfolioViewStocks",? ?????????????????????????????"Exception?getting?stock?data",?e); ?????????????}? ?????????} ?????}; ?????Thread?dataThread?=?new?Thread(task); ?????dataThread.start(); ?}? 在 清單 2 和 清單 3 中的代碼有兩個主要的不同。明顯的差異是 Handler 的存在。第二個不同是,在衍生線程中,您不能修改 UI。相反的,當(dāng)您將消息發(fā)送到 Handler,然后由 Handler 來修改 UI。也要注意,在線程中您不能修改 stocks 成員變量,正如您之前所做的。相反地您可以修改數(shù)據(jù)的本地副本。嚴(yán)格地來說,這是不是必須的,但這更為安全。
清單 3 說明了在并發(fā)編程中一些非常普遍的模式:復(fù)制數(shù)據(jù)、將數(shù)據(jù)解析到執(zhí)行長期任務(wù)的線程中、將結(jié)果數(shù)據(jù)傳遞回主 UI 線程、以及根據(jù)所屬數(shù)據(jù)更新主 UI 線程。Handlers 是 Android 中的主要通信機(jī)制,它們使這個模式易于實現(xiàn)。然而,清單 3 中仍然有一些樣本代碼。幸好,Android 提供方法來封裝和消除大多數(shù)樣本代碼。清單 4 演示了這一過程。
清單 4. 用一個 AsyncTask 使多線程更容易
private?void?refreshStockData()?{ ?????new?AsyncTask<Stock,?Void,?ArrayList<Stock>>(){ ?????????@Override?????????protected?void?onPostExecute(ArrayList<Stock>?result)?{ ?????????????ViewStocks.this.stocks?=?result; ?????????????refresh(); ?????????} ??????????@Override?????????protected?ArrayList<Stock>?doInBackground(Stock...?stocks){ ?????????????try?{ ?????????????????return?fetchStockData(stocks); ?????????????}?catch?(Exception?e)?{ ?????????????????Log.e("StockPortfolioViewStocks",?"Exception?getting?stock?data",?e); ?????????????} ?????????????return?null; ?????????} ?????}.execute(stocks.toArray(new?Stock[stocks.size()])); ?}? 如您所見,清單 4 比起 清單 3 樣本代碼明顯減少。您不能創(chuàng)建任何線程或 Handlers。使用 AsyncTask 來封裝所有樣本代碼。要創(chuàng)建 AsyncTask,您必須實現(xiàn) doInBackground 方法。該方法總是在獨(dú)立的線程中執(zhí)行,因此您可以自由調(diào)用長期運(yùn)行任務(wù)。它的輸入類型來自您所創(chuàng)建的 AsyncTask 的類型參數(shù)。在本例中,第一個類型參數(shù)是 Stock,因此 doInBackground 獲得傳遞給它的一組 Stock 對象。類似地,它返回一個 ArrayList<Stock>,因為這是 AsyncTask 的第三個類型參數(shù)。在此例中,我也選擇重寫 onPostExecute 方法。這是一個可選方法,如果您需要使用從 doInBackground 返回的數(shù)據(jù)來進(jìn)行一些操作,您可以選用這種方法來實現(xiàn)。這個方法總是在主 UI 線程上被執(zhí)行,因此對于修改 UI 這是一個很好的選擇。
有了 AsyncTask,您就完全可以簡化多線程代碼。它可以將許多并發(fā)陷阱從您的開發(fā)路徑刪除,您仍然可以使用 AsyncTask 尋找一些潛在問題,例如,在 doInBackground 方法對象執(zhí)行的同時設(shè)備上的方向發(fā)生改變時可能發(fā)生什么。更多關(guān)于如何處理這類案例的技術(shù),見 參考資料 的鏈接。
現(xiàn)在我們開始討論另一個常見任務(wù),其中 Android 明顯背離常用的 Java 方法 — 使用數(shù)據(jù)庫進(jìn)行處理。
Android 數(shù)據(jù)庫連通性
Android 中一個非常有用的特征就是存在本地關(guān)系數(shù)據(jù)庫。保證您能在本地文件中存儲您的數(shù)據(jù),但通常更有用的是使用一個關(guān)系型數(shù)據(jù)庫管理系統(tǒng)(Relational Database Management System,RDBMS)來存儲。Android 提供給您常用的 SQLite 數(shù)據(jù)庫來進(jìn)行處理,因為對于像 Android 這類嵌入式系統(tǒng)它是高度優(yōu)化的。它被 Android 上的核心應(yīng)用程序所用。例如,用戶地址簿是存儲在一個 SQLite 數(shù)據(jù)庫中。現(xiàn)在,對于給定的 Android 的 Java 實現(xiàn),您可以使用 JDBC 來訪問這些數(shù)據(jù)庫。出人意料的是,Android 甚至包括構(gòu)成主要部分 JDBC API 的 java.sql 和 javax.sql 包。然而,當(dāng)涉及使用本地 Android 數(shù)據(jù)庫進(jìn)行處理時,這毫無用處。相反地,您想要使用 android.database 和 android.database.sqlite 包。清單 5 是一個使用這些類存儲和檢索數(shù)據(jù)的示例。
清單 5. 使用 Android 進(jìn)行數(shù)據(jù)庫訪問
public?class?StocksDb?{ ?????private?static?final?String?DB_NAME?=?"stocks.db"; ?????private?static?final?int?DB_VERSION?=?1; ?????private?static?final?String?TABLE_NAME?=?"stock"; ?????private?static?final?String?CREATE_TABLE?=?"CREATE?TABLE?"?+? ?????????TABLE_NAME?+?"?(id?INTEGER?PRIMARY?KEY,?symbol?TEXT,?max_price?DECIMAL(8,2),?"?+ ?????????????"min_price?DECIMAL(8,2),?price_paid?DECIMAL(8,2),?"?+ ?????????????"quantity?INTEGER)"; ?????private?static?final?String?INSERT_SQL?=?"INSERT?INTO?"?+?TABLE_NAME?+ ?????????????"?(symbol,?max_price,?min_price,?price_paid,?quantity)?"?+ ?????????????"VALUES?(?,?,?,?,?)"; ?????private?static?final?String?READ_SQL?=?"SELECT?id,?symbol,?max_price,?"?+ ?????????????"min_price,?price_paid,?quantity?FROM?"?+?TABLE_NAME; ?????private?final?Context?context; ?????private?final?SQLiteOpenHelper?helper; ?????private?final?SQLiteStatement?stmt; ?????private?final?SQLiteDatabase?db; ?????public?StocksDb(Context?context){ ?????????this.context?=?context; ?????????helper?=?new?SQLiteOpenHelper(context,?DB_NAME,?null,? ?????????????????DB_VERSION){ ?????????????@Override?????????????public?void?onCreate(SQLiteDatabase?db)?{ ?????????????????db.execSQL(CREATE_TABLE); ?????????????} ??????????????@Override?????????????public?void?onUpgrade(SQLiteDatabase?db,?int?oldVersion,? ?????????????????????int?newVersion)?{ ?????????????????throw?new?UnsupportedOperationException(); ?????????????} ?????????}; ?????????db?=?helper.getWritableDatabase(); ?????????stmt?=?db.compileStatement(INSERT_SQL); ?????} ?????public?Stock?addStock(Stock?stock){ ?????????stmt.bindString(1,?stock.getSymbol()); ?????????stmt.bindDouble(2,?stock.getMaxPrice()); ?????????stmt.bindDouble(3,?stock.getMinPrice()); ?????????stmt.bindDouble(4,?stock.getPricePaid()); ?????????stmt.bindLong(5,?stock.getQuantity()); ?????????int?id?=?(int)?stmt.executeInsert(); ?????????return?new?Stock?(stock,?id); ?????} ?????public?ArrayList<Stock>?getStocks()?{ ?????????Cursor?results?=?db.rawQuery(READ_SQL,?null); ?????????ArrayList<Stock>?stocks?=? ??????????????????new?ArrayList<Stock>(results.getCount()); ?????????if?(results.moveToFirst()){ ?????????????int?idCol?=?results.getColumnIndex("id"); ?????????????int?symbolCol?=?results.getColumnIndex("symbol"); ?????????????int?maxCol?=?results.getColumnIndex("max_price"); ?????????????int?minCol?=?results.getColumnIndex("min_price"); ?????????????int?priceCol?=?results.getColumnIndex("price_paid"); ?????????????int?quanitytCol?=?results.getColumnIndex("quantity"); ?????????????do?{ ?????????????????Stock?stock?=?new?Stock(results.getString(symbolCol),? ?????????????????????????results.getDouble(priceCol),? ?????????????????????????results.getInt(quanitytCol),? ?????????????????????????????????????results.getInt(idCol)); ?????????????????stock.setMaxPrice(results.getDouble(maxCol)); ?????????????????stock.setMinPrice(results.getDouble(minCol)); ?????????????????stocks.add(stock); ?????????????}?while?(results.moveToNext()); ?????????} ?????????if?(!results.isClosed()){ ?????????????results.close(); ?????????} ?????????return?stocks; ?????} ?????public?void?close(){ ?????????helper.close(); ?????}???? ?}? 清單 5 中的類完全封裝了一個用于存儲股票信息的 SQLite 數(shù)據(jù)庫。因為您將要使用一個嵌入式數(shù)據(jù)庫,不僅是您的應(yīng)用程序要使用它,而且也要通過應(yīng)用程序來創(chuàng)建它。您需要提供代碼來創(chuàng)建該數(shù)據(jù)庫。Android 提供一個有用的抽象幫助類 SQLiteOpenHelper。要完成這一操作,您需要擴(kuò)展這個抽象類并提供代碼通過使用 onCreate 方法創(chuàng)建您的數(shù)據(jù)庫。當(dāng)您有一個幫助程序?qū)嵗龝r,就可以獲取一個 SQLiteDatabase 實例,您可以用來執(zhí)行任意 SQL 語句。
您的數(shù)據(jù)庫類有兩個較為方便的方法。第一個是 addStock,用于將新股票保存到數(shù)據(jù)庫中。注意,您使用了一個 SQLiteStatement 實例,這類似于一個 java.sql.PreparedStatement。需要注意的是,在您的類構(gòu)造器中如何對其進(jìn)行編譯,使其在每次調(diào)用 addStock 時都能重復(fù)利用。在每個 addStock 調(diào)用中,SQLiteStatement 的變量(INSERT_SQL 字符串中的問號)必然要將數(shù)據(jù)傳遞給 addStock。再一次強(qiáng)調(diào),這類似于 PreparedStatement ,您可以從 JDBC 了解它。
另一個方法是 getStocks。顧名思義,它從數(shù)據(jù)庫中檢索所有股票。注意,您再次使用一個 SQL 字符串,正如您在 JDBC 中所用的那樣。您可以在 SQLiteDatabase 類上通過使用 rawQuery 方法來進(jìn)行處理。這個類也有幾個查詢方法,讓您可以不使用 SQL 直接查詢數(shù)據(jù)庫。所有這些方法都返回一個 Cursor 對象,和 java.sql.ResultSet 非常相似。您可以將 Cursor 移動到從數(shù)據(jù)庫中返回的數(shù)據(jù)所在行,在每一行,您可以使用 getInt、getString 和其他的方法來檢索您要查詢的數(shù)據(jù)庫中各列相關(guān)的值。再一次強(qiáng)調(diào),這和 ResultSet 十分相似。也和 ResultSet 比較相似,當(dāng)您完成操作之后,關(guān)閉 Cursor 也十分重要的。如果您沒有關(guān)閉 Cursors,那么可能會迅速地耗盡內(nèi)存并導(dǎo)致您的應(yīng)用程序崩潰。
查詢本地數(shù)據(jù)庫是一個比較慢的過程,特別是,如果您有多行數(shù)據(jù)或者您需要在多個表之間運(yùn)行復(fù)雜的查詢語句。然而,數(shù)據(jù)庫查詢或插入超過 5 秒且出現(xiàn)一個 Application Not Responding 對話框,這種情況不太可能發(fā)生,但是當(dāng)您的數(shù)據(jù)庫忙于讀取和寫入數(shù)據(jù)時,凍結(jié)您的 UI 是不明智的。當(dāng)然,避免這種情況最好的辦法是使用 AsyncTask。清單 6 展示了這個示例。
清單 6. 在一個單獨(dú)的線程上插入數(shù)據(jù)庫
Button?button?=?(Button)?findViewById(R.id.btn); ?button.setOnClickListener(new?OnClickListener(){ ?????public?void?onClick(View?v)?{ ?????????String?symbol?=?symbolIn.getText().toString(); ?????????symbolIn.setText(""); ?????????double?max?=?Double.parseDouble(maxIn.getText().toString()); ?????????maxIn.setText(""); ?????????double?min?=?Double.parseDouble(minIn.getText().toString()); ?????????minIn.setText(""); ?????????double?pricePaid?=? ?????????????????Double.parseDouble(priceIn.getText().toString()); ?????????priceIn.setText(""); ?????????int?quantity?=?Integer.parseInt(quantIn.getText().toString()); ?????????quantIn.setText(""); ?????????Stock?stock?=?new?Stock(symbol,?pricePaid,?quantity); ?????????stock.setMaxPrice(max); ?????????stock.setMinPrice(min); ?????????new?AsyncTask<Stock,Void,Stock>(){ ?????????????@Override?????????????protected?Stock?doInBackground(Stock...?newStocks)?{ ??????????????????????????????????return?db.addStock(newStocks[0]); ?????????????} ?????????????@Override?????????????protected?void?onPostExecute(Stock?s){ ?????????????????addStockAndRefresh(s); ?????????????} ?????????}.execute(stock); ?????} ?});? 您可以先為按鈕創(chuàng)建一個實踐監(jiān)聽器。當(dāng)用戶點(diǎn)擊按鈕時,您可以從各個小部件(確切地說是 EditText 小部件)讀取股票數(shù)據(jù)并填入一個新的 Stock 對象。您可以創(chuàng)建一個 AsyncTask,并通過 doInBackground 方法從 清單 5 中調(diào)用 addStock 方法。如此,addStock 將在一個背景線程上執(zhí)行,而不是在主 UI 線程上。完成之后,將新 Stock 對象從數(shù)據(jù)庫傳遞到在主 UI 線程上執(zhí)行的 addStockAndRefresh 方法。
結(jié)束語
本文顯示了,即使 Android 在 Java 環(huán)境下僅支持眾多 API 的一個子集,但是其功能卻一點(diǎn)都不遜色。在某些示例中,比如網(wǎng)絡(luò),它完全實現(xiàn)了熟悉的 API,也提供了一些更為便捷的方法。在其他的示例中,比如并發(fā)性,Android 添加了額外 API 以及一些必須遵循的慣例。最后,在數(shù)據(jù)庫訪問案例中,Android 提供了完全不同的方法來訪問數(shù)據(jù)庫,但使用的是熟悉的概念。這不僅僅是標(biāo)準(zhǔn) Java 和 Android Java 技術(shù)之間主觀上的差異:它們形成了Android 開發(fā)的基礎(chǔ)構(gòu)建元件。
作者簡介:
Michael Galpin
Michael Galpin 是eBay 的一名架構(gòu)師。他經(jīng)常為 developerWorks 撰寫文章,同時在 TheServerSide.com 和 Java Developer 期刊上發(fā)表文章,他還有自己的博客。他從 1998 年開始做職業(yè)程序員,并擁有加州理工學(xué)院數(shù)學(xué)專業(yè)的學(xué)士學(xué)位。
更多內(nèi)容請見:http://www.ibm.com/developerworks/cn/xml/x-gourmetandroid/index.html
轉(zhuǎn)載于:https://www.cnblogs.com/mixer/archive/2010/09/10/2448796.html
總結(jié)
以上是生活随笔為你收集整理的享受Android应用程序的Java技术盛宴的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。