Android 常見面試 知識點小結
來自: http://blog.csdn.net//never_cxb/article/details/50457410
前言
根據筆者自己的閱讀以及項目經驗總結而言,不同于網上的copy來copy去。很多內容加上了自己的理解,難免有錯誤不當之處,煩請指出。
Handler
在子線程里面創建 Handler 對象會拋出異常Can't create handler inside thread that has not called Looper.prepare()
。
但是加上Looper.prepare();
不會拋出異常, 這個因為 Handler 對應一個 Looper,一個 Looper 對應一個線程。
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); handler2 = new Handler(); } }).start();
用 ThreadLocal 保存 Looper 對象
ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
通過Looper.prepare()來創建Looper對象(消息隊列封裝在Looper對象中),并且保存在sThreadLoal中,然后通過Looper.loop()來執行消息循環,這兩步通常是成對出現的!!
xml解析
個人理解:
Dom把xml全部加載,比較耗內存。
Sax是事件驅動,但是不支持隨機讀取,不能只處理部分元素。
Pull是Sax的改進,可以提前結束讀取,Sax不能提前結束。
基本的解析方式包含兩種,一種是事件驅動的(代表SAX),另一種方式是基于文檔結構(代表DOM)。其他的只不過語法不一樣而已。
Dom 方式解析xml,比較簡單,并可以訪問兄弟元素,但是需要將整個xml文檔加載到內存中,對于android設備來說,不推薦使用dom的方式解析xml。
// 接收一個xml的字符串來解析xml,Document代表整個xml文檔 Document document = builder.parse(inputStream); // 得到xml文檔的根元素節點 Element personsElement = document.getDocumentElement(); // 得到標簽為person的Node對象的集合NodeList NodeList nodeList = personsElement.getElementsByTagName("person");Dom解析加載整個xml文檔的樹形結構,可以隨時訪問兄弟節點,父節點等。Sax只能向前遍歷(單遍解析),使它不能支持隨機訪問。</pre>
sax和pull都是基于事件驅動的xml解析器,在解析xml時并不會加載整個的xml文檔,占用內存較少,因此在android開發中建議使用sax或者pull來解析xml文檔。
SAX,全稱Simple API for XML,既是一種接口,也是一種軟件包。它是一種XML解析的替代方法。SAX不同于DOM解析,它逐行掃描文檔,一邊掃描一邊解析。由于應用程序只是在讀取數據時檢查數據,因此不需要將數據存儲在內存中,這對于大型文檔的解析是個巨大優勢。
繼承 extends DefaultHandler 需要復寫下面幾個方法。這幾個方法是回調方法,解析時,若發生事件(文檔開頭結尾 元素開頭結尾)的話會被調用這些回調方法。
- startDocument() and endDocument() – Method called at the start and end of an XML document.
- startElement() and endElement() – Method called at the start and end of a document element.
- characters() – Method called with the text contents in between the start and end tags of an XML document element.
pull和sax的區別
Pull解析器和SAX解析器雖有區別但也有相似性。他們的區別為:SAX解析器的工作方式是自動將事件推入注冊的事件處理器進行處理,因此你不能控制事件的處理主動結束;
而Pull解析器的工作方式為允許你的應用程序代碼主動從解析器中獲取事件,正因為是主動獲取事件,因此可以在滿足了需要的條件后不再獲取事件,結束解析。這是他們主要的區別。
Android 系統架構
- Linux 內核層,為 Android 設備提供了底層的驅動,如藍牙驅動、照相機驅動等等
- 系統運行庫層,這些曾通過一些 C/C++ 庫為 Android 系統提供了主要的特性支持,如 SQlite 提宮數據庫,Webkit 提高了瀏覽器內核
需要注意的是這一層也有 Android 運行時候需要的核心庫
Android 運行時庫包括了 Dalvik 虛擬機 - 應用框架層,這一層提供了編寫 App 時候需要用到的 Api
- 應用層,所有安裝在手機上的 App 都屬于這一層,包括短信、QQ 等
四大組件
activity和service默認是運行在應用進程的主線程中,四大組件默認都是和activity運行在同一個主線程中的
- Activity 活動
- Service 服務
Service的Oncreate()方法只有第一次執行,而OnStartCommand()每次啟動服務都會調用 - Broadcast 廣播接收器
最常用的廣播就是android.provider.Telephony.SMS_RECEIVED
// AndroidManifest.xml <uses-permission android:name="android.permission.RECEIVE_SMS" /> ... <application ...> <receiver android:name=".SMSReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
</application>// SMSReceiver.java public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Log.i(TAG, "SMS received."); .... } }</pre>
- ContentProvider 內容提供者
注意如果自己定義一個ContentProvider暴露為其他 App 使用,通常用SQLite或者文件實現,也可以用 Json 或者 Xml 實現等等。
4大組件都需要在 xml 文件里注冊,注意它們是平級的關系。
存儲化技術
- 文件存儲 存儲一些圖片、視頻、doc 等等,保密性不強
- SharedPreference 多用于存儲用戶配置、密碼等等
也可在里面存儲視頻當前播放的位置,按home鍵返回后重新從當前位置讀取 - 數據庫 Sqlite 存儲一些格式復雜的數據,比如短信中 聯系人和對應的短信記錄
文件的操作模式
- MODE_APPEND 追加內容
- MODE_PRIVATE 覆蓋文件中的內容
MODE_WORLD_READABLE已經在4.2廢棄MODE_WORLD_WRITEABLE已經在4.2廢棄
打開文件示例代碼
FileOutputStream out = null; BufferedWriter writer = null; try { out = openFileOutput("data", Context.MODE_PRIVATE); writer = new BufferedWriter(new OutputStreamWriter(out)); } catch (FileNotFoundException e) { e.printStackTrace(); } finally { try { if (writer != null) writer.close(); } catch (IOException e) { e.printStackTrace(); } }
讀取文件 代碼示例
FileInputStream in = null; BufferedReader reader = null; StringBuilder content = null; try { in = openFileInput("data"); reader = new BufferedReader(new InputStreamReader(in)); String line = ""; if ((line = reader.readLine()) != null) { content.append(line); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { try { if(reader != null) reader.close(); } catch (IOException e) { e.printStackTrace(); } }
TestUtils
String x = "test"; TextUtils.isEmpty(x);
這兒的 isEmpty 方法可以判斷 x 為 null 或者長度為 0
SQlite
使用adb shell
中的sqlite3
打開數據庫
getWritableDatabase VS getReadableDatabase
getWritableDatabase
和 getReadableDatabase
取得的實例對數據庫進行讀和寫的功能,不是像字面意思上一個讀寫權限和只讀權限
兩者的區別在于
- getWritableDatabase取得的實例是以讀寫的方式打開數據庫,如果打開的數據庫磁盤滿了,此時只能讀不能寫,此時調用了getWritableDatabase的實例,那么將會發生錯誤(異常)
- getReadableDatabase取得的實例是先調用getWritableDatabase以讀寫的方式打開數據庫,如果數據庫的磁盤滿了,此時返回打開失敗,繼而用getReadableDatabase的實例以只讀的方式去打開數據庫
intent-filter
intent-filter 可以動態注冊也可以靜態注冊
下面是靜態注冊的代碼,直接寫在xml文件里
<application> <activity name=""/> <receiver android:name=".MyBroadcastReceiver"> <!-- intent過濾器,指定可以匹配哪些intent, 一般需要定義action 可以是自定義的也可是系統的 --> <intent-filter> <action android:name="com.app.bc.test"/> </intent-filter> </receiver> </application> // // java code 這兒的intent也叫隱式intent Intent intent = new Intent(“com.app.bc.test”); sendBroadcast(intent);//發送廣播事件
動態注冊
//生成一個BroadcastReceiver對象 SMSReceiver smsReceiver = new SMSReceiver(); //生成一個IntentFilter對象 IntentFilter filter = new IntentFilter(); filter.addAction(“android.provider.Telephony.SMS_RECEIVED”); //將BroadcastReceiver對象注冊到系統當中 //此處表示該接收器會處理短信事件 TestBC1Activity.this.registerReceiver(smsReceiver, filter);
- 靜態注冊:在AndroidManifest.xml注冊,android不能自動銷毀廣播接收器,也就是說當應用程序關閉后,還是會接收廣播。
- 動態注冊:在代碼中通過registerReceiver()手工注冊.當程序關閉時,該接收器也會隨之銷毀。當然,也可手工調用unregisterReceiver()進行銷毀。
顯示intent 和隱式intent
顯示
Intent intent = new Intent(this, SecondActivity.class); startActivity(intent);
隱式,它會自動尋找能處理 intent-filter 里面設置了處理該的 action 的Activity。
Intent intent = new Intent(); intent.setAction(“android.provider.Telephony.SMS_RECEIVED”); startActivity(intent);
比如有手機裝了多個瀏覽器,使用隱式intent會彈出讓用戶選擇哪個瀏覽器。
Timer 和 Alarm
Timer 不適用需要長期在后臺運行的定時任務,長時間不操作,CPU會睡眠狀態,這會影響Timer的定時任務無法執行
Alarm 機制具有喚醒CPU的功能
工具方法
訪問Url,利用get請求,請返回的數據轉化為字符串
HttpURLConnection connection = null; ... connection.setReadTimeOut(8000); InputStream in = connection.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); StringBuilder response = new StringBuilder(); String line = ""; while( (line = reader.readLine()) !=null ) { response.append(line); }
怎么更新UI
注意不能使用子線程更新 UI,可以用 Handler 或者 Activity.runOnUiThread
public final void runOnUiThread (Runnable action)
Added in API level 1 Runs the specified action on the UI thread. If the current thread is the UI thread, then the action is executed immediately. If the current thread is not the UI thread, the action is posted to the event queue of the UI thread.Parameters action the action to run on the UI thread</pre>
Adapter 4個重寫方法
BaseAdapter基本結構
- public int getCount() :適配器中數據集中 數據的個數,即ListView需要顯示的數據個數
- public Object getItem(int position) : 獲取數據集中與指定索引對應的數據項
- public long getItemId(int position) : 獲取指定行對應的ID
- public View getView(int position, View convertView, ViewGroup parent) :獲取每一個Item的顯示內容
public class MyBaseAdapter extends BaseAdapter {ArrayList myList = new ArrayList(); LayoutInflater inflater; Context context; public MyBaseAdapter(Context context, ArrayList myList) { this.myList = myList; this.context = context; inflater = LayoutInflater.from(this.context); } @Override public int getCount() { return myList.size(); } @Override public ListData getItem(int position) { return myList.get(position); } @Override public long getItemId(int position) { return 0; } @Override public View getView(int position, View convertView, ViewGroup parent) { MyViewHolder mViewHolder; if (convertView == null) { convertView = inflater.inflate(R.layout.layout_list_item, parent, false); mViewHolder = new MyViewHolder(convertView); convertView.setTag(mViewHolder); } else { mViewHolder = (MyViewHolder) convertView.getTag(); } ListData currentListData = getItem(position); mViewHolder.tvTitle.setText(currentListData.getTitle()); mViewHolder.tvDesc.setText(currentListData.getDescription()); mViewHolder.ivIcon.setImageResource(currentListData.getImgResId()); return convertView; } private class MyViewHolder { TextView tvTitle, tvDesc; ImageView ivIcon; public MyViewHolder(View item) { tvTitle = (TextView) item.findViewById(R.id.tvTitle); tvDesc = (TextView) item.findViewById(R.id.tvDesc); ivIcon = (ImageView) item.findViewById(R.id.ivIcon); } }
}</pre>
在使用BaseAdapter時,我們需要自己創建一個類繼承BaseAdapter,然后Eclipse會提醒我們實現上述四個方法,當給ListView設置了我們自己寫的Adapter后,ListView內部會調用上述四個方法。
參考文章
ListView using BaseAdapter – Android http://www.pcsalt.com/android/listview-using-baseadapter-android/
</div>