繼續進階-你還應該掌握的高階技巧

2020-09-28 11:00:57

全域性獲取Context的技巧

 Android提供了一個Application類,每當應用程式啟動的時候,系統就會自動把這個類進行初始化。我們可客製化一個自己的Application類,用於獲取全域性Context
範例:
 1.建立Application的子類物件,提供獲取content的方法

import android.app.Application;
import android.content.Context;

public class MyApplication extends Application {
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }
    //提供getContext()方法
    public static Context getContext(){
        return context;
    }
}

 2.在AndroidManifest.xml檔案的application標籤下指定,告知系統啟動的是MyApplication,而不是預設的Application

<application
        android:name=".MyApplication"
   ......
    </application>

問題:為了讓LitePal正常工作,必須要設定

<application
        android:name="org.litepal.LitePalApplication"
   ......
    </application>

 這樣設定之後,LitePal就可在內部自動獲取到Context了。但是這樣既無法設定MyApplication了,因為一個專案只能設定一個Application
解決辦法:在我們自己的Application中呼叫LitePal的初始化方法就行

public class MyApplication extends Application {
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
        LitePal.initialize(context);
    }
    //提供getContext()方法
    public static Context getContext(){
        return context;
    }
}

使用Intent傳遞物件

平時我們在用Intent傳遞資料時都用的putExtra()方法,但是當我們要傳遞的資料是自定義物件時,無法傳遞。實現方法如下:

Serializable

序列化的意思,表示吧一個物件轉換成可儲存或可傳輸的狀態,序列化後的物件可以在網路上傳輸,也可儲存到本地
是一個介面,用時直接實現介面就可

  class Person implements Serializable{
        private String name;
        private int age;

       public String getName() {
               return name;
       }

      public void setName(String name) {
             this.name = name;
       }
      public int getAge() {
             return age;
       }
      public void setAge(int age) {
           	this.age = age;
       }
 }

傳送:

 Person person = new Person();
            person.setAge(12);
            person.setName("dag");
            Intent intent = new Intent(MainActivity.this,MainActivity2.class);
            intent.putExtra("person_data",person);
            startActivity(intent);

接收:(注意:與原來的接收不同)

 Intent intent = getIntent();
 Person person = (Person)intent.getSerializableExtra("person_data");

Parcelable

它的實現原理是將一個完整的物件進行分解,分解後的每一部分都是Intent所支援的資料型別

class people implements Parcelable{

    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

	//直接返回0
    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        //呼叫Parcel的writeXXX方法,吧類中的欄位一一寫出
        dest.writeString(name);
        dest.writeInt(age);
    }
    public static final Parcelable.Creator<people> CREATOR = new Parcelable.Creator<people>(){

        @Override
        public people createFromParcel(Parcel source) {
            //讀取剛才寫出的name和age欄位,讀取順序一定要和寫入的完全相同
            people people1 = new people();

            people1.name = source.readString();
            people1.age = source.readInt();
            return people1;
        }
		
        @Override
        public people[] newArray(int size) {
            return new people[size];
        }
    };
}

傳送:

people people1 = new people();
        people1.setAge(12);
        people1.setName("134455");
        Intent intent1 = new Intent(MainActivity.this,MainActivity2.class);
        intent.putExtra("people_data",person);
        startActivity(intent);

接收:(注意:與原來的接收不同)

Intent intent = getIntent();
people people1 = (people) intent.getParcelableExtra("people_data");

客製化自己的紀錄檔工具

目的:自由的控制紀錄檔的列印
範例:

class LogUtil{
    public static final int VERBOSE = 1;
    public static final int DEBUG = 2;
    public static final int INFO = 3;
    public static final int ERROR = 5;
    public static final int WARN = 4;
    public static final int NOTHING = 6;
    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.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);
    }

}

之後只需要修改level變數,就可控制列印情況

