Android學習--還有一些小技巧

 

這些小技巧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"
相關文章
相關標籤/搜索