Andorid-的面試題

轉自:http://blog.csdn.net/cym492224103/article/details/38417927

前言

最近纔開的博客,希望大家多多關注,andorid開發也做了3年有餘了,也面試多家企業,藉此機會分享一下,我們中遇到過的問題以及解決方案吧,希望能夠對正在找工作的andoird程序員有一定的幫助。學完本人博客發表《ym--andorid從零開始教程》+面試題目全理解,年薪18w以上絕對沒問題。

特別獻上整理過的50道面試題目

1.listView的優化方式

重用convertView
viewHolder
static class viewHolder
在列表裏面有圖片的情況下,監聽滑動不加載圖片
多個不同佈局,可以創建不同的viewHolder和convertView進行重用
2.listView展示數據幾種形式

從sqlite拉取數據源顯示
從xml使用pull解析拉取數據源顯示
從網絡上拉取數據源顯示
3.ipc

進程間通信主要包括管道, 系統IPC(Inter-Process Communication,進程間通信)(包括消息隊列,信號,共享存儲), 套接字(SOCKET).
目的:
l 數據傳輸:一個進程需要將它的數據發送給另一個進程,發送的數據量在一個字節到幾兆字節之間。
l 共享數據:多個進程想要操作共享數據,一個進程對共享數據的修改,別的進程應該立刻看到。
l 通知事件:一個進程需要向另一個或一組進程發送消息,通知它(它們)發生了某種事件(如進程終止時要通知父進程)。
資源共享:多個進程之間共享同樣的資源。爲了作到這一點,需要內核提供鎖和同步機制。
l 進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,並能夠及時知道它的狀態改變。
進程通過與內核及其它進程之間的互相通信來協調它們的行爲。Linux支持多種進程間通信(IPC)機制,信號和管道是其中的兩種。除此之外,Linux還支持System V 的IPC機制(用首次出現的Unix版本命名)。 

4.Parcel的機制

Android中的Parcel機制
    實現了Bundle傳遞對象
    使用Bundle傳遞對象,首先要將其序列化,但是,在Android中要使用這種傳遞對象的方式需要用到Android Parcel機制,即,Android實現的輕量級的高效的對象序列化和反序列化機制。

    JAVA中的Serialize機制,譯成串行化、序列化……,其作用是能將數據對象存入字節流當中,在需要時重新生成對象。主要應用是利用外部存儲設備保存對象狀態,以及通過網絡傳輸對象等。
    
    Android中的新的序列化機制
        在Android系統中,定位爲針對內存受限的設備,因此對性能要求更高,另外系統中採用了新的IPC(進程間通信)機制,必然要求使用性能更出色的對象傳輸方式。在這樣的環境下,
        Parcel被設計出來,其定位就是輕量級的高效的對象序列化和反序列化機制。
        Android中序列化有以下幾個特徵:
        1. 整個讀寫全是在內存中進行,所以效率比JAVA序列化中使用外部存儲器會高很多;
        2. 讀寫時是4字節對齊的
        3. 如果預分配的空間不夠時,會一次多分配50%;
        4. 對於普通數據,使用的是mData內存地址,對於IBinder類型的數據以及FileDescriptor使用的是mObjects內存地址。後者是通過flatten_binder()和unflatten_binder()實現的,目的是反序列化時讀出的對象就是原對象而不用重新new一個新對象。

代碼:
activity代碼:
 Intent mIntent =newIntent(this,ParcelableDemo.class);   
        Bundle mBundle =newBundle();   
        mBundle.putParcelable(PAR_KEY, mPolice);   
        mIntent.putExtras(mBundle);   

實體類:
public class Police implements Parcelable {
       
    private String name;
    private int workTime;
   
    public String getName() {
        returnname;
    }
   
    public void setName(String name) {
        this.name = name;
    }
   
    public int getWorkTime() {
        returnworkTime;
    }
   
    public void setWorkTime(int workTime) {
        this.workTime = workTime;
    }
       
    public static final Parcelable.Creator<Police> CREATOR =newCreator<Police>() {
   
        @Override
        public Police createFromParcel(Parcel source) {
            Police police =newPolice();
            police.name = source.readString();
            police.workTime = source.readInt();
            returnpolice;
        }
   
        @Override
        public Police[] newArray(int size) {
            returnnewPolice[size];
        }
    };
   
    @Override
    public int describeContents() {
        return0;
    }
   
    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeString(name);
        parcel.writeInt(workTime);
    }
}
5.JNI調用

(1) Eclipse中新建android工程 
工程名 JNItest 
Package名com.ura.test 
Activity名 JNItest 
應用程序名 JNItest 
(2) 編輯main.xml 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView 
    android:id="@+id/JNITest"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/JNITest"
    />
</LinearLayout>
 (3)編輯java文件 
package com.ura.test;
 

 import android.app.Activity;
 import android.os.Bundle;
 import android.widget.TextView;
 public class JNITest extends Activity {
     /** Called when the activity is first created. */
    static {
             System.loadLibrary("JNITest");
    }
    public native String GetTest();
          @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
         String str =GetTest();
         TextView JNITest = (TextView)findViewById(R.id.JNITest);
         JNITest.setText(str);
     }
 }
 (4)生成head文件 
編譯上面工程聲稱class文件,然後用javah工具生成c/c++ 頭文件 
javah -classpath bin -d jni com.ura.test.JNItest
生成的頭文件如下 
/* DO NOT EDIT THIS FILE - it is machine generated */
 #include <jni.h>
 /* Header for class com_ura_test_JNITest */
 

 #ifndef _Included_com_ura_test_JNITest
 #define _Included_com_ura_test_JNITest
 #ifdef __cplusplus
 extern "C" {
 #endif
 /*
 * Class:     com_ura_test_JNITest
 * Method:    GetTest
 * Signature: ()Ljava/lang/String;
 */
 JNIEXPORT jstring JNICALL Java_com_ura_test_JNITest_GetTest
   (JNIEnv *, jobject);
 

 #ifdef __cplusplus
 }
 #endif
 #endif
 (5)編寫c/c++文件如下 
include "com_ura_test_JNITest.h"

#define LOG_TAG "JNITest"
#undef LOG
#include <utils/Log.h>

 JNIEXPORT jstring JNICALL Java_com_ura_test_JNITest_GetTest
   (JNIEnv * env, jobject obj)
 {
     return (*env)->NewStringUTF(env, (char *)"JNITest Native String");
     LOGD("Hello LIB!\n");
 

 }
(6)編寫android.mk文件 
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
    com_ura_test_JNITest.c
LOCAL_C_INCLUDES := \
    $(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := libutils
LOCAL_PRELINK_MODULE := false
LOCAL_MODULE := libJNITest
include $(BUILD_SHARED_LIBRARY) 
(7)編譯生成動態庫 
新建文件夾 
~/mydroid/external/libJNITest 
把上面編寫好的頭文件,c/c++源文件,make文件拷貝進上面目錄中 
* 需要注意的是把PRELINK_MOUDULE設置成false 
否則需要重新做成img文件再燒入。 
在 ubuntu中執行 
cd
cd mydroid/build/
envsetup.sh
cd ~/mydroid
cd external/libJNITest/
mm 
編譯成功的後會在下面目錄中生成libJNITest.so文件 
~mydroid/out/target/product/generic/system/lib/ 
(8)在模擬器中執行程序 
首先要把動態庫拷進/system/lib中。 
啓動模擬器 
adb shell
adb remount
adb push libJNITest.so /system/lib 
確認拷貝成功
 cd /system/lib
 ls
然後不要關閉模擬器(關掉再開動態庫就沒了,因爲模擬器rom是隻讀) 
執行java程序JNITest 
會看到屏幕上打印出 
JNITest Native String 
6.細談四大組件

     activity

     1.什麼是activity?

四大組件之一,一般的,一個用戶交互界面對應一個activity
setContentView() ,// 要顯示的佈局
button.setOnclickLinstener{
}
, activity 是Context的子類,同時實現了window.callback和keyevent.callback, 可以處理與窗體用戶交互的事件.
 
裏面不能進行耗時操作
 
我開發常用的的有ListActivity  , PreferenceActivity ,TabAcitivty等…
 
如果界面有共同的特點或者功能的時候,還會自己定義一個BaseActivity.
     2.activity生命週期?

Activity生命週期
1 完整生命週期
  onCreate()  
  --> onStart()
  --> onResume()
   可以在手機上看見activity
  ---> onPause()
  --> onStop()
   看不見了
  ---> onDestory()
   銷燬了

2 前臺生命週期
  onstart()  ---> onStop()之間進行切換
  onCreate() --> onStart() --> onResume()
   現在有一個activity完全覆蓋
  onPause()  ----> onStop()
   如果上面的activity關閉
  onRestart() ---> onStart() --> onResume()
   
3 可視生命週期
   onResume()  ---> onPause()之間進行切換
  onCreate() --> onStart() --> onResume()
   現在有一個activity沒有完全覆蓋
  onPause()
   如果上面的activity關閉
  onResume() 

     3.橫豎屏切換時候activity的生命週期?

 這個生命週期跟清單文件裏的配置有關係
1、不設置Activity的android:configChanges時,切屏會重新調用各個生命週期
默認首先銷燬當前activity,然後重新加載
 
2、設置Activity的android:configChanges="orientation|keyboardHidden"時,切屏不會重新調用各個生命週期,只會執行onConfigurationChanged方法
 
遊戲開發中, 屏幕的朝向都是寫死的.
    
      4.如何將一個activity設置成窗口的樣式?
 可以自定義一個activity的樣式,詳細見手機衛士的程序詳細信息
android:theme="@style/FloatActivity" 
E:\day9\mobilesafe\res\values\style
 

     5.activity啓動模式?
Activity啓動模式任務堆棧
Activity中的任務是與用戶交互的一組Activity的集合,Activity會被按打開順序安排在一個堆棧裏。
任務棧:並不是Activity是Activity的引用(內存地址) 
standard 標準模式
每次激活Activity時都會創建Activity,並放入任務棧中
默認模式
  singleTop 獨享堆棧頂端
如果在任務的棧頂正好存在該Activity的實例,就重用該實例,否者就會創建新的實例並放入棧頂(即使棧中已經存在該Activity實例,只要不在棧頂,都會創建實例)
瀏覽器的書籤
  singleTask 獨享任務堆棧
如果在棧中已經有該Activity的實例,就重用該實例(會調用實例的onNewIntent())。重用時,會讓該實例回到棧頂,因此在它上面的實例將會被移除棧。如果棧中不存在該實例,將會創建新的實例放入棧中
瀏覽器的主頁
  singleInstance單例
在一個新棧中創建該Activity實例,並讓多個應用共享該棧中的該Activity實例。一旦該模式的Activity的實例存在於某個棧中,任何應用再激活該Activity時都會重用該棧中的實例,其效果相當於多個應用程序共享一個應用,不管誰激活該Activity都會進入同一個應用中
通話界面
Singletop:如果重複使用上一次的Activity,就重用。
singleTask:如果使用已經實例化Activity,就重用,並且刪除重用Activity前面的Activity,重用的Activity置頂任務棧。
singleInstance:在一個新棧中創建該Activity實例,並讓多個應用共享該棧中的該Activity實例。(調用Activity和重用Activity不在一個棧中)
singleTop 、singleTask 、singleInstance 優化性能、重用Activity
     6.後臺activity被系統回收了怎麼辦?如果後臺activity由於某種原因被系統回收了,如何保存之前狀態?
除了在棧頂的activity,其他的activity都有可能在內存不足的時候被系統回收,一個activity越處於棧底,被回收的可能性越大.
protected void onSaveInstanceState(Bundle outState) {
       super.onSaveInstanceState(outState);
       outState.putLong("id", 1234567890);
}
public void onCreate(Bundle savedInstanceState) {
//判斷savedInstanceState是不是空.
//如果不爲空就取出來
        super.onCreate(savedInstanceState);
}
     7.如何退出activity,如何安全退出已調用多個activity的application?
退出activity 直接調用 finish () 方法 . //用戶點擊back鍵 就是退出一個activity
退出activity 會執行 onDestroy()方法 .
1、拋異常強制退出:
該方法通過拋異常,使程序Force Close。
驗證可以,但是,需要解決的問題是,如何使程序結束掉,而不彈出Force Close的窗口。
 
       //安全結束進程        android.os.Process.killProcess(android.os.Process.myPid());
2、記錄打開的Activity:
每打開一個Activity,就記錄下來。在需要退出時,關閉每一個Activity即可。
 
                     List<Activity> lists ; 在application 全集的環境裏面
              lists = new ArrayList<Activity>();
 
lists.add(activity);
 
for(Activity activity: lists)
{
       activity.finish();
}
 
3、發送特定廣播:
在需要結束應用時,發送一個特定的廣播,每個Activity收到廣播後,關閉即可。
//給某個activity 註冊接受接受廣播的意圖   
       registerReceiver(receiver, filter)
 
//如果過接受到的是 關閉activity的廣播  就調用finish()方法 把當前的activity finish()掉
 
4、遞歸退出
在打開新的Activity時使用startActivityForResult,然後自己加標誌,在onActivityResult中處理,遞歸關閉。
 
 
上面是網上的一些做法.
 
其實 可以通過 intent的flag 來實現.. intent.setFlag(FLAG_ACTIVITY_CLEAR_TOP)激活一個新的activity,然後在新的activity的oncreate方法裏面 finish掉.
     8.兩activity之間怎麼傳遞數據?
基本數據類型可以通過.  Intent 傳遞數據 
在A activity中
Intent intent = new Intent();
intent.putExtra(name, value) 
      Bundle bundle = new Bundle();
      bundle.putBoolean(key,value);
      intent.putExtras(bundle);
extras.putDouble(keyvalue)
// 通過intent putExtra 方法 基本數據類型 都傳遞
     
      Intent i = getIntent();
      i.getExtras();
 
intent.getStringExtra("key","value");
intent.getBooleanExtra("key","value")
      Bundle bundle = new  Bundle();
       bumdle.putShort(key, value);
       intent.putExtras(bumdle);
intent.putExtras(bundle)
--------------
Application 全局裏面存放 對象 ,自己去實現自己的application的這個類,
基礎系統的application , 每個activity都可以取到
-----------------
 
讓對象實現 implements  Serializable 接口把對象存放到文件上. 
讓類實現Serializable 接口,然後可以通過ObjectOutputStream              //對象輸出流 
            File file = new File("c:\1.obj");
            FileOutputStream fos  = new FileOutputStream(file);
            ObjectOutputStream oos = new ObjectOutputStream(fos);
           
            Student stu = new Student();
            oos.writeObject(stu);
           
           
            //從文件中把對象讀出來 
            ObjectInputStream ois = new ObjectInputStream(arg0);
             Student stu1 = (Student) ois.readObject();
 
文件/網絡
 
intent.setData(Uri)
Uri.fromFile();  //大圖片的傳遞

     9.講一講對activity的理解?
把上面的幾點用自己的心得寫出來
     
     service

     1.什麼是Service以及描述下它的生命週期。Service有哪些啓動方法,有什麼區別,怎樣停用Service?
在Service的生命週期中,被回調的方法比Activity少一些,只有onCreate, onStart, onDestroy,
onBind和onUnbind。
通常有兩種方式啓動一個Service,他們對Service生命週期的影響是不一樣的。
1 通過startService
    Service會經歷 onCreate 到onStart,然後處於運行狀態,stopService的時候調用onDestroy方法。
   如果是調用者自己直接退出而沒有調用stopService的話,Service會一直在後臺運行。
  2 通過bindService  
    Service會運行onCreate,然後是調用onBind, 這個時候調用者和Service綁定在一起。調用者退出了,Srevice就會調用onUnbind->onDestroyed方法。
   所謂綁定在一起就共存亡了。調用者也可以通過調用unbindService方法來停止服務,這時候Srevice就會調用onUnbind->onDestroyed方法。
需要注意的是如果這幾個方法交織在一起的話,會出現什麼情況呢?
一個原則是Service的onCreate的方法只會被調用一次,就是你無論多少次的startService又bindService,Service只被創建一次。
如果先是bind了,那麼start的時候就直接運行Service的onStart方法,
 
如果先是start,那麼bind的時候就直接運行onBind方法。
 
如果service運行期間調用了bindService,這時候再調用stopService的話,service是不會調用onDestroy方法的,service就stop不掉了,只能調用UnbindService, service就會被銷燬
 
 
如果一個service通過startService 被start之後,多次調用startService 的話,service會多次調用onStart方法。多次調用stopService的話,service只會調用一次onDestroyed方法。
 
 
如果一個service通過bindService被start之後,多次調用bindService的話,service只會調用一次onBind方法。
 
多次調用unbindService的話會拋出異常。

     2.service是否在main thread中執行, service裏面是否能執行耗時的操作?
默認情況,如果沒有顯示的指定service所運行的進程, Service和activity是運行在當前app所在進程的main thread(UI主線程)裏面 
service裏面不能執行耗時的操作(網絡請求,拷貝數據庫,大文件 )
在子線程中執行 new Thread(){}.start();
 
特殊情況 ,可以在清單文件配置 service 執行所在的進程 ,讓service在另外的進程中執行

     3.怎麼讓在啓動一個Activity是就啓動一個service?
在activity的onCreate()方法裏面 startService();

     4.Activity怎麼和service綁定,怎麼在activity中啓動自己對應的service?
startService() 一旦被創建  調用着無關   沒法使用service裏面的方法
bindService () 把service 與調用者綁定 ,如果調用者被銷燬, service會銷燬
bindService() 我們可以使用service 裏面的方法
       bindService().  讓activity能夠訪問到 service裏面的方法
       構建一個intent對象,
Intent service = new Intent(this,MyService.class);
 通過bindService的方法去啓動一個服務,
    bindService(intent, new MyConn(), BIND_AUTO_CREATE);
       ServiceConnection 對象(重寫onServiceConnected和OnServiceDisconnected方法) 和BIND_AUTO_CREATE.
       private class myconn implements ServiceConnection
 
       {
 
              public void onServiceConnected(ComponentName name, IBinder service) {
                     // TODO Auto-generated method stub
                     //可以通過IBinder的對象 去使用service裏面的方法
              }
 
              public void onServiceDisconnected(ComponentName name) {
                     // TODO Auto-generated method stub
                    
              }
             
       }

     5.不用service,B頁面爲音樂播放,從A跳轉到B,再返回,如何使音樂繼續播放?
 這個問題問的很山寨.默認不做任何處理,B裏面的音樂都能播放.
遇到問題, 可以隨機應變,靈活發揮,多考慮些細節,比如說這個題就可以這樣說,說說你對startActivityForResult的理解()
B的結束的時候 setResult()
 
A會調用到onActivityResult()
就會獲取到resultCode 
A開啓B的時候,用startActivityForResult()方法, B返回的時候把播放的狀態信息返回給A ,A繼續播放音樂.
seekTo(resultCode)
     6.什麼是IntentService?有何優點?
普通的service ,默認運行在ui main 主線程
    Sdk給我們提供的方便的,帶有異步處理的service類,
       可以在OnHandleIntent() 處理耗時的操作

     7.什麼時候使用Service?
後臺操作,耗時操作的時候
 
       擁有service的進程具有較高的優先級
    官方文檔告訴我們,Android系統會盡量保持擁有service的進程運行,只要在該service已經被啓動(start)或者客戶端連接(bindService)到它。當內存不足時,需要保持,擁有service的進程具有較高的優先級。
1. 如果service正在調用onCreate,  onStartCommand或者onDestory方法,那麼用於當前service的進程相當於前臺進程以避免被killed。
2. 如果當前service已經被啓動(start),擁有它的進程則比那些用戶可見的進程優先級低一些,但是比那些不可見的進程更重要,這就意味着service一般不會被killed.
3. 如果客戶端已經連接到service (bindService),那麼擁有Service的進程則擁有最高的優先級,可以認爲service是可見的。
4. 如果service可以使用startForeg round(int, Notification)方法來將service設置爲前臺狀態,那麼系統就認爲是對用戶可見的,並不會在內存不足時killed。
如果有其他的應用組件作爲Service,Activity等運行在相同的進程中,那麼將會增加該進程的重要性。
       1.Service的特點可以讓他在後臺一直運行,可以在service裏面創建線程去完成耗時的操作.
new Thread(){
TimerTask // 循環的執行一個定時的任務
 
}.start();    
       2.Broadcast receiver捕獲到一個事件之後,可以起一個service來完成一個耗時的操作.
ANR  new Service()
 
       3.遠程的service如果被啓動起來,可以被多次bind, 但不會重新create.  索愛手機X10i的人臉識別的service可以被圖庫使用,可以被攝像機,照相機等程序使用.
畫廊 攝像機 照相機  bindService()  Ibinder的對象, 訪問service

     8.如何在讓Service殺不死?
Android開發的過程中,每次調用startService(Intent)的時候,都會調用該Service對象的onStartCommand(Intent,int,int)方法,然後在onStartCommand方法中做一些處理。
從Android官方文檔中,我們知道onStartCommand有4種int返回值,首先簡單地講講int返回值的作用。
一、onStartCommand有4種返回值:
START_STICKY:如果service進程被kill掉,保留service的狀態爲開始狀態,但不保留遞送的intent對象。隨後系統會嘗試重新創建service,由於服務狀態爲開始狀態,所以創建服務後一定會調用onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啓動命令被傳遞到service,那麼參數Intent將爲null。
START_NOT_STICKY:“非粘性的”。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啓該服務。
START_REDELIVER_INTENT:重傳Intent。使用這個返回值時,如果在執行完onStartCommand後,服務被異常kill掉,系統會自動重啓該服務,並將Intent的值傳入。
START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保證服務被kill後一定能重啓。
 
二、創建不被殺死的service
1.在service中重寫下面的方法,這個方法有三個返回值, START_STICKY(或START_STICKY_COMPATIBILITY)是service被kill掉後自動重寫創建
@Override  public int onStartCommand(Intent intent, int flags, int startId)  {   return START_STICKY_COMPATIBILITY;    //return super.onStartCommand(intent, flags, startId);  }

 @Override  public int onStartCommand(Intent intent, int flags, int startId)  {   flags = START_STICKY;   return super.onStartCommand(intent, flags, startId);   // return START_REDELIVER_INTENT;  }
@Override public void onStart(Intent intent, int startId) { // 再次動態註冊廣播 IntentFilter localIntentFilter = new IntentFilter("android.intent.action.USER_PRESENT"); localIntentFilter.setPriority(Integer.MAX_VALUE);// 整形最大值 myReceiver searchReceiver = new myReceiver(); registerReceiver(searchReceiver, localIntentFilter); super.onStart(intent, startId); }
2.在Service的onDestroy()中重啓Service.
 public void onDestroy()  {   Intent localIntent = new Intent();   localIntent.setClass(this, MyService.class); // 銷燬時重新啓動Service   this.startService(localIntent);  }
3.創建一個廣播
public class myReceiver extends BroadcastReceiver {  @Override  public void onReceive(Context context, Intent intent)  {   context.startService(new Intent(context, Google.class));  } }
4.AndroidManifest.xml中註冊廣播myReceiver及MyService服務
<receiver android:name=".myReceiver" >             <intent-filter android:priority="2147483647" ><!--優先級加最高-->                 <!-- 系統啓動完成後會調用 -->                 <action android:name="android.intent.action.BOOT_COMPLETED" />                                <!-- 解鎖完成後會調用 -->                 <action android:name="android.intent.action.USER_PRESENT" />                 <!-- 監聽情景切換 -->                 <action android:name="android.media.RINGER_MODE_CHANGED" />                            </intent-filter> </receiver>
<service android:name=".MyService" >
注:解鎖,啓動,切換場景激活廣播需加權限,如啓動完成,及手機機狀態等。
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 親測ZTE U795手機Android 4.0.4版本adb push到system\app下android:persistent="true" 變成核心程序,在360殺掉進程的時候,myReceiver照樣有效,保證service重生。呃
KILL問題: 1. settings 中stop service onDestroy方法中,調用startService進行Service的重啓。 2.settings中force stop 應用 捕捉系統進行廣播(action爲android.intent.action.PACKAGE_RESTARTED) 3. 藉助第三方應用kill掉running task 提升service的優先級,程序簽名,或adb push到system\app下等
相較於/data/app下的應用,放在/system/app下的應用享受更多的特權,比如若在其Manifest.xml文件中設置persistent屬性爲true,則可使其免受out-of-memory killer的影響。如應用程序'Phone'的AndroidManifest.xml文件:
    <application android:name="PhoneApp"
                 android:persistent="true"
                 android:label="@string/dialerIconLabel"
                 android:icon="@drawable/ic_launcher_phone">
         ...
    </application>
設置後app提升爲系統核心級別
     Broadcast Receiver

     1.什麼是Broadcast Receiver

下面是Android Doc中關於BroadcastReceiver的概述:①廣播接收器是一個專注於接收廣播通知信息,並做出對應處理的組件。很多廣播是源自於系統代碼的──比如,通知時區改變、電池電量低、拍攝了一張照片或者用戶改變了語言選項。應用程序也可以進行廣播──比如說,通知其它應用程序一些數據下載完成並處於可用狀態。
②應用程序可以擁有任意數量的廣播接收器以對所有它感興趣的通知信息予以響應。所有的接收器均繼承自BroadcastReceiver基類。
③廣播接收器沒有用戶界面。然而,它們可以啓動一個activity來響應它們收到的信息,或者用NotificationManager來通知用戶。通知可以用很多種方式來吸引用戶的注意力──閃動背燈、震動、播放聲音等等。一般來說是在狀態欄上放一個持久的圖標,用戶可以打開它並獲取消息。

有很多廣播接收者 ,系統已經實現了.
廣播分兩種 有序廣播
無序廣播
 指定接收者的有序廣播 .
 sendOrderedBroadcast(intent,receiverPermission,resultReceiver,scheduler,initialCode,initialData,initialExtras)
   接受者一定會獲取到 廣播的事件 
 
sendStickyBroadcast(intent)  //陰魂不散
廣播接受者在onReceive 方法獲取到廣播的事件
 
Wifi設置  等待wifi狀態更新完畢
 
  是不可以被攔截掉的 
<intent-filter android:priority="1000"> -1000 - 1000
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
 abortBroadcast();
 
 
代碼配置優先級比xml配置優先級的級別高,因爲代碼運行在內存中,而清單在系統中
 
 手機衛士中自定義一個broadcast receiver
<intent-filter  android:> <action> sms_received </action>  </intent-filter>
 
來獲取短信到來的廣播, 根據黑名單來判斷是否攔截該短信.
 畫畫板生成圖片後,發送一個sd掛載的通知,通知系統的gallery去獲取到新的圖片.
Intent intent = new Intent(Intent.ACTION_MEDIA_MOUNTED,Uri.parse("file://"+Environment.getExternalStorageDirectory()));
                        sendBroadcast(intent);

     2.什麼時候使用Broadcast Receiver
用於接收系統的廣播通知, 系統會有很多sd卡掛載,手機重啓,廣播通知,低電量,來電,來短信等….

     3.如何使用Broadcast Receiver
設置廣播接收者的優先級,設置廣播接受者的action名字 等…
詳細見工程代碼.
         <intent-filter android:priority="1000">
                 <action android:name="android.intent.action.NEW_OUTGOING_CALL"/>        
         </intent-filter>
        </receiver>
              <receiver android:name=".SmsReceiver">
                     <intent-filter android:priority="1000">
                            <action android:name="android.provider.Telephony.SMS_RECEIVED"/>
                     </intent-filter>
              </receiver>
              <receiver android:name=".BootCompleteReceiver">
                     <intent-filter >
                            <action android:name="android.intent.action.BOOT_COMPLETED"      />           
                            </intent-filter>
              </receiver>
 
可以通過代碼   registerReceiver(receiver,filter)
     ContentProvider

     1.什麼是ContentProvider
ContentProvider內容提供者
ContentProvider 進程間通訊,進程間數據的訪問/對外共享數據用
優點:提供了統一的訪問方式
原理分析圖


     2.什麼時候使用ContentProvider
需要訪問別人的數據的時候

     3.如何使用ContentProvider
1.先是提供的數據類型等數據的類。package org.juetion.cp;

import android.net.Uri;
import android.provider.BaseColumns;

/**
 * 提供的數據類型等數據。
 * Created by juetionke on 13-12-21.
 */
public class MyProviderMetaData {


    public static final String AUTHORIY = "org.juetion.cp.MyContentProvider";
    /**
     * 數據庫名稱
     */
    public static final String DATABASE_NAME = "MyProvider.db";
    /**
     * 數據庫版本
     */
    public static final int DATABASE_VERSION = 1;
    /**
     * 表名
     */
    public static final String USERS_TABLE_NAME = "users";

    /**
     * 繼承了BaseColumns,所以已經有了_ID
     */
    public static final class UserTableMetaData implements BaseColumns {
        /**
         * 表名
         */
        public static final String TABLE_NAME = "users";
        /**
         * 訪問該ContentProvider的URI
         */
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORIY + "/users");
        /**
         * 該ContentProvider所返回的數據類型定義
         */
        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/org.juetion.user";
        public static final String CONTENT_TYPE_ITEM = "vnd.android.cursor.item/org.juetion.user";
        /**
         * 列名
         */
        public static final String USER_NAME = "name";
        public static final String USER_AGE = "age";
        /**
         * 默認的排序方法
         */
        public static final String DEFAULT_SORT_ORDER = "_id desc";
    }
}
2,繼承ContentProvider的類:
package org.juetion.cp;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

import org.juetion.sqlite3.DatabaseHelper;

import java.util.HashMap;

/**
 * Created by juetionke on 13-12-21.
 */
public class MyContentProvider extends ContentProvider {

    /**
     * 定義規則
     */
    public static final UriMatcher uriMatcher;
    public static final int USERS_COLLECTION = 1;//用於標記
    public static final int USERS_SINGLE = 2;//用於標記
    private DatabaseHelper databaseHelper;//這裏的數據共享是共享Sqlite裏的數據,當然,可以試用其他,如文本數據共享。
    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);//試用一個沒有規則的Uri。然後下面自己匹配。
        uriMatcher.addURI(MyProviderMetaData.AUTHORIY,"/users",USERS_COLLECTION);//自己定義的規則,有點像路由器,是uri匹配的方案。
        uriMatcher.addURI(MyProviderMetaData.AUTHORIY,"/users/#",USERS_SINGLE);//同上。
    }

    /**
     * 爲列定義別名
     */
    public static HashMap<String,String> usersMap;
    static {
        usersMap = new HashMap<String, String>();
        usersMap.put(MyProviderMetaData.UserTableMetaData._ID, MyProviderMetaData.UserTableMetaData._ID);
        usersMap.put(MyProviderMetaData.UserTableMetaData.USER_NAME, MyProviderMetaData.UserTableMetaData.USER_NAME);
        usersMap.put(MyProviderMetaData.UserTableMetaData.USER_AGE, MyProviderMetaData.UserTableMetaData.USER_AGE);
    }


    @Override
    public boolean onCreate() {
        Log.i("juetion","onCreate");
        databaseHelper = new DatabaseHelper(getContext(), MyProviderMetaData.DATABASE_NAME);//這裏的實現,常見前篇關於Sqlite的文章。
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        Log.i("juetion","query");
        SQLiteQueryBuilder sqLiteQueryBuilder = new SQLiteQueryBuilder();//寫入查詢條件,有點像Hibernate。
        switch (uriMatcher.match(uri)) {//判斷查詢的是單個數據還是多個數據。
            case USERS_SINGLE:
                sqLiteQueryBuilder.setTables(MyProviderMetaData.UserTableMetaData.TABLE_NAME);//需要查詢的表
                sqLiteQueryBuilder.setProjectionMap(usersMap);//列的別名定義
                sqLiteQueryBuilder.appendWhere(MyProviderMetaData.UserTableMetaData._ID + "=" + uri.getPathSegments().get(1));
                //查詢條件,uri.getPathSegments().get(1),getPathSegments是將內容根據/劃分成list。
                break;
            case USERS_COLLECTION:
                sqLiteQueryBuilder.setTables(MyProviderMetaData.UserTableMetaData.TABLE_NAME);
                sqLiteQueryBuilder.setProjectionMap(usersMap);
                break;
        }
        String orderBy;//判斷sortOrder是否爲空,加入默認。
        if (TextUtils.isEmpty(sortOrder)) {
            orderBy = MyProviderMetaData.UserTableMetaData.DEFAULT_SORT_ORDER;
        } else {
            orderBy = sortOrder;
        }
        SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
        Cursor cursor = sqLiteQueryBuilder.query(sqLiteDatabase, projection, selection, selectionArgs, null, null, sortOrder);//可以使用下面的方法,不過此時sqLiteDatabase將會沒有用。
        //Cursor cursor = sqLiteDatabase.query(MyProviderMetaData.UserTableMetaData.TABLE_NAME, projection, selection, selectionArgs, null, null, orderBy);
        cursor.setNotificationUri(getContext().getContentResolver(),uri);
        return cursor;
    }

    /**
     * 根據傳入的URI,返回URI說表示的數據類型
     * @param uri
     * @return
     */
    @Override
    public String getType(Uri uri) {
        Log.i("juetion","getType");
        switch (uriMatcher.match(uri)) {//匹配uri的規則
            case USERS_COLLECTION:
                return MyProviderMetaData.UserTableMetaData.CONTENT_TYPE;
            case USERS_SINGLE:
                return MyProviderMetaData.UserTableMetaData.CONTENT_TYPE_ITEM;
            default:
                throw new IllegalArgumentException("Unknown URI" + uri);
        }
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        Log.i("juetion","insert");
        SQLiteDatabase sqLiteDatabase = databaseHelper.getWritableDatabase();
        long rowId = sqLiteDatabase.insert(MyProviderMetaData.UserTableMetaData.TABLE_NAME, null, values);
        if (rowId > 0) {
            Uri insertUserUri = ContentUris.withAppendedId(MyProviderMetaData.UserTableMetaData.CONTENT_URI, rowId);//簡單來說就是字符串拼湊一下。只不過是uri專用的。
            //通知監聽器
            getContext().getContentResolver().notifyChange(insertUserUri,null);
            return insertUserUri;
        }else
            throw new IllegalArgumentException("Failed to insert row into" + uri);
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        Log.i("juetion","delete");
        return 0;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        Log.i("juetion","update");
        return 0;
    }
}
還有重要的一點,再第二個APP的AndroidManifest.xml裏面需要添加
<!-- provider name填寫ContentProvider那個類的全稱,authorities填寫MyProviderMetaData裏的AUTHORIY -->
        <provider
            android:authorities="org.juetion.cp.MyContentProvider"
            android:name="org.juetion.cp.MyContentProvider”/>


以下代碼是在第一個APP裏面的。
關於使用。只需要將uri的string提供給第一個APP。
例如在第一個APP的Activity調用數據插入:
ContentValues contentValues = new ContentValues();
                    contentValues.put("name","zhangsan");
                    contentValues.put("age",19);
                    Uri uri = getContentResolver().insert(Uri.parse("content://org.juetion.cp.MyContentProvider/users"),contentValues);
                    Log.i("juetion", "insert uri-->" + uri.toString()); 

例如在第一個APP的Activity調用數據的查詢:
Cursor cursor = getContentResolver().query(Uri.parse("content://org.juetion.cp.MyContentProvider/users"),
                            new String[]{"name", "age"},
                            null, null, null);
                    while (cursor.moveToNext()) {
                        Log.i("juetion", cursor.getString(cursor.getColumnIndex("name")));
                    }

     4.請介紹下ContentProvider是如何實現數據共享的。
ContentProvider 可以屏蔽數據操作的細節 文件 xml
MyContentProvider 可以在不同應用程序之間共享數據  sharedpreference db
       把自己的數據通過uri的形式共享出去
android  系統下 不同程序 數據默認是不能共享訪問
      
       需要去實現一個類去繼承ContentProvider
       public class PersonContentProvider extends ContentProvider{
       public boolean onCreate(){
              //..
       }
query(Uri, String[], String, String[], String)
insert(Uri, ContentValues)
update(Uri, ContentValues, String, String[])
delete(Uri, String, String[])
 
聯繫人的信息 sms的內容content://sms/
}
     安全共同點
android安全學習 
簽名作用
1.sharedUserId 一樣並且簽名一次 可以實現數據共享
2.升級應用
權限:細粒度的特權管理
權限與操作關聯
應用需要顯式申請權限
用戶對權限可知(不可控)
對特權權限單獨控制
四大組件
exported = true 等於public
exported = false 等於private
默認組件private
如果該組件設置了intent-filter默認是public
如果同時希望是private,就需要主動設置expoted=false
Securing Activities
可知指定權限才能啓動activity
service同上
BoradcastReceiver可以設置接發的權限
ContentPrivider 可設置讀寫Permission
7.多線程管理

本篇隨筆將講解一下Android的多線程的知識,以及如何通過AsyncTask機制來實現線程之間的通信。
一、Android當中的多線程
在Android當中,當一個應用程序的組件啓動的時候,並且沒有其他的應用程序組件在運行時,Android系統就會爲該應用程序組件開闢一個新的線程來執行。默認的情況下,在一個相同Android應用程序當中,其裏面的組件都是運行在同一個線程裏面的,這個線程我們稱之爲Main線程。當我們通過某個組件來啓動另一個組件的時候,這個時候默認都是在同一個線程當中完成的。當然,我們可以自己來管理我們的Android應用的線程,我們可以根據我們自己的需要來給應用程序創建額外的線程。
二、Main Thread 和 Worker Thread
在Android當中,通常將線程分爲兩種,一種叫做Main Thread,除了Main Thread之外的線程都可稱爲Worker Thread。
當一個應用程序運行的時候,Android操作系統就會給該應用程序啓動一個線程,這個線程就是我們的Main Thread,這個線程非常的重要,它主要用來加載我們的UI界面,完成系統和我們用戶之間的交互,並將交互後的結果又展示給我們用戶,所以Main Thread又被稱爲UI Thread。
Android系統默認不會給我們的應用程序組件創建一個額外的線程,所有的這些組件默認都是在同一個線程中運行。然而,某些時候當我們的應用程序需要完成一個耗時的操作的時候,例如訪問網絡或者是對數據庫進行查詢時,此時我們的UI Thread就會被阻塞。例如,當我們點擊一個Button,然後希望其從網絡中獲取一些數據,如果此操作在UI Thread當中完成的話,當我們點擊Button的時候,UI線程就會處於阻塞的狀態,此時,我們的系統不會調度任何其它的事件,更糟糕的是,當我們的整個現場如果阻塞時間超過5秒鐘(官方是這樣說的),這個時候就會出現 ANR (Application Not Responding)的現象,此時,應用程序會彈出一個框,讓用戶選擇是否退出該程序。對於Android開發來說,出現ANR的現象是絕對不能被允許的。
另外,由於我們的Android UI控件是線程不安全的,所以我們不能在UI Thread之外的線程當中對我們的UI控件進行操作。因此在Android的多線程編程當中,我們有兩條非常重要的原則必須要遵守:
絕對不能在UI Thread當中進行耗時的操作,不能阻塞我們的UI Thread
不能在UI Thread之外的線程當中操縱我們的UI元素
 三、如何處理UI Thread 和 Worker Thread之間的通信
既然在Android當中有兩條重要的原則要遵守,那麼我們可能就有疑問了?我們既不能在主線程當中處理耗時的操作,又不能在工作線程中來訪問我們的UI控件,那麼我們比如從網絡中要下載一張圖片,又怎麼能將其更新到UI控件上呢?這就關係到了我們的主線程和工作線程之間的通信問題了。在Android當中,提供了兩種方式來解決線程直接的通信問題,一種是通過Handler的機制(這種方式在後面的隨筆中將詳細介紹),還有一種就是今天要詳細講解的 AsyncTask 機制。
四、AsyncTask
AsyncTask:異步任務,從字面上來說,就是在我們的UI主線程運行的時候,異步的完成一些操作。AsyncTask允許我們的執行一個異步的任務在後臺。我們可以將耗時的操作放在異步任務當中來執行,並隨時將任務執行的結果返回給我們的UI線程來更新我們的UI控件。通過AsyncTask我們可以輕鬆的解決多線程之間的通信問題。
怎麼來理解AsyncTask呢?通俗一點來說,AsyncTask就相當於Android給我們提供了一個多線程編程的一個框架,其介於Thread和Handler之間,我們如果要定義一個AsyncTask,就需要定義一個類來繼承AsyncTask這個抽象類,並實現其唯一的一個 doInBackgroud 抽象方法。要掌握AsyncTask,我們就必須要一個概念,總結起來就是: 3個泛型,4個步驟。
3個泛型指的是什麼呢?我們來看看AsyncTask這個抽象類的定義,當我們定義一個類來繼承AsyncTask這個類的時候,我們需要爲其指定3個泛型參數:
AsyncTask <Params, Progress, Result>
Params: 這個泛型指定的是我們傳遞給異步任務執行時的參數的類型
Progress: 這個泛型指定的是我們的異步任務在執行的時候將執行的進度返回給UI線程的參數的類型
Result: 這個泛型指定的異步任務執行完後返回給UI線程的結果的類型
 我們在定義一個類繼承AsyncTask類的時候,必須要指定好這三個泛型的類型,如果都不指定的話,則都將其寫成Void,例如:
AsyncTask <Void, Void, Void>
4個步驟:當我們執行一個異步任務的時候,其需要按照下面的4個步驟分別執行
onPreExecute(): 這個方法是在執行異步任務之前的時候執行,並且是在UI Thread當中執行的,通常我們在這個方法裏做一些UI控件的初始化的操作,例如彈出要給ProgressDialog
doInBackground(Params... params): 在onPreExecute()方法執行完之後,會馬上執行這個方法,這個方法就是來處理異步任務的方法,Android操作系統會在後臺的線程池當中開啓一個worker thread來執行我們的這個方法,所以這個方法是在worker thread當中執行的,這個方法執行完之後就可以將我們的執行結果發送給我們的最後一個 onPostExecute 方法,在這個方法裏,我們可以從網絡當中獲取數據等一些耗時的操作
onProgressUpdate(Progess... values): 這個方法也是在UI Thread當中執行的,我們在異步任務執行的時候,有時候需要將執行的進度返回給我們的UI界面,例如下載一張網絡圖片,我們需要時刻顯示其下載的進度,就可以使用這個方法來更新我們的進度。這個方法在調用之前,我們需要在 doInBackground 方法中調用一個 publishProgress(Progress) 的方法來將我們的進度時時刻刻傳遞給 onProgressUpdate 方法來更新
onPostExecute(Result... result): 當我們的異步任務執行完之後,就會將結果返回給這個方法,這個方法也是在UI Thread當中調用的,我們可以將返回的結果顯示在UI控件上
 爲什麼我們的AsyncTask抽象類只有一個 doInBackground 的抽象方法呢??原因是,我們如果要做一個異步任務,我們必須要爲其開闢一個新的Thread,讓其完成一些操作,而在完成這個異步任務時,我可能並不需要彈出要給ProgressDialog,我並不需要隨時更新我的ProgressDialog的進度條,我也並不需要將結果更新給我們的UI界面,所以除了 doInBackground 方法之外的三個方法,都不是必須有的,因此我們必須要實現的方法是 doInBackground 方法。
五、通過AsyncTask來從網絡上下載一張圖片
下面我們就通過兩個代碼示例,來看看如何通過AsyncTask來從網絡上下載一張圖片,並更新到我們的ImageView控件上。
①下載圖片時,彈出一個ProgressDialog,但是不顯示實時進度
我們來看看佈局文件:

<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"]]
>


    <ImageView
       android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitCenter"/>

    <Button
       android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/imageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="41dp"
        android:text="從網絡上下載一張圖片"/>

</RelativeLayout]]
>


