這些小技巧java
經過上面的這些文章,就把簡單的安卓項目總結了一遍,固然你說懂這些就能夠作Android開發的話仍是不行的,欠缺的還有不少,但欠缺的這些咱們有隻能在工做中去總結以及不斷的提升,這篇文章咱們還有一些小技巧須要咱們總結一下,而後在後面準備作一個完整的實驗項目,讓咱們把學的這些串聯起來,這篇咱們將說說下面這些技巧:android
1、獲取全局Context微信
2、使用Intent傳遞對象網絡
一、Serializable方式app
二、Parcelable方式異步
3、日誌控制ide
4、建立定時任務學習
5、聊聊Doze模式this
6、多窗口debug
七、禁止多窗口模式
8、lambda表達式 這個表達式是JAVA 8 的新特性,咱們直接在後面完整的Demo中使用,用到的時候再具體的說明
獲取全局Context
這裏咱們考慮這樣一個問題,咱們再一個類中進行了一些異步操做,完了以後咱們須要一個Toast提示,這時候咱們須要Context,那咱們有那麼獲取Context。
首先就有這樣一種,咱們直接在初始化這個類的時候傳遞一個Context,的確這樣是能解決問題的,但這不是最好的解決問題的辦法,最好的辦法是咱們獲取一個全局的Context,下面咱們總結如何獲取一個全局的Context。
package com.example.skotc.servicedemo; import android.app.Application; import android.content.Context; /** * Created by skotc on 2018/8/20. */ public class MyApplication extends Application { private static Context context; @Override public void onCreate() { super.onCreate(); context = getApplicationContext(); } public static Context getContext() { return context; } }
上面的代碼咱們就建立了一個MyApplication繼承自Application,而後再之後的使用中咱們就能夠直接調用這個方法獲得全局的Context, MyApplication.getContext()方法獲取獲得Context。
還有一點須要咱們注意一下的,就是在建立了MyApplication以後咱們仍是須要在AndroidManifest.xml中聲明一下。具體的代碼以下,主要的就是這句: android:name=".MyApplication"
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" android:name=".MyApplication" > <activity android:name=".ServiceMainActivity" android:label="@string/app_name" android:theme="@style/AppTheme.NoActionBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application>
使用Intent傳遞對象
Intent相信咱們都比較熟悉了,咱們可使它來啓動活動,發送廣播,啓動廣播等,在進行上述操做的時候,咱們還能夠在Intent中添加一些附加數據,已達到傳值的效果,好比咱們見過的調用 putExtra(鍵,值)方法來添加要傳遞的數據,以後經過調用 getIntent().getStringExtra(鍵)來獲取咱們傳遞的值,經過這種方法咱們能傳遞的對象類型是有限的,也就常見的類型,那咱們有沒有想過,要是須要專遞的是一個自定義的對象的時候呢,咱們該怎樣作?
下面咱們就討論一下這個問題:
一、Serializable方式 (序列化)
Serializable是序列化的意思,表示將一個對象轉換成可存儲或者可傳輸的狀態,序列化後的對象能夠在網絡上進行傳輸,也能夠存儲在本地,至於序列化的方法也是很簡單,只須要讓一個類去實現Serializable接口就能夠。
好比咱們實現了一個person類,讓它實現Serializable接口:
class person implements Serializable{ private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } }
接下來咱們看看這個自定義對象的傳遞以及獲取:
person zhangxu = new person(); zhangxu.setAge(18); zhangxu.setName("tiancia"); //傳遞 Intent intent = new Intent(ServiceMainActivity.this,SecondActivity.class); intent.putExtra("person", zhangxu); startActivity(intent); // 獲取 person zhangxu2 = (person) getIntent().getSerializableExtra("person");
一句話總結:咱們之因此能將咱們自定義的類在Intent中傳遞就是由於咱們自定義爲類實現了 Serializable 接口。
Parcelable
Parcelable方式的實現原理是將一個完整的對象進行分解,而分解後的每一部分都將是 Intent 所支持的數據類型,這樣也就實現傳遞對象的功能。
接下來咱們修改咱們的額person類,修改這個類的注意事項咱們在代碼中都有加註釋
/* * * 實現Parcelable接口 * 就要重寫裏面的兩個方法 * describeContents * writeToParcel * * */ class person implements Parcelable{ private String name; private int age; public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public int getAge() { return age; } @Override public int describeContents() { return 0; } // 把數據寫入到parcel中 @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(name); parcel.writeInt(age); } // 咱們還必須在person類中建立一個CREATOR常量,這裏建立了一個Parcelable.Creator接口的實現 // 而且將泛型類型指定爲person,接着重寫裏面的兩個方法 // createFromParcel 這個方法中讀取剛纔存入的字段 // newArray public static final Parcelable.Creator<person>CREATOR = new Parcelable.Creator<person>(){ @Override public person createFromParcel(Parcel parcel) { person person = new person(); // 讀取數據 person.name = parcel.readString(); person.age = parcel.readInt(); return person; } @Override public person[] newArray(int i) { return new person[i]; } }; }
說說它的傳遞方式,傳入時候和咱們以前寫的Serializable是同樣的,就不在重複,只是在讀取的時候,有一點須要咱們注意一下,就是方法名改變了:
person zhangxu3 = (person) getIntent().getParcelableExtra("person");
它們倆的區別:
serializable的方式比較簡單,但因爲會把整個對象進行序列化,所以效率會比Parcelable低一些,因此在一般狀況下咱們仍是建議使用Parcelable方式!
日誌控制
在iOS中咱們常常有用到這個日誌控制的問題,在安卓中也是,就是在debug階段咱們須要大量的日誌,可是在release狀態咱們是不須要的,日誌不只僅會增長程序運行的成本,還會泄漏一些重要的信息,因此在編譯release狀態咱們是須要控制日誌打印的,在安卓中咱們能夠寫這樣的一個類來進行處理。
class LogUntil{ 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 leven = VERBOSE; public static void v(String tag,String msg){ if (leven<=VERBOSE){ Log.d(tag,msg); } } public static void d(String tag,String msg){ if (leven<=DEBUG){ Log.d(tag,msg); } } public static void i(String tag,String msg){ if (leven<=INFO){ Log.d(tag,msg); } } public static void w(String tag,String msg){ if (leven<=WARN){ Log.d(tag,msg); } } public static void e(String tag,String msg){ if (leven<=ERROR){ Log.d(tag,msg); } } }
上面的這段代碼就是咱們經常使用的日誌控制,在咱們要發佈的時候,咱們設置leven的值爲NOTHING的時候咱們的日誌也就不見了!和咱們iOS的理解方式是同樣的,咱們iOS中會用到DEBUG這個變量,具體的我也就再也不多說了,有興趣的能夠本身找找這方面的問題,咱們直說安卓的。
建立定時任務
在Android中,實現定時器的任務是有兩種方式的,一種是使用Java API 提供的Timer類,一種是使用Android的Alarm機制,這令中方式在大多數狀況下都能實現相似的效果,可是Timer有一個致命的短板,它並不適用於那些長期在後臺運行的定時器任務,咱們都知道爲了能讓電池更加耐用,每一種手機都會有本身的休眠策略,Android手機在長時間不操做的狀況下會讓CPU處於睡眠狀態,就會致使Timer中的定時器任務沒法正常運行,而Alarm則具備喚醒CPU的功能,它保證在大多數狀況下須要執行任務的時候CPU都能正常運行。這裏須要注意喚醒CPU和喚醒屏幕徹底不是同一個概念!不要混淆。
下面咱們用代碼寫一個Alarm的實際例子:
class LongRunningService extends Service{ @Nullable @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 manager = (AlarmManager)getSystemService(ALARM_SERVICE); // 一個小時的毫秒數 int anHour = 60*60*1000; // SystemClock.elapsedRealtime方法表示系統開機至今經歷的毫秒數 // SystemClock.currentTimeMillis方法表示獲取1970年1月1日零時至今所經歷的毫秒數 long triggerAtTime = SystemClock.elapsedRealtime()+anHour; // 指定處理定時任務的服務爲LongRunningService 最後在調用set方法 Intent i = new Intent(this,LongRunningService.class); PendingIntent pendingIntent = PendingIntent.getService(this,0,i,0); //AlarmManager.ELAPSED_REALTIME_WAKEUP 表示讓定時任務的觸發時間從系統開機算起,可是會喚醒CPU //AlarmManager.ELAPSED_REALTIME 表示讓定時任務的觸發時間從系統開機算起,可是不會喚醒CPU //AlarmManager.RTC 表示讓定時任務的觸發時間從1970,1,1算起,可是不會喚醒CPU //AlarmManager.RTC_WAKEUP 表示讓定時任務的觸發時間從1970,1,1算起,可是會喚醒CPU //triggerAtTime 時間 manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent); return super.onStartCommand(intent, flags, startId); } }
聊聊Doze模式
咱們說說這個Doze模式,說說到底什麼是Doze模式。當用戶的設備是6.0或者以上系統的時候,若是該設備沒有接電源,且並木關閉了一段時間以後,就會進入Doze模式。在Doze模式下,系統會對CPU,網絡,Alarm等活動進行限制,從而延長電池的使用壽命。固然系統也不會一直處於Doze模式,而是間接性的退出Doze模式一小段時間,而在這一下歐丹時間中,應用就能夠完成他們的同步操做,Alarm任務等等,
接下來看看在Doze模式下那些功能會受到影響:
一、網絡訪問被限制
二、系統忽略喚醒CPU或者屏幕操做
三、系統再也不執行WIFI掃描
四、系統再也不執行同步服務
五、Alarm任務將會在下次退出Doze模式的時候執行
多窗口
Android在7.0以後導入了多窗口模式,在這裏咱們能夠大概的學習一下多窗口模式。
在這裏咱們說一下,在多窗口模式下並不會改變活動原有的生命週期,只是會將用戶最近交互過的那個活動設置爲運行狀態,而將多窗口模式下另一個可見的活動設置爲暫停狀態,若是這時候用戶又和暫停的活動進行交互,那麼該活動就會進入運行狀態,以前處於運行狀態的活動變成暫停狀態。
前面咱們說到在多窗口模式下,活動的生命週期是不會發生改變的,那麼有一些問題咱們就能夠隨之考慮一下:
好比說,在多窗口模式下,用戶任然處於能夠看到暫停狀態的應用,那麼像視頻播放之類的應用在此時就應該是繼續播放視頻纔對,所以,咱們最好不要在活動的onPause方法中處理視頻播放器的暫停邏輯,而是應該在onStop()方法中處理,而且在onStart方法中回覆視頻的播放。
另外,針對進入多窗口模式時候,活動會被從新建立,若是你想改變這一默認行爲,能夠在 Androidmainfest.xml中進行以下配置:
<activity android:name=".SecondActivity" android:configChanges="orientation|keyboardHidden|screenSize|screenLayout" ></activity>
加入這個配置以後,不論是進入多窗口模式,仍是橫豎屏切換,活動都不會被從新建立,而是會將屏幕發生變化的事件通知到Activity的onConfigurationChanged()方法中,因此你要是想在屏幕發生改變的時候進行相應的邏輯處理,那麼在活動中重寫onConfigurationChanged()方法便可。
禁止多窗口模式
上面咱們說了一些關於多窗口模式的一些問題,如今咱們再想一個場景,若是咱們作的是遊戲,要是進入了多窗口模式是否是很尷尬,總不是一邊發微信一遍玩遊戲的吧,看着本身GG,固然咱們也有辦法避免應用進入多窗口模式,禁止的方式也很簡單:
Androidmainfest.xml 中這樣配置:
android:resizeableActivity="false" true表示支持,false表示禁止
這樣就OK了嗎?其實還有一個問題須要咱們考慮一下這個問題,這個屬性是在咱們指定 targetSdkVersion 大於等於24的時候纔有效的,那小於24呢?沒有這個屬性咱們怎麼處理呢?咱們再這裏說一種解決方案:
Android規定,若是項目指定的targetSdkVersion低於24,而且活動是不容許橫豎屏切換的,那麼該應用也將不支持多窗口模式。
默認狀況下,咱們的應用是支持橫豎屏切換的,若是想一想要讓應用不容許橫豎屏切換,那麼就須要在 Androidmainfest.xml的<activity>標籤中加以下配置:
其中 portrait 表示豎屏 landscape 表示橫屏
android:screenOrientation="portrait"