這一章學到的:
- 全局獲取Context的技巧
- 使用Intent傳遞對象
- Serialiable方式
- Parcelable方式
- 定制自己的日志工具
- 調試Android程序
13.1 全局獲取Context的技巧
當應用程序的架構逐漸開始復雜起來的時候,很多的邏輯代碼都將脫離Activity類,但此時你又恰恰需要使用Context,也許這個時候你就會感到有些傷腦經了。
在某些情況下,獲取Context并非是那么一件事,下面我們就來學習一種技巧,讓你在項目的任何地方都能夠輕松獲取到Context。
Android提供了一個Application類,每當應用程序啟動的時候,系統就會自動將這個類進行初始化。而我們可以定制一個自己的Aoolication類,以便于管理程序內一些全局的狀態信息,比如說全局Context。
定制一個自己的Application其實并不困難,首先我們需要創建一個MyApplication類繼承自Application。
代碼如下:
public class MyApplication extends Application
{
private static Context mContext;
@Override
public void onCreate()
{
mContext = getApplicationContext();
}
public static Context getContext()
{
return mContext;
}
}
這里我們重寫了父類的onCreate()方法,并通過調用getApplicationContext()方法得到了一個應用程序級別的Context,然后又提供了一個靜態的getContext()方法,在這里將剛才獲取到的Context進行返回。
注意:getContext()返回的也要是一個static的變量,所以mContext要加static。
接下來我們需要告知系統,當程序啟動的時候應該初始化MyApplication類,而不是默認的Application類。這一步也很簡單,在AndroidManifest.xml文件的<application>標簽下進行指定就可以了。
代碼如下所示:
<application
········
android:name="com.example.wumen.getcontext.MyApplication">
·········
</application>
注意這里在指定MyApplication的時候,一定要加上完整的包名,不然系統將無法找到這個類。
這樣我們就已經實現了一種全局獲取Context的機制,之后不管你想在項目的任何地方使用Context,只需要調用一下MyApplication.getContext()就可以了。
任何一個項目都只能配置一個Application。
13.2 使用Intent傳遞對象
我們可以借助Intent來啟動活動,發送廣播,啟動服務等。在進行上述操作的時候,我們還可以在Intent中添加一些附加數據,已達到傳值的效果。
putExtra()方法中所支持的數據類型是有限的,雖然常用的一些數據類型它都會支持,但是當你想去傳遞一些自定義對象的時候,就會發現無從下手。
13.2.1 Serializable方式(誰瑞來日報)
Serializable是序列化的意思,表示將一個對象轉換成可存儲或可傳輸的狀態。序列化后的對象可以在網絡上進行傳輸,也可以存儲到本地。至于序列化的方法也很簡單,只需要讓一個類去實現Serializable這個借口就可以了。
比如說Person類:
public class Person implements Serializable
{
private String name;
private int age;
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age = age;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
讓Person類去實現了Serializable接口,這樣所有的Person對象就都是可序列化的了。
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("person_data",person);
startActivity(intent);
這里我們創建了一個Person的實例,然后就直接將它傳入到putExtra()方法中了。由于Person類實現了Serializable接口,所以才可以這樣寫。
Intent intent = getIntent();
Person person = (Person) intent.getSerializableExtra("person_data");
這里調用了getSerializableExtra()方法來獲取通過參數傳遞過來的序列化對象,接著再將它向下轉型成Person對象,這樣我們就成功實現了使用Intent來傳遞對象的功能了。
13.2.2 Parcelable方式(撲瑞賽了包)
不同于將對象進行序列化,Parcelable
方式的實現原理是將一個完整的對象進行分解,而分解后的每一部分都是Intent
所支持的數據類型,這樣也就實現傳遞對象的功能了。
public class Person implements Parcelable
{
private String name;
private int age;
············
@Override
public int describeContents()
{
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags)
{
dest.writeString(name);//寫出name
dest.writeInt(age);//寫出age
}
public static final Creator<Person> CREATOR = new Creator<Person>()
{
@Override
public Person createFromParcel(Parcel in)
{
Person person = new Person();
person.name = in.readString();//讀取name
person.age = in.readInt();//讀取age
return person;
}
@Override
public Person[] newArray(int size)
{
return new Person[size];
}
};
}
Parcelable
的實現方式要復雜一些。首先我們讓Person
類去實現了Parcelable
接口,這樣就必須重寫describeContents()
和writeToParcel()
這兩個方法。其中describeContents()
方法直接返回0就可以了,而writeToParcel()
方法中我們需要調用Parcel
的writeXxx()
方法,將Person
類中的字段一一寫出。注意,字符串型數據就調用writeString()
方法,整型數據就調用writeInt()
方法,依此類推。
除此之外,我們還必須在Person
類中提供一個名為CREATOR
的常量,這里創建了Parcelable.Creator
接口的一個實現,并將泛型指定為Person
。接著需要重寫createFromParcel()
和newArray()
這兩個方法,在createFromParcel()
方法中我們要去讀取剛才寫出的name
和age
字段,并創建一個Person
對象進行返回,其中name
和age
都是調用Parcel
的readXxx()
方法讀取到的,注意這里讀取的順序一定要和剛才寫出的順序完全一致。而newArray()
方法中的實現就簡單多了,只需要new
出一個Person
數組,并使用方法中傳入的size
作為數組大小就可以了。
Person person = new Person();
person.setName("Tom");
person.setAge(20);
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("person_data",person);
startActivity(intent);
我們仍然可以使用相同的代碼來傳遞Person
對象,只不過在獲取對象的時候需要稍加改動。
Intent intent = getIntent();
Person person = (Person) intent.getParcelableExtra("person_data");
對比一下,Serializable
的方式較為簡單,但由于會把整個對象進行序列化,因此效率會比 Parcelable
方式低一些,所以在通常情況下還是更加推薦使用 Parcelable
的方式來實現Intent
傳遞對象的功能。
13.3 定制自己的日志工具
最理想的情況是能夠自由地控制日志的打印,當程序處于開發階段時就讓日志打印出來,當程序上線了之后就把日志屏蔽掉。
看起來好像是挺高級的一個功能,其實并不復雜,我們只需要定制一個自己的日志工具就可以輕松完成了。比如新建一個LogUtil類,代碼如下所示:
public class LogUtil
{
public static final int VERBOSE = 1;
public static final int DEBUG = 2;
public static final int INFO = 3;
public static final int WARN = 4;
public static final int ERROR = 5;
public static final int NOTHING = 6;
public static final int level = VERBOSE;
public static void v(String tag,String msg)
{
if (level <= VERBOSE)
{
Log.v(tag,msg);
}
}
public static void d(String tag,String msg)
{
if (level <= DEBUG)
{
Log.d(tag,msg);
}
}
public static void i(String tag,String msg)
{
if (level <= INFO)
{
Log.i(tag,msg);
}
}
public static void w(String tag,String msg)
{
if (level <= WARN)
{
Log.w(tag,msg);
}
}
public static void e(String tag,String msg)
{
if (level <= ERROR)
{
Log.e(tag,msg);
}
}
}
我們在LogUtil中先是定義了VERBOSE,DEBUG,INFO,WARN,ERROR,NOTHING這6個整形常量,并且它們對應的值都是遞增的。然后又定義了一個靜態變量level,可以將它的值指定為上面6個常量中的任意一個。
接下里我們提供了v(),d(),i(),w(),e()這5個自定義的日志方法,在其內部分別調用了Log.v(),Log.d(),Log.i(),Log.w(),Log.e()這5個方法來打印日志,只不過在這些自定義的方法中我們都加入了一個if判斷,只有當level的值小于或等于對應日志級別值的時候,才會將日志打印出來。
這樣就把一個自定義的日志工具創建好了,之后在項目里我們可以像使用普通的日志工具一樣使用LogUtil,比如打印一行DEBUG級別的日志就可以這樣寫:
LogUtil.d("Tag","debug log");
然后我們只需要修改level變量的值,就可以自由地控制日志的打印行為了。比如讓level等于VERBOSE就可以把所有的日志都打印出來,讓level等于WARN就可以只打印警告以上級別的日志,讓level等于NOTHING就可以把所有日志都屏蔽掉。
13.4 調試Android程序
當開發過程中遇到一些奇怪的bug,但又遲遲定位不出來原因是什么的時候。最好的解決辦法就是調試了。調試允許我們逐行地執行代碼,并可以實時觀察內存中的數據,從而能夠比較輕易地查出問題的原因。
調試工作的第一步肯定是添加斷點,這里由于我們要調試登陸部分的問題,所以斷點可以加在登錄按鈕的點擊時間里。添加斷點的方法也很簡單,只需要在相應代碼行的左邊點擊一下就可以了。
如果想要取消這個斷點,對著它再次點擊就可以了。
添加好了斷點,接下來就可以對程序進行調試了,點擊Android Studio頂部工具欄中的Debug按鈕(最右邊的按鈕),就會使用調試模式來啟動程序。
在輸入框中輸入賬號和密碼,并點擊Login按鈕,這時Android Studio就會自動打開Debug窗口。
接下來每按一次F8鍵,代碼就會向下執行一行,并且通過Variables視圖還可以看到內存中的數據。
調試完之后點擊Debug窗口中的Stop按鈕(最下邊的按鈕)來結束調試即可。
這種調試方式雖然完全可以正常工作,但在調試模式下,程序的運行效率將會大大地降低,如果你的斷點加在一個比較靠后的位置,需要執行很多的操作才能運行到這個斷點,那么前面這些操作就都會有一些卡頓的感覺。
Android還提供了另外一種調試的方式,可以讓程序隨時進入到調試模式。
這次不需要選擇調試模式來啟動程序了,就使用正常的方式來啟動程序。把賬號和密碼輸入好,然后點擊Android Studio頂部工具欄的Attach debugger to Android process按鈕(最左邊的按鈕)
此時會讓彈出一個進程選擇提示框
這里目前只列出了一個進程,也就是我們當前程序的進程,選中這個進程,然后點擊Ok按鈕,就會讓這個進程進入到調試模式了。
接下來在程序中點擊Login按鈕,Android Studio同樣也會自動打開Debug窗口,之后的流程就都是相同的了。