就是很簡單的一個ImageView控件和一個Button控件,當點擊Button控件時,彈出一個ProgressDialog,然後開啓一個異步任務,從網絡中下載一張圖片,並更新到我們的ImageView上。這裏還要注意一點,如果我們要使用手機訪問網絡,必須還要給其授權才行,在後續的學習當中,將會詳細講解Android當中的授權的知識。我們來看看
AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?><manifestxmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xiaoluo.android_asynctast"
    android:versionCode="1"
    android:versionName="1.0"]]
>


    <uses-sdk
       android:minSdkVersion="8"
        android:targetSdkVersion="18"/>
    
    <!-- 授權手機能夠訪問網絡 -->
    <uses-permissionandroid:name="android.permission.INTERNET"/>
    
    <application
       android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme"]]
>

        <activity
           android:name="com.xiaoluo.android_asynctast.MainActivity"
            android:label="@string/app_name"]]
>

            <intent-filter]]
>

               <actionandroid:name="android.intent.action.MAIN"/>

               <categoryandroid:name="android.intent.category.LAUNCHER"/>
            </intent-filter]]
>

        </activity]]
>

    </application]]
>


</manifest]]
>


接下來我們來看看我們的Activity代碼:

publicclass MainActivityextends Activity
{
    private Button button;
    private ImageView imageView;
    private ProgressDialog progressDialog;
    privatefinal String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg";
//    private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg";    @Override
    protectedvoid onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        button = (Button)findViewById(R.id.button);
        imageView = (ImageView)findViewById(R.id.imageView);
        //    彈出要給ProgressDialog
        progressDialog =new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下載中,請稍後......");
        //    設置setCancelable(false); 表示我們不能取消這個彈出框,等下載完成之後再讓彈出框消失
        progressDialog.setCancelable(false);
        //    設置ProgressDialog樣式爲圓圈的形式        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            publicvoid onClick(View v)
            {         // 在UI Thread當中實例化AsyncTask對象,並調用execute方法
               new MyAsyncTask().execute(IMAGE_PATH);
            }
        });
    }
    
    /**
     * 定義一個類,讓其繼承AsyncTask這個類
     * Params: String類型,表示傳遞給異步任務的參數類型是String,通常指定的是URL路徑
     * Progress: Integer類型,進度條的單位通常都是Integer類型
     * Result:byte[]類型,表示我們下載好的圖片以字節數組返回
     *@author xiaoluo
     *
     */
    publicclass MyAsyncTaskextends AsyncTask<String, Integer,byte[]>
    {
        @Override
        protectedvoid onPreExecute()
        {
            super.onPreExecute();
            //    在onPreExecute()中我們讓ProgressDialog顯示出來            progressDialog.show();
        }
        @Override
        protectedbyte[] doInBackground(String... params)
        {
            //    通過Apache的HttpClient來訪問請求網絡中的一張圖片
            HttpClient httpClient =new DefaultHttpClient();
            HttpGet httpGet =new HttpGet(params[0]);
            byte[] image =newbyte[]{};
            try
            {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
               if(httpEntity !=null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                    image = EntityUtils.toByteArray(httpEntity);
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                httpClient.getConnectionManager().shutdown();
            }
            return image;
        }
        @Override
        protectedvoid onProgressUpdate(Integer... values)
        {
            super.onProgressUpdate(values);
        }
        @Override
        protectedvoid onPostExecute(byte[] result)
        {
            super.onPostExecute(result);
            //    將doInBackground方法返回的byte[]解碼成要給Bitmap
            Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
            //    更新我們的ImageView控件            imageView.setImageBitmap(bitmap);
            //    使ProgressDialog框消失            progressDialog.dismiss();
        }
    }
    
    @Override
    publicboolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main, menu);
        returntrue;
    }

}

我們來看看效果圖:
 
 
②帶有進度條更新的下載一張網絡圖片
下面這個代碼示例,將會在下載圖片的時候,顯示進度條的更新,配置文件都不變,我們來看看Activity代碼:

publicclass MainActivityextends Activity
{
    private Button button;
    private ImageView imageView;
    private ProgressDialog progressDialog;
    privatefinal String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg";
//    private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg";    @Override
    protectedvoid onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        button = (Button)findViewById(R.id.button);
        imageView = (ImageView)findViewById(R.id.imageView);
        //    彈出要給ProgressDialog
        progressDialog =new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下載中,請稍後......");
        //    設置setCancelable(false); 表示我們不能取消這個彈出框,等下載完成之後再讓彈出框消失
        progressDialog.setCancelable(false);
        //    設置ProgressDialog樣式爲水平的樣式        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            publicvoid onClick(View v)
            {
               new MyAsyncTask().execute(IMAGE_PATH);
            }
        });
    }
    
    /**
     * 定義一個類,讓其繼承AsyncTask這個類
     * Params: String類型,表示傳遞給異步任務的參數類型是String,通常指定的是URL路徑
     * Progress: Integer類型,進度條的單位通常都是Integer類型
     * Result:byte[]類型,表示我們下載好的圖片以字節數組返回
     *@author xiaoluo
     *
     */
    publicclass MyAsyncTaskextends AsyncTask<String, Integer,byte[]>
    {
        @Override
        protectedvoid onPreExecute()
        {
            super.onPreExecute();
            //    在onPreExecute()中我們讓ProgressDialog顯示出來            progressDialog.show();
        }
        @Override
        protectedbyte[] doInBackground(String... params)
        {
            //    通過Apache的HttpClient來訪問請求網絡中的一張圖片
            HttpClient httpClient =new DefaultHttpClient();
            HttpGet httpGet =new HttpGet(params[0]);
            byte[] image =newbyte[]{};
            try
            {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                InputStream inputStream =null;
                ByteArrayOutputStream byteArrayOutputStream =newByteArrayOutputStream();
               if(httpEntity !=null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                   //    得到文件的總長度
                   long file_length = httpEntity.getContentLength();
                   //    每次讀取後累加的長度
                   long total_length = 0;
                   int length = 0;
                   //    每次讀取1024個字節
                   byte[] data =newbyte[1024];
                    inputStream = httpEntity.getContent();
                   while(-1 != (length = inputStream.read(data)))
                    {
                       //    每讀一次,就將total_length累加起來
                        total_length += length;
                       //    邊讀邊寫到ByteArrayOutputStream當中
                        byteArrayOutputStream.write(data, 0, length);
                       //    得到當前圖片下載的進度
                       int progress = ((int)(total_length/(float)file_length) * 100);
                       //    時刻將當前進度更新給onProgressUpdate方法                        publishProgress(progress);
                    }
                }
                image = byteArrayOutputStream.toByteArray();
                inputStream.close();
                byteArrayOutputStream.close();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                httpClient.getConnectionManager().shutdown();
            }
            return image;
        }
        @Override
        protectedvoid onProgressUpdate(Integer... values)
        {
            super.onProgressUpdate(values);
            //    更新ProgressDialog的進度條
            progressDialog.setProgress(values[0]);
        }
        @Override
        protectedvoid onPostExecute(byte[] result)
        {
            super.onPostExecute(result);
            //    將doInBackground方法返回的byte[]解碼成要給Bitmap
            Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
            //    更新我們的ImageView控件            imageView.setImageBitmap(bitmap);
            //    使ProgressDialog框消失            progressDialog.dismiss();
        }
    }
    
    @Override
    publicboolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main, menu);
        returntrue;
    }

}

我們來看看效果圖:

 
這樣我們就能夠通過AsyncTask來實現從網絡中下載一張圖片,然後將其更新到UI控件中,並時時刻刻的更新當前的進度這個功能了。
六、AsyncTask的重要知識點
在上面兩節已經詳細講解了AsyncTask的工作原理了,這裏我們還要補充一下AsyncTask的一些其他知識點:
1.Cancelling a Task
我們可以在任何時刻來取消我們的異步任務的執行,通過調用 cancel(boolean)方法,調用完這個方法後系統會隨後調用 isCancelled() 方法並且返回true。如果調用了這個方法,那麼在 doInBackgroud() 方法執行完之後,就不會調用 onPostExecute() 方法了,取而代之的是調用 onCancelled() 方法。爲了確保Task已經被取消了,我們需要經常調用 isCancelled() 方法來判斷,如果有必要的話。
2.在使用AsyncTask做異步任務的時候必須要遵循的原則:
AsyncTask類必須在UI Thread當中加載,在Android Jelly_Bean版本後這些都是自動完成的
AsyncTask的對象必須在UI Thread當中實例化
execute方法必須在UI Thread當中調用
不要手動的去調用AsyncTask的onPreExecute, doInBackground, publishProgress, onProgressUpdate, onPostExecute方法,這些都是由Android系統自動調用的
AsyncTask任務只能被執行一次
 
到此,有關AsyncTask的總結就到此爲止了,本篇隨筆主要講解了Android中的多線程知識,並且詳細地講解了 AsyncTask 異步任務的概念和實現機制,並通過實例來了解 AsyncTask 的執行過程,最後還補充了 AsyncTask 的一些重要知識點,包括如何取消一個 AsyncTask 以及,我們在使用 AsyncTask 時所必須遵循的規則。
  
8.android內存優化及管理

