Android開(kāi)發(fā)網(wǎng)上的一些重要知識(shí)點(diǎn)[經(jīng)驗(yàn)分享]
文章分類(lèi):公司動(dòng)態(tài) 發(fā)布時(shí)間:2013-07-27 原文作者:admin 閱讀( )
1. android單實(shí)例運(yùn)行方法
我們都知道Android平臺(tái)沒(méi)有任務(wù)管理器,而內(nèi)部App維護(hù)者一個(gè)Activity history stack來(lái)實(shí)現(xiàn)窗口顯示和銷(xiāo)毀,對(duì)于常規(guī)從快捷方式運(yùn)行來(lái)看都是startActivity可能會(huì)使用FLAG_ACTIVITY_NEW_TASK標(biāo)記來(lái)打開(kāi)一個(gè)新窗口,比如Launcher,所以考慮單任務(wù)的實(shí)現(xiàn)方法比較簡(jiǎn)單,首先Android123糾正下大家一種錯(cuò)誤的方法就是直接在androidmanifest.xml的application節(jié)點(diǎn)中加入android:launchMode="singleInstance"這句,其實(shí)這樣將不會(huì)起到任何作用,Apps內(nèi)部維護(hù)的歷史棧作用于Activity,我們必須在activity節(jié)點(diǎn)中加入android:launchMode="singleInstance" 這句才能保證單實(shí)例,當(dāng)然一般均加在主程序啟動(dòng)窗口的Activity。
2. px像素如何轉(zhuǎn)為dip設(shè)備獨(dú)立像素
最近有網(wǎng)友問(wèn)如何將px像素轉(zhuǎn)為dip獨(dú)立設(shè)備像素,由于Android的設(shè)備分辨率眾多,目前主流的為wvga,而很多老的設(shè)備為hvga甚至低端的qvga,對(duì)于兼容性來(lái)說(shuō)使用dip無(wú)非是比較方便的,由于他和分辨率無(wú)關(guān)和屏幕的密度大小有關(guān),所以推薦使用。 px= (int) (dip*density+0.5f) //這里android開(kāi)發(fā)網(wǎng)提示大家很多網(wǎng)友獲取density(密度)的方法存在問(wèn)題,從資源中獲取的是靜態(tài)定義的,一般為1.0對(duì)于HVGA是正好的,而對(duì)于wvga這樣的應(yīng)該從WindowsManager中獲取,WVGA為1.5 這里可以再補(bǔ)充一下dip,sip的知識(shí)
3. Android中動(dòng)態(tài)改變ImageView大小
很多網(wǎng)友可能發(fā)現(xiàn)在layout.xml文件中定義了ImageView的絕對(duì)大小后,無(wú)法動(dòng)態(tài)修改以后的大小顯示,其實(shí)Android平臺(tái)在設(shè)計(jì)UI控件時(shí)考慮到這個(gè)問(wèn)題,為了適應(yīng)不同的Drawable可以通過(guò)在xml的相關(guān)ImageView中加入android:scaleType="fitXY" 這行即可,但因?yàn)槭褂昧丝s放可能會(huì)造成當(dāng)前UI有所變形。使用的前提是限制ImageView所在的層,可以使用一個(gè)內(nèi)嵌的方法限制顯示。
4. 如何判斷Android手機(jī)當(dāng)前是否聯(lián)網(wǎng)?
如果擬開(kāi)發(fā)一個(gè)網(wǎng)絡(luò)應(yīng)用的程序,首先考慮是否接入網(wǎng)絡(luò),在Android手機(jī)中判斷是否聯(lián)網(wǎng)可以通過(guò) ConnectivityManager 類(lèi)的isAvailable()方法判斷,首先獲取網(wǎng)絡(luò)通訊類(lèi)的實(shí)例 ConnectivityManager cwjManager=(ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE); ,使用cwjManager.getActiveNetworkInfo().isAvailable(); 來(lái)返回是否有效,如果為T(mén)rue則表示當(dāng)前Android手機(jī)已經(jīng)聯(lián)網(wǎng),可能是WiFi或GPRS、HSDPA等等,具體的可以通過(guò)ConnectivityManager 類(lèi)的getActiveNetworkInfo() 方法判斷詳細(xì)的接入方式,需要注意的是有關(guān)調(diào)用需要加入<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> 這個(gè)權(quán)限,android開(kāi)發(fā)網(wǎng)提醒大家在真機(jī)上Market和Browser程序都使用了這個(gè)方法,來(lái)判斷是否繼續(xù),同時(shí)在一些網(wǎng)絡(luò)超時(shí)的時(shí)候也可以檢查下網(wǎng)絡(luò)連接是否存在,以免浪費(fèi)手機(jī)上的電力資源。
5. Drawable、Bitmap、Canvas和Paint的關(guān)系
很多網(wǎng)友剛剛開(kāi)始學(xué)習(xí)Android平臺(tái),對(duì)于Drawable、Bitmap、Canvas和Paint它們之間的概念不是很清楚,其實(shí)它們除了Drawable外早在Sun的J2ME中就已經(jīng)出現(xiàn)了,但是在Android平臺(tái)中,Bitmap、Canvas相關(guān)的都有所變化。 首先讓我們理解下Android平臺(tái)中的顯示類(lèi)是View,但是還提供了底層圖形類(lèi)android.graphics,今天所說(shuō)的這些均為graphics底層圖形接口。 Bitmap - 稱(chēng)作位圖,一般位圖的文件格式后綴為bmp,當(dāng)然編碼器也有很多如RGB565、RGB888。作為一種逐像素的顯示對(duì)象執(zhí)行效率高,但是缺點(diǎn)也很明顯存儲(chǔ)效率低。我們理解為一種存儲(chǔ)對(duì)象比較好。 Drawable - 作為Android平下通用的圖形對(duì)象,它可以裝載常用格式的圖像,比如GIF、PNG、JPG,當(dāng)然也支持BMP,當(dāng)然還提供一些高級(jí)的可視化對(duì)象,比如漸變、圖形等。 Canvas - 名為畫(huà)布,我們可以看作是一種處理過(guò)程,使用各種方法來(lái)管理Bitmap、GL或者Path路徑,同時(shí)它可以配合Matrix矩陣類(lèi)給圖像做旋轉(zhuǎn)、縮放等操作,同時(shí)Canvas類(lèi)還提供了裁剪、選取等操作。 Paint - 我們可以把它看做一個(gè)畫(huà)圖工具,比如畫(huà)筆、畫(huà)刷。他管理了每個(gè)畫(huà)圖工具的字體、顏色、樣式。 如果涉及一些Android游戲開(kāi)發(fā)、顯示特效可以通過(guò)這些底層圖形類(lèi)來(lái)高效實(shí)現(xiàn)自己的應(yīng)用。
6. Activity切換導(dǎo)致的onCreate重復(fù)執(zhí)行
部分網(wǎng)友會(huì)發(fā)現(xiàn)Activity在切換到后臺(tái)或布局從橫屏LANDSCAPE切換到PORTRAIT,會(huì)重新切換Activity會(huì)觸發(fā)一次onCreate方法,我們可以在androidmanifest.xml中的activit元素加入這個(gè)屬性android:configChanges="orientation|keyboardHidden" 即可,比如 <activity android:name=".android123" android:configChanges="orientation|keyboardHidden"android:label="@string/app_name"> 同時(shí)在Activity的Java文件中重載onConfigurationChanged(Configuration newConfig)這個(gè)方法,這樣就不會(huì)在布局切換或窗口切換時(shí)重載onCreate等方法。代碼如下: @Override
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE)
{
//land
}
else if (this.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
{
//port
}
}
7. Android的ImageButton問(wèn)題
很多網(wǎng)友對(duì)Android提供的ImageButton有個(gè)疑問(wèn),當(dāng)顯示Drawable圖片時(shí)就不會(huì)再顯示文字了,其實(shí)解決的方法有兩種,第一種就是圖片中就寫(xiě)入文字,但是這樣解決會(huì)增加程序體積,同時(shí)硬編碼方式會(huì)影響多國(guó)語(yǔ)言的發(fā)布。第二種解決方法很簡(jiǎn)單,通過(guò)分析可以看到ImageButton的layout,我們可以直接直接繼承,添加一個(gè)TextView,對(duì)齊方式為右側(cè)即可實(shí)現(xiàn)ImageButton支持文字右側(cè)顯示。
8. Android代碼優(yōu)化技術(shù)
1.Java內(nèi)存控制 對(duì)于字符串操作而言如果需要連加這樣的操作建議使用StringBuilder,經(jīng)過(guò)調(diào)試不難發(fā)現(xiàn)如果你的字符串每次連加,使用String需要的內(nèi)存開(kāi)銷(xiāo)會(huì)遠(yuǎn)大于StringBuilder,然后Android手機(jī)常規(guī)的運(yùn)行內(nèi)存大約在128MB左右,對(duì)于運(yùn)行多任務(wù)就需要考慮了,Android開(kāi)發(fā)網(wǎng)提示因?yàn)镴ava有GC不需要手動(dòng)釋放那么分配的時(shí)候就要格外的小心,頻繁的GC操作仍然是很影響性能的,在調(diào)試時(shí)我們可以通過(guò)logcat查看內(nèi)存釋放情況。 2.循環(huán)使用 平時(shí)在訪問(wèn)一個(gè)屬性的時(shí)候效率遠(yuǎn)比一個(gè)固定變量低,如果你的循環(huán)估計(jì)次數(shù)常常大于5,假設(shè)xxx.GetLength()方法的值一般大于5,推薦這樣寫(xiě),比如 for(int i=0;i<xxx.GetLength();i++) 這里xxx.GetLength在每次循環(huán)都要調(diào)用,必然會(huì)影響程序效率,在游戲開(kāi)發(fā)中顯得更為明顯,改進(jìn)的方法應(yīng)該為 int j=xxx.GetLength() for(int i=0;i<j;i++) 3.圖片的優(yōu)化 在Android平臺(tái)中2維圖像處理庫(kù)BitmapFactory做的比較智能,為了減少文件體積和效率,常常不用很多資源文件,而把很多小圖片放在一個(gè)圖片中,有切片的方式來(lái)完成,在J2ME中我們這樣是為了將少文件頭而解決MIDP這些設(shè)備的問(wèn)題,而Android中雖然機(jī)型硬件配置都比較高,有關(guān)Android G1硬件配置可以參考G1手機(jī)參數(shù)以及評(píng)測(cè),但是當(dāng)資源多時(shí)這樣的運(yùn)行效率還是令人滿意的,至少Dalvik優(yōu)化的還不是很夠。
9. Android開(kāi)發(fā)進(jìn)階之NIO非阻塞包(一)
對(duì)于Android的網(wǎng)絡(luò)通訊性能的提高,我們可以使用Java上高性能的NIO (New I/O) 技術(shù)進(jìn)行處理,NIO是從JDK 1.4開(kāi)始引入的,NIO的N我們可以理解為Noblocking即非阻塞的意思,相對(duì)應(yīng)傳統(tǒng)的I/O,比如Socket的accpet()、read()這些方法而言都是阻塞的。 NIO主要使用了Channel和Selector來(lái)實(shí)現(xiàn),Java的Selector類(lèi)似Winsock的Select模式,是一種基于事件驅(qū)動(dòng)的,整個(gè)處理方法使用了輪訓(xùn)的狀態(tài)機(jī),如果你過(guò)去開(kāi)發(fā)過(guò)Symbian應(yīng)用的話這種方式有點(diǎn)像活動(dòng)對(duì)象,好處就是單線程更節(jié)省系統(tǒng)開(kāi)銷(xiāo),NIO的好處可以很好的處理并發(fā),對(duì)于Android網(wǎng)游開(kāi)發(fā)來(lái)說(shuō)比較關(guān)鍵,對(duì)于多點(diǎn)Socket連接而言使用NIO可以大大減少線程使用,降低了線程死鎖的概率,畢竟手機(jī)游戲有UI線程,音樂(lè)線程,網(wǎng)絡(luò)線程,管理的難度可想而知,同時(shí)I/O這種低速設(shè)備將影響游戲的體驗(yàn)。 NIO作為一種中高負(fù)載的I/O模型,相對(duì)于傳統(tǒng)的BIO (Blocking I/O)來(lái)說(shuō)有了很大的提高,處理并發(fā)不用太多的線程,省去了創(chuàng)建銷(xiāo)毀的時(shí)間,如果線程過(guò)多調(diào)度是問(wèn)題,同時(shí)很多線程可能處于空閑狀態(tài),大大浪費(fèi)了CPU時(shí)間,同時(shí)過(guò)多的線程可能是性能大幅下降,一般的解決方案中可能使用線程池來(lái)管理調(diào)度但這種方法治標(biāo)不治本。使用NIO可以使并發(fā)的效率大大提高。當(dāng)然NIO和JDK 7中的AIO還存在一些區(qū)別,AIO作為一種更新的當(dāng)然這是對(duì)于Java而言,如果你開(kāi)發(fā)過(guò)Winsock服務(wù)器,那么IOCP這樣的I/O完成端口可以解決更高級(jí)的負(fù)載,當(dāng)然了今天Android123主要給大家講解下為什么使用NIO在Android中有哪些用處。 NIO我們分為幾個(gè)類(lèi)型分別描述,作為Java的特性之一,我們需要了解一些新的概念,比如ByteBuffer類(lèi),Channel,SocketChannel,ServerSocketChannel,Selector和SelectionKey。有關(guān)具體的使用,Android開(kāi)發(fā)網(wǎng)將在明天詳細(xì)講解。網(wǎng)友可以在Android SDK文檔中看下java.nio和java.nio.channels兩個(gè)包了解。http://www.android123.com.cn/androidkaifa/695.html
了解下這種技術(shù),看看在馬上要做的項(xiàng)目中是否用得到
10. Android Theme和Styles內(nèi)部定義解析
昨天我們講到的有關(guān)在AndroidManifest.xml中定義Activity的theme方法來(lái)實(shí)現(xiàn)無(wú)標(biāo)題的方法,在使用xml讓你的Activity無(wú)標(biāo)題方法 一文中講到的,很多網(wǎng)友不明白為什么這樣做,其實(shí)在Android123以前的文章中多次提到了styles樣式定義方法,今天Android開(kāi)發(fā)網(wǎng)再次把一些網(wǎng)友回顧了解下android樣式的內(nèi)部定義。在一個(gè)工程的res/values/theme.xml中我們可以方便的定義自己的風(fēng)格主題,比如下面的cwjTheme中我們使用了基于android內(nèi)部的白色調(diào)的背景Theme.Light,設(shè)置windowsNoTitle為true代表沒(méi)有標(biāo)題,背景顏色我們使用了android內(nèi)部定義的透明,同時(shí)設(shè)置listView控件的樣式為cwjListView,xml樣式代碼如下: <?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="cwjTheme" parent="android:Theme.Light">
<item name="android:windowNoTitle">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:listViewStyle">@style/cwjListView</item>
</style> 有關(guān)ListView控件我們自定義的風(fēng)格就是修改下系統(tǒng)listview這個(gè)控件的每行分隔符樣式,這里我們?cè)诠こ滔聄es/drawable文件夾下放一個(gè)圖片名為list_selector圖片,這樣我們的cwjListView的代碼可以這樣寫(xiě) <style name="cwjListView" parent="@android:style/Widget.ListView">
<item name="android:listSelector">@drawable/list_selector</item>
</style>
</resources> 通過(guò)定義style可以設(shè)置更多,比如讓cwjListView的字體顏色就加入textAppearance屬性,比如 <item name="textAppearance">@android:style/TextAppearance</item> 等等。
11.Android JSON解析示例代碼
來(lái)自Google官方的有關(guān)Android平臺(tái)的JSON解析示例,如果遠(yuǎn)程服務(wù)器使用了json而不是xml的數(shù)據(jù)提供,在Android平臺(tái)上已經(jīng)內(nèi)置的org.json包可以很方便的實(shí)現(xiàn)手機(jī)客戶端的解析處理。下面Android123一起分析下這個(gè)例子,幫助Android開(kāi)發(fā)者需要有關(guān) HTTP通訊、正則表達(dá)式、JSON解析、appWidget開(kāi)發(fā)的一些知識(shí)。 public class WordWidget extends AppWidgetProvider { //appWidget
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
context.startService(new Intent(context, UpdateService.class)); //避免ANR,所以Widget中開(kāi)了個(gè)服務(wù)
} public static class UpdateService extends Service {
@Override
public void onStart(Intent intent, int startId) {
// Build the widget update for today
RemoteViews updateViews = buildUpdate(this); ComponentName thisWidget = new ComponentName(this, WordWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
} public RemoteViews buildUpdate(Context context) {
// Pick out month names from resources
Resources res = context.getResources();
String[] monthNames = res.getStringArray(R.array.month_names); Time today = new Time();
today.setToNow(); String pageName = res.getString(R.string.template_wotd_title,
monthNames[today.month], today.monthDay);
RemoteViews updateViews = null;
String pageContent = ""; try {
SimpleWikiHelper.prepareUserAgent(context);
pageContent = SimpleWikiHelper.getPageContent(pageName, false);
} catch (ApiException e) {
Log.e("WordWidget", "Couldn't contact API", e);
} catch (ParseException e) {
Log.e("WordWidget", "Couldn't parse API response", e);
} Pattern pattern = Pattern.compile(SimpleWikiHelper.WORD_OF_DAY_REGEX); //正則表達(dá)式處理,有關(guān)定義見(jiàn)下面的SimpleWikiHelper類(lèi)
Matcher matcher = pattern.matcher(pageContent);
if (matcher.find()) {
updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_word); String wordTitle = matcher.group(1);
updateViews.setTextViewText(R.id.word_title, wordTitle);
updateViews.setTextViewText(R.id.word_type, matcher.group(2));
updateViews.setTextViewText(R.id.definition, matcher.group(3).trim()); String definePage = res.getString(R.string.template_define_url,
Uri.encode(wordTitle));
Intent defineIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(definePage)); //這里是打開(kāi)相應(yīng)的網(wǎng)頁(yè),所以Uri是http的url,action是view即打開(kāi)web瀏覽器
PendingIntent pendingIntent = PendingIntent.getActivity(context,
0 /* no requestCode */, defineIntent, 0 /* no flags */);
updateViews.setOnClickPendingIntent(R.id.widget, pendingIntent); //單擊Widget打開(kāi)Activity } else {
updateViews = new RemoteViews(context.getPackageName(), R.layout.widget_message);
CharSequence errorMessage = context.getText(R.string.widget_error);
updateViews.setTextViewText(R.id.message, errorMessage);
}
return updateViews;
} @Override
public IBinder onBind(Intent intent) {
// We don't need to bind to this service
return null;
}
}
} 有關(guān)網(wǎng)絡(luò)通訊的實(shí)體類(lèi),以及一些常量定義如下: public class SimpleWikiHelper {
private static final String TAG = "SimpleWikiHelper"; public static final String WORD_OF_DAY_REGEX =
"(?s)\\{\\{wotd\\|(.+?)\\|(.+?)\\|([^#\\|]+).*?\\}\\}"; private static final String WIKTIONARY_PAGE =
"http://en.wiktionary.org/w/api.php?action=query&prop=revisions&titles=%s&" +
"rvprop=content&format=json%s"; private static final String WIKTIONARY_EXPAND_TEMPLATES =
"&rvexpandtemplates=true"; private static final int HTTP_STATUS_OK = 200; private static byte[] sBuffer = new byte[512]; private static String sUserAgent = null; public static class ApiException extends Exception {
public ApiException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
} public ApiException(String detailMessage) {
super(detailMessage);
}
} public static class ParseException extends Exception {
public ParseException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
} public static void prepareUserAgent(Context context) {
try {
// Read package name and version number from manifest
PackageManager manager = context.getPackageManager();
PackageInfo info = manager.getPackageInfo(context.getPackageName(), 0);
sUserAgent = String.format(context.getString(R.string.template_user_agent),
info.packageName, info.versionName); } catch(NameNotFoundException e) {
Log.e(TAG, "Couldn't find package information in PackageManager", e);
}
} public static String getPageContent(String title, boolean expandTemplates)
throws ApiException, ParseException {
String encodedTitle = Uri.encode(title);
String expandClause = expandTemplates ? WIKTIONARY_EXPAND_TEMPLATES : ""; String content = getUrlContent(String.format(WIKTIONARY_PAGE, encodedTitle, expandClause));
try {
JSONObject response = new JSONObject(content);
JSONObject query = response.getJSONObject("query");
JSONObject pages = query.getJSONObject("pages");
JSONObject page = pages.getJSONObject((String) pages.keys().next());
JSONArray revisions = page.getJSONArray("revisions");
JSONObject revision = revisions.getJSONObject(0);
return revision.getString("*");
} catch (JSONException e) {
throw new ParseException("Problem parsing API response", e);
}
} protected static synchronized String getUrlContent(String url) throws ApiException {
if (sUserAgent == null) {
throw new ApiException("User-Agent string must be prepared");
} HttpClient client = new DefaultHttpClient();
HttpGet request = new HttpGet(url);
request.setHeader("User-Agent", sUserAgent); //設(shè)置客戶端標(biāo)識(shí) try {
HttpResponse response = client.execute(request); StatusLine status = response.getStatusLine();
if (status.getStatusCode() != HTTP_STATUS_OK) {
throw new ApiException("Invalid response from server: " +
status.toString());
} HttpEntity entity = response.getEntity();
InputStream inputStream = entity.getContent(); //獲取HTTP返回的數(shù)據(jù)流 ByteArrayOutputStream content = new ByteArrayOutputStream(); int readBytes = 0;
while ((readBytes = inputStream.read(sBuffer)) != -1) {
content.write(sBuffer, 0, readBytes); //轉(zhuǎn)化為字節(jié)數(shù)組流
} return new String(content.toByteArray()); //從字節(jié)數(shù)組構(gòu)建String
} catch (IOException e) {
throw new ApiException("Problem communicating with API", e);
}
}
} 有關(guān)整個(gè)每日維基的widget例子比較簡(jiǎn)單,主要是幫助大家積累常用代碼,了解Android平臺(tái) JSON的處理方式,畢竟很多Server還是Java的。
12.Android中使用定時(shí)器TimerTask類(lèi)介紹
在Android平臺(tái)中需要反復(fù)按周期執(zhí)行方法可以使用Java上自帶的TimerTask類(lèi),TimerTask相對(duì)于Thread來(lái)說(shuō)對(duì)于資源消耗的更低,除了使用Android自帶的AlarmManager使用Timer定時(shí)器是一種更好的解決方法。 我們需要引入import java.util.Timer; 和 import java.util.TimerTask; private Timer mTimer = new Timer(true);
private TimerTask mTimerTask; mTimerTask = new TimerTask()
{
public void run()
{
Log.v("android123","cwj");
}
};
mTimer.schedule(mTimerTask, 5000,1000); //在1秒后每5秒執(zhí)行一次定時(shí)器中的方法,比如本文為調(diào)用log.v打印輸出。 如果想取消可以調(diào)用下面方法,取消定時(shí)器的執(zhí)行 while(!mTimerTask.cancel());
mTimer.cancel(); 最后Android123提示大家,如果處理的東西比較耗時(shí)還是開(kāi)個(gè)線程比較好,Timer還是會(huì)阻塞主線程的執(zhí)行,更像是一種消息的執(zhí)行方式。當(dāng)然比Handler的postDelay等方法更適合處理計(jì)劃任務(wù)。