第十三章主要講了一些日常開發(fā)中常用的技巧
一洛勉、全局獲取 Context 的技巧
??截止到目前,我們還沒有碰見過為得不到 Context 而發(fā)愁的情況纵穿,因?yàn)槲覀兒芏嗟牟僮鞫际窃诨顒?dòng)中進(jìn)行的下隧,而活動(dòng)本身就是一個(gè) Context 對(duì)象,但是谓媒,當(dāng)應(yīng)用程序的架構(gòu)逐漸復(fù)雜起來的時(shí)候淆院,很多邏輯代碼都將脫離 Activity 類,但此時(shí)你又恰恰需要使用 Context句惯,這就傷腦筋了土辩。
??舉個(gè)例子,你寫了一個(gè) Util 類抢野,該類的某個(gè)方法需要用到 Context拷淘,怎么辦?也許你會(huì)想到通過方法傳遞進(jìn)來指孤,還是用代碼來說明吧启涯。
public class Util {
public static void needContext(){
//下面的邏輯處理需要用到 Context 對(duì)象
}
}
??大多數(shù)人的第一反應(yīng)就是通過方法的參數(shù)傳遞進(jìn)來
public class Util {
public static void needContext(Context context){
//下面的邏輯處理需要用到 Context 對(duì)象
}
}
??是的,沒問題恃轩,可以這樣寫结洼,但是如果調(diào)用方也獲取不到 Context 怎么辦?我們可以使用另一種更好的方法來解決這個(gè)問題叉跛。
Android 提供了一個(gè) Application 類松忍,每當(dāng)應(yīng)用程序啟動(dòng)的時(shí)候,系統(tǒng)就會(huì)自動(dòng)將這個(gè)類進(jìn)行初始化昧互。我們也可以定制一個(gè)自己的 Application 類挽铁,以便于管理程序內(nèi)一些全局的狀態(tài)信息伟桅,比如全局的 Context.
??首先你需要新建一個(gè)類敞掘,繼承自 Application
/**
* 自定義 Application
*/
public class MyApplication extends Application {
private static Context mContext;
/**
* 通過靜態(tài)方法獲取 Context
*/
public static Context getContext(){
return mContext;
}
/**
* 重寫 onCreate() 方法
*/
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
}
??接下來我們需要告知系統(tǒng),當(dāng)程序啟動(dòng)的時(shí)候應(yīng)該初始化 MyApplication 類楣铁,而不是默認(rèn)的 Application 類玖雁。
<!-- 這里注意,一定是全包名盖腕,或者前面有個(gè)" . "赫冬,否則系統(tǒng)找不到這個(gè)類 -->
<application
android:name=".MyApplication"
...>
...
</application>
??接下來,我們就可以在 Util 類里面這樣獲取 Context 了溃列。
public class Util {
public static void needContext(){
//下面的邏輯處理需要用到 Context 對(duì)象
Context context = MyApplication.getContext();
}
}
二劲厌、使用 Intent 傳遞對(duì)象
??我們可以借助 Intent 的 putExtra() 方法來傳遞數(shù)據(jù),里面可以傳遞boolean听隐、byte补鼻、short、int、long风范、float咨跌、double、char硼婿、String锌半,以及前面這些數(shù)據(jù)類型所對(duì)應(yīng)的數(shù)組形式的數(shù)據(jù)類型
,那么傳遞對(duì)象呢寇漫?
??putExtra() 方法還有兩個(gè)數(shù)據(jù)類型:Serializable 和 Parcelable
- Serializable 方式:
??Serializable 是序列化的意思刊殉,表示將一個(gè)對(duì)象轉(zhuǎn)換成可存儲(chǔ)或可傳輸?shù)臓顟B(tài)。序列化后的對(duì)象可以在網(wǎng)絡(luò)上進(jìn)行傳輸州胳,也可以存儲(chǔ)到本地冗澈。至于序列化的方法也很簡(jiǎn)單,只需要讓類實(shí)現(xiàn) Serializable 接口即可陋葡。
- Serializable 方式:
/**
* 實(shí)體類
*/
public class Fruit implements Serializable{
...
}
??使用 Intent 傳遞對(duì)象
//發(fā)送方
Fruit fruit = new Fruit();
fruit.setName("蘋果");
Intent intent = new Intent(this,MainActivity.class);
intent.putExtra("fruit_data",fruit);
startActivity(intent);
//接收方
/*
通過 getSerializable() 方法獲取序列化對(duì)象亚亲,再向下轉(zhuǎn)型成 Fruit 對(duì)象即可
*/
Fruit fruit_data = (Fruit) getIntent().getSerializableExtra("fruit_data");
- Parcelable 方式:
??不同于 Serializable ,Parcelable 方式的實(shí)現(xiàn)原理是將一個(gè)完整的對(duì)象進(jìn)行分解腐缤,而分解后的每一部分都是 Intent 所支持的數(shù)據(jù)類型捌归,這樣就實(shí)現(xiàn)傳遞對(duì)象的功能了。
??實(shí)現(xiàn) Parcelable 的方式首先也是讓一個(gè)類去實(shí)現(xiàn) Parcelable 接口岭粤。
- Parcelable 方式:
/**
* 實(shí)體類
*/
public class Fruit implements Parcelable {
private String name;//水果的名字
private int imageId;//水果對(duì)應(yīng)資源的圖片id
public Fruit() {
}
public Fruit(String name, int imageId) {
this.name = name;
this.imageId = imageId;
}
protected Fruit(Parcel in) {
/*
這里注意惜索,讀取的順序一定要和剛才寫入的順序一致。
*/
name = in.readString();
imageId = in.readInt();
}
/**
* 必須重寫的方法
*/
@Override
public int describeContents() {
//這里直接返回 0 就可以了
return 0;
}
/**
* 必須重寫的方法
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
/*
將字段一一寫出
字符串調(diào)用 writeString()剃浇,整數(shù)調(diào)用 writeInt() 依此類對(duì)
*/
dest.writeString(name);
dest.writeInt(imageId);
}
/**
* 定義一個(gè)常量巾兆,創(chuàng)建 Parcelable.Creator 接口的實(shí)現(xiàn),并且指定泛型為當(dāng)前類虎囚,
*/
public static final Creator<Fruit> CREATOR = new Creator<Fruit>() {
@Override
public Fruit createFromParcel(Parcel in) {
return new Fruit(in);
}
@Override
public Fruit[] newArray(int size) {
return new Fruit[size];
}
};
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getImageId() {
return imageId;
}
public void setImageId(int imageId) {
this.imageId = imageId;
}
@Override
public String toString() {
return "Fruit{" +
"name='" + name + '\'' +
", imageId=" + imageId +
'}';
}
}
??具體的使用方法和 Serializable 類似角塑,只是在獲取的時(shí)候,只需要把 getSerializableExtra() 方法換成 getParcelableExtra() 方法即可淘讥。
注意:Serializable 的方式較為簡(jiǎn)單圃伶,但由于會(huì)把整個(gè)對(duì)象進(jìn)行序列化,因此效率會(huì)比 Parcelable 方式低一些蒲列,所以在通常情況下還是更加推薦使用 Parcelable 的方式來實(shí)現(xiàn) Intent 傳遞對(duì)象的功能窒朋。
三、定制自己的日志工具
??理想狀態(tài)下蝗岖,我們的日志打印是可以控制的侥猩,當(dāng)程序處于開發(fā)階段時(shí)就讓日志打印出來,當(dāng)程序上線了之后就把日志屏蔽掉抵赢。
/**
* 日志工具類
*/
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;
/*
這里通過改變 level 的值來控制打印日志的行為欺劳,比如:
讓 level 等于 VERBOSE 就是打印所有日志洛退,讓 level 等于
WARN 就是只打印警告以上級(jí)別的日志,讓 level 等于
NOTHING 就是把所有日志都屏蔽掉杰标。
*/
public static 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.v(tag, msg);
}
}
public static void i(String tag, String msg) {
if (level <= INFO) {
Log.v(tag, msg);
}
}
public static void w(String tag, String msg) {
if (level <= WARN) {
Log.v(tag, msg);
}
}
public static void e(String tag, String msg) {
if (level <= ERROR) {
Log.v(tag, msg);
}
}
}
四兵怯、創(chuàng)建定時(shí)任務(wù)
??Android 中定時(shí)任務(wù)一般有兩種實(shí)現(xiàn)方式,一種是使用 Java API 里提供的 Timer 類腔剂,一種是使用 Android 的 Alarm 機(jī)制媒区。在大多數(shù)情況下,這兩種方式都能實(shí)現(xiàn)類似的效果掸犬,但是 Timer 有一個(gè)明顯的短板袜漩,它并不太適用于那么些需要長(zhǎng)期在后臺(tái)運(yùn)行的定時(shí)任務(wù),為了讓電池更加耐用湾碎,每種手機(jī)都會(huì)有自己的休眠策略宙攻,Android 手機(jī)會(huì)在長(zhǎng)時(shí)間不操作的情況下自動(dòng)讓 CPU 進(jìn)入休眠功狀態(tài),這就可能導(dǎo)致 Timer 中的定時(shí)任務(wù)無法正常運(yùn)行介褥。而 Alarm 則具有喚醒 CPU 的功能座掘,它可以保證在大多數(shù)情況下需要執(zhí)行定時(shí)任務(wù)的時(shí)候 CPU 都能正常工作,需要注意柔滔,這里喚醒 CPU 喚醒屏幕完全不是一個(gè)概念溢陪。
Alarm機(jī)制
??主要是借助了 AlarmManager 類來實(shí)現(xiàn),這個(gè)類 NotificationManager 有點(diǎn)類似睛廊。
/*
SystemClock.elapsedRealtime() 方法可以獲取到系統(tǒng)開機(jī)至今所經(jīng)歷時(shí)間的毫秒數(shù)
System.currentTimeMillis() 方法獲取到的是1970年1月1日0點(diǎn)至今所經(jīng)時(shí)間的毫秒數(shù)
*/
long triggerAtTime = SystemClock.elapsedRealtime() + 10 * 1000;
AlarmManager manager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
/*
參1:用于指定AlarmManager的工作類型形真,有四種值可選:
1.ELAPSED_REALTIME :表示讓定時(shí)任務(wù)的觸發(fā)時(shí)間從系統(tǒng)開機(jī)開始算起,但不會(huì)喚醒CPU
2.ELAPSED_REALTIME_WAKEUP :表示讓定時(shí)任務(wù)的觸發(fā)時(shí)間從系統(tǒng)開機(jī)開始算起超全,但會(huì)喚醒CPU
3.RTC :表示讓定時(shí)任務(wù)的觸發(fā)時(shí)間從1970年1月1日0時(shí)開始算起咆霜,但不會(huì)喚醒CPU
4.RTC_WAKEUP :表示讓定時(shí)任務(wù)的觸發(fā)時(shí)間從1970年1月1日0時(shí)開始算起,但會(huì)喚醒CPU
*/
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
??如果我們需要實(shí)現(xiàn)一個(gè)長(zhǎng)時(shí)間在后臺(tái)定時(shí)運(yùn)行的服務(wù)要怎么做嘶朱?
public class LongRunningService extends Service {
public LongRunningService() {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
//處理具體的邏輯
}
}).start();
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
int anHour = 60 * 60 * 1000;//一小時(shí)的毫秒數(shù)
//設(shè)置時(shí)間為一小時(shí)后執(zhí)行
long triggerAtTime = SystemClock.elapsedRealtime() + anHour;
Intent i = new Intent(this,LongRunningService.class);
PendingIntent pendingIntent = PendingIntent.getService(this,0,i,0);
//執(zhí)行定時(shí)任務(wù)蛾坯,這樣會(huì)使得 LongRunningService 的onStartCommand() 方法每隔一小時(shí)就執(zhí)行一次
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);
return super.onStartCommand(intent, flags, startId);
}
}
注意:從Android 4.4 系統(tǒng)開始, Alarm 任務(wù)的觸發(fā)時(shí)間將變得不準(zhǔn)確见咒,有可能會(huì)延遲一段時(shí)間后任務(wù)才能得到執(zhí)行偿衰。這不是 bug挂疆,而是系統(tǒng)在耗電性方面進(jìn)行的優(yōu)化改览。系統(tǒng)會(huì)自動(dòng)檢測(cè)目前有多少 Alarm 任務(wù)存在,然后將觸發(fā)時(shí)間相近的幾個(gè)任務(wù)放在一起執(zhí)行缤言,這樣可以大幅度減少 CPU 被喚醒的次數(shù)宝当,從而有效延長(zhǎng)電池的使用時(shí)間。
??如果你要求 Alarm 任務(wù)的執(zhí)行事件必須準(zhǔn)確無誤胆萧,就要使用 AlarmManager 的 setExact() 方法代替 set() 方法庆揩。
Doze 模式
??Doze模式是Android6.0系統(tǒng)中新加入的模式俐东,從而可以極大幅度地延長(zhǎng)電池的使用壽命。
??Doze模式就是當(dāng)設(shè)備是 Android 6.0 及以上系統(tǒng)時(shí)订晌,如果該設(shè)備未插接電源虏辫,處于靜止?fàn)顟B(tài)(Android 7.0 中刪除了這一條件),且屏幕關(guān)閉了一段時(shí)間后锈拨,就會(huì)進(jìn)入到 Doze 模式砌庄,在 Doze 模式下,系統(tǒng)會(huì)對(duì) CPU奕枢、網(wǎng)絡(luò)娄昆、Alarm 等活動(dòng)進(jìn)行限制,從而延長(zhǎng)電池的使用壽命缝彬。
??當(dāng)然萌焰,系統(tǒng)并不會(huì)一直處于 Doze 模式,而是會(huì)間歇性地退出 Doze 模式一小段時(shí)間谷浅,在這段時(shí)間中扒俯,應(yīng)用就可以去完成他們的同步操作、Alarm任務(wù)
注意:隨著設(shè)備進(jìn)入 Doze 模式的時(shí)間越長(zhǎng)一疯,間歇性地退出 Doze 模式的時(shí)間間隔也會(huì)越長(zhǎng)陵珍,因?yàn)槿绻O(shè)備長(zhǎng)時(shí)間不使用的話,是沒有必要頻繁退出 Doze 模式來執(zhí)行同步等操作的违施。其中 Doze 模式下會(huì)受到限制的功能:
1.網(wǎng)絡(luò)訪問被禁止
2.系統(tǒng)忽略喚醒CPU或屏幕操作
3.系統(tǒng)不在執(zhí)行WIFI掃描
4.系統(tǒng)不再執(zhí)行同步服務(wù)
5.Alarm任務(wù)將會(huì)在下次退出Doze模式的時(shí)候執(zhí)行
??注意最后一條互纯,如果在 Doze 模式下,我們的任務(wù)將會(huì)變得不準(zhǔn)時(shí)
??如果你要求Alarm任務(wù)即使在Doze模式下也必須正常執(zhí)行磕蒲,Android還提供了解決方案留潦。調(diào)用 AlarmManager 的setAndAllowWhileIdle() 或者 setExactAndAllowWhileIdle() 方法就能讓定時(shí)任務(wù)即使在 Doze 模式下也能正常執(zhí)行了,這兩個(gè)方法的區(qū)別和 set()與setEsact()方法之間的區(qū)別是一樣的辣往。
五兔院、多窗口模式編程
??Android 7.0 系統(tǒng)中引入的特色功能,允許我們?cè)谕粋€(gè)屏幕中同時(shí)打開兩個(gè)應(yīng)用程序站削。
??如何進(jìn)入多窗口模式坊萝?一般手機(jī)底部按鍵有三個(gè),左邊的是返回鍵许起,右邊的是HOME鍵十偶,右邊的是Overview鍵,點(diǎn)一下Overview鍵园细,會(huì)打開一個(gè)最近訪問過的活動(dòng)或任務(wù)的列表界面惦积。
??那么我們可以通過兩種方式進(jìn)入多窗口模式:
- 1.在 Overview 列表界面長(zhǎng)按任意一個(gè)活動(dòng)的標(biāo)題,將該活動(dòng)拖動(dòng)到屏幕突出顯示的區(qū)域猛频,則可以進(jìn)入多窗口模式狮崩。
-
2.打開任意一個(gè)程序蛛勉,長(zhǎng)按 Overview 按鈕,也可以進(jìn)入多窗口模式(沒有試出來)
??另外睦柴,我們還可以旋轉(zhuǎn)诽凌,使上下分屏變成左右分屏
??如果想要退出多窗口模式,只要將屏幕中央的分割線向屏幕任意方向拖動(dòng)到底即可坦敌。
多窗口模式下的生命周期
??多窗口模式不會(huì)改變活動(dòng)原有的生命周期皿淋,只會(huì)將正和用戶交互的那個(gè)活動(dòng)設(shè)置為運(yùn)行狀態(tài),將另一個(gè)活動(dòng)設(shè)置為暫停狀態(tài)恬试。
??我們新建兩個(gè)項(xiàng)目窝趣,并且將這兩個(gè)項(xiàng)目的啟動(dòng) Activity 的生命周期都進(jìn)行打印。
??先運(yùn)行第一個(gè)項(xiàng)目
??在運(yùn)行第二個(gè)項(xiàng)目
??接下來我們進(jìn)入到多窗口模式
??可以看到训柴,第一個(gè)項(xiàng)目先銷毀然后重新創(chuàng)建哑舒,因?yàn)檫M(jìn)入多窗口模式后活動(dòng)的大小發(fā)生了比較大的變化,默認(rèn)是會(huì)重新創(chuàng)建活動(dòng)的幻馁。由于我進(jìn)入多窗口模式后又讓該項(xiàng)目獲取了焦點(diǎn)洗鸵,所以多執(zhí)行了一個(gè)onResume(),接下來將第二個(gè)項(xiàng)目從 Overview 列表中選中仗嗦。
??這時(shí)候只要我們點(diǎn)擊任意一個(gè)項(xiàng)目膘滨,就會(huì)切換這兩個(gè)項(xiàng)目的暫停還是活動(dòng)的狀態(tài)。
??一個(gè)onPause稀拐,另一個(gè)onResume
注意:在多窗口模式下我們需要考慮一些關(guān)鍵性的點(diǎn)火邓,比如說,在多窗口模式下德撬,用戶仍然可以看到處于暫停狀態(tài)的應(yīng)用铲咨,如果是播放器之類的應(yīng)用,此時(shí)就應(yīng)該能繼續(xù)播放視頻才對(duì)蜓洪。因此纤勒,我們最好不要在活動(dòng)的 onPause() 方法中去處理視頻播放器的暫停邏輯,應(yīng)該在 onStop() 方法中處理隆檀,并且在 onStart() 方法中恢復(fù)視頻播放摇天。
注意:如果想改變進(jìn)入多窗口模式時(shí)活動(dòng)會(huì)被重新創(chuàng)建這一默認(rèn)行為,可以在清單文件中進(jìn)行如下配置恐仑,加入了如下配置后泉坐,不管是進(jìn)入多窗口模式,還是橫豎屏切換菊霜,活動(dòng)都不會(huì)被重新創(chuàng)建坚冀,而是會(huì)將屏幕發(fā)生變化的事件通知到 Activity 的 onConfigurationChanged() 方法當(dāng)中
<activity android:name=".MainActivity"
android:configChanges="orientation|keyboardHidden|screenSize|screenLayout">
</activity>
禁用多窗口模式
??如果你不希望應(yīng)用能夠在多窗口模式下運(yùn)行,就可以將這個(gè)功能關(guān)閉掉鉴逞,禁用多窗口模式的方法很簡(jiǎn)單记某,只需要在清單文件中的 <application> 或 <activity> 標(biāo)簽中加入如下屬性即可:
android:resizeableActivity="false"
,其中如果傳 true 表示支持多窗口模式构捡,也是默認(rèn)的情況液南,如果傳入 false 表示不支持多窗口模式。
注意:雖然說 android:resizeableActivity 屬性的用法很簡(jiǎn)單勾徽,但是他有一個(gè)很嚴(yán)重的問題滑凉,就是這個(gè)屬性只有當(dāng)項(xiàng)目的 targetSdkVersion 指定成 24 或更高的時(shí)候才會(huì)有用,否則這個(gè)屬性是無效的喘帚。
??解決方案:Android 規(guī)定畅姊,如果項(xiàng)目指定 targetSdkVersion 低于 24,并且活動(dòng)是不允許橫豎屏切換的吹由,那么該應(yīng)用也將不支持多窗口模式若未。
??那么讓應(yīng)用不允許橫豎屏切換,只需要在清單文件的 <activity> 標(biāo)簽中加入如下配置即可:android:screenOrientation="portrait" 或 android:screenOrientation="landscape"
倾鲫,其中 portrait 表示活動(dòng)只支持豎屏粗合,landscape 表示活動(dòng)只支持橫屏。
六乌昔、Lambda表達(dá)式
??Java 8 中引入了一些非常有特色的功能隙疚,如 Lambda 表達(dá)式、stream API磕道、接口默認(rèn)實(shí)現(xiàn)等等供屉。其中,stream API 和接口默認(rèn)實(shí)現(xiàn)等特性都只支持 Android 7.0 及以上的系統(tǒng)溺蕉,而 Lambda 表達(dá)式最低兼容到 Android 2.3 系統(tǒng)贯卦,所以我們來學(xué)習(xí)一下 Lambda 表達(dá)式。
Lambda 表達(dá)式本質(zhì)上是一個(gè)匿名方法焙贷,它既沒有方法名撵割,也沒有訪問修飾符和返回值類型,使用它來編寫代碼將會(huì)更加簡(jiǎn)潔辙芍,更加易讀啡彬。
??如果要在 Android 項(xiàng)目中使用 Labmda 表達(dá)式或者 Java 8中的其他新特性,需要在 app/build.gradle 中添加如下配置:
android {
...
defaultConfig {
...
jackOptions.enabled = true;
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
注意:如果你的項(xiàng)目中有 Android Library 模塊故硅,那么上述方式是不能引用成功的庶灿,我們首先要在最外層的 project build.gradle 中加入這句話
classpath 'me.tatarka:gradle-retrolambda:3.2.0'
然后在要使用 lambda 表達(dá)式的 Module 的 build.gradle 中加入如下代碼
apply plugin: 'me.tatarka.retrolambda'
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
??寫個(gè)簡(jiǎn)單的例子:
//開啟線程傳統(tǒng)寫法
new Thread(new Runnable() {
@Override
public void run() {
//具體處理邏輯
}
}).start();
//開啟線程 Lambda 表達(dá)式
new Thread(() -> {
//具體處理偶偶機(jī)
}).start();
??能這樣寫是因?yàn)?Thread 類的構(gòu)造函數(shù)接收的參數(shù)是一個(gè) Runnable 接口,并且改接口中只有一個(gè)待實(shí)現(xiàn)的方法吃衅。
注意:凡是這種只有一個(gè)待實(shí)現(xiàn)方法的接口往踢,都可以使用 Lambda 表達(dá)式的寫法
//傳統(tǒng)寫法
Runnable runnable = new Runnable() {
@Override
public void run() {
//具體處理邏輯
}
};
//Lambda 寫法
Runnable runnable1 = () -> {
//具體處理邏輯
};
??接下來,我們自定義一個(gè)接口徘层,再使用 Lambda 表達(dá)式的方式進(jìn)行實(shí)現(xiàn)峻呕。
/**
* 定義一個(gè)接口
*/
public interface MyListener {
//注意利职,該方法有返回值,有參數(shù)
String doSomething(String a,int b);
}
//Lambda 表達(dá)式寫法
MyListener myListneer2 = (String a,int b) -> {
return a + b;
};
/*
還可以精簡(jiǎn)成這樣
這里瘦癌,Java 會(huì)自動(dòng)判斷出 a 和 b 的數(shù)據(jù)類型
*/
MyListener myListener2 = (a, b) -> {
return a + b;
};
??假如有一個(gè)方法猪贪,需要傳入 MyListener 接口
public void hello(MyListener mListener) {
String a = "Hello Lambda";
int b = 1024;
String result = mListener.doSomething(a, b);
Log.d(TAG, "hello: " + result);
}
//傳統(tǒng)寫法
hello(new MyListener() {
@Override
public String doSomething(String a, int b) {
return a + b;
}
});
//Lambda 表達(dá)式
hello((a, b) -> {
return a + b;
});
??Lambda表達(dá)式的基本用法已經(jīng)掌握了,接下來看一看在 Android 中有哪些常用的功能是可以使用 Lambda 表達(dá)式替換的讯私。
??其實(shí)热押,只要符合接口中只有一個(gè)待實(shí)現(xiàn)方法這個(gè)規(guī)則的功能,都是可以使用 Lambda 表達(dá)式來編寫的斤寇,除了開啟子線程之外桶癣,還有點(diǎn)擊事件之類的功能。
//傳統(tǒng)寫法
findViewById(R.id.tv).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//處理點(diǎn)擊事件
}
});
//Lambda 表達(dá)式
findViewById(R.id.tv).setOnClickListener((v) -> {
//處理點(diǎn)擊事件
});
注意:另外娘锁,當(dāng)接口的待實(shí)現(xiàn)方法有且只有一個(gè)參數(shù)的時(shí)候牙寞,我們還可以進(jìn)一步簡(jiǎn)化,將參數(shù)外面的括號(hào)去掉
//Lambda 表達(dá)式進(jìn)一步簡(jiǎn)化
findViewById(R.id.tv).setOnClickListener(v -> {
//處理點(diǎn)擊事件
});