1、使用優化過的數據容器。 在Android framework下,建議使用優化過的數據容器比如:SparseArray,SparseBooleanArray,LongSparseArray。通用的HashMap實現的內存使用率非常的低,因爲他需要爲每一個mapping創建一個分離的entry object。另外,SparseArray類避免了系統對有些key的自動裝箱,因而帶來了更高的效率。 2、注意內存的開銷。[size=12.800000190734863px]  注意你使用的語言和第三方庫的成本和開銷,要自始至終的將這些因素考慮在你的程序設計中。通常,有些事情表面上看着沒什麼問題但實際上的開銷讓人驚歎。比如: 
   ·枚舉相對於靜態常量來說,需要兩倍甚至更多的內存。你應該完全避免在Android中使用枚舉。 
    [size=12.666666984558105px]·每一個在java中的類(包括匿名內部類)使用大約500 bytes的代碼量。    ·每一個類的實例擁有12-16 bytes的RAM消耗。    ·放置一個單獨的實體到HashMap中,一個額外加的實體對象分配需要花費32 bytes。
[size=12.666666984558105px]3、關於代碼的抽象  抽象是一個好的編程的基礎,因爲抽象可以提高代碼的靈活性和可維護性。然而抽象也帶來了一定的花銷,一般情況下,他們有更多的代碼需要執行,需要更多的時間和更多RAM來將這些代碼映射到內存中。因此,如果你的抽象不能帶來巨大的好處,你就應該割掉你的贅肉。
4、避免依賴注入框架  雖然注入框架給我們帶來了很多方便,但是在使用這些框架的時候,框架需要花費很多時間來掃描我們自己寫的代碼,甚至會將哪些你根本不需要的東西也加載到內存中。
5、小心的使用擴展庫  很多擴展庫的代碼不是針對手機環境開發的,可能在用到移動客戶端的時候會導致很低的效率。因此在使用之前,需要評估一下其佔用內存的大小。     即使庫針對手機開發,也會有潛在的危險,因爲每一個Library做的事情不盡相同。比如,一個Library使用nano protobufs而另一個使用micro protobufs。現在,在你的app中就有兩個protobuf。類似情況經常發生。
6、使用混淆器移除不必要的代碼  ProGuard工具通過移除無用代碼,使用語意模糊來保留類,字段和方法來壓縮,優化和混淆代碼。可以使你的代碼更加完整,更少的RAM 映射頁。
7、使用多個進程(注意是process 不是 thread ok?)  如果這適合你的app,可能幫助你管理你的app的內存就是將你的app多個部分分配到多個進程中。該技術必須小心使用並且大多數應用不應該運行在多個進程下。這個技術的主要應用是後臺工作跟天台工作一樣重要的情況下。典型應用就是:當音樂播放器從服務器下載並長時間播放音樂,一般將其分爲兩個進程:一個是UI,另一個位置後臺服務的運行。 like this:



1
<serviceandroid:name=".PlaybackService"         android:process=":background"/>


[size=12.666666984558105px]process後面需要記住要有個":",這表示該進程屬於你的app。 一般情況下,一塊基本的空進程需要的內存大小在1.4m左右。



1
adb shell dumpsys meminfo com.example.android.apis:empty

8、基本性能優化方法的基本原則:    

1)不要做你不必要的工作;

2)不要申請不必要的內存;
    例如,你明明知道一個方法返回一個String之後,你需要對這個String重新進行修改,那麼就不要返回一個String,返回一個StringBuffer會是你更好的選擇。     再比如,使用int比使用Integer佔用更少的空間。這個大家肯定都是曉得的。     數組比一個Map擁有更好的性能。     如果你的方法不需要訪問類字段,那麼讓你的方法是static的吧,這將會帶來15%-20%速度的提升。     對於常量,請儘量使用static and final定義。如果使用final定義常量之後,會減少編譯器在類生成時初始化<clinit>方法調用時對常量的存儲,對於int型常量,將會直接使用其數值來進行替換,而對於String對象將會使用相對廉價的“string constant”指令來替換字段查找表。雖然這個方法並不完全對所有類型都有效,但是,將常量聲明爲static final絕對是一個好的做法。     避免Getters/Setters。雖然在一般的面向對象的設計模式中使用Getter和Setter是稀鬆平常的事情,但是在Android中使用getters/Setters是一個非常糟糕的主意,方法的調用相對於直接查找字段來說十分的昂貴。在沒有JIT的情況下,直接對字段進行訪問要比通過Getter訪問快了近3倍。在有JIT的情況下,前者比後者快近7倍。     使用最新的循環方式。比如增強for。     避免使用浮點類型。在某些可以的情況下,將浮點替換成整型數據,然後進行計算會得到更精確的結果和更快的速度。     小心使用Native Methods。這裏需要糾正的是,Native 方法並不一定能提高你應用的速度,有些甚至會拖後腿,因爲,首先來說就需要一部分開銷在Java-native transition上,而且JIT並不能對其進行優化。另外你需要爲每個你想要在其上運行的系統結構上進行編譯;即便是同一個處理器上,你也可能需要多個版本,比如爲G1上的ARM處理器編譯的就不能很好的在Nexus One的ARM上運行。Native代碼最主要的用途是,你已經有了很多native 代碼,並且你迫切希望接入Android中。而不是使用Native Method來提高你應用中某部分代碼的運行速度。     [size=12.666666984558105px] 對於效率的提高除了使用遵守上面兩條外基本準則外,選擇合適的算法和數據結構也是非常關鍵的。

9、關於UI上的一些問題[size=12.800000190734863px]  Hierarchy Viewer[size=12.666666984558105px] [size=12.666666984558105px]通過他,可以看到你自己的Layout文件存在的問題。你可以看到你的Layout每一部分計算,佈局,渲染所需要的時間。儘量的使你的Layout扁平話,深度最好保持 在三層之內[size=12.666666984558105px] RelativeLayout[size=12.666666984558105px] 是解決使用LinearLayout堆疊多層問題的利劍。那些爲了方便 使[size=12.666666984558105px] 用LinearLayout的layout_weight屬性[size=12.666666984558105px] 的哥們,需要重點注意,這個屬性真的可以減慢measure速度。所以在使用之前,一定要再三考慮,是否真的不能通過其他方法來完成你要的效果? 
    官方文檔上 推薦使用RelativeLayout和GridLayout來避免Layout深度過深的問題[size=12.666666984558105px] 。 
    之前看文檔,Google提供一個叫 ViewSub[size=12.666666984558105px] 的控件來優化那些不是必須要立即在UI上顯示的控件,感興趣的同學可以去看看。在API Level 1中就提供了這個東西,但是在實際開發中很少見到有人用或者提及(可能是我孤陋寡聞,公司就兩個Android開發,另一個還要轉IOS,我們倆的Android技術就代表了我們公司的Android技術能力,想想真悲哀!),其實蠻好用的。 
    重用Layout。可以使用<include/> <merge/>將其他佈局嵌入到當前佈局中。 
    ListView的優化:ViewHolder的使用;AsyncTask的使用;針對ListView當前滑動狀態,對圖片數據的加載進行控制;(ListView在配以AsyncTask加載圖片時需要注[size=12.666666984558105px] 意圖片的加載完顯示的位置以及圖片的緩存問題,具體可以參考Google的Demo[size=12.666666984558105px] [size=12.666666984558105px] 
[size=12.800000190734863px]10、將大消耗操作交給多個線程。 11、如果你的應用需要發送Broadcast但是又不希望別的應用獲取到,或者你不希望處理別的應用發送的同樣的action,那麼請使用LocalBroadcastManager
[size=12.666666984558105px]    該類是在Android Support v4中提供的,用來在同一個應用內的不同組件之間發送Broadcast。好處上面說了,可以保證應用的私密性。會比全局廣播有更高的效率,但是官方文檔沒有說明具體數值。具體使用方法:



1
LocalBroadcastManager.getInstance(this).registerReceiver( mStateReceiver, mStatusIntentFilter);LocalBroadcastManager.getInstance(this).sendBroadcast(localBroadcastIntent);


使用oc計數器的方式可以更好的控制內存




Android OnLowMemory和OnTrimMemory 

OnLowMemory
OnLowMemory是Android提供的API,在系統內存不足,所有後臺程序(優先級爲background的進程,不是指後臺運行的進程)都被殺死時,系統會調用OnLowMemory。系統提供的回調有:
Application.onLowMemory()
Activity.OnLowMemory()
Fragement.OnLowMemory()
Service.OnLowMemory()
ContentProvider.OnLowMemory()
除了上述系統提供的API,還可以自己實現ComponentCallbacks,通過API註冊,這樣也能得到OnLowMemory回調。例如:
public static class MyCallback implements ComponentCallbacks {
 
        @Override
        public void onConfigurationChanged(Configuration arg) {
 
        }
 
        @Override
        public void onLowMemory() {
            //do release operation
        }
    }
然後,通過Context.registerComponentCallbacks ()在合適的時候註冊回調就可以了。通過這種自定義的方法,可以在很多地方註冊回調,而不需要侷限於系統提供的組件。
OnTrimMemory
OnTrimMemory是Android 4.0之後提供的API,系統會根據不同的內存狀態來回調。系統提供的回調有:
Application.onTrimMemory()
Activity.onTrimMemory()
Fragement.OnTrimMemory()
Service.onTrimMemory()
ContentProvider.OnTrimMemory()
OnTrimMemory的參數是一個int數值,代表不同的內存狀態:
TRIM_MEMORY_COMPLETE:內存不足,並且該進程在後臺進程列表最後一個,馬上就要被清理
TRIM_MEMORY_MODERATE:內存不足,並且該進程在後臺進程列表的中部。
TRIM_MEMORY_BACKGROUND:內存不足,並且該進程是後臺進程。
TRIM_MEMORY_UI_HIDDEN:內存不足,並且該進程的UI已經不可見了。      
      以上4個是4.0增加
TRIM_MEMORY_RUNNING_CRITICAL:內存不足(後臺進程不足3個),並且該進程優先級比較高,需要清理內存
TRIM_MEMORY_RUNNING_LOW:內存不足(後臺進程不足5個),並且該進程優先級比較高,需要清理內存
TRIM_MEMORY_RUNNING_MODERATE:內存不足(後臺進程超過5個),並且該進程優先級比較高,需要清理內存      
 
      以上3個是4.1增加
系統也提供了一個ComponentCallbacks2,通過Context.registerComponentCallbacks()註冊後,就會被系統回調到。
OnLowMemory和OnTrimMemory的比較
1,OnLowMemory被回調時,已經沒有後臺進程;而onTrimMemory被回調時,還有後臺進程。 2,OnLowMemory是在最後一個後臺進程被殺時調用,一般情況是low memory killer 殺進程後觸發;而OnTrimMemory的觸發更頻繁,每次計算進程優先級時,只要滿足條件,都會觸發。 3,通過一鍵清理後,OnLowMemory不會被觸發,而OnTrimMemory會被觸發一次。
9.圖片緩存機制

目前很多商業應用都會涉及到從網絡上讀取圖片數據的問題,爲了節約用戶流量,應用一般會將圖片緩存起來。圖片緩存一般分爲內存緩存和外存緩存。內存緩存運用java的緩存機制,在程序完全退出後,緩存所在的內存空間可能被其它應用程序佔用從而丟失。外存緩存一般放在程序特有的訪問空間或者sd卡中,在sd卡中存放的資源爲公有資源,其它程序也可以訪問,且對用戶來講沒有一個強制清除緩存的規範機制。綜合以上,本文采用將緩存圖片放置在程序的特有空間中, 其它應用程序無法訪問,且用戶可以在應用程序管理中的"清除數據"選項中清除緩存。 
      本文提供三種緩存策略:(1)LRU算法,固定緩存圖片數量(max_num),當圖片數量超出max_num時,將緩存中最近用的最少的圖片刪除。(2)FTU算法,固定每張圖片的緩存時限,以最後一次使用算起,超過時限後刪除。(3)FMU算法,在存儲器中固定一定大小的存儲空間,超過固定空間後將緩存中佔用最大尺寸的圖片刪除。使用時只需要向方法體中傳遞圖片的URL即可。

代碼片段

使用方法:
    1.導入jar;
    2. 獲取服務;
    3.提交url,交給程序去判斷是否下載。 
 

public class ImagecachetacticsdemoActivity extends Activity {     public void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);         setContentView(R.layout.item);         /*FMU*/        imageCacheManager = ImageCacheManager.getImageCacheService(this,                 ImageCacheManager.MODE_FIXED_MEMORY_USED, "memory");         imageCacheManager.setMax_Memory(1024 * 1024);         /*FTU*/        // imageCacheManager = ImageCacheManager.getImageCacheService(this,         // ImageCacheManager.MODE_FIXED_TIMED_USED, "time");         // imageCacheManager.setDelay_millisecond(3 * 60 * 1000);                    /*LRU*/        // imageCacheManager = ImageCacheManager.getImageCacheService(this,         // ImageCacheManager.MODE_LEAST_RECENTLY_USED, "num");         // imageCacheManager.setMax_num(5);         // imageCacheManager = ImageCacheManager.getImageCacheService(this,         // ImageCacheManager.MODE_NO_CACHE_USED, "nocache");        mImageView = (ImageView) findViewById(R.id.imageView);         new DownloadTask()                 .execute("http://www.touxiang99.com/uploads/allimg/110417/1_110417112640_2.jpg");     }     private class DownloadTask extends AsyncTask<String, Void, Bitmap> {         @Override         protected Bitmap doInBackground(String... params) {             try {                 return imageCacheManager.downlaodImage(new URL(params[0]));             } catch (IOException e) {                 e.printStackTrace();             }             return null;         }         @Override         protected void onPostExecute(Bitmap result) {             mImageView.setImageBitmap(result);             super.onPostExecute(result);         }         @Override         protected void onPreExecute() {             mImageView.setImageResource(R.drawable.ic_launcher);             super.onPreExecute();         }     }     private ImageView mImageView;     private ImageCacheManager imageCacheManager;
}  

 
10.Handler機制

11.什麼是ANR 如何避免它?

ANR:Application NotResponding,五秒
  在Android中,活動管理器和窗口管理器這兩個系統服務負責監視應用程序的響應。當出現下列情況時,Android就會顯示ANR對話框了:
  對輸入事件(如按鍵、觸摸屏事件)的響應超過5秒
  意向接受器(intentReceiver)超過10秒鐘仍未執行完畢
  Android應用程序完全運行在一個獨立的線程中(例如main)。這就意味着,任何在主線程中運行的,需要消耗大量時間的操作都會引發ANR。因爲此時,你的應用程序已經沒有機會去響應輸入事件和意向廣播(Intentbroadcast)。
  因此,任何運行在主線程中的方法,都要儘可能的只做少量的工作。特別是活動生命週期中的重要方法如onCreate()和 onResume()等更應如此。潛在的比較耗時的操作,如訪問網絡和數據庫;或者是開銷很大的計算,比如改變位圖的大小,需要在一個單獨的子線程中完成(或者是使用異步請求,如數據庫操作)。但這並不意味着你的主線程需要進入阻塞狀態已等待子線程結束 -- 也不需要調用Therad.wait()或者Thread.sleep()方法。取而代之的是,主線程爲子線程提供一個句柄(Handler),讓子線程在即將結束的時候調用它(xing:可以參看Snake的例子,這種方法與以前我們所接觸的有所不同)。使用這種方法涉及你的應用程序,能夠保證你的程序對輸入保持良好的響應,從而避免因爲輸入事件超過5秒鐘不被處理而產生的ANR。這種實踐需要應用到所有顯示用戶界面的線程,因爲他們都面臨着同樣的超時問題。

12.sqlite操作使用

本帖最後由 y_m 於 2013-6-2 00:27 編輯SQLite數據庫
數據庫:它就是一個軟件,需要安裝,安裝完後就有自己的目錄結構。
         都有客戶端和服務端,所有的數據庫都實現了SQL標準
SQLite數據庫:它是一個輕量級數據庫,設計目的是嵌入式的,而且它佔用的資源非常少
        注意:除了主鍵不能存儲任意的類型之外,其他的字段可以存放任意的數據類型
                SqliteDatabase它要求:表的主鍵的字段名最好是_id,一定要_id  否則報錯;




Cmd操作指令:
sqlite3 qjq.db 進入數據庫
.tables 查看數據庫裏面的表
創建數據庫文件:
三種方式:
第一種通過上下文創建數據庫:
public class DBsqlite {
        private Context context;
        public DBsqlite(Context context) {
                super();
                this.context = context;
        }
        public void createDB() {
                //通過上下文創建數據庫
                   context.openOrCreateDatabase("persons.db", Context.MODE_PRIVATE, null);
        }

第二種SQLiteDatabase創建數據庫
    public void createDB(){
                String dir="/data/data/"+context.getPackageName();
                File file=new File(dir,"persons.db");
                SQLiteDatabase.openOrCreateDatabase(file, null);
        }
第三種創建一個help類繼承SQLiteOpenHelper
實現DBhelp構造onCreate方法onUpgrade方法
public class DBhelp extends SQLiteOpenHelper {
        public DBhelp(Context context) {
//                上下文        ,數據庫名,遊標工廠 ,數據版本
                super(context, "persons.db", null, 2);
                // TODO Auto-generated constructor stub
        }
        //數據庫第一次創建之後調用該方法。創建表、視圖。。。 或者初始化表信息
        public void onCreate(SQLiteDatabase db) {
                // 創建數據庫
                db.execSQL("create table fish(_id integer primary key autoincrement,name text)");
        }
        @Override
//當數據版本被改變則會執行該方法super(context, "persons.db", null, 3);
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                // 版本修改表添加一列
                db.execSQL("ALTER TABLE fish ADD amount integer");
        }
Crud
以下分別用兩種方式crud了,一種是面向SQL ,一種是面向對象,但是面向對象的源碼裏面其實也是在幫你拼接sql
public class OtherFishService {
        private SQLiteOpenHelper mOpenHelper;
        public OtherFishService(Context context) {
                // TODO Auto-generated constructor stub
                mOpenHelper = new DBHelper(context);
        }
        /**
         * 插入數據
         * @param name
         */
        public void insert(String name){
                SQLiteDatabase db = mOpenHelper.getWritableDatabase();
//                String sql = "insert into fish(name) values(?)";
                if(db.isOpen()){
//                        db.execSQL(sql, new Object[]{name});
                        //insert into fish
                        //ContentValues裏面就是要插入的值
                        ContentValues values = new ContentValues();
                        values.put("name", name);
                        db.insert("fish", "_id", values);
                        db.close();
                }
        }
        public List<Fish> query(){
                List<Fish> fishs = new ArrayList<Fish>();
                SQLiteDatabase db = mOpenHelper.getReadableDatabase();
//                String sql ="select * from fish";
                if(db.isOpen()){
                        //cursor 就是resultset
//                        Cursor cursor = db.rawQuery(sql, null);
                        Cursor cursor = db.query("fish",//表名
                                        new String[]{"*"},//要查詢的列名
                                        null,//查詢條件
                                        null,//條件參數
                                        null,//分組
                                        null,//條件
                                        null);//排序
                        while(cursor.moveToNext()){
                                //得到_id的下標
                                int column_index = cursor.getColumnIndex("_id");
                                //得到_id的值
                                int _id = cursor.getInt(column_index);
                                String name = cursor.getString(cursor.getColumnIndex("name"));
                                Fish fish = new Fish(_id, name);
                                fishs.add(fish);
                        }
                        //cursor使用完成之後一定要關閉
                        cursor.close();
                        db.close();
                }
                return fishs;
        }
        public void update(Fish fish){
                SQLiteDatabase db = mOpenHelper.getWritableDatabase();
                if(db.isOpen()){
//                        String sql = "update Fish set name = ? where _id = ?";
//                        db.execSQL(sql,new Object[]{fish.name,fish._id});
                        ContentValues values = new ContentValues();
                        values.put("name",fish.name);
                        String whereClause = " _id = ?";
                        String[] whereArgs = new String[]{fish._id+""};
                        db.update("fish", values, whereClause, whereArgs);
                }
        }
        public void delete(int _id){
                SQLiteDatabase db = mOpenHelper.getWritableDatabase();
                if(db.isOpen()){
//                        String sql = "delete from fish where _id = ?";
//                        db.execSQL(sql,new Object[]{_id});
                        String whereClause = " _id = ?";
                        String[] whereArgs = new String[]{_id+""};
                        db.delete("fish", whereClause, whereArgs);
                }
        }


SqliteDatabase事務列表顯示多條數據事務,以及事務的完整性SQLiteDatabase控制事務方法:
beginTransaction();        開啓事務
setTransactionSuccessful();        設置事務成功
endTransaction();結束事務
db.beginTransaction();                //開始事務
        try {
                db.execSQL("update student set amount=amount-300 where name=?", new Object[]{"張三"});
                db.execSQL("update student set amount=amount+300 where name=?", new Object[]{"李四"});
                db.setTransactionSuccessful();         //設置事務成功
       
        } catch (Exception e) {
                System.out.println("事務執行出現異常,不能設置事務成功!");
        }finally{
        //無論是否出現異常,都要結束事務。
                db.endTransaction();
        }
cmd常見指令:
Sqlite3 xx.db                查看xx數據庫
.tables                        查看所有表
.exit                                退出
.databases                查看所有數據庫
標籤:
include標籤實現組件複用
將另一個佈局文件加載到當前佈局文件,實現組件複用
<!-- 加載一個佈局文件 -->
<include layout="@layout/item"/>
屬性 android:visibility=有三個選項
<!--
Visible :可見
           都是不可見
    invisible : 還佔據空間
    gone:不佔據空間
     -->
列表顯示多條數據 simpleAdapter
代碼:
OtherFishService ofs = new OtherFishService(this);
            List<Fish> fishs = ofs.query();
            List<Map<String,Object>>data=new ArrayList<Map<String,Object>>();
            for(Fish f:fishs){
                    Map<String,Object> map = new HashMap<String, Object>();
                    map.put("_id", f._id);
                    map.put("name",f.name);
                    map.put("age", f.age);
                    data.add(map);
            }
            String[] from = new String[]{"_id","name","age"};
            int[] to = new int[]{R.id.tv_id,R.id.tv_name,R.id.tv_age};
            SimpleAdapter adapter = new SimpleAdapter(this,
                            data,
                            R.layout.item,
                            from,
                            to);
            lv.setAdapter(adapter);
以上可以實現但是過於麻煩!
我們還可以使用專門爲數據庫提供的
Cursoradapter
OtherFishService ofs = new OtherFishService(this);
            Cursor c = ofs.getAllCursor();
            String[] from = new String[]{"_id","name","age"};
            int[] to = new int[]{R.id.tv_id,R.id.tv_name,R.id.tv_age};
            SimpleCursorAdapter adapter = new SimpleCursorAdapter(
                            this,
                            R.layout.item,
                            c,
                            from,
                            to);
            lv.setAdapter(adapter);
我們不單單隻用android給我們提過的CursorAdapter我們還可以自定義一個CursorAdapter
自定義CursorAdapter
public class MyCursorAdapter extends CursorAdapter {
        //佈局加載器(是一個服務,用來加載xml佈局文件到java代碼中)
        private LayoutInflater mInflater;
       
        public MyCursorAdapter(Context context, Cursor c) {
                super(context, c);
        mInflater=(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
               
                mInflater = LayoutInflater.from(context);
               
        }
        //創建一個item的佈局
        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
                // TODO Auto-generated method stub
                //通過佈局加載器加載佈局
                View view = mInflater.inflate(R.layout.item, null);
                return view;
        }
        //把數據綁定給item裏面的佈局
        @Override
        public void bindView(View view, Context context, Cursor cursor) {
                // TODO Auto-generated method stub
        //1 先得到控件
                //2 得到數據
                //3 綁定數據給控件
                TextView tv_id = (TextView) view.findViewById(R.id.tv_id);
                TextView tv_name = (TextView) view.findViewById(R.id.tv_name);
                TextView tv_age = (TextView) view.findViewById(R.id.tv_age);
               
                String _id = cursor.getString(0);
                String name = cursor.getString(1);
                String age = cursor.getString(2);
               
                tv_id.setText(_id);
                tv_name.setText(name);
                tv_age.setText(age);
               
                int position = cursor.getPosition();
                if(position%2 ==0){
                        view.setBackgroundColor(Color.RED);
                }else{
                        view.setBackgroundColor(Color.GREEN);
                }
               
        }
}
爲了實現 我們想要的並且系統沒提供的效果所以我們要自定義Adapter
不僅可以繼承CursorAdapter 實現自定義CursorAdapter ,我們還可以通
過繼承BaseAdapter 。
private class MyCursorAdapter extends BaseAdapter {
                private Context context;
                private Cursor c;
                private LayoutInflater mInflater;
               