建立定時任務

 Android中的定時任務一般有兩種實現方式,一種是Java API裡的Timer類,一種是Android的Alarm機制
  Timer並不太實用於那些需要長期在後臺執行的定時任務,當長時間時,CPU進入到睡眠狀態,可能導致Timer的定時任務無法正常執行

Alarm機制

  可以保證大多數情況下需要執行定時任務時CPU都能正常工作
如下是一個長時間在後臺定時執行的服務

public class LongRuningService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    /*
    一旦啟動LongRuningService就會在onStartCommand中執行一個定時任務,
    	這樣一個小時後將在此啟動LongRuningService,形成迴圈
     */
    @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;
        Long l = System.currentTimeMillis();
      	//SystemClock.elapsedRealtime()獲取開機至今的毫秒數
       //System.currentTimeMillis() 獲取1970年 一月一日0點至今所經歷的毫秒數
        Long triggerAtTime = SystemClock.elapsedRealtime()+anHour;
        Intent intent1 = new Intent(this,LongRuningService.class);
        PendingIntent pendingIntent = PendingIntent.getService(this,0,intent1,0);
        //設定定時任務
        manager.set(AlarmManager.RTC_WAKEUP,triggerAtTime,pendingIntent);
        /*三個引數:
         第一個是整型引數用於指定AlarmManager的工作型別
           第二個:觸發時間
           第三個是 PendingIntent

         */
        return super.onStartCommand(intent,flags,startId);
    }
}

AlarmManager的工作型別說明
ELAPSED_REALTIME讓定時任務從系統開機開始算起,但不會喚醒CPU
ELAPSED_REALTIME_WAKEUP讓定時任務從系統開機開始算起,喚醒CPU
RTC讓系統任務從1970年一月一日0點開始算起,但不會喚醒CPU
RTC_WAKEUP讓系統任務從1970年一月一日0點開始算起,喚醒CPU

注:對於不同的工作型別,要選擇不同的時間
如果你要求Alarm任務的執行時間必須準確無誤,應用setExact()方法替換set()方法

Doze模式

 當使用者裝置是Android 6.0 或以上系統時,如果未插接電源,且螢幕關閉了一段時間之後就會進入到Doze模式
  在該模式下,系統會對CPU,網路,Alarm等活動進行限制,從而延長電池的使用壽命
 當然,它不會一直處於該模式,而是會間隙性的退出Doze模式一小段時間,在這段時間中去完成同步操作,Alarm任務
注:Doze模式下,Alarm任務就會變的不準時,解決方案
 吧set()方法換成setExactAndAllowWhileIdle()或setAndAllowWhileIdle()方法就可讓定時任務在Doze模式下也可正常執行

多視窗模式

即我們常見的分屏操作,在該模式下活動會有一個重新建立的過程,如果操作一個程式,另一個程式會進入暫停狀態(onPause)

 如果你想改變進入多視窗模式活動被重新建立,可新增如下設定

<activity android:name=".MainActivity"
            android:configChanges="orientation|keyboardHidden|screenSize|screenLayout">
           .....
        </activity>

禁用多視窗模式

<application
		....
        android:resizeableActivity="false">
      .....  
</application>      

true表示支援,false表示不支援,預設是支援

Lambda表示式

android studio 中的設定

1.app的build檔案下設定好

jackOptions{
            enabled true
        }
compileOptions{
   sourceCompatibility JavaVersion.VERSION_1_8
   targetCompatibility JavaVersion.VERSION_1_8
 }

  retrolambda {//指定將原始碼編譯的級別,使用下列程式碼,會將程式碼編譯到相容1.6的位元組碼格式
        javaVersion JavaVersion.VERSION_1_6
    }

2.專案build檔案下也要加上設定

apply plugin: 'me.tatarka.retrolambda'//lambda
dependencies {
        classpath 'com.android.tools.build:gradle:3.1.3'
        classpath 'me.tatarka:gradle-retrolambda:3.7.0' //lambda

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }

剩下的使用和java使用方法一樣
Lambda表示式