                public MycursorAdapter1(Context context, Cursor c) {
                        super();
                        this.context = context;
                        this.c = c;
                        mInflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                }
                @Override
                public int getCount() {
                        // TODO Auto-generated method stub
                        return c.getCount();
                }
                @Override
                public Object getItem(int position) {
                        Person p = null;
                        if (c.moveToPosition(position)) {
                                String id = c.getString(0);
                                String name = c.getString(1);
                                String age = c.getString(2);
                                p = new Person(id, name, age);
                        }
                        return p;
                }
                @Override
                public long getItemId(int position) {
                        // TODO Auto-generated method stub
                        return position;
                }
                @Override
                public View getView(int position, View convertView, ViewGroup parent) {
                        //1得到視圖
//2從視圖中拿到要顯示對象
//3從cursor裏面拿值
//4綁定
//5返回
注意:getView方法實際會執行原本次數2倍(例:7條數據與,getView執行7*2次),第一遍執行是爲了測試有多少條數據,第2遍執行纔是真正的現實操作
                        View view = mInflater.inflate(R.layout.item, null);
                        TextView tv_id = (TextView) view.findViewById(R.id.bt_id);
                        TextView tv_name = (TextView) view.findViewById(R.id.bt_name);
                        TextView tv_age = (TextView) view.findViewById(R.id.bt_age);
                        if (c.moveToPosition(position)) {
                                String id = c.getString(0);
                                String name = c.getString(1);
                                String age = c.getString(2);
                                tv_id.setText(id);
                                tv_name.setText(name);
                                tv_age.setText(age);
                                 
                        }
                        if ( position% 2 == 0) {
                                view.setBackgroundColor(Color.RED);
                        }
                        return view;
                }
        }
當我們用完cursor的時候,怎麼關閉他?
//應用回收資源  退出程序時調用該方法
        @Override
        protected void onDestroy() {
                // TODO Auto-generated method stub
                super.onDestroy();r
                Cursor c = adapter.getCursor();
                if(c != null && !c.isClosed()){
                        c.close();
                }
        }

13請描述一下Intent 和 Intent Filter。

Android 中通過 Intent 對象來表示一條消息,一個 Intent 對象不僅包含有這個消息的目的地,還可以包含消息的內容,這好比一封Email,其中不僅應該包含收件地址,還可以包含具體的內容。對於一個 Intent 對象,消息“目的地”是必須的,而內容則是可選項。
通過Intent 可以實現各種系統組件的調用與激活. 
 
 
Intent filter: 可以理解爲郵局或者是一個信箋的分揀系統…
這個分揀系統通過3個參數來識別
Action: 動作    view
Data: 數據uri   uri
Category : 而外的附加信息 
Action 匹配
Action 是一個用戶定義的字符串,用於描述一個 Android 應用程序組件,一個 Intent Filter 可以包含多個 Action。在 AndroidManifest.xml 的 Activity 定義時可以在其 <intent-filter >節點指定一個 Action 列表用於標示 Activity 所能接受的“動作”,例如:
 <intent-filter >
 <action android:name="android.intent.action.MAIN" />
 <action android:name="cn.itcast.action" />
……
 </intent-filter>
 
如果我們在啓動一個 Activity 時使用這樣的 Intent 對象:
 Intent intent =new Intent();
 intent.setAction("cn.itcast.action");
 startActivity(intent);
 
那麼所有的 Action 列表中包含了“cn.itcast”的 Activity 都將會匹配成功。
Android 預定義了一系列的 Action 分別表示特定的系統動作。這些 Action 通過常量的方式定義在 android.content. Intent中,以“ACTION_”開頭。我們可以在 Android 提供的文檔中找到它們的詳細說明。
URI 數據匹配
一個 Intent 可以通過 URI 攜帶外部數據給目標組件。在 <intent-filter >節點中,通過 <data/>節點匹配外部數據。
mimeType 屬性指定攜帶外部數據的數據類型,scheme 指定協議,host、port、path 指定數據的位置、端口、和路徑。如下:
 <data android:mimeType="mimeType" android:scheme="scheme"
 android:host="host" android:port="port" android:path="path"/>
Intent intent = new Intent();
intent.setAction(Intent.ACTION_CALL);
insent.setData( Uri.parse(tel:12345));
startAcitivty();
電話的uri   tel: 12345
           http://www.baidu.com
自己定義的uri  itcast://cn.itcast/person/10
 
如果在 Intent Filter 中指定了這些屬性,那麼只有所有的屬性都匹配成功時 URI 數據匹配纔會成功。
Category 類別匹配
<intent-filter >節點中可以爲組件定義一個 Category 類別列表,當 Intent 中包含這個列表的所有項目時 Category 類別匹配纔會成功。
默認是DEFAULT
14.Intent傳遞數據時,可以傳遞哪些類型數據?

1.一般的基本數據類型  Intent .putextra() intent.getextra();
Parselable Serializable
 
       2.數據的uri, intent.setData() intent.getData();

15.Android事件傳遞機制

Android中dispatchTouchEvent,onInterceptTouchEvent, onTouchEvent的理解ec
android中的事件類型分爲按鍵事件和屏幕觸摸事件,Touch事件是屏幕觸摸事件的基礎事件,有必要對它進行深入的瞭解。 一個最簡單的屏幕觸摸動作觸發了一系列Touch事件:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE->ACTION_MOVE...->ACTION_MOVE->ACTION_UP
android的事件處理分爲3步。
1)public booleandispatchTouchEvent(MotionEvent ev)  這個方法用來分發TouchEvent 2)public boolean onInterceptTouchEvent(MotionEvent ev) 這個方法用來攔截TouchEvent 3)public boolean onTouchEvent(MotionEvent ev) 這個方法用來處理TouchEvent
假設當前Activity 佈局如下:


dispatchTouchEvent事件分發
當TouchEvent發生時,首先Activity將TouchEvent傳遞給最頂層的View, TouchEvent最先到達最頂層 view 的 dispatchTouchEvent 。然後由  dispatchTouchEvent 方法進行分發,如果dispatchTouchEvent返回true ,則交給這個view的onTouchEvent處理,如果dispatchTouchEvent返回 false ,則交給這個 view 的 onInterceptTouchEvent方法來決定是否要攔截這個事件,
如果onInterceptTouchEvent返回 true ,也就是攔截掉了,則交給它的 onTouchEvent 來處理,如果onInterceptTouchEvent返回 false ,那麼就傳遞給子 view,由子 view 的 dispatchTouchEvent 再來開始這個事件的分發。
 如圖:



事件攔截:onInterceptTouchEvent
 onInterceptTouchEvent用於改變事件的傳遞方向。決定傳遞方向的是返回值,返回爲false時事件會傳遞給子控件,返回值爲true時事件會傳遞給當前控件的onTouchEvent(),這就是所謂的Intercept(攔截)。
[tisa ps:正確的使用方法是,在此方法內僅判斷事件是否需要攔截,然後返回。即便需要攔截也應該直接返回true,然後由onTouchEvent方法進行處理。]
onTouchEvent用於處理事件,返回值決定當前控件是否消費(consume)了這個事件。尤其對於ACTION_DOWN事件,返回true,表示我想要處理後續事件(ACTION_MOVE或者ACTION_UP);返回false,表示不關心此事件,並返回由父類進行處理。 
在沒有重寫onInterceptTouchEvent()和onTouchEvent()的情況下(他們的返回值都是false), 對上面這個佈局,MotionEvent事件的傳遞順序如下:


當某個控件的onInterceptTouchEvent()返回值爲true時,就會發生截斷,事件被傳到當前控件的onTouchEvent()。如我們將LayoutView2的onInterceptTouchEvent()返回值爲true,則傳遞流程變成:



 如果我們同時將LayoutView2的onInterceptTouchEvent()和onTouchEvent()設置成true,那麼LayoutView2將消費被傳遞的事件,同時後續事件(如跟着ACTION_DOWN的ACTION_MOVE或者ACTION_UP)會直接傳給LayoutView2的onTouchEvent(),不傳給其他任何控件的任何函數。同時傳遞給子空間一個ACTION_CANCEL事件。傳遞流程變成(圖中沒有畫出ACTION_CANCEL事件):



小總結:onInterceptTouchEvent是自rootiew向下傳遞, onTouchEvent正好相反。
16.請介紹下Android中常用的五種佈局。

FrameLayout(幀佈局),LinearLayout (線性佈局),AbsoluteLayout(絕對佈局),RelativeLayout(相對佈局),TableLayout(表格佈局)
   FrameLayout
    從屏幕的左上角開始佈局,疊加顯示, 實際應用 播放器的暫停按鈕.   
   LinearLayout
線性佈局,這個東西,從外框上可以理解爲一個div,他首先是一個一個從上往下羅列在屏幕上。每一個LinearLayout裏面又可分爲垂直佈局
(android:orientation="vertical")和水平佈局(android:orientation="horizontal"
)。當垂直佈局時,每一行就只有一個元素,多個元素依次垂直往下;水平佈局時,只有一行,每一個元素依次向右排列。
AbsoluteLayout
絕對佈局猶如div指定了absolute屬性,用X,Y座標來指定元素的位置android:layout_x="20px"  view
android:layout_y="12px" fwvga 854*480apk
qq鬥地主 qq遊戲大廳800*480 800*480.apk  fwvga  854*480
指定平板機型的遊戲開發中經常用到絕對佈局  widget 絕對佈局 
指定機型的平板遊戲開發. 2.3 3.0
1.  界面佈局  任務管理器 gridview
2.  手機 任務管理 listview
 
lephone 
lepad 
  RelativeLayout
    相對佈局可以理解爲某一個元素爲參照物,來定位的佈局方式。主要屬性有:
        相對於某一個元素
    android:layout_below="@id/aaa" 該元素在 id爲aaa的下面
    android:layout_toLeftOf="@id/bbb" 改元素的左邊是bbb
        相對於父元素的地方
     android:layout_alignParentLeft="true"  在父元素左對齊
android:layout_alignParentRight="true" 在父元素右對齊
 
 
Android  oa客戶端.
TableLayout <table>
表格佈局類似Html裏面的Table。每一個TableLayout裏面有表格行TableRow,TableRow裏面可以具體定義每一個元素,設定他的對齊方式android:gravity="" 。
每一個佈局都有自己適合的方式,另外,這五個佈局元素可以相互嵌套應用,做出美觀的界面。
oa  自動化 生成報表 ,圖標 表示   webview  
css div
webview
 
17.談談UI中,Padding和Margin有什麼區別?
Padding 文字對邊框, margin是控件對父窗體.
Padding 盒子裏面的內容距離盒子的距離 , margin 盒子與盒子之間的距離 
18.請解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關係。




簡單的說,Handler獲取當前線程中的looper對象,looper用來從存放Message的MessageQueue中取出Message,再有Handler進行Message的分發和處理.
Message Queue(消息隊列):用來存放通過Handler發佈的消息,通常附屬於某一個創建它的線程,可以通過Looper.myQueue()得到當前線程的消息隊列.
Handler:可以發佈或者處理一個消息或者操作一個Runnable,通過Handler發佈消息,消息將只會發送到與它關聯的消息隊列,然也只能處理該消息隊列中的消息.
Looper:是Handler和消息隊列之間通訊橋樑,程序組件首先通過Handler把消息傳遞給Looper,Looper把消息放入隊列。Looper也把消息隊列裏的消息廣播給所有的Handler:Handler接受到消息後調用handleMessage進行處理.
Message:消息的類型,在Handler類中的handleMessage方法中得到單個的消息進行處理,在單線程模型下,爲了線程通信問題,Android設計了一個Message Queue(消息隊列),線程間可以通過該Message Queue並結合Handler和Looper組件進行信息交換。下面將對它們進行分別介紹:
1. Message
Message消息,理解爲線程間交流的信息,處理數據後臺線程需要更新UI,則發送Message內含一些數據給UI線程。
2. Handler
Handler處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理。後臺線程就是通過傳進來的 Handler對象引用來sendMessage(Message)。而使用Handler,需要implement 該類的 handleMessage(Message)方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。
3. Message Queue
Message Queue消息隊列,用來存放通過Handler發佈的消息,按照先進先出執行。
每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾並按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過sendMessage發送的是一個message對象,會被 Handler的handleMessage()函數處理;而通過post方法發送的是一個runnable對象,則會自己執行。
4. Looper
Looper是每條線程裏的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)建立Message Queue,但在子線程裏並沒有建立Message Queue。所以調用Looper.getMainLooper()得到的主線程的Looper不爲NULL,但調用Looper.myLooper() 得到當前線程的Looper就有可能爲NULL。對於子線程使用Looper,API Doc提供了正確的使用方法:這個Message機制的大概流程:
1. 在Looper.loop()方法運行開始後,循環地按照接收順序取出Message Queue裏面的非NULL的Message。
2. 一開始Message Queue裏面的Message都是NULL的。當Handler.sendMessage(Message)到Message Queue,該函數裏面設置了那個Message對象的target屬性是當前的Handler對象。隨後Looper取出了那個Message,則調用該Message的target指向的Hander的dispatchMessage函數對Message進行處理。在dispatchMessage方法裏,如何處理Message則由用戶指定,三個判斷,優先級從高到低:
1) Message裏面的Callback,一個實現了Runnable接口的對象,其中run函數做處理工作;
2) Handler裏面的mCallback指向的一個實現了Callback接口的對象,由其handleMessage進行處理;
3) 處理消息Handler對象對應的類繼承並實現了其中handleMessage函數,通過這個實現的handleMessage函數處理消息。
由此可見,我們實現的handleMessage方法是優先級最低的!
3. Handler處理完該Message (update UI) 後,Looper則設置該Message爲NULL,以便回收!
在網上有很多文章講述主線程和其他子線程如何交互,傳送信息,最終誰來執行處理信息之類的,個人理解是最簡單的方法——判斷Handler對象裏面的Looper對象是屬於哪條線程的,則由該線程來執行!
1. 當Handler對象的構造函數的參數爲空,則爲當前所在線程的Looper;
2. Looper.getMainLooper()得到的是主線程的Looper對象,Looper.myLooper()得到的是當前線程的Looper對象。

19.AIDL的全稱是什麼?如何工作?

AIDL的英文全稱是Android Interface Define Language當A進程要去調用B進程中的service時,並實現通信,我們通常都是通過AIDL來操作的A工程:首先我們在net.blogjava.mobile.aidlservice包中創建一個RemoteService.aidl文件,在裏面我們自定義一個接口,含有方法get。ADT插件會在gen目錄下自動生成一個RemoteService.java文件,該類中含有一個名爲RemoteService.stub的內部類,該內部類中含有aidl文件接口的get方法。說明一:aidl文件的位置不固定,可以任意然後定義自己的MyService類,在MyService類中自定義一個內部類去繼承RemoteService.stub這個內部類,實現get方法。在onBind方法中返回這個內部類的對象,系統會自動將這個對象封裝成IBinder對象,傳遞給他的調用者。其次需要在AndroidManifest.xml文件中配置MyService類,代碼如下:<!-- 註冊服務 -->  <service android:name=".MyService">    <intent-filter>    <!--  指定調用AIDL服務的ID  -->        <action android:name="net.blogjava.mobile.aidlservice.RemoteService" />     </intent-filter> </service>爲什麼要指定調用AIDL服務的ID,就是要告訴外界MyService這個類能夠被別的進程訪問,只要別的進程知道這個ID,正是有了這個ID,B工程才能找到A工程實現通信。說明:AIDL並不需要權限B工程:      首先我們要將A工程中生成的RemoteService.java文件拷貝到B工程中,在bindService方法中綁定aidl服務      綁定AIDL服務就是將RemoteService的ID作爲intent的action參數。      說明:如果我們單獨將RemoteService.aidl文件放在一個包裏,那個在我們將gen目錄下的該包拷貝到B工程中。如果我們將RemoteService.aidl文件和我們的其他類存放在一起,那麼我們在B工程中就要建立相應的包,以保證RmoteService.java文件的報名正確,我們不能修改RemoteService.java文件           bindService(new Inten("net.blogjava.mobile.aidlservice.RemoteService"), serviceConnection, Context.BIND_AUTO_CREATE);        ServiceConnection的onServiceConnected(ComponentName name, IBinder service)方法中的service參數就是A工程中MyService類中繼承了RemoteService.stub類的內部類的對象。


20.請解釋下Android程序運行時權限與文件系統權限的區別。

Android程序執行需要讀取到安全敏感項必需在androidmanifest.xml中聲明相關權限請求, 打電話,訪問網絡,獲取座標,讀寫sd卡,讀寫聯繫人等..安裝的時候會提示用戶…
 
文件系統的權限是linux權限. 比如說sharedpreference裏面的Context.Mode.private  Context.Mode.world_read_able   Context.Mode_world_writeable 
777自己 同組 其他
21.系統上安裝了多種瀏覽器,能否指定某瀏覽器訪問指定頁面

 Intent intent =newIntent();        
         intent.setAction("android.intent.action.VIEW");    
         Uri content_url =Uri.parse("http://www.163.com");   
         intent.setData(content_url);           
         intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");   
         startActivity(intent);
只要修改以intent.setClassName("com.android.browser","com.android.browser.BrowserActivity");
中相應的應用程序packagename 和要啓動的activity即可啓動其他瀏覽器來
uc瀏覽器":"com.uc.browser", "com.uc.browser.ActivityUpdate“
opera瀏覽器:"com.opera.mini.android", "com.opera.mini.android.Browser"
qq瀏覽器:"com.tencent.mtt", "com.tencent.mtt.MainActivity"
22.對android主線程的運用和理解。

主ui線程不能執行耗時的操作
23.對android虛擬機的理解,包括內存管理機制垃圾回收機制。dalvik和art區別


虛擬機很小,空間很小,談談移動設備的虛擬機的大小限制 16M ,
談談加載圖片的時候怎麼處理大圖片的,
outmemoryException
BitmapFactory.option 
垃圾回收,沒有引用的對象,在某個時刻會被系統gc掉 .

Dalvik和標準Java虛擬機(JVM)首要差別
Dalvik 基於寄存器,而 JVM 基於棧。
基於寄存器虛擬機對於編譯後變大的程序來說,在它們執行的時候,花費的時間更短。
Dalvik和Java運行環境的區別
1:Dalvik主要是完成對象生命週期管理,堆棧管理,線程管理,安全和異常管理,以及垃圾回收等等重要功能。
2:Dalvik負責進程隔離和線程管理,每一個Android應用在底層都會對應一個獨立的Dalvik虛擬機實例,其代碼在虛擬機的解釋下得以執行。
3:不同於Java虛擬機運行java字節碼,Dalvik虛擬機運行的是其專有的文件格式Dex
4:dex文件格式可以減少整體文件尺寸,提高I/o操作的類查找速度。
5:odex是爲了在運行過程中進一步提高性能,對dex文件的進一步優化。
6:所有的Android應用的線程都對應一個Linux線程,虛擬機因而可以更多的依賴操作系統線程調度和管理機制
7:有一個特殊的虛擬機進程Zygote,他是虛擬機實例的孵化器。它在系統啓動的時候就會產生,它會完成虛擬機的初始化,庫的加載,預製類庫和初始化的操作。如果系統需要一個新的虛擬機實例,它會迅速複製自身,以最快的數據提供給系統。對於一些只讀的系統庫,所有虛擬機實例都和Zygote共享一塊內存區域。
8:Dalvik是由Dan Bornstein編寫的,名字來源於他的祖先曾經居住過名叫Dalvík的小漁村,村子位於冰島Eyjafj&ouml;r&eth;ur

許多GC實現都是在對象開頭的地方留一小塊空間給GC標記用。Dalvik VM則不同,在進行GC的時候會單獨申請一塊空間,以位圖的形式來保存整個堆上的對象的標記,在GC結束後就釋放該空間。 
dalvik是執行的時候編譯+運行,安裝比較快,開啓應用比較慢,應用佔用空間小ART是安裝的時候就編譯好了,執行的時候直接就可以運行的,安裝慢,開啓應用快,佔用空間大用個比喻來說就是,騎自行車dalvik 是已經摺疊起來的自行車,每次騎都要先組裝自行車才能騎ART 是已經組裝好的自行車,每次騎直接上車就能走人效率高在開啓的時候,運行中的速度是差不多的
24.Framework工作方式及原理,Activity是如何生成一個view的,機制是什麼。

主要用到反射和代理的知識。主要用pull解析xml,然後通過反射得到屬性,在onDrew畫出來
 
反射 , 配置文件 
 可以講下activity的源碼,比如說 每個activity裏面都有window.callback和keyevent.callback,一些回調的接口或者函數吧. 框架把activity創建出來就會調用裏面的這些回調方法,會調用activity生命週期相關的方法.
 
 
Activity創建一個view是通過ondraw 畫出來的, 畫這個view之前呢,還會調用onmeasure方法來計算顯示的大小.
25.android本身的一些限制,比如apk包大小限制,讀取大文件時的時間限

這個問題問的有問題, apk包大小限制不好說,
極品飛車有100M 還是能裝到手機上,
世面google market 上大程序  主程序 很小 5~10M    下載sdcard
200~300M 
15分鐘之內 申請退款 
apk包,精簡包, 素材存放在服務器. 遊戲程序.
 
 
讀大文件的時間限制應該是main線程裏面的時間限制吧.5秒.
26.Android程序與Java程序的區別?

Android程序用android sdk開發,java程序用javasdk開發.
Android SDK引用了大部分的Java SDK,少數部分被Android SDK拋棄,比如說界面部分,java.awt  swing  package除了java.awt.font被引用外,其他都被拋棄,在Android平臺開發中不能使用。 android sdk 添加工具jar httpclient , pull  opengl
 
將Java 遊戲或者j2me程序移植到Android平臺的過程中,
Android  SDK 與Java SDK的區別是很需要注意的地方。
sampleDataAdpter("YY-MM-DD")
27.Android中Task任務棧的分配

首先我們來看下Task的定義,Google是這樣定義Task的:a task is what the user experiences as an "application." It's a group of related activities, arranged in a stack. A task is a stack of activities, not a class or an element in the manifest file. 這意思就是說Task實際上是一個Activity棧,通常用戶感受的一個Application就是一個Task。從這個定義來看,Task跟Service或者其他Components是沒有任何聯繫的,它只是針對Activity而言的。
Activity有不同的啓動模式, 可以影響到task的分配
Task,簡單的說,就是一組以棧的模式聚集在一起的Activity組件集合。它們有潛在的前後驅關聯,新加入的Activity組件,位於棧頂,並僅有在棧頂的Activity,纔會有機會與用戶進行交互。而當棧頂的Activity完成使命退出的時候,Task會將其退棧,並讓下一個將跑到棧頂的Activity來於用戶面對面,直至棧中再無更多Activity,Task結束。
事件 Task棧(粗體爲棧頂組件)
點開Email應用,進入收件箱(Activity A) A
選中一封郵件,點擊查看詳情(Activity B) AB
點擊回覆,開始寫新郵件(Activity C) ABC
寫了幾行字,點擊選擇聯繫人,進入選擇聯繫人界面(Activity D) ABCD
選擇好了聯繫人,繼續寫郵件 ABC
寫好郵件,發送完成,回到原始郵件 AB
點擊返回,回到收件箱 A
退出Email程序 null
 
如上表所示,是一個實例。從用戶從進入郵箱開始,到回覆完成,退出應用整個過程的Task棧變化。這是一個標準的棧模式,對於大部分的狀況,這樣的Task模型,足以應付,但是,涉及到實際的性能、開銷等問題,就會變得殘酷許多。
 
比如,啓動一個瀏覽器,在Android中是一個比較沉重的過程,它需要做很多初始化的工作,並且會有不小的內存開銷。但與此同時,用瀏覽器打開一些內容,又是一般應用都會有的一個需求。設想一下,如果同時有十個運行着的應用(就會對應着是多個Task),都需要啓動瀏覽器,這將是一個多麼殘酷的場面,十個Task棧都堆積着很雷同的瀏覽器Activity,
是多麼華麗的一種浪費啊。
於是你會有這樣一種設想,瀏覽器Activity,可不可以作爲一個單獨的Task而存在,不管是來自那個Task的請求,瀏覽器的Task,都不會歸並過去。這樣,雖然瀏覽器Activity本身需要維繫的狀態更多了,但整體的開銷將大大的減少,這種舍小家爲大家的行爲,還是很值得歌頌的
standard", "singleTop", "singleTask", "singleInstance"。
 
standard模式, 是默認的也是標準的Task模式,在沒有其他因素的影響下,使用此模式的Activity,會構造一個Activity的實例,加入到調用者的Task棧中去,對於使用頻度一般開銷一般什麼都一般的Activity而言,standard模式無疑是最合適的,因爲它邏輯簡單條理清晰,所以是默認的選擇。
 
而singleTop模式,基本上於standard一致,僅在請求的Activity正好位於棧頂時,有所區別。此時,配置成singleTop的Activity,不再會構造新的實例加入到Task棧中,而是將新來的Intent發送到棧頂Activity中,棧頂的Activity可以通過重載onNewIntent來處理新的Intent(當然,也可以無視...)。這個模式,降低了位於棧頂時的一些重複開銷,更避免了一些奇異的行爲(想象一下,如果在棧頂連續幾個都是同樣的Activity,再一級級退出的時候,這是怎麼樣的用戶體驗...),很適合一些會有更新的列表Activity展示。一個活生生的實例是,在Android默認提供的應用中,瀏覽器(Browser)的書籤Activity(BrowserBookmarkPage),就用的是singleTop。
 
singleTask,和singleInstance,則都採取的另闢Task的蹊徑。
標誌爲singleTask的Activity,最多僅有一個實例存在,並且,位於以它爲根的Task中。所有對該Activity的請求,都會跳到該Activity的Task中展開進行。singleTask,很象概念中的單件模式,所有的修改都是基於一個實例,這通常用在構造成本很大,但切換成本較小的Activity中。最典型的例子,還是瀏覽器應用的主Activity(名爲Browser...),它是展示當前tab,當前頁面內容的窗口。它的構造成本大,但頁面的切換還是較快的,於singleTask相配,還是挺天作之合的。
 
singleInstance顯得更爲極端一些。在大部分時候singleInstance與singleTask完全一致,唯一的不同在於,singleInstance的Activity,是它所在棧中僅有的一個Activity,如果涉及到的其他Activity,都移交到其他Task中進行。這使得singleInstance的Activity,像一座孤島,徹底的黑盒,它不關注請求來自何方,也不計較後續由誰執行。在Android默認的各個應用中,很少有這樣的Activity,在我個人的工程實踐中,曾嘗試在有道詞典的快速取詞Activity中採用過,
是因爲我覺得快速取詞入口足夠方便(從notification中點選進入),並且會在各個場合使用,應該做得完全獨立。
 
大的apk 拆成 很多小的apk 
  ●Activity的android:affinity屬性
1.配置後 當啓動這個activity時就先去找有沒有activity的親和力屬性相同 有就加入這個
       activity所在的任務中沒有就新開任務
2.affinity起作用需要的條件而者具備一個:
              1.intent包含FLAG_ACTIVITY_NEW_TASK標記
              2.activity元素啓用了allowTaskReparenting屬性.
28.在Android中,怎麼節省內存的使用,怎麼主動回收內存?

  回收已經使用的資源, 
  合理的使用緩存
合理設置變量的作用範圍… 
application 對象
//未來的某一段時間執行 
System.gc();
29.不同工程中的方法是否可以相互調用?

可以,列舉aidl訪問遠程服務的例子.
30.dvm的進程和Linux的進程, 應用程序的進程是否爲同一個概念  是同一概念

Dvm的進程是dalivk虛擬機進程,每個android程序都運行在自己的進程裏面,
每個android程序系統都會給他分配一個單獨的liunx  uid(user id),
每個dvm都是linux裏面的一個進程.所以說這兩個進程是一個進程.
31.在Android中是如何實現判斷區分電話的狀態,去電,來電、未接來電?

Day8 showAddressService.java
TelephoneyManger.listen();
32.談談Android的優點和不足之處。

1、開放性,開源ophone  阿里雲( 完全兼容android)
2、掙脫運營商束縛 
3、豐富的硬件選擇 mtk android 
4、不受任何限制的開發商
5、無縫結合的Google應用
 
缺點也有5處:
1、安全問題、隱私問題 
2、賣手機的不是最大運營商
3、運營商對Android手機仍然有影響
4、山寨化嚴重
5、過分依賴開發商,缺乏標準配置
33.Android系統中GC什麼情況下會出現內存泄露呢?

視頻編解碼/內存泄露
檢測內存泄露   工具 
 
導致內存泄漏主要的原因是,先前申請了內存空間而忘記了釋放。如果程序中存在對無用對象的引用,那麼這些對象就會駐留內存,消耗內存,因爲無法讓垃圾回收器GC驗證這些對象是否不再需要。如果存在對象的引用,這個對象就被定義爲"有效的活動",同時不會被釋放。要確定對象所佔內存將被回收,我們就要務必確認該對象不再會被使用。典型的做法就是把對象數據成員設爲null或者從集合中移除該對象。但當局部變量不需要時,不需明顯的設爲null,因爲一個方法執行完畢時,這些引用會自動被清理。
Java帶垃圾回收的機制,爲什麼還會內存泄露呢?
 
Vector v = new Vector(10);    
 for (int i = 1; i < 100; i++)      {     
 Object o = new Object();       
v.add(o);       
o = null;     
}//此時,所有的Object對象都沒有被釋放,因爲變量v引用這些對象。 
 
Java 內存泄露的根本原因就是 保存了不可能再被訪問的變量類型的引用
34.Android UI中的View如何刷新。

在主線程中  拿到view調用Invalide()方法,查看畫畫板裏面更新imageview的方法
 
在子線程裏面可以通過postInvalide()方法;
35. 簡單描述下Android 數字簽名。

Android 數字簽名
       在Android系統中,所有安裝到系統的應用程序都必有一個數字證書,此數字證書用於標識應用程序的作者和在應用程序之間建立信任關係 
Android系統要求每一個安裝進系統的應用程序都是經過數字證書籤名的,數字證書的私鑰則保存在程序開發者的手中。Android將數字證書用來標識應用程序的作者和在應用程序之間建立信任關係,不是用來決定最終用戶可以安裝哪些應用程序。
這個數字證書並不需要權威的數字證書籤名機構認證(CA),它只是用來讓應用程序包自我認證的。
同一個開發者的多個程序儘可能使用同一個數字證書,這可以帶來以下好處。
(1)有利於程序升級,當新版程序和舊版程序的數字證書相同時,Android系統纔會認爲這兩個程序是同一個程序的不同版本。如果新版程序和舊版程序的數字證書不相同,則Android系統認爲他們是不同的程序,併產生衝突,會要求新程序更改包名。
 
(2)有利於程序的模塊化設計和開發。Android系統允許擁有同一個數字簽名的程序運行在一個進程中,Android程序會將他們視爲同一個程序。所以開發者可以將自己的程序分模塊開發,而用戶只需要在需要的時候下載適當的模塊。
在簽名時,需要考慮數字證書的有效期:
(1)數字證書的有效期要包含程序的預計生命週期,一旦數字證書失效,持有改數字證書的程序將不能正常升級。
(2)如果多個程序使用同一個數字證書,則該數字證書的有效期要包含所有程序的預計生命週期。
(3)Android Market強制要求所有應用程序數字證書的有效期要持續到2033年10月22日以後。 
Android數字證書包含以下幾個要點:
 (1)所有的應用程序都必須有數字證書,Android系統不會安裝一個沒有數字證書的應用程序
 (2)Android程序包使用的數字證書可以是自簽名的,不需要一個權威的數字證書機構簽名認證
 (3)如果要正式發佈一個Android ,必須使用一個合適的私鑰生成的數字證書來給程序簽名,而不能使用adt插件或者ant工具生成的調試證書來發布。
 (4)數字證書都是有有效期的,Android只是在應用程序安裝的時候纔會檢查證書的有效期。如果程序已經安裝在系統中,即使證書過期也不會影響程序的正常功能。
36.android中的動畫有哪幾類,它們的特點和區別是什麼?

兩種,一種是Tween動畫、還有一種是Frame動畫。
Tween動畫,這種實現方式可以使視圖組件移動、放大、縮小以及產生透明度的變化;
可以通過佈局文件,可以通過代碼
  1、  控制View的動畫 
a)     alpha(AlphaAnimation)
漸變透明     
b)     scale(ScaleAnimation)
漸變尺寸伸縮 
c)     translate(TranslateAnimation)
畫面轉換、位置移動   
d)     rotate(RotateAnimation)
畫面轉移,旋轉動畫   
 
2、    控制一個Layout裏面子View的動畫效果
a)     layoutAnimation(LayoutAnimationController)
b)     gridAnimation(GridLayoutAnimationController)
另一種Frame動畫,傳統的動畫方法,通過順序的播放排列好的圖片來實現,類似電影。
37.說說mvc模式的原理,它在android中的運用

Android中沒有mvc,只有在j2ee中有,是用於邏輯也界面分離的
MVC英文即Model-View-Controller,即把一個應用的輸入、處理、輸出流程按照Model、View、Controller的方式進行分離,這樣一個應用被分成三個層——模型層、視圖層、控制層。
 
Android中界面部分也採用了當前比較流行的MVC框架,在Android中M就是應用程序中二進制的數據,V就是用戶的界面。Android的界面直接採用XML文件保存的,界面開發變的很方便。在Android中C也是很簡單的,一個Activity可以有多個界面,只需要將視圖的ID傳遞到setContentView(),就指定了以哪個視圖模型顯示數據。
 
 
在Android SDK中的數據綁定,也都是採用了與MVC框架類似的方法來顯示數據。在控制層上將數據按照視圖模型的要求(也就是Android SDK中的Adapter)封裝就可以直接在視圖模型上顯示了,從而實現了數據綁定。比如顯示Cursor中所有數據的ListActivity,其視圖層就是一個ListView,將數據封裝爲ListAdapter,並傳遞給ListView,數據就在ListView中顯示。
38.通過點擊一個網頁上的url 就可以完成程序的自動安裝,描述下原理

Day11 AddJavascriptInterface
new Object{
       callphone();
       installapk();
}

將js與java相互調用再來一個例子,解決相互調用之間的關係。

首先說明一重要代碼的情況:

android中的關鍵代碼
webview.getSettings().setJavaScriptEnabled(true);
webview.addJavascriptInterface(object,"name"); //把Name="name"的對象添加到object中。object如果是this,就是window.name
webview.loadUrl("file:///android_asset/index.html"); //注意這個資源的位置放在assets文件夾下。


js或html中調用android中方法代碼:
js中使用 window.name.java中的方法();
android中調用js的function方法:
         Callfunction(){
         webview.loadUrl("javascript: function ()");
需要注意的地方,很多數據類型js中不認識,最好是在android那邊封裝好,提供必要的方法接口。比如傳到js中的list,在js中是沒辦法去得到裏面的元素的。
addJavascriptInterface:addJavascriptInterface方法中要綁定的Java對象及方法要運行在另外的線程中,不能運行在構造他的線程中,也就是說不能運行在當前的activity線程中,就是把這個方法綁定到頁面中,js也可以調用。
文檔中的解釋:
Use this function to bind an object to Javascript so that the methods can be accessed from Javascript.
The Java object that is bound runs in another thread and not in the thread that it was constructed in.

下面給出具體的測試代碼:
1、Activity 代碼

[java] view plaincopyprint?
public class TestWebView extends Activity {  
    private WebView mWebView;  
    private List<String> list;  
    private int mkeyCode;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        mWebView = (WebView) findViewById(R.id.htmlview);  
  
        initData();  
          
        WebSettings webSettings = mWebView.getSettings();  
          
        // 是否允許在webview中執行javascript  
        webSettings.setJavaScriptEnabled(true);  
          
        mWebView.addJavascriptInterface(this"javatojs");  
  
        //加載網頁  
        mWebView.loadUrl("file:///android_asset/index.html");  
    }  
      
    @Override  
    public boolean onKeyUp(int keyCode, KeyEvent event) {  
        mkeyCode = keyCode;  
        Log.i("AA","keyCode="+keyCode);  
        mWebView.loadUrl("javascript: OnKeyUp()");  
        return super.onKeyUp(keyCode, event);  
    }  
      
    public int getKeyCode(){  
        return mkeyCode;  
    }  
      
    void initData() {  
        list = new ArrayList<String>();  
        for (int i = 0; i < 5; i++) {  
            list.add("我是List中的第" + (i + 1) + "行");  
        }  
    }  
      
    /** 
     * 該方法將在js腳本中,通過window.javatojs.....()進行調用 
     *  
     * @return 
     */  
    public Object getObject(int index) {  
        Log.i("A","getObject");       
        return list.get(index);  
    }  
  
    public int getSize() {  
        Log.i("A","getSize");  
        return list.size();  
    }  
  
    public void Callfunction() {  
        Log.i("A","Callfunction");  
        mWebView.loadUrl("javascript: GetList()");  
    }  
      
    public void printStr(String str){  
        Log.i("A","GetList:" + str);  
    }     
}  
    2、js 代碼 index.html


[javascript] view plaincopyprint?
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">  
<html>  
  
<head>  
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  
  
<title>demotitle></title>  
  
<script language="javascript">   
  
function GetList(){  
    var i=window.javatojs.getSize();  
  
    for(var n=0;n<i;n++){  
        var jsdata= window.javatojs.getObject(n);//拿到activity裏面的屬性javadata  
        window.javatojs.printStr("test");  
    }  
}  
  
function OnKeyUp() {  
    var keycode = window.javatojs.getKeyCode();  
    window.javatojs.printStr(keycode);  
}  
  
</script>  
</head>  
  
 <body style="background-color:#000;">  
  <table width="400" align="center"><tr><td>  
  <p> </p>  
  <p>  
<table width="400" align="center">  
<div id="output" >test</div>  
    <input type="submit" value="buttons"  
    οnclick="document.getElementById('output').innerHTML=window.javatojs.Callfunction()"/>  
  <br>  
  
  </p>  
  </td></tr></table>  
</body>  
  
</html>  
以上代碼主要測試js與java相互調用,而由於按鍵這種系統事件被webview截獲掉,有如下兩種方式進行處理

1、把方向鍵的流程改成:先傳給webcore,假如沒處理,再在webview裏面處理,這個需要修改webview.java代碼
2、直接應用搞定,java捕獲按鍵,然後調js函數,上面代碼就是使用這種方法。

測試結果如下: 點擊buttons按鈕:
I/A       ( 4990): Callfunction
I/A       ( 4990): getSize
I/A       ( 4990): getObject
I/A       ( 4990): GetList:test
I/A       ( 4990): getObject
I/A       ( 4990): GetList:test
I/A       ( 4990): getObject
I/A       ( 4990): GetList:test
I/A       ( 4990): getObject
I/A       ( 4990): GetList:test
I/A       ( 4990): getObject
I/A       ( 4990): GetList:test

you press KEY_RIGHT
I/AA      ( 4990): keyCode=22
I/A       ( 4990): GetList:22

you press KEY_UP
I/AA      ( 4990): keyCode=19
I/A       ( 4990): GetList:19

you press KEY_DOWN
I/AA      ( 4990): keyCode=20
I/A       ( 4990): GetList:20

you press KEY_LEFT
I/AA      ( 4990): keyCode=21
I/A       ( 4990): GetList:21

這裏爲何使用這種方式,是因爲對於上下左右及確定這種功能鍵被webview截取掉了,無法傳遞到webcore中,而只能重載OnKeyDown/OnKeyUp方法,再由js調用java方法來獲取得。
對於數字鍵的處理可以直接在js中進行處理:
logcat中會有明顯的打印,對於這些鍵沒有截掉,所以可以直接獲取得到:
D/webcore ( 4990): proc key: code=12
D/webcore ( 4990): proc key: nativeKey return false
D/webcore ( 4990): proc key: nativeKey return true
js代碼可以如此編寫:

[javascript] view plaincopyprint?
 <script language="JavaScript">  
  
document.onkeypress = grab_keypress_event;  
document.onirkeypress = grab_irkeypress_event;  
document.onsystemevent = grab_system_event;  
document.onkeydown = grab_keydown_event;  
document.onkeyup = grab_keyup_event;  
  
function init()  
{  
    document.getElementById("txt_keypress").innerHTML = "";  
    document.getElementById("txt_irkey").innerHTML= "";  
    document.getElementById("txt_systemevent").innerHTML="";  
    document.getElementById("txt_keydown").innerHTML="";  
    document.getElementById("txt_keyup").innerHTML="";  
}  
  
function grab_keypress_event(event)  
{  
    var keycode = event.keyCode;  
    document.getElementById("txt_keypress").innerHTML=keycode;  
}  
  
function grab_irkeypress_event(event)  
{  
    var keycode = event.keyCode;  
    document.getElementById("txt_irkey").innerHTML=keycode;  
}  
  
function grab_system_event(event)  
{  
    var keycode = event.which;  
    document.getElementById("txt_systemevent").innerHTML=keycode;  
}  
  
function grab_keydown_event(event)  
{  
    var keycode = event.keyCode;  
    var type = event.type;  
    var mod = event.modifiers;  
    document.getElementById("txt_keydown").innerHTML=keycode;  
}  
  
function grab_keyup_event(event)  
{  
    var keycode = event.keyCode;  
    document.getElementById("txt_keyup").innerHTML=keycode;  
}  
 </script>  
39.Service和Activity在同一個線程嗎

默認情況同一線程 main主線程 ui線程
40.ViewStub的應用

  在開發應用程序的時候,經常會遇到這樣的情況,會在運行時動態根據條件來決定顯示哪個View或某個佈局。那麼最通常的想法就是把可能用到的View都寫在上面,先把它們的可見性都設爲View.GONE,然後在代碼中動態的更改它的可見性。這樣的做法的優點是邏輯簡單而且控制起來比較靈活。但是它的缺點就是,耗費資源。雖然把View的初始可見View.GONE但是在Inflate佈局的時候View仍然會被Inflate,也就是說仍然會創建對象,會被實例化,會被設置屬性。也就是說,會耗費內存等資源。
      推薦的做法是使用android.view.ViewStub,ViewStub是一個輕量級的View,它一個看不見的,不佔佈局位置,佔用資源非常小的控件。可以爲ViewStub指定一個佈局,在Inflate佈局的時候,只有ViewStub會被初始化,然後當ViewStub被設置爲可見的時候,或是調用了ViewStub.inflate()的時候,ViewStub所向的佈局就會被Inflate和實例化,然後ViewStub的佈局屬性都會傳給它所指向的佈局。這樣,就可以使用ViewStub來方便的在運行時,要還是不要顯示某個佈局。
      但ViewStub也不是萬能的,下面總結下ViewStub能做的事兒和什麼時候該用ViewStub,什麼時候該用可見性的控制。
     首先來說說ViewStub的一些特點:
         1. ViewStub只能Inflate一次,之後ViewStub對象會被置爲空。按句話說,某個被ViewStub指定的佈局被Inflate後,就不會夠再通過ViewStub來控制它了。
         2. ViewStub只能用來Inflate一個佈局文件,而不是某個具體的View,當然也可以把View寫在某個佈局文件中。
     基於以上的特點,那麼可以考慮使用ViewStub的情況有:
         1. 在程序的運行期間,某個佈局在Inflate後,就不會有變化,除非重新啓動。
              因爲ViewStub只能Inflate一次,之後會被置空,所以無法指望後面接着使用ViewStub來控制佈局。所以當需要在運行時不止一次的顯示和隱藏某個佈局,那麼ViewStub是做不到的。這時就只能使用View的可見性來控制了。
         2. 想要控制顯示與隱藏的是一個佈局文件,而非某個View。
              因爲設置給ViewStub的只能是某個佈局文件的Id,所以無法讓它來控制某個View。
     所以,如果想要控制某個View(如Button或TextView)的顯示與隱藏,或者想要在運行時不斷的顯示與隱藏某個佈局或View,只能使用View的可見性來控制。
下面來看一個實例
在這個例子中,要顯示二種不同的佈局,一個是用TextView顯示一段文字,另一個則是用ImageView顯示一個圖片。這二個是在onCreate()時決定是顯示哪一個,這裏就是應用ViewStub的最佳地點。
先來看看佈局,一個是主佈局,裏面只定義二個ViewStub,一個用來控制TextView一個用來控制ImageView,另外就是一個是爲顯示文字的做的TextView佈局,一個是爲ImageView而做的佈局:

[html] view plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  android:orientation="vertical"  
  android:layout_width="fill_parent"  
  android:layout_height="fill_parent"  
  android:gravity="center_horizontal"]] >   
  <ViewStub   
    android:id="@+id/viewstub_demo_text"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:layout_marginLeft="5dip"  
    android:layout_marginRight="5dip"  
    android:layout_marginTop="10dip"  
    android:layout="@layout/viewstub_demo_text_layout"/>  
  <ViewStub   
    android:id="@+id/viewstub_demo_image"  
    android:layout_width="wrap_content"  
    android:layout_height="wrap_content"  
    android:layout_marginLeft="5dip"  
    android:layout_marginRight="5dip"  
    android:layout="@layout/viewstub_demo_image_layout"/>  
</LinearLayout]] >   
爲TextView的佈局:


[html] view plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  android:orientation="vertical"  
  android:layout_width="wrap_content"  
  android:layout_height="wrap_content"]] >   
    <TextView  
        android:id="@+id/viewstub_demo_textview"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:background="#aa664411"  
        android:textSize="16sp"/>  
</LinearLayout]] >   
爲ImageView的佈局:


[html] view plaincopyprint?
<?xml version="1.0" encoding="utf-8"?>  
<LinearLayout  
  xmlns:android="http://schemas.android.com/apk/res/android"  
  android:orientation="vertical"  
  android:layout_width="wrap_content"  
  android:layout_height="wrap_content"]] >   
    <ImageView  
        android:id="@+id/viewstub_demo_imageview"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"/>  
</LinearLayout]] >   
下面來看代碼,決定來顯示哪一個,只需要找到相應的ViewStub然後調用其infalte()就可以獲得相應想要的佈局:


[java] view plaincopyprint?
package com.effective;  
  
import android.app.Activity;  
import android.os.Bundle;  
import android.view.ViewStub;  
import android.widget.ImageView;  
import android.widget.TextView;  
  
public class ViewStubDemoActivity extends Activity {  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.viewstub_demo_activity);  
        if ((((int) (Math.random() * 100)) & 0x01) == 0) {  
            // to show text  
            // all you have to do is inflate the ViewStub for textview  
            ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_text);  
            stub.inflate();  
            TextView text = (TextView) findViewById(R.id.viewstub_demo_textview);  
            text.setText("The tree of liberty must be refreshed from time to time" +  
                    " with the blood of patroits and tyrants! Freedom is nothing but " +  
                    "a chance to be better!");  
        } else {  
            // to show image  
            // all you have to do is inflate the ViewStub for imageview  
            ViewStub stub = (ViewStub) findViewById(R.id.viewstub_demo_image);  
            stub.inflate();  
            ImageView image = (ImageView) findViewById(R.id.viewstub_demo_imageview);  
            image.setImageResource(R.drawable.happy_running_dog);  
        }  
    }  
}  
運行結果:


使用的時候的注意事項:
1. 某些佈局屬性要加在ViewStub而不是實際的佈局上面,纔會起作用,比如上面用的android:layout_margin*系列屬性,如果加在TextView上面,則不會起作用,需要放在它的ViewStub上面纔會起作用。而ViewStub的屬性在inflate()後會都傳給相應的佈局。
41.android開發中怎麼去調試bug

邏輯錯誤 
1.斷點 debug 
2. logcat ,
界面佈局,顯示 hierarchyviewer.bat
42.書寫出android工程的目錄結構以及相關作用

下面是HelloAndroid項目在eclipse中的目錄層次結構:

由上圖可以看出項目的根目錄下共有九個文件(夾),下面就這九個文件(夾)進行詳解:
1.1src文件夾和assets文件夾:
每個Android程序都包含資源目錄(src)和資產目錄(assets),資源和資產聽起來感覺沒有多大差別,但在存儲外部內容時用資源(src)比較多,其中它們的區別在於存放在資源(src)下的內容可以通過應用程序的R類進行訪問,而存放在資產(assets)下的內容會保持原始文件的格式,如果需要訪問,則必須使用AssetManager以字節流的方式來讀取,用起來非常的不方便。爲了方便使用,通常文件和數據都會保存在資源(src)目錄下
1.2res(Resource)目錄:資源目錄
可以存放一些圖標,界面文件和應用中用到的文字信息,下圖爲res目錄截圖:
1.2.1 drawable-*dpi文件夾:將圖標按分辨率的高低放入不同的目錄,其中draeable-hdpi用來存放高分辨率的圖標,drawable-mdpi用來存放中等分辨率的圖標,drawable-ldpi用來存放低分辨率的圖標
1.2.2 values文件夾:用來存放文字的信息
(1)strings.xml:用來定義字符串和數值
<?xml version="1.0"encoding="utf-8"?>
<resources>
 
    <string name="hello">Hello World, Hello 3G</string>
    <string name="app_name">Android1.1</string>
        <string name="test">哥想你了</string>
        <string name="startButton">按鈕1</string>
        <string name="start">按鈕1</string> 
</resources>
每個string標籤生命了一個字符串,name屬性指定它的引用值
(2)爲什麼要把這些出現的文字單獨放在strings.xml文件中?
答案:一是爲了國際化,如果需要將文件中的文字換成別的國家的語言,就可以只需要替換掉一個strings.xml文件就可以了
二是爲了減少應用的體積,例如,我們要在應用中使用“哥想你了”這句話1000次,如果我們沒有將“哥想你了”定義在strings.xml文件中,而是直接在應用中使用時寫上這幾個字,那麼我們就會在應用中寫4000個字。4000個字和4個字佔用的內存可是有很大差距的啊,況且手機的內存本來就小,所以應該是能省就省
(3)另外還有arrays.xml,color.xml等定義數組,顏色的,都最好用單獨的一個xml文檔
1.2.3 layout文件:用來存放界面信息
本例中的佈局文件是自動生成的“main.xml”
<?xml version="1.0"encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
 
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/test"/>
   
</LinearLayout>
<LinearLayout>元素:線性佈局的意思,在該元素下的所有子元素都會根據他的”orientation”屬性來決定是按行還是按列或者按逐個顯示進行佈局的
<TextView>元素:是一種顯示控件,他的”text”屬性指定了在這個元素上顯示的內容
1.3 gen目錄:gen目錄下只有一個自動生成的“R.java”文件
/*AUTO-GENERATED FILE.  DO NOT MODIFY.
 *
 * This class was automatically generated bythe
 * aapt tool from the resource data itfound.  It
 * should not be modified by hand.
 */
 
packagecn.csdn.android.demo;
 
public final class R {
    public static final class attr {
    }
    public static final class drawable {
        public static final int ic_launcher=0x7f020000;
    }
    public static final class id {
        public static final int button1=0x7f050000;
        public static final int radioButton1=0x7f050001;
        public static final int toggleButton1=0x7f050002;
    }
    public static final class layout {
        public static final int main=0x7f030000;
    }
    public static final class string {
        public static final int app_name=0x7f040001;
        public static final int hello=0x7f040000;
        public static final int start=0x7f040004;
        public static final int startButton=0x7f040003;
        public static final int test=0x7f040002;
    }
}
 
R.java文件:默認有attr,drawable,layout,string這四個靜態內部類,每個靜態內部類對應一中資源,如layout靜態內部類對應layout中的界面文件,string靜態內部類對應string內部的string標籤。如果在layout中在增加一個界面文件或者在string內增加一個string標籤,R.java會自動在其對應的內部類增加所增加的內容。
R.java除了自動標識資源的索引功能外,還有另一個功能,就是當res文件中的某個資源在應用中沒有被用到,在這個應用被編譯時,系統不會把對應的資源編譯到應用中的APR包中。
1.4 AndroidManifest.xml 功能清單文件
每個應用程序都會有一個AndroidManifest在它的根目錄裏面。這個清單爲Android系統提供了這個應用的基本信息,系統在運行之前必須知道這些信息,另外,如果我們使用系統自帶的服務,如撥號服務,應用安裝服務等,都必須在AndroidManifest.xml文件中聲明權限
AndroidManifest.xml的功能:
命名應用程序的Java應用包,這個包名用來唯一標識應用程序;
描述應用程序的組件,對實現每個組件和公佈其功能的類進行命名,這些聲明使得Android系統瞭解這些組件以及它們在什麼條件下可以被啓動
決定哪個組件運行在哪個進程裏面
聲明應用程序必須具備的權限,用以訪問受保護的API,以及和其他進程的交互
聲明應用程序其他的必備權限,用以組件之間的交互
列舉application所需要鏈接的庫
以HelloAndroid項目的功能清單爲例子進行講解:
<?xml version="1.0"encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.csdn.android.demo"
    android:versionCode="1"
    android:versionName="1.0">
 
    <uses-sdk android:minSdkVersion="8"/>
 
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:label="@string/app_name"
            android:name=".HelloActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
1.4.1 <manifest>元素
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="cn.csdn.android.demo"
    android:versionCode="1"
    android:versionName="1.0">
<manifest>元素是AndroidManifest.xml的根元素,”xmlns:android”是指該文件的命名空間,“package”屬性是Android應用所在的包,“android:versionCode”指定應用的版本號,如果應用不斷升級,則需要修改這個值,”android:versionName”是版本的名稱,這個可以根據自己的喜愛改變
1.4.2 <application> 元素
<application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name">
        <activity
            android:label="@string/app_name"
            android:name=".HelloActivity">
            <intent-filter >
                <action android:name="android.intent.action.MAIN"/>
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
<application>元素是一個很重要的元素,開發組件都會在此下定義
<application>元素的”icon”屬性是用來設定應用的圖標,其中“@drawable/ic_launcher”的意思是:在R.java文件中的drawable靜態內部類下的icon,如下圖所示
<application>元素的“label”屬性用來設定應用的名稱,其中“@string/app_name”和上述的一樣,也是R.java文件中的string靜態內部類下的app_name
1.4.3 <activity>元素
<activity
            android:label="@string/app_name"
           android:name=".HelloActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
<activity>元素的作用是註冊一個activity信息,當我們在創建“HelloAndroid”這個項目時,指定了“Created Activity”屬性爲“HelloActivity”,然後ADT在生成項目時幫我們自動創建了一個Activity,就是“HelloActivity.java”;
<activity>元素的“name“屬性指定的是Activity的類名,其中“.HelloActivity”中的“.”指的是<manifest>元素中的“package”屬性中指定的當前包,所以“.HelloActivity”就相當於“cn.csdn.android.demo.HelloActivity.java”,如果Activity在應用的包中可以不寫“.”,但是爲了避免出錯,還是寫上這個點把
1.4.4<intent-filter>元素
<intent-filter >
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<intent-filter>如果直接翻譯的話是“意圖過濾器”,組件通過<intent-filter>告訴它們所具備的功能,就是能響應意圖類型,在intent中設置action, data, categroy之後在對應的intentfilter中設置相同的屬性即可通過過濾被activity調用
1.5<project.properties>應用要求運行的最低Android版本
1.6<android 2.2>  存放Android自身的jar包 
43.ddms 和traceview的區別.

 daivilk debug manager system
1.在應用的主activity的onCreate方法中加入Debug.startMethodTracing("要生成的traceview文件的名字");
2.同樣在主activity的onStop方法中加入Debug.stopMethodTracing();
3.同時要在AndroidManifest.xml文件中配置權限
   <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
3.重新編譯,安裝,啓動服務,測試完成取對應的traceview文件(adb pull /sdcard/xxxx.trace)。
4.直接在命令行輸入traceview xxxxtrace,彈出traceview窗口,分析對應的應用即可。
 
traceview 分析程序執行時間和效率
 
KPI : key performance information : 關鍵性能指標:
 splash界面不能超過5秒
 從splash 界面加載mainactivity 不能超過0.7秒 
44.談談NDK
NDK全稱:Native Development Kit。
1、NDK是一系列工具的集合。
* NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一起打包成apk。這些工具對開發者的幫助是巨大的。[1] 
* NDK集成了交叉編譯器,並提供了相應的mk文件隔離平臺、CPU、API等差異,開發人員只需要簡單修改mk文件(指出“哪些文件需要編譯”、“編譯特性要求”等),就可以創建出so。
* NDK可以自動地將so和Java應用一起打包,極大地減輕了開發人員的打包工作。
2、NDK提供了一份穩定、功能有限的API頭文件聲明。
Google明確聲明該API是穩定的,在後續所有版本中都穩定支持當前發佈的API。從該版本的NDK中看出,這些API支持的功能非常有限,包含有:C標準庫(libc)、標準數學庫(libm)、壓縮庫(libz)、Log庫(liblog)。 

45.請介紹下Android的數據存儲方式。

一.SharedPreferences方式
二sdcard
三內部存儲
四SqliteDatabase
  五. 網絡存儲方式
46.談談推送,優缺點以及實現原理

本文主旨在於,對目前Android平臺上最主流的幾種消息推送方案進行分析和對比,比較客觀地反映出這些推送方案的優缺點,幫助大家選擇最合適的實施方案。

方案1、使用GCM服務(Google Cloud Messaging)
簡介:Google推出的雲消息服務,即第二代的C2DM。
優點:Google提供的服務、原生、簡單,無需實現和部署服務端。
缺點:Android版本限制(必須大於2.2版本),該服務在國內不夠穩定、需要用戶綁定Google帳號,受限於Google。

方案2、使用XMPP協議(Openfire + Spark + Smack)
簡介:基於XML協議的通訊協議,前身是Jabber,目前已由IETF國際標準化組織完成了標準化工作。
優點:協議成熟、強大、可擴展性強、目前主要應用於許多聊天系統中,且已有開源的Java版的開發實例androidpn。
缺點:協議較複雜、冗餘(基於XML)、費流量、費電,部署硬件成本高。

方案3、使用MQTT協議(更多信息見:http://mqtt.org/
簡介:輕量級的、基於代理的“發佈/訂閱”模式的消息傳輸協議。
優點:協議簡潔、小巧、可擴展性強、省流量、省電,目前已經應用到企業領域(參考:http://mqtt.org/software),且已有C++版的服務端組件rsmb。
缺點:不夠成熟、實現較複雜、服務端組件rsmb不開源,部署硬件成本較高。

方案4、使用HTTP輪循方式
簡介:定時向HTTP服務端接口(Web Service API)獲取最新消息。
優點:實現簡單、可控性強,部署硬件成本低。
缺點:實時性差。

對各個方案的優缺點的研究和對比,推薦使用MQTT協議的方案進行實現,主要原因是:MQTT最快速,也最省流量(固定頭長度僅爲2字節),且極易擴展,適合二次開發。接下來,我們就來分析使用MQTT方案進行Android消息的原理和方法,並架設自己的推送服務。
1、推送原理分析



實際上,其他推送系統(包括GCM、XMPP方案)的原理都與此類似。

2、推送服務端準備

a> 下載&解壓rsmb安裝包(下載地址:http://www.alphaworks.ibm.com/tech/rsmb
b> 進入對應的目錄,比如32位的Linux系統則應該進入linux_ia32目錄。
c> 編輯配置文件broker_1883.cfg,配置如下:

[html] view plaincopy
port 1883  
max_inflight_messages 10  
max_queued_messages 1000  
d> 運行./broker broker_1883.cfg,顯示如下:
20120823 110454.039 CWNAN9999I Really Small Message Broker
20120823 110454.039 CWNAN9997I Licensed Materials - Property of IBM
20120823 110454.039 CWNAN9996I Copyright IBM Corp. 2007, 2010 All Rights Reserved
20120823 110454.039 CWNAN9995I US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
20120823 110454.039 CWNAN0049I Configuration file name is broker_1883.cfg
20120823 110454.040 CWNAN0053I Version 1.2.0, Aug 18 2010 17:03:35
20120823 110454.040 CWNAN0054I Features included: bridge
20120823 110454.040 CWNAN9993I Author: Ian Craggs ([email protected])
20120823 110454.040 CWNAN0014I MQTT protocol starting, listening on port 1883
... ...
這樣,推送服務的服務端就已經準備好了,監聽1883端口。

3、推送客戶端準備

a> 下載&解壓AndroidPushNotificationsDemo項目(下載地址:https://github.com/tokudu/AndroidPushNotificationsDemo
b> 將該項目導入Eclipse中(File -> Export -> Existing Projects into Workspace)
c> 修改PushService.java中的MQTT_HOST常量爲推送服務端的IP地址。
d> 啓動Android模擬器,並安裝該項目。

注意:在新版本的Android SDK中可能會遇到以下錯誤。
... ...
08-23 02:28:44.184: W/dalvikvm(282): VFY: unable to find class referenced in signature (Lcom/ibm/mqtt/MqttPersistence;)
08-23 02:28:44.194: I/dalvikvm(282): Failed resolving Lcom/tokudu/demo/PushService$MQTTConnection; interface 35 'Lcom/ibm/mqtt/MqttSimpleCallback;'
08-23 02:28:44.194: W/dalvikvm(282): Link of class 'Lcom/tokudu/demo/PushService$MQTTConnection;' failed
08-23 02:28:44.194: E/dalvikvm(282): Could not find class 'com.tokudu.demo.PushService$MQTTConnection', referenced from method com.tokudu.demo.PushService.connect
08-23 02:28:44.194: W/dalvikvm(282): VFY: unable to resolve new-instance 42 (Lcom/tokudu/demo/PushService$MQTTConnection;) in Lcom/tokudu/demo/PushService;
... ...
08-23 02:28:44.404: E/AndroidRuntime(282): java.lang.VerifyError: com.tokudu.demo.PushService
08-23 02:28:44.404: E/AndroidRuntime(282):     at com.tokudu.demo.PushActivity$1.onClick(PushActivity.java:32)
08-23 02:28:44.404: E/AndroidRuntime(282):     at android.view.View.performClick(View.java:2408)
08-23 02:28:44.404: E/AndroidRuntime(282):     at android.view.View$PerformClick.run(View.java:8816)
08-23 02:28:44.404: E/AndroidRuntime(282):     at android.os.Handler.handleCallback(Handler.java:587)
08-23 02:28:44.404: E/AndroidRuntime(282):     at android.os.Handler.dispatchMessage(Handler.java:92)
08-23 02:28:44.404: E/AndroidRuntime(282):     at android.os.Looper.loop(Looper.java:123)
08-23 02:28:44.404: E/AndroidRuntime(282):     at android.app.ActivityThread.main(ActivityThread.java:4627)
08-23 02:28:44.404: E/AndroidRuntime(282):     at java.lang.reflect.Method.invokeNative(Native Method)
08-23 02:28:44.404: E/AndroidRuntime(282):     at java.lang.reflect.Method.invoke(Method.java:521)
08-23 02:28:44.404: E/AndroidRuntime(282):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868)
08-23 02:28:44.404: E/AndroidRuntime(282):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626)
08-23 02:28:44.404: E/AndroidRuntime(282):     at dalvik.system.NativeStart.main(Native Method)
... ...
原因是發佈的時候沒有加入wmqtt.jar包,解決辦法如下:
1> 在項目根目錄下創建libs目錄,並把wmqtt.jar包移入該目錄。
2> 重新配置項目的Java Build Path(右鍵菜單中的Properties選項中)。
3> 重新打包發佈即可。

運行效果如下:



點擊“Start Push Service”按鈕即可開啓推送服務。這時我們可以看到rsmb的服務日誌中打出以下提示:
20120823 113742.297 CWNAN0033I Connection attempt to listener 1883 received from client tokudu/9774d56d682e549c on address 192.168.28.39:3345
其中的“9774d56d682e549c”就是對應的客戶端ID號。

4、發送服務準備

a> 下載&解壓PHP版的發送服務端代碼send_mqtt.zip(下載地址:http://download.csdn.net/detail/shagoo/4520102
b> 修改etc/config.php中推送服務端的IP地址和端口號,即MQTT_SERVER_HOSTMQTT_SERVER_POST常量。
c> 打開對應的URL地址,就可以看到發送服務的界面,實際上就是向對應的推送客戶端推送消息。
47.談談數據加密

數據加密又稱密碼學,它是一門歷史悠久的技術,指通過加密算法和加密密鑰將明文轉變爲密文,而解密則是通過解密算法和解密密鑰將密文恢復爲明文。數據加密目前仍是計算機系統對信息進行保護的一種最可靠的辦法。它利用密碼技術對信息進行加密,實現信息隱蔽,從而起到保護信息的安全的作用。用自己的話來說就是,只有雙方纔知道的協議。
數據加密 - 密碼算法分類
1按發展進程分密碼的發展:古典密碼,對稱密鑰 密碼公開密鑰密碼.
2按加密模式分對稱算法:序列密碼和分組密碼.
經典密碼 代替密碼: 簡單代替多名或同音代替多表代替多字母或多碼代替換位密碼: •對稱加密算法 DES AES •非對稱公鑰算法 RSA 揹包密碼McEliece密碼Rabin 橢圓曲線EIGamal D_H 
48.解決問題和思考問題的方式

首先查看官方提供的API,通過自己對功能的理解,然後通過網絡的途徑,下載demo可以選擇 github ,查找問題可以選擇 stackover flow,然後可以問身邊的朋友。
49.列舉7到12個設計模式  以及它們的應用場景

設計模式,提供了很多軟件工程問題所需處理的解決方案。
根據模式的目的可分爲3類: 1.創建型模式:與對象的創建有關。 2.結構性模式:處理類與對象的組合。 3.行爲性模式:對類或對象怎樣交互和怎樣 分配職責進行描述。
面向對象設計的2個基本原則: 1.針對接口編程,而不是針對實現編程。 2.優先使用對象組合,而不是類繼承。 
面向對象設計的5個設計原則: 1.單一職責原則(SRP) 2.開放封閉原則(OCP)  3.Liskov替換原則(LSP) 4.依賴倒置原則(DIP) 5.接口隔離原則(ISP) 
23中設計模式: 1.創建型模式: (1).工廠方法模式 (2).抽象工廠模式 (3).創建者模式 (4).原型模式 (5).單例模式 2.結構型模式: (6).適配器模式 (7).橋模式 (8).組合模式 (9).裝飾模式 (10).外觀模式 (11).享元模式 (12).代理模式 3.行爲型模式 (13).解釋器模式 (14).模板方法模式 (15).職責鏈模式 (16).命令模式 (17).迭代器模式 (18).中介者模式 (19).備忘錄模式 (20).觀察者模式 (21).狀態模式 (22).策略模式 (23).訪問者模式  除此之外,後來人發現很多新的模式,如空模式等。
下面列舉幾個常見的問題導致重新設計,可能需要設計模式來分析解決: 1.通過顯示的指定一個類來創建對象 2.對特殊操作的依賴 3.對硬件和軟件平臺的依賴 4.對對象表示或實現的依賴 5.算法依賴 6.緊耦合 7.通過生產子類來擴展功能 8.不能方便的對類進行修改
軟件的設計臭味: 1.僵化性 2.脆弱性 3.頑固性 4.粘滯性 5.不必要的複雜性 6.不必要的重複 7.晦澀性  ... ... 總而言之,一句話,面向對象特性+原則+模式,折騰來折騰去就是這麼個回事。
 
設計模式(Design Patterns)
                                  ——可複用面向對象軟件的基礎
設計模式(Design pattern)是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。使用設計模式是爲了可重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。 毫無疑問,設計模式於己於他人於系統都是多贏的,設計模式使代碼編制真正工程化,設計模式是軟件工程的基石,如同大廈的一塊塊磚石一樣。項目中合理的運用設計模式可以完美的解決很多問題,每種模式在現在中都有相應的原理來與之對應,每一個模式描述了一個在我們周圍不斷重複發生的問題,以及該問題的核心解決方案,這也是它能被廣泛應用的原因。本章系Java之美[從菜鳥到高手演變]系列之設計模式,我們會以理論與實踐相結合的方式來進行本章的學習,希望廣大程序愛好者,學好設計模式,做一個優秀的軟件工程師!
在閱讀過程中有任何問題,請及時聯繫:egg。
郵箱:[email protected] 微博:http://weibo.com/xtfggef
如有轉載,請說明出處:http://blog.csdn.net/zhangerqing
企業級項目實戰(帶源碼)地址:http://zz563143188.iteye.com/blog/1825168
23種模式java實現源碼及收集五年的開發資料下載地址:  http://pan.baidu.com/share/home?uk=4076915866&view=share
一、設計模式的分類
總體來說設計模式分爲三大類:
創建型模式,共五種:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式。
結構型模式,共七種:適配器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。
行爲型模式,共十一種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。
其實還有兩類:併發型模式和線程池模式。用一個圖片來整體描述一下:

 
二、設計模式的六大原則
1、開閉原則(Open Close Principle)
開閉原則就是說對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現一個熱插拔的效果。所以一句話概括就是:爲了使程序的擴展性好,易於維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,後面的具體設計中我們會提到這點。
2、里氏代換原則(Liskov Substitution Principle)
里氏代換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。 里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現。 LSP是繼承複用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被複用,而衍生類也能夠在基類的基礎上增加新的行爲。里氏代換原則是對“開-閉”原則的補充。實現“開-閉”原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範。—— From Baidu 百科
3、依賴倒轉原則(Dependence Inversion Principle)
這個是開閉原則的基礎,具體內容:真對接口編程,依賴於抽象而不依賴於具體。
4、接口隔離原則(Interface Segregation Principle)
這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。還是一個降低類之間的耦合度的意思,從這兒我們看出,其實設計模式就是一個軟件的設計思想,從大型軟件架構出發,爲了升級和維護方便。所以上文中多次出現:降低依賴,降低耦合。
5、迪米特法則(最少知道原則)(Demeter Principle)
爲什麼叫最少知道原則,就是說:一個實體應當儘量少的與其他實體之間發生相互作用,使得系統功能模塊相對獨立。
6、合成複用原則(Composite Reuse Principle)
原則是儘量使用合成/聚合的方式,而不是使用繼承。
三、Java的23中設計模式
從這一塊開始,我們詳細介紹Java中23種設計模式的概念,應用場景等情況,並結合他們的特點及設計模式的原則進行分析。
1、工廠方法模式(Factory Method)
工廠方法模式分爲三種:
11、普通工廠模式,就是建立一個工廠類,對實現了同一接口的一些類進行實例的創建。首先看下關係圖:

舉例如下:(我們舉一個發送郵件和短信的例子)
首先,創建二者的共同接口:
[java] view plaincopy
public interface Sender {  
    public void Send();  
}  
其次,創建實現類:
[java] view plaincopy
public class MailSender implements Sender {  
    @Override  
    public void Send() {  
        System.out.println("this is mailsender!");  
    }  
}  
[java] view plaincopy
public class SmsSender implements Sender {  
  
    @Override  
    public void Send() {  
        System.out.println("this is sms sender!");  
    }  
}  
最後,建工廠類:
[java] view plaincopy
public class SendFactory {  
  
    public Sender produce(String type) {  
        if ("mail".equals(type)) {  
            return new MailSender();  
        } else if ("sms".equals(type)) {  
            return new SmsSender();  
        } else {  
            System.out.println("請輸入正確的類型!");  
            return null;  
        }  
    }  
}  
我們來測試下:
public class FactoryTest {  
  
    public static void main(String[] args) {  
        SendFactory factory = new SendFactory();  
        Sender sender = factory.produce("sms");  
        sender.Send();  
    }  
}  
輸出:this is sms sender!
22、多個工廠方法模式,是對普通工廠方法模式的改進,在普通工廠方法模式中,如果傳遞的字符串出錯,則不能正確創建對象,而多個工廠方法模式是提供多個工廠方法,分別創建對象。關係圖:

將上面的代碼做下修改,改動下SendFactory類就行,如下:
[java] view plaincopypublic class SendFactory {  
   public Sender produceMail(){  
        return new MailSender();  
    }  
      
    public Sender produceSms(){  
        return new SmsSender();  
    }  
}  
測試類如下:
[java] view plaincopy
public class FactoryTest {  
  
    public static void main(String[] args) {  
        SendFactory factory = new SendFactory();  
        Sender sender = factory.produceMail();  
        sender.Send();  
    }  
}  
輸出:this is mailsender!
33、靜態工廠方法模式,將上面的多個工廠方法模式裏的方法置爲靜態的,不需要創建實例,直接調用即可。
[java] view plaincopy
public class SendFactory {  
      
    public static Sender produceMail(){  
        return new MailSender();  
    }  
      
    public static Sender produceSms(){  
        return new SmsSender();  
    }  
}  
[java] view plaincopy
public class FactoryTest {  
  
    public static void main(String[] args) {      
        Sender sender = SendFactory.produceMail();  
        sender.Send();  
    }  
}  
輸出:this is mailsender!
總體來說,工廠模式適合:凡是出現了大量的產品需要創建,並且具有共同的接口時,可以通過工廠方法模式進行創建。在以上的三種模式中,第一種如果傳入的字符串有誤,不能正確創建對象,第三種相對於第二種,不需要實例化工廠類,所以,大多數情況下,我們會選用第三種——靜態工廠方法模式。
2、抽象工廠模式(Abstract Factory)
工廠方法模式有一個問題就是,類的創建依賴工廠類,也就是說,如果想要拓展程序,必須對工廠類進行修改,這違背了閉包原則,所以,從設計角度考慮,有一定的問題,如何解決?就用到抽象工廠模式,創建多個工廠類,這樣一旦需要增加新的功能,直接增加新的工廠類就可以了,不需要修改之前的代碼。因爲抽象工廠不太好理解,我們先看看圖,然後就和代碼,就比較容易理解。

請看例子:
[java] view plaincopy
public interface Sender {  
    public void Send();  
}  
兩個實現類:
[java] view plaincopy
public class MailSender implements Sender {  
    @Override  
    public void Send() {  
        System.out.println("this is mailsender!");  
    }  
}  
[java] view plaincopy
public class SmsSender implements Sender {  
  
    @Override  
    public void Send() {  
        System.out.println("this is sms sender!");  
    }  
}  
兩個工廠類:
[java] view plaincopy
public class SendMailFactory implements Provider {  
      
    @Override  
    public Sender produce(){  
        return new MailSender();  
    }  
}  
[java] view plaincopy
public class SendSmsFactory implements Provider{  
  
    @Override  
    public Sender produce() {  
        return new SmsSender();  
    }  
}  
在提供一個接口:
[java] view plaincopy
public interface Provider {  
    public Sender produce();  
}  
測試類:
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
        Provider provider = new SendMailFactory();  
        Sender sender = provider.produce();  
        sender.Send();  
    }  
}  
其實這個模式的好處就是,如果你現在想增加一個功能:發及時信息,則只需做一個實現類,實現Sender接口,同時做一個工廠類,實現Provider接口,就OK了,無需去改動現成的代碼。這樣做,拓展性較好!
3、單例模式(Singleton)
單例對象(Singleton)是一種常用的設計模式。在Java應用中,單例對象能保證在一個JVM中,該對象只有一個實例存在。這樣的模式有幾個好處:
1、某些類創建比較頻繁,對於一些大型的對象,這是一筆很大的系統開銷。
2、省去了new操作符,降低了系統內存的使用頻率,減輕GC壓力。
3、有些類如交易所的核心交易引擎,控制着交易流程,如果該類可以創建多個的話,系統完全亂了。(比如一個軍隊出現了多個司令員同時指揮,肯定會亂成一團),所以只有使用單例模式,才能保證核心交易服務器獨立控制整個流程。
首先我們寫一個簡單的單例類:
[java] view plaincopy
public class Singleton {  
  
    /* 持有私有靜態實例,防止被引用,此處賦值爲null,目的是實現延遲加載 */  
    private static Singleton instance = null;  
  
    /* 私有構造方法,防止被實例化 */  
    private Singleton() {  
    }  
  
    /* 靜態工程方法,創建實例 */  
    public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
  
    /* 如果該對象被用於序列化,可以保證對象在序列化前後保持一致 */  
    public Object readResolve() {  
        return instance;  
    }  
}  
這個類可以滿足基本要求,但是,像這樣毫無線程安全保護的類,如果我們把它放入多線程的環境下,肯定就會出現問題了,如何解決?我們首先會想到對getInstance方法加synchronized關鍵字,如下:
[java] view plaincopy
public static synchronized Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
    }  
但是,synchronized關鍵字鎖住的是這個對象,這樣的用法,在性能上會有所下降,因爲每次調用getInstance(),都要對對象上鎖,事實上,只有在第一次創建對象的時候需要加鎖,之後就不需要了,所以,這個地方需要改進。我們改成下面這個:
[java] view plaincopy
public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (instance) {  
                if (instance == null) {  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
似乎解決了之前提到的問題,將synchronized關鍵字加在了內部,也就是說當調用的時候是不需要加鎖的,只有在instance爲null,並創建對象的時候才需要加鎖,性能有一定的提升。但是,這樣的情況,還是有可能有問題的,看下面的情況:在Java指令中創建對象和賦值操作是分開進行的,也就是說instance = new Singleton();語句是分兩步執行的。但是JVM並不保證這兩個操作的先後順序,也就是說有可能JVM會爲新的Singleton實例分配空間,然後直接賦值給instance成員,然後再去初始化這個Singleton實例。這樣就可能出錯了,我們以A、B兩個線程爲例:
a>A、B線程同時進入了第一個if判斷
b>A首先進入synchronized塊,由於instance爲null,所以它執行instance = new Singleton();
c>由於JVM內部的優化機制,JVM先畫出了一些分配給Singleton實例的空白內存,並賦值給instance成員(注意此時JVM沒有開始初始化這個實例),然後A離開了synchronized塊。
d>B進入synchronized塊,由於instance此時不是null,因此它馬上離開了synchronized塊並將結果返回給調用該方法的程序。
e>此時B線程打算使用Singleton實例,卻發現它沒有被初始化,於是錯誤發生了。
所以程序還是有可能發生錯誤,其實程序在運行過程是很複雜的,從這點我們就可以看出,尤其是在寫多線程環境下的程序更有難度,有挑戰性。我們對該程序做進一步優化:
[java] view plaincopy
private static class SingletonFactory{           
        private static Singleton instance = new Singleton();           
    }           
    public static Singleton getInstance(){           
        return SingletonFactory.instance;           
    }   
實際情況是,單例模式使用內部類來維護單例的實現,JVM內部的機制能夠保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當我們第一次調用getInstance的時候,JVM能夠幫我們保證instance只被創建一次,並且會保證把賦值給instance的內存初始化完畢,這樣我們就不用擔心上面的問題。同時該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了低性能問題。這樣我們暫時總結一個完美的單例模式:
[java] view plaincopy
public class Singleton {  
  
    /* 私有構造方法,防止被實例化 */  
    private Singleton() {  
    }  
  
    /* 此處使用一個內部類來維護單例 */  
    private static class SingletonFactory {  
        private static Singleton instance = new Singleton();  
    }  
  
    /* 獲取實例 */  
    public static Singleton getInstance() {  
        return SingletonFactory.instance;  
    }  
  
    /* 如果該對象被用於序列化,可以保證對象在序列化前後保持一致 */  
    public Object readResolve() {  
        return getInstance();  
    }  
}  
其實說它完美,也不一定,如果在構造函數中拋出異常,實例將永遠得不到創建,也會出錯。所以說,十分完美的東西是沒有的,我們只能根據實際情況,選擇最適合自己應用場景的實現方法。也有人這樣實現:因爲我們只需要在創建類的時候進行同步,所以只要將創建和getInstance()分開,單獨爲創建加synchronized關鍵字,也是可以的:
[java] view plaincopy
public class SingletonTest {  
  
    private static SingletonTest instance = null;  
  
    private SingletonTest() {  
    }  
  
    private static synchronized void syncInit() {  
        if (instance == null) {  
            instance = new SingletonTest();  
        }  
    }  
  
    public static SingletonTest getInstance() {  
        if (instance == null) {  
            syncInit();  
        }  
        return instance;  
    }  
}  
考慮性能的話,整個程序只需創建一次實例,所以性能也不會有什麼影響。
補充:採用"影子實例"的辦法爲單例對象的屬性同步更新
[java] view plaincopy
public class SingletonTest {  
  
    private static SingletonTest instance = null;  
    private Vector properties = null;  
  
    public Vector getProperties() {  
        return properties;  
    }  
  
    private SingletonTest() {  
    }  
  
    private static synchronized void syncInit() {  
        if (instance == null) {  
            instance = new SingletonTest();  
        }  
    }  
  
    public static SingletonTest getInstance() {  
        if (instance == null) {  
            syncInit();  
        }  
        return instance;  
    }  
  
    public void updateProperties() {  
        SingletonTest shadow = new SingletonTest();  
        properties = shadow.getProperties();  
    }  
}  
通過單例模式的學習告訴我們:
1、單例模式理解起來簡單,但是具體實現起來還是有一定的難度。
2、synchronized關鍵字鎖定的是對象,在用的時候,一定要在恰當的地方使用(注意需要使用鎖的對象和過程,可能有的時候並不是整個對象及整個過程都需要鎖)。
到這兒,單例模式基本已經講完了,結尾處,筆者突然想到另一個問題,就是採用類的靜態方法,實現單例模式的效果,也是可行的,此處二者有什麼不同?
首先,靜態類不能實現接口。(從類的角度說是可以的,但是那樣就破壞了靜態了。因爲接口中不允許有static修飾的方法,所以即使實現了也是非靜態的)
其次,單例可以被延遲初始化,靜態類一般在第一次加載是初始化。之所以延遲加載,是因爲有些類比較龐大,所以延遲加載有助於提升性能。
再次,單例類可以被繼承,他的方法可以被覆寫。但是靜態類內部方法都是static,無法被覆寫。
最後一點,單例類比較靈活,畢竟從實現上只是一個普通的Java類,只要滿足單例的基本需求,你可以在裏面隨心所欲的實現一些其它功能,但是靜態類不行。從上面這些概括中,基本可以看出二者的區別,但是,從另一方面講,我們上面最後實現的那個單例模式,內部就是用一個靜態類來實現的,所以,二者有很大的關聯,只是我們考慮問題的層面不同罷了。兩種思想的結合,才能造就出完美的解決方案,就像HashMap採用數組+鏈表來實現一樣,其實生活中很多事情都是這樣,單用不同的方法來處理問題,總是有優點也有缺點,最完美的方法是,結合各個方法的優點,才能最好的解決問題!
4、建造者模式(Builder)
工廠類模式提供的是創建單個類的模式,而建造者模式則是將各種產品集中起來進行管理,用來創建複合對象,所謂複合對象就是指某個類具有不同的屬性,其實建造者模式就是前面抽象工廠模式和最後的Test結合起來得到的。我們看一下代碼:
還和前面一樣,一個Sender接口,兩個實現類MailSender和SmsSender。最後,建造者類如下:
[java] view plaincopy
public class Builder {  
      
    private List<Sender> list = new ArrayList<Sender>();  
      
    public void produceMailSender(int count){  
        for(int i=0; i<count; i++){  
            list.add(new MailSender());  
        }  
    }  
      
    public void produceSmsSender(int count){  
        for(int i=0; i<count; i++){  
            list.add(new SmsSender());  
        }  
    }  
}  
測試類:
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
        Builder builder = new Builder();  
        builder.produceMailSender(10);  
    }  
}  
從這點看出,建造者模式將很多功能集成到一個類裏,這個類可以創造出比較複雜的東西。所以與工程模式的區別就是:工廠模式關注的是創建單個產品,而建造者模式則關注創建符合對象,多個部分。因此,是選擇工廠模式還是建造者模式,依實際情況而定。
5、原型模式(Prototype)
原型模式雖然是創建型的模式,但是與工程模式沒有關係,從名字即可看出,該模式的思想就是將一個對象作爲原型,對其進行復制、克隆,產生一個和原對象類似的新對象。本小結會通過對象的複製,進行講解。在Java中,複製對象是通過clone()實現的,先創建一個原型類:
[java] view plaincopy
public class Prototype implements Cloneable {  
  
    public Object clone() throws CloneNotSupportedException {  
        Prototype proto = (Prototype) super.clone();  
        return proto;  
    }  
}  
很簡單,一個原型類,只需要實現Cloneable接口,覆寫clone方法,此處clone方法可以改成任意的名稱,因爲Cloneable接口是個空接口,你可以任意定義實現類的方法名,如cloneA或者cloneB,因爲此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()方法,而在Object類中,clone()是native的,具體怎麼實現,我會在另一篇文章中,關於解讀Java中本地方法的調用,此處不再深究。在這兒,我將結合對象的淺複製和深複製來說一下,首先需要了解對象深、淺複製的概念:
淺複製:將一個對象複製後,基本數據類型的變量都會重新創建,而引用類型,指向的還是原對象所指向的。
深複製:將一個對象複製後,不論是基本數據類型還有引用類型,都是重新創建的。簡單來說,就是深複製進行了完全徹底的複製,而淺複製不徹底。
此處,寫一個深淺複製的例子:
[java] view plaincopy
public class Prototype implements Cloneable, Serializable {  
  
    private static final long serialVersionUID = 1L;  
    private String string;  
  
    private SerializableObject obj;  
  
    /* 淺複製 */  
    public Object clone() throws CloneNotSupportedException {  
        Prototype proto = (Prototype) super.clone();  
        return proto;  
    }  
  
    /* 深複製 */  
    public Object deepClone() throws IOException, ClassNotFoundException {  
  
        /* 寫入當前對象的二進制流 */  
        ByteArrayOutputStream bos = new ByteArrayOutputStream();  
        ObjectOutputStream oos = new ObjectOutputStream(bos);  
        oos.writeObject(this);  
  
        /* 讀出二進制流產生的新對象 */  
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
        ObjectInputStream ois = new ObjectInputStream(bis);  
        return ois.readObject();  
    }  
  
    public String getString() {  
        return string;  
    }  
  
    public void setString(String string) {  
        this.string = string;  
    }  
  
    public SerializableObject getObj() {  
        return obj;  
    }  
  
    public void setObj(SerializableObject obj) {  
        this.obj = obj;  
    }  
  
}  
  
class SerializableObject implements Serializable {  
    private static final long serialVersionUID = 1L;  
}  
 
要實現深複製,需要採用流的形式讀入當前對象的二進制輸入,再寫出二進制數據對應的對象。
我們接着討論設計模式,上篇文章我講完了5種創建型模式,這章開始,我將講下7種結構型模式:適配器模式、裝飾模式、代理模式、外觀模式、橋接模式、組合模式、享元模式。其中對象的適配器模式是各種模式的起源,我們看下面的圖:

 適配器模式將某個類的接口轉換成客戶端期望的另一個接口表示,目的是消除由於接口不匹配所造成的類的兼容性問題。主要分爲三類:類的適配器模式、對象的適配器模式、接口的適配器模式。首先,我們來看看類的適配器模式,先看類圖:

核心思想就是:有一個Source類,擁有一個方法,待適配,目標接口時Targetable,通過Adapter類,將Source的功能擴展到Targetable裏,看代碼:
[java] view plaincopy
public class Source {  
  
    public void method1() {  
        System.out.println("this is original method!");  
    }  
}  
[java] view plaincopy
public interface Targetable {  
  
    /* 與原類中的方法相同 */  
    public void method1();  
  
    /* 新類的方法 */  
    public void method2();  
}  
[java] view plaincopy
public class Adapter extends Source implements Targetable {  
  
    @Override  
    public void method2() {  
        System.out.println("this is the targetable method!");  
    }  
}  
Adapter類繼承Source類,實現Targetable接口,下面是測試類:
[java] view plaincopy
public class AdapterTest {  
  
    public static void main(String[] args) {  
        Targetable target = new Adapter();  接下來我們將要談談責任鏈模式,有多個對象,每個對象持有對下一個對象的引用,這樣就會形成一條鏈,請求在這條鏈上傳遞,直到某一對象決定處理該請求。但是發出者並不清楚到底最終那個對象會處理該請求,所以,責任鏈模式可以實現,在隱瞞客戶
        target.method1();  
        target.method2();  
    }  
}  
輸出:
this is original method! this is the targetable method!
這樣Targetable接口的實現類就具有了Source類的功能。
對象的適配器模式
基本思路和類的適配器模式相同,只是將Adapter類作修改,這次不繼承Source類,而是持有Source類的實例,以達到解決兼容性的問題。看圖:

 
只需要修改Adapter類的源碼即可:
[java] view plaincopy
public class Wrapper implements Targetable {  
  
    private Source source;  
      
    public Wrapper(Source source){  
        super();  
        this.source = source;  
    }  
    @Override  
    public void method2() {  
        System.out.println("this is the targetable method!");  
    }  
  
    @Override  
    public void method1() {  
        source.method1();  
    }  
}  
測試類:
[java] view plaincopy
public class AdapterTest {  
  
    public static void main(String[] args) {  
        Source source = new Source();  
        Targetable target = new Wrapper(source);  
        target.method1();  
        target.method2();  
    }  
}  
輸出與第一種一樣,只是適配的方法不同而已。
第三種適配器模式是接口的適配器模式,接口的適配器是這樣的:有時我們寫的一個接口中有多個抽象方法,當我們寫該接口的實現類時,必須實現該接口的所有方法,這明顯有時比較浪費,因爲並不是所有的方法都是我們需要的,有時只需要某一些,此處爲了解決這個問題,我們引入了接口的適配器模式,藉助於一個抽象類,該抽象類實現了該接口,實現了所有的方法,而我們不和原始的接口打交道,只和該抽象類取得聯繫,所以我們寫一個類,繼承該抽象類,重寫我們需要的方法就行。看一下類圖:

這個很好理解,在實際開發中,我們也常會遇到這種接口中定義了太多的方法,以致於有時我們在一些實現類中並不是都需要。看代碼:
[java] view plaincopy
public interface Sourceable {  
      
    public void method1();  
    public void method2();  
}  
抽象類Wrapper2:
[java] view plaincopy
public abstract class Wrapper2 implements Sourceable{  
      
    public void method1(){}  
    public void method2(){}  
}  
[java] view plaincopy
public class SourceSub1 extends Wrapper2 {  
    public void method1(){  
        System.out.println("the sourceable interface's first Sub1!");  
    }  
}  
[java] view plaincopy
public class SourceSub2 extends Wrapper2 {  
    public void method2(){  
        System.out.println("the sourceable interface's second Sub2!");  
    }  
}  
[java] view plaincopy
public class WrapperTest {  
  
    public static void main(String[] args) {  
        Sourceable source1 = new SourceSub1();  
        Sourceable source2 = new SourceSub2();  
          
        source1.method1();  
        source1.method2();  
        source2.method1();  
        source2.method2();  
    }  
}  
測試輸出:
the sourceable interface's first Sub1! the sourceable interface's second Sub2!
達到了我們的效果!
 講了這麼多,總結一下三種適配器模式的應用場景:
類的適配器模式:當希望將一個類轉換成滿足另一個新接口的類時,可以使用類的適配器模式,創建一個新類,繼承原有的類,實現新的接口即可。
對象的適配器模式:當希望將一個對象轉換成滿足另一個新接口的對象時,可以創建一個Wrapper類,持有原類的一個實例,在Wrapper類的方法中,調用實例的方法就行。
接口的適配器模式:當不希望實現一個接口中所有的方法時,可以創建一個抽象類Wrapper,實現所有方法,我們寫別的類的時候,繼承抽象類即可。
7、裝飾模式(Decorator)
顧名思義,裝飾模式就是給一個對象增加一些新的功能,而且是動態的,要求裝飾對象和被裝飾對象實現同一個接口,裝飾對象持有被裝飾對象的實例,關係圖如下:

Source類是被裝飾類,Decorator類是一個裝飾類,可以爲Source類動態的添加一些功能,代碼如下:
[java] view plaincopy
public interface Sourceable {  
    public void method();  
}  
[java] view plaincopy
public class Source implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("the original method!");  
    }  
}  
[java] view plaincopy
public class Decorator implements Sourceable {  
  
    private Sourceable source;  
      
    public Decorator(Sourceable source){  
        super();  
        this.source = source;  
    }  
    @Override  
    public void method() {  
        System.out.println("before decorator!");  
        source.method();  
        System.out.println("after decorator!");  
    }  
}  
測試類:
[java] view plaincopy
public class DecoratorTest {  
  
    public static void main(String[] args) {  
        Sourceable source = new Source();  
        Sourceable obj = new Decorator(source);  
        obj.method();  
    }  
}  
輸出:
before decorator! the original method! after decorator!
裝飾器模式的應用場景:
1、需要擴展一個類的功能。
2、動態的爲一個對象增加功能,而且還能動態撤銷。(繼承不能做到這一點,繼承的功能是靜態的,不能動態增刪。)
缺點:產生過多相似的對象,不易排錯!
8、代理模式(Proxy)
其實每個模式名稱就表明了該模式的作用,代理模式就是多一個代理類出來,替原對象進行一些操作,比如我們在租房子的時候回去找中介,爲什麼呢?因爲你對該地區房屋的信息掌握的不夠全面,希望找一個更熟悉的人去幫你做,此處的代理就是這個意思。再如我們有的時候打官司,我們需要請律師,因爲律師在法律方面有專長,可以替我們進行操作,表達我們的想法。先來看看關係圖:
 
根據上文的闡述,代理模式就比較容易的理解了,我們看下代碼:
[java] view plaincopy
public interface Sourceable {  
    public void method();  
}  
[java] view plaincopy
public class Source implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("the original method!");  
    }  
}  
[java] view plaincopy
public class Proxy implements Sourceable {  
  
    private Source source;  
    public Proxy(){  
        super();  
        this.source = new Source();  
    }  
    @Override  
    public void method() {  
        before();  
        source.method();  
        atfer();  
    }  
    private void atfer() {  
        System.out.println("after proxy!");  
    }  
    private void before() {  
        System.out.println("before proxy!");  
    }  
}  
測試類:
[java] view plaincopy
public class ProxyTest {  
  
    public static void main(String[] args) {  
        Sourceable source = new Proxy();  
        source.method();  
    }  
  
}  
輸出:
before proxy! the original method! after proxy!
代理模式的應用場景:
如果已有的方法在使用的時候需要對原有的方法進行改進,此時有兩種辦法:
1、修改原有的方法來適應。這樣違反了“對擴展開放,對修改關閉”的原則。
2、就是採用一個代理類調用原有的方法,且對產生的結果進行控制。這種方法就是代理模式。
使用代理模式,可以將功能劃分的更加清晰,有助於後期維護!
9、外觀模式(Facade)
外觀模式是爲了解決類與類之家的依賴關係的,像spring一樣,可以將類和類之間的關係配置到配置文件中,而外觀模式就是將他們的關係放在一個Facade類中,降低了類類之間的耦合度,該模式中沒有涉及到接口,看下類圖:(我們以一個計算機的啓動過程爲例)

我們先看下實現類:
[java] view plaincopy
public class CPU {  
      
    public void startup(){  
        System.out.println("cpu startup!");  
    }  
      
    public void shutdown(){  
        System.out.println("cpu shutdown!");  
    }  
}  
[java] view plaincopy
public class Memory {  
      
    public void startup(){  
        System.out.println("memory startup!");  
    }  
      
    public void shutdown(){  
        System.out.println("memory shutdown!");  
    }  
}  
[java] view plaincopy
public class Disk {  
      
    public void startup(){  
        System.out.println("disk startup!");  
    }  
      
    public void shutdown(){  
        System.out.println("disk shutdown!");  
    }  
}  
[java] view plaincopy
public class Computer {  
    private CPU cpu;  
    private Memory memory;  
    private Disk disk;  
      
    public Computer(){  
        cpu = new CPU();  
        memory = new Memory();  
        disk = new Disk();  
    }  
      
    public void startup(){  
        System.out.println("start the computer!");  
        cpu.startup();  
        memory.startup();  
        disk.startup();  
        System.out.println("start computer finished!");  
    }  
      
    public void shutdown(){  
        System.out.println("begin to close the computer!");  
        cpu.shutdown();  
        memory.shutdown();  
        disk.shutdown();  
        System.out.println("computer closed!");  
    }  
}  
User類如下:
[java] view plaincopy
public class User {  
  
    public static void main(String[] args) {  
        Computer computer = new Computer();  
        computer.startup();  
        computer.shutdown();  
    }  
}  
輸出:
start the computer! cpu startup! memory startup! disk startup! start computer finished! begin to close the computer! cpu shutdown! memory shutdown! disk shutdown! computer closed!
如果我們沒有Computer類,那麼,CPU、Memory、Disk他們之間將會相互持有實例,產生關係,這樣會造成嚴重的依賴,修改一個類,可能會帶來其他類的修改,這不是我們想要看到的,有了Computer類,他們之間的關係被放在了Computer類裏,這樣就起到了解耦的作用,這,就是外觀模式!
10、橋接模式(Bridge)
橋接模式就是把事物和其具體實現分開,使他們可以各自獨立的變化。橋接的用意是:將抽象化與實現化解耦,使得二者可以獨立變化,像我們常用的JDBC橋DriverManager一樣,JDBC進行連接數據庫的時候,在各個數據庫之間進行切換,基本不需要動太多的代碼,甚至絲毫不用動,原因就是JDBC提供統一接口,每個數據庫提供各自的實現,用一個叫做數據庫驅動的程序來橋接就行了。我們來看看關係圖:

實現代碼:
先定義接口:
[java] view plaincopy
public interface Sourceable {  
    public void method();  
}  
分別定義兩個實現類:
[java] view plaincopy
public class SourceSub1 implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("this is the first sub!");  
    }  
}  
[java] view plaincopy
public class SourceSub2 implements Sourceable {  
  
    @Override  
    public void method() {  
        System.out.println("this is the second sub!");  
    }  
}  
定義一個橋,持有Sourceable的一個實例:
[java] view plaincopy
public abstract class Bridge {  
    private Sourceable source;  
  
    public void method(){  
        source.method();  
    }  
      
    public Sourceable getSource() {  
        return source;  
    }  
  
    public void setSource(Sourceable source) {  
        this.source = source;  
    }  
}  
[java] view plaincopy
public class MyBridge extends Bridge {  
    public void method(){  
        getSource().method();  
    }  
}  
測試類:
[java] view plaincopy
public class BridgeTest {  
      
    public static void main(String[] args) {  
          
        Bridge bridge = new MyBridge();  
          
        /*調用第一個對象*/  
        Sourceable source1 = new SourceSub1();  
        bridge.setSource(source1);  
        bridge.method();  
          
        /*調用第二個對象*/  
        Sourceable source2 = new SourceSub2();  
        bridge.setSource(source2);  
        bridge.method();  
    }  
}  
output:
this is the first sub! this is the second sub!
這樣,就通過對Bridge類的調用,實現了對接口Sourceable的實現類SourceSub1和SourceSub2的調用。接下來我再畫個圖,大家就應該明白了,因爲這個圖是我們JDBC連接的原理,有數據庫學習基礎的,一結合就都懂了。

11、組合模式(Composite)
組合模式有時又叫部分-整體模式在處理類似樹形結構的問題時比較方便,看看關係圖:

直接來看代碼:
[java] view plaincopy
public class TreeNode {  
      
    private String name;  
    private TreeNode parent;  
    private Vector<TreeNode> children = new Vector<TreeNode>();  
      
    public TreeNode(String name){  
        this.name = name;  
    }  
  
    public String getName() {  
        return name;  
    }  
  
    public void setName(String name) {  
        this.name = name;  
    }  
  
    public TreeNode getParent() {  
        return parent;  
    }  
  
    public void setParent(TreeNode parent) {  
        this.parent = parent;  
    }  
      
    //添加孩子節點  
    public void add(TreeNode node){  
        children.add(node);  
    }  
      
    //刪除孩子節點  
    public void remove(TreeNode node){  
        children.remove(node);  
    }  
      
    //取得孩子節點  
    public Enumeration<TreeNode> getChildren(){  
        return children.elements();  
    }  
}  
[java] view plaincopy
public class Tree {  
  
    TreeNode root = null;  
  
    public Tree(String name) {  
        root = new TreeNode(name);  
    }  
  
    public static void main(String[] args) {  
        Tree tree = new Tree("A");  
        TreeNode nodeB = new TreeNode("B");  
        TreeNode nodeC = new TreeNode("C");  
          
        nodeB.add(nodeC);  
        tree.root.add(nodeB);  
        System.out.println("build the tree finished!");  
    }  
}  
使用場景:將多個對象組合在一起進行操作,常用於表示樹形結構中,例如二叉樹,數等。
12、享元模式(Flyweight)
享元模式的主要目的是實現對象的共享,即共享池,當系統中對象多的時候可以減少內存的開銷,通常與工廠模式一起使用。

FlyWeightFactory負責創建和管理享元單元,當一個客戶端請求時,工廠需要檢查當前對象池中是否有符合條件的對象,如果有,就返回已經存在的對象,如果沒有,則創建一個新對象,FlyWeight是超類。一提到共享池,我們很容易聯想到Java裏面的JDBC連接池,想想每個連接的特點,我們不難總結出:適用於作共享的一些個對象,他們有一些共有的屬性,就拿數據庫連接池來說,url、driverClassName、username、password及dbname,這些屬性對於每個連接來說都是一樣的,所以就適合用享元模式來處理,建一個工廠類,將上述類似屬性作爲內部數據,其它的作爲外部數據,在方法調用時,當做參數傳進來,這樣就節省了空間,減少了實例的數量。
看個例子:

看下數據庫連接池的代碼:
[java] view plaincopy
public class ConnectionPool {  
      
    private Vector<Connection> pool;  
      
    /*公有屬性*/  
    private String url = "jdbc:mysql://localhost:3306/test";  
    private String username = "root";  
    private String password = "root";  
    private String driverClassName = "com.mysql.jdbc.Driver";  
  
    private int poolSize = 100;  
    private static ConnectionPool instance = null;  
    Connection conn = null;  
  
    /*構造方法,做一些初始化工作*/  
    private ConnectionPool() {  
        pool = new Vector<Connection>(poolSize);  
  
        for (int i = 0; i < poolSize; i++) {  
            try {  
                Class.forName(driverClassName);  
                conn = DriverManager.getConnection(url, username, password);  
                pool.add(conn);  
            } catch (ClassNotFoundException e) {  
                e.printStackTrace();  
            } catch (SQLException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
  
    /* 返回連接到連接池 */  
    public synchronized void release() {  
        pool.add(conn);  
    }  
  
    /* 返回連接池中的一個數據庫連接 */  
    public synchronized Connection getConnection() {  
        if (pool.size() > 0) {  
            Connection conn = pool.get(0);  
            pool.remove(conn);  
            return conn;  
        } else {  
            return null;  
        }  
    }  
}  
 
通過連接池的管理,實現了數據庫連接的共享,不需要每一次都重新創建連接,節省了數據庫重新創建的開銷,提升了系統的性能!本章講解了7種結構型模式,因爲篇幅的問題,剩下的11種行爲型模式,
本章是關於設計模式的最後一講,會講到第三種設計模式——行爲型模式,共11種:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式。這段時間一直在寫關於設計模式的東西,終於寫到一半了,寫博文是個很費時間的東西,因爲我得爲讀者負責,不論是圖還是代碼還是表述,都希望能儘量寫清楚,以便讀者理解,我想不論是我還是讀者,都希望看到高質量的博文出來,從我本人出發,我會一直堅持下去,不斷更新,源源動力來自於讀者朋友們的不斷支持,我會盡自己的努力,寫好每一篇文章!希望大家能不斷給出意見和建議,共同打造完美的博文!
 
 
先來張圖,看看這11中模式的關係:
第一類:通過父類與子類的關係進行實現。第二類:兩個類之間。第三類:類的狀態。第四類:通過中間類

13、策略模式(strategy)
策略模式定義了一系列算法,並將每個算法封裝起來,使他們可以相互替換,且算法的變化不會影響到使用算法的客戶。需要設計一個接口,爲一系列實現類提供統一的方法,多個實現類實現該接口,設計一個抽象類(可有可無,屬於輔助類),提供輔助函數,關係圖如下:

圖中ICalculator提供同意的方法, AbstractCalculator是輔助類,提供輔助方法,接下來,依次實現下每個類:
首先統一接口:
[java] view plaincopy
public interface ICalculator {  
    public int calculate(String exp);  
}  
輔助類:
[java] view plaincopy
public abstract class AbstractCalculator {  
      
    public int[] split(String exp,String opt){  
        String array[] = exp.split(opt);  
        int arrayInt[] = new int[2];  
        arrayInt[0] = Integer.parseInt(array[0]);  
        arrayInt[1] = Integer.parseInt(array[1]);  
        return arrayInt;  
    }  
}  
三個實現類:
[java] view plaincopy
public class Plus extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\+");  
        return arrayInt[0]+arrayInt[1];  
    }  
}  
[java] view plaincopy
public class Minus extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"-");  
        return arrayInt[0]-arrayInt[1];  
    }  
  
}  
[java] view plaincopy
public class Multiply extends AbstractCalculator implements ICalculator {  
  
    @Override  
    public int calculate(String exp) {  
        int arrayInt[] = split(exp,"\\*");  
        return arrayInt[0]*arrayInt[1];  
    }  
}  
簡單的測試類:
[java] view plaincopy
public class StrategyTest {  
  
    public static void main(String[] args) {  
        String exp = "2+8";  
        ICalculator cal = new Plus();  
        int result = cal.calculate(exp);  
        System.out.println(result);  
    }  
}  
輸出:10
策略模式的決定權在用戶,系統本身提供不同算法的實現,新增或者刪除算法,對各種算法做封裝。因此,策略模式多用在算法決策系統中,外部用戶只需要決定用哪個算法即可。
14、模板方法模式(Template Method)
解釋一下模板方法模式,就是指:一個抽象類中,有一個主方法,再定義1...n個方法,可以是抽象的,也可以是實際的方法,定義一個類,繼承該抽象類,重寫抽象方法,通過調用抽象類,實現對子類的調用,先看個關係圖:

就是在AbstractCalculator類中定義一個主方法calculate,calculate()調用spilt()等,Plus和Minus分別繼承AbstractCalculator類,通過對AbstractCalculator的調用實現對子類的調用,看下面的例子:
[java] view plaincopy
public abstract class AbstractCalculator {  
      
    /*主方法,實現對本類其它方法的調用*/  
    public final int calculate(String exp,String opt){  
        int array[] = split(exp,opt);  
        return calculate(array[0],array[1]);  
    }  
      
    /*被子類重寫的方法*/  
    abstract public int calculate(int num1,int num2);  
      
    public int[] split(String exp,String opt){  
        String array[] = exp.split(opt);  
        int arrayInt[] = new int[2];  
        arrayInt[0] = Integer.parseInt(array[0]);  
        arrayInt[1] = Integer.parseInt(array[1]);  
        return arrayInt;  
    }  
}  
[java] view plaincopy
public class Plus extends AbstractCalculator {  
  
    @Override  
    public int calculate(int num1,int num2) {  
        return num1 + num2;  
    }  
}  
測試類:
[java] view plaincopy
public class StrategyTest {  
  
    public static void main(String[] args) {  
        String exp = "8+8";  
        AbstractCalculator cal = new Plus();  
        int result = cal.calculate(exp, "\\+");  
        System.out.println(result);  
    }  
}  
我跟蹤下這個小程序的執行過程:首先將exp和"\\+"做參數,調用AbstractCalculator類裏的calculate(String,String)方法,在calculate(String,String)裏調用同類的split(),之後再調用calculate(int ,int)方法,從這個方法進入到子類中,執行完return num1 + num2後,將值返回到AbstractCalculator類,賦給result,打印出來。正好驗證了我們開頭的思路。
15、觀察者模式(Observer)
包括這個模式在內的接下來的四個模式,都是類和類之間的關係,不涉及到繼承,學的時候應該 記得歸納,記得本文最開始的那個圖。觀察者模式很好理解,類似於郵件訂閱和RSS訂閱,當我們瀏覽一些博客或wiki時,經常會看到RSS圖標,就這的意思是,當你訂閱了該文章,如果後續有更新,會及時通知你。其實,簡單來講就一句話:當一個對象變化時,其它依賴該對象的對象都會收到通知,並且隨着變化!對象之間是一種一對多的關係。先來看看關係圖:

我解釋下這些類的作用:MySubject類就是我們的主對象,Observer1和Observer2是依賴於MySubject的對象,當MySubject變化時,Observer1和Observer2必然變化。AbstractSubject類中定義着需要監控的對象列表,可以對其進行修改:增加或刪除被監控對象,且當MySubject變化時,負責通知在列表內存在的對象。我們看實現代碼:
一個Observer接口:
[java] view plaincopy
public interface Observer {  
    public void update();  
}  
兩個實現類:
[java] view plaincopy
public class Observer1 implements Observer {  
  
    @Override  
    public void update() {  
        System.out.println("observer1 has received!");  
    }  
}  
[java] view plaincopy
public class Observer2 implements Observer {  
  
    @Override  
    public void update() {  
        System.out.println("observer2 has received!");  
    }  
  
}  
Subject接口及實現類:
[java] view plaincopy
public interface Subject {  
      
    /*增加觀察者*/  
    public void add(Observer observer);  
      
    /*刪除觀察者*/  
    public void del(Observer observer);  
      
    /*通知所有的觀察者*/  
    public void notifyObservers();  
      
    /*自身的操作*/  
    public void operation();  
}  
[java] view plaincopy
public abstract class AbstractSubject implements Subject {  
  
    private Vector<Observer> vector = new Vector<Observer>();  
    @Override  
    public void add(Observer observer) {  
        vector.add(observer);  
    }  
  
    @Override  
    public void del(Observer observer) {  
        vector.remove(observer);  
    }  
  
    @Override  
    public void notifyObservers() {  
        Enumeration<Observer> enumo = vector.elements();  
        while(enumo.hasMoreElements()){  
            enumo.nextElement().update();  
        }  
    }  
}  
[java] view plaincopy
public class MySubject extends AbstractSubject {  
  
    @Override  
    public void operation() {  
        System.out.println("update self!");  
        notifyObservers();  
    }  
  
}  
測試類:
[java] view plaincopy
public class ObserverTest {  
  
    public static void main(String[] args) {  
        Subject sub = new MySubject();  
        sub.add(new Observer1());  
        sub.add(new Observer2());  
          
        sub.operation();  
    }  
  
}  
輸出:
update self! observer1 has received! observer2 has received!
 這些東西,其實不難,只是有些抽象,不太容易整體理解,建議讀者:根據關係圖,新建項目,自己寫代碼(或者參考我的代碼),按照總體思路走一遍,這樣才能體會它的思想,理解起來容易! 
16、迭代子模式(Iterator)
顧名思義,迭代器模式就是順序訪問聚集中的對象,一般來說,集合中非常常見,如果對集合類比較熟悉的話,理解本模式會十分輕鬆。這句話包含兩層意思:一是需要遍歷的對象,即聚集對象,二是迭代器對象,用於對聚集對象進行遍歷訪問。我們看下關係圖:
 
這個思路和我們常用的一模一樣,MyCollection中定義了集合的一些操作,MyIterator中定義了一系列迭代操作,且持有Collection實例,我們來看看實現代碼:
兩個接口:
[java] view plaincopy
public interface Collection {  
      
    public Iterator iterator();  
      
    /*取得集合元素*/  
    public Object get(int i);  
      
    /*取得集合大小*/  
    public int size();  
}  
[java] view plaincopy
public interface Iterator {  
    //前移  
    public Object previous();  
      
    //後移  
    public Object next();  
    public boolean hasNext();  
      
    //取得第一個元素  
    public Object first();  
}  
兩個實現:
[java] view plaincopy
public class MyCollection implements Collection {  
  
    public String string[] = {"A","B","C","D","E"};  
    @Override  
    public Iterator iterator() {  
        return new MyIterator(this);  
    }  
  
    @Override  
    public Object get(int i) {  
        return string[i];  
    }  
  
    @Override  
    public int size() {  
        return string.length;  
    }  
}  
[java] view plaincopy
public class MyIterator implements Iterator {  
  
    private Collection collection;  
    private int pos = -1;  
      
    public MyIterator(Collection collection){  
        this.collection = collection;  
    }  
      
    @Override  
    public Object previous() {  
        if(pos > 0){  
            pos--;  
        }  
        return collection.get(pos);  
    }  
  
    @Override  
    public Object next() {  
        if(pos<collection.size()-1){  
            pos++;  
        }  
        return collection.get(pos);  
    }  
  
    @Override  
    public boolean hasNext() {  
        if(pos<collection.size()-1){  
            return true;  
        }else{  
            return false;  
        }  
    }  
  
    @Override  
    public Object first() {  
        pos = 0;  
        return collection.get(pos);  
    }  
  
}  
測試類:
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
        Collection collection = new MyCollection();  
        Iterator it = collection.iterator();  
          
        while(it.hasNext()){  
            System.out.println(it.next());  
        }  
    }  
}  
輸出:A B C D E
此處我們貌似模擬了一個集合類的過程,感覺是不是很爽?其實JDK中各個類也都是這些基本的東西,加一些設計模式,再加一些優化放到一起的,只要我們把這些東西學會了,掌握好了,我們也可以寫出自己的集合類,甚至框架!
17、責任鏈模式(Chain of Responsibility)接下來我們將要談談責任鏈模式,有多個對象,每個對象持有對下一個對象的引用,這樣就會形成一條鏈,請求在這條鏈上傳遞,直到某一對象決定處理該請求。但是發出者並不清楚到底最終那個對象會處理該請求,所以,責任鏈模式可以實現,在隱瞞客戶端的情況下,對系統進行動態的調整。先看看關係圖:
 
 
Abstracthandler類提供了get和set方法,方便MyHandle類設置和修改引用對象,MyHandle類是核心,實例化後生成一系列相互持有的對象,構成一條鏈。
[java] view plaincopy
public interface Handler {  
    public void operator();  
}  
[java] view plaincopy
public abstract class AbstractHandler {  
      
    private Handler handler;  
  
    public Handler getHandler() {  
        return handler;  
    }  
  
    public void setHandler(Handler handler) {  
        this.handler = handler;  
    }  
      
}  
[java] view plaincopy
public class MyHandler extends AbstractHandler implements Handler {  
  
    private String name;  
  
    public MyHandler(String name) {  
        this.name = name;  
    }  
  
    @Override  
    public void operator() {  
        System.out.println(name+"deal!");  
        if(getHandler()!=null){  
            getHandler().operator();  
        }  
    }  
}  
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
        MyHandler h1 = new MyHandler("h1");  
        MyHandler h2 = new MyHandler("h2");  
        MyHandler h3 = new MyHandler("h3");  
  
        h1.setHandler(h2);  
        h2.setHandler(h3);  
  
        h1.operator();  
    }  
}  
輸出:
h1deal! h2deal! h3deal!
此處強調一點就是,鏈接上的請求可以是一條鏈,可以是一個樹,還可以是一個環,模式本身不約束這個,需要我們自己去實現,同時,在一個時刻,命令只允許由一個對象傳給另一個對象,而不允許傳給多個對象。
 18、命令模式(Command)
命令模式很好理解,舉個例子,司令員下令讓士兵去幹件事情,從整個事情的角度來考慮,司令員的作用是,發出口令,口令經過傳遞,傳到了士兵耳朵裏,士兵去執行。這個過程好在,三者相互解耦,任何一方都不用去依賴其他人,只需要做好自己的事兒就行,司令員要的是結果,不會去關注到底士兵是怎麼實現的。我們看看關係圖:

Invoker是調用者(司令員),Receiver是被調用者(士兵),MyCommand是命令,實現了Command接口,持有接收對象,看實現代碼:
[java] view plaincopy
public interface Command {  
    public void exe();  
}  
[java] view plaincopy
public class MyCommand implements Command {  
  
    private Receiver receiver;  
      
    public MyCommand(Receiver receiver) {  
        this.receiver = receiver;  
    }  
  
    @Override  
    public void exe() {  
        receiver.action();  
    }  
}  
[java] view plaincopy
public class Receiver {  
    public void action(){  
        System.out.println("command received!");  
    }  
}  
[java] view plaincopy
public class Invoker {  
      
    private Command command;  
      
    public Invoker(Command command) {  
        this.command = command;  
    }  
  
    public void action(){  
        command.exe();  
    }  
}  
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
        Receiver receiver = new Receiver();  
        Command cmd = new MyCommand(receiver);  
        Invoker invoker = new Invoker(cmd);  
        invoker.action();  
    }  
}  
輸出:command received!
這個很哈理解,命令模式的目的就是達到命令的發出者和執行者之間解耦,實現請求和執行分開,熟悉Struts的同學應該知道,Struts其實就是一種將請求和呈現分離的技術,其中必然涉及命令模式的思想!
其實每個設計模式都是很重要的一種思想,看上去很熟,其實是因爲我們在學到的東西中都有涉及,儘管有時我們並不知道,其實在Java本身的設計之中處處都有體現,像AWT、JDBC、集合類、IO管道或者是Web框架,裏面設計模式無處不在。因爲我們篇幅有限,很難講每一個設計模式都講的很詳細,不過我會盡我所能,儘量在有限的空間和篇幅內,把意思寫清楚了,更好讓大家明白。本章不出意外的話,應該是設計模式最後一講了,首先還是上一下上篇開頭的那個圖:

本章講講第三類和第四類。
19、備忘錄模式(Memento)
主要目的是保存一個對象的某個狀態,以便在適當的時候恢復對象,個人覺得叫備份模式更形象些,通俗的講下:假設有原始類A,A中有各種屬性,A可以決定需要備份的屬性,備忘錄類B是用來存儲A的一些內部狀態,類C呢,就是一個用來存儲備忘錄的,且只能存儲,不能修改等操作。做個圖來分析一下:

Original類是原始類,裏面有需要保存的屬性value及創建一個備忘錄類,用來保存value值。Memento類是備忘錄類,Storage類是存儲備忘錄的類,持有Memento類的實例,該模式很好理解。直接看源碼:
[java] view plaincopy
public class Original {  
      
    private String value;  
      
    public String getValue() {  
        return value;  
    }  
  
    public void setValue(String value) {  
        this.value = value;  
    }  
  
    public Original(String value) {  
        this.value = value;  
    }  
  
    public Memento createMemento(){  
        return new Memento(value);  
    }  
      
    public void restoreMemento(Memento memento){  
        this.value = memento.getValue();  
    }  
}  
[java] view plaincopy
public class Memento {  
      
    private String value;  
  
    public Memento(String value) {  
        this.value = value;  
    }  
  
    public String getValue() {  
        return value;  
    }  
  
    public void setValue(String value) {  
        this.value = value;  
    }  
}  
[java] view plaincopy
public class Storage {  
      
    private Memento memento;  
      
    public Storage(Memento memento) {  
        this.memento = memento;  
    }  
  
    public Memento getMemento() {  
        return memento;  
    }  
  
    public void setMemento(Memento memento) {  
        this.memento = memento;  
    }  
}  
測試類:
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
          
        // 創建原始類  
        Original origi = new Original("egg");  
  
        // 創建備忘錄  
        Storage storage = new Storage(origi.createMemento());  
  
        // 修改原始類的狀態  
        System.out.println("初始化狀態爲:" + origi.getValue());  
        origi.setValue("niu");  
        System.out.println("修改後的狀態爲:" + origi.getValue());  
  
        // 回覆原始類的狀態  
        origi.restoreMemento(storage.getMemento());  
        System.out.println("恢復後的狀態爲:" + origi.getValue());  
    }  
}  
輸出:
初始化狀態爲:egg 修改後的狀態爲:niu 恢復後的狀態爲:egg
簡單描述下:新建原始類時,value被初始化爲egg,後經過修改,將value的值置爲niu,最後倒數第二行進行恢復狀態,結果成功恢復了。其實我覺得這個模式叫“備份-恢復”模式最形象。
20、狀態模式(State)
核心思想就是:當對象的狀態改變時,同時改變其行爲,很好理解!就拿QQ來說,有幾種狀態,在線、隱身、忙碌等,每個狀態對應不同的操作,而且你的好友也能看到你的狀態,所以,狀態模式就兩點:1、可以通過改變狀態來獲得不同的行爲。2、你的好友能同時看到你的變化。看圖:

State類是個狀態類,Context類可以實現切換,我們來看看代碼:
 
[java] view plaincopy
package com.xtfggef.dp.state;  
  
/** 
 * 狀態類的核心類 
 * 2012-12-1 
 * @author erqing 
 * 
 */  
public class State {  
      
    private String value;  
      
    public String getValue() {  
        return value;  
    }  
  
    public void setValue(String value) {  
        this.value = value;  
    }  
  
    public void method1(){  
        System.out.println("execute the first opt!");  
    }  
      
    public void method2(){  
        System.out.println("execute the second opt!");  
    }  
}  
[java] view plaincopy
package com.xtfggef.dp.state;  
  
/** 
 * 狀態模式的切換類   2012-12-1 
 * @author erqing 
 *  
 */  
public class Context {  
  
    private State state;  
  
    public Context(State state) {  
        this.state = state;  
    }  
  
    public State getState() {  
        return state;  
    }  
  
    public void setState(State state) {  
        this.state = state;  
    }  
  
    public void method() {  
        if (state.getValue().equals("state1")) {  
            state.method1();  
        } else if (state.getValue().equals("state2")) {  
            state.method2();  
        }  
    }  
}  
測試類:
 
 
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
          
        State state = new State();  
        Context context = new Context(state);  
          
        //設置第一種狀態  
        state.setValue("state1");  
        context.method();  
          
        //設置第二種狀態  
        state.setValue("state2");  
        context.method();  
    }  
}  
輸出:
 
execute the first opt! execute the second opt!
根據這個特性,狀態模式在日常開發中用的挺多的,尤其是做網站的時候,我們有時希望根據對象的某一屬性,區別開他們的一些功能,比如說簡單的權限控制等。21、訪問者模式(Visitor)
訪問者模式把數據結構和作用於結構上的操作解耦合,使得操作集合可相對自由地演化。訪問者模式適用於數據結構相對穩定算法又易變化的系統。因爲訪問者模式使得算法操作增加變得容易。若系統數據結構對象易於變化,經常有新的數據對象增加進來,則不適合使用訪問者模式。訪問者模式的優點是增加操作很容易,因爲增加操作意味着增加新的訪問者。訪問者模式將有關行爲集中到一個訪問者對象中,其改變不影響系統數據結構。其缺點就是增加新的數據結構很困難。—— From 百科
簡單來說,訪問者模式就是一種分離對象數據結構與行爲的方法,通過這種分離,可達到爲一個被訪問者動態添加新的操作而無需做其它的修改的效果。簡單關係圖:

來看看原碼:一個Visitor類,存放要訪問的對象,
 
[java] view plaincopy
public interface Visitor {  
    public void visit(Subject sub);  
}  
[java] view plaincopy
public class MyVisitor implements Visitor {  
  
    @Override  
    public void visit(Subject sub) {  
        System.out.println("visit the subject:"+sub.getSubject());  
    }  
}  
Subject類,accept方法,接受將要訪問它的對象,getSubject()獲取將要被訪問的屬性,
[java] view plaincopy
public interface Subject {  
    public void accept(Visitor visitor);  
    public String getSubject();  
}  
[java] view plaincopy
public class MySubject implements Subject {  
  
    @Override  
    public void accept(Visitor visitor) {  
        visitor.visit(this);  
    }  
  
    @Override  
    public String getSubject() {  
        return "love";  
    }  
}  
測試:
 
 
 
 
 
 
 
 
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
          
        Visitor visitor = new MyVisitor();  
        Subject sub = new MySubject();  
        sub.accept(visitor);      
    }  
}  
輸出:visit the subject:love
 
 
 
 
 
 
 
該模式適用場景:如果我們想爲一個現有的類增加新功能,不得不考慮幾個事情:1、新功能會不會與現有功能出現兼容性問題?2、以後會不會再需要添加?3、如果類不允許修改代碼怎麼辦?面對這些問題,最好的解決方法就是使用訪問者模式,訪問者模式適用於數據結構相對穩定的系統,把數據結構和算法解耦,22、中介者模式(Mediator)
中介者模式也是用來降低類類之間的耦合的,因爲如果類類之間有依賴關係的話,不利於功能的拓展和維護,因爲只要修改一個對象,其它關聯的對象都得進行修改。如果使用中介者模式,只需關心和Mediator類的關係,具體類類之間的關係及調度交給Mediator就行,這有點像spring容器的作用。先看看圖:

User類統一接口,User1和User2分別是不同的對象,二者之間有關聯,如果不採用中介者模式,則需要二者相互持有引用,這樣二者的耦合度很高,爲了解耦,引入了Mediator類,提供統一接口,MyMediator爲其實現類,裏面持有User1和User2的實例,用來實現對User1和User2的控制。這樣User1和User2兩個對象相互獨立,他們只需要保持好和Mediator之間的關係就行,剩下的全由MyMediator類來維護!基本實現:
 
[java] view plaincopy
public interface Mediator {  
    public void createMediator();  
    public void workAll();  
}  
[java] view plaincopy
public class MyMediator implements Mediator {  
  
    private User user1;  
    private User user2;  
      
    public User getUser1() {  
        return user1;  
    }  
  
    public User getUser2() {  
        return user2;  
    }  
  
    @Override  
    public void createMediator() {  
        user1 = new User1(this);  
        user2 = new User2(this);  
    }  
  
    @Override  
    public void workAll() {  
        user1.work();  
        user2.work();  
    }  
}  
[java] view plaincopy
public abstract class User {  
      
    private Mediator mediator;  
      
    public Mediator getMediator(){  
        return mediator;  
    }  
      
    public User(Mediator mediator) {  
        this.mediator = mediator;  
    }  
  
    public abstract void work();  
}  
[java] view plaincopy
public class User1 extends User {  
  
    public User1(Mediator mediator){  
        super(mediator);  
    }  
      
    @Override  
    public void work() {  
        System.out.println("user1 exe!");  
    }  
}  
[java] view plaincopy
public class User2 extends User {  
  
    public User2(Mediator mediator){  
        super(mediator);  
    }  
      
    @Override  
    public void work() {  
        System.out.println("user2 exe!");  
    }  
}  
測試類:
 
 
 
 
 
 
 
 
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
        Mediator mediator = new MyMediator();  
        mediator.createMediator();  
        mediator.workAll();  
    }  
}  
輸出:
 
 
 
 
 
 
 
user1 exe! user2 exe! 23、解釋器模式(Interpreter) 解釋器模式是我們暫時的最後一講,一般主要應用在OOP開發中的編譯器的開發中,所以適用面比較窄。

Context類是一個上下文環境類,Plus和Minus分別是用來計算的實現,代碼如下:
 
[java] view plaincopy
public interface Expression {  
    public int interpret(Context context);  
}  
[java] view plaincopy
public class Plus implements Expression {  
  
    @Override  
    public int interpret(Context context) {  
        return context.getNum1()+context.getNum2();  
    }  
}  
[java] view plaincopy
public class Minus implements Expression {  
  
    @Override  
    public int interpret(Context context) {  
        return context.getNum1()-context.getNum2();  
    }  
}  
[java] view plaincopy
public class Context {  
      
    private int num1;  
    private int num2;  
      
    public Context(int num1, int num2) {  
        this.num1 = num1;  
        this.num2 = num2;  
    }  
      
    public int getNum1() {  
        return num1;  
    }  
    public void setNum1(int num1) {  
        this.num1 = num1;  
    }  
    public int getNum2() {  
        return num2;  
    }  
    public void setNum2(int num2) {  
        this.num2 = num2;  
    }  
      
      
}  
[java] view plaincopy
public class Test {  
  
    public static void main(String[] args) {  
  
        // 計算9+2-8的值  
        int result = new Minus().interpret((new Context(new Plus()  
                .interpret(new Context(92)), 8)));  
        System.out.println(result);  
    }  
}  
最後輸出正確的結果:3。
 
 
 
 
 
 
 
基本就這樣,解釋器模式用來做各種各樣的解釋器,如正則表達式等的解釋器等等!
設計模式基本就這麼大概講完了,總體感覺有點簡略,的確,這麼點兒篇幅,不足以對整個23種設計模式做全面的闡述,此處讀者可將它作爲一個理論基礎去學習,通過這四篇博文,先基本有個概念,雖然我講的有些簡單,但基本都能說明問題及他們的特點,如果對哪一個感興趣,可以繼續深入研究!同時我也會不斷更新,儘量補全遺漏、修正不足,歡迎廣大讀者及時提出好的建議,我們一起學習!項目中涉及到的代碼,已經放到了我的資源裏:http://download.csdn.net/detail/zhangerqing/4835830(因爲我不喜歡不勞而獲,所以沒有免積分,只設置了5個,如果有人實在沒積分又急要,那麼聯繫我吧,我給你發過去)。
50.談談你對框架的理解,設計框架的時候你是怎麼考慮的,重構項目的時候你都遵循什麼原則。

1.框架不要爲應用做過多的假設
關於框架爲應用做過多的假設,
一個非常具體的現象就是,
框架越俎代庖,
把本來是應
用要做的事情攬過來自己做。
這是一種典型的吃力不討好的做法。
框架越俎代庖,
也許會使
得某一個具體應用的開發變得簡單,
卻會給其它更多想使用該框架的應用增加了本沒有必要
的束縛和負擔。
2.使用接口,保證框架提供的所有重要實現都是可以被替換的
 框架終究不是應用,
所以框架無法考慮所有應用的具體情況,
保證所有重要的組件的實
現都是可以被替換的,
這一點非常重要,
它使得應用可以根據當前的實際情況來替換掉框架
提供的部分組件的默認實現。
使用接口來定義框架中各個組件及組件間的聯繫,
將提高框架
的可複用性。
 3.框架應當簡潔、一致、且目標集中
 框架應當簡潔,
不要包含那些對框架目標來說無關緊要的東西,
保證框架中的每個組件
的存在都是爲了支持框架目標的實現。包含過多無謂的元素(類、接口、枚舉等)
,會使框
架變得難以理解,
嘗試將這些對於框架核心目標不太重要的元素轉移到類庫中,
可以使得框
架更清晰、目標更集中。
 4.提供一個常用的骨架,但是不要固定骨架的結構,使骨架也是可以組裝的
 比如說,
如果是針對某種業務處理的框架,
那麼框架不應該只提供一套不可變更的業務
處理流程,而是應該將處理流程
單步
化,使得各個步驟是可以重新組裝的,如此一來,應
用便可以根據實際情況來改變框架默認的處理流程。
這種框架的可定製化能力可以極大地提
高框架的可複用性。
 5.不斷地重構框架
 如果說設計和實現一個高質量的框架有什麼祕訣?答案只有一個,重構、不斷地重構。
重構框架的實現代碼、
甚至重構框架的設計。
重構的驅動力源於幾個方面,
比如對要解決的
本質問題有了更清晰準備的認識,在使用框架的時候發現某些組件職責不明確、難以使用,
框架的層次結構不夠清晰等。
51.LRU算法

假設 序列爲 4 3 4 2 3 1 4 2
物理塊有3個 則
首輪 4調入內存 4
次輪 3調入內存 3 4
之後 4調入內存 4 3
之後 2調入內存 2 4 3
之後 3調入內存 3 2 4
之後 1調入內存 1 3 2(因爲最少使用的是4,所以丟棄4)
之後 4調入內存 4 1 3(原理同上)
最後 2調入內存 2 4 1 
在指定內存中如果超過內存剔除最近最少用的。

51.自定義控件的生命週期



onFinishInflate() 當View中所有的子控件均被映射成xml後觸發 
onMeasure( int ,  int ) 確定所有子元素的大小 
onLayout( boolean ,  int ,  int ,  int ,  int ) 當View分配所有的子元素的大小和位置時觸發     
onSizeChanged( int ,  int ,  int ,  int ) 當view的大小發生變化時觸發  
onDraw(Canvas) view渲染內容的細節  
onKeyDown( int , KeyEvent) 有按鍵按下後觸發  
onKeyUp( int , KeyEvent) 有按鍵按下後彈起時觸發  
onTrackballEvent(MotionEvent) 軌跡球事件  
onTouchEvent(MotionEvent) 觸屏事件  
onFocusChanged( boolean ,  int , Rect) 當View獲取或失去焦點時觸發   
onWindowFocusChanged( boolean ) 當窗口包含的view獲取或失去焦點時觸發  
onAttachedToWindow() 當view被附着到一個窗口時觸發  
onDetachedFromWindow() 當view離開附着的窗口時觸發,Android123提示該方法和  onAttachedToWindow() 是相反的。  
onWindowVisibilityChanged( int ) 當窗口中包含的可見的view發生變化時觸發 

20
 
0
20
 
0
onFinishInflate() 當View中所有的子控件均被映射成xml後觸發 
onMeasure( int ,  int ) 確定所有子元素的大小 
onLayout( boolean ,  int ,  int ,  int ,  int ) 當View分配所有的子元素的大小和位置時觸發     
onSizeChanged( int ,  int ,  int ,  int ) 當view的大小發生變化時觸發  
onDraw(Canvas) view渲染內容的細節  
onKeyDown( int , KeyEvent) 有按鍵按下後觸發  
onKeyUp( int , KeyEvent) 有按鍵按下後彈起時觸發  
onTrackballEvent(MotionEvent) 軌跡球事件  
onTouchEvent(MotionEvent) 觸屏事件  
onFocusChanged( boolean ,  int , Rect) 當View獲取或失去焦點時觸發   
onWindowFocusChanged( boolean ) 當窗口包含的view獲取或失去焦點時觸發  
onAttachedToWindow() 當view被附着到一個窗口時觸發  
onDetachedFromWindow() 當view離開附着的窗口時觸發,Android123提示該方法和  onAttachedToWindow() 是相反的。  
onWindowVisibilityChanged( int ) 當窗口中包含的可見的view發生變化時觸發 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章