13(上) 繼續進階---你還應該掌握高級技巧

這一章學到的:

  • 全局獲取Context的技巧
  • 使用Intent傳遞對象
    1. Serialiable方式
    2. Parcelable方式
  • 定制自己的日志工具
  • 調試Android程序
動漫圖.jpg

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()方法中我們需要調用ParcelwriteXxx()方法,將Person類中的字段一一寫出。注意,字符串型數據就調用writeString()方法,整型數據就調用writeInt()方法,依此類推。

除此之外,我們還必須在Person類中提供一個名為CREATOR的常量,這里創建了Parcelable.Creator接口的一個實現,并將泛型指定為Person。接著需要重寫createFromParcel()newArray()這兩個方法,在createFromParcel()方法中我們要去讀取剛才寫出的nameage字段,并創建一個Person對象進行返回,其中nameage都是調用ParcelreadXxx()方法讀取到的,注意這里讀取的順序一定要和剛才寫出的順序完全一致。而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,但又遲遲定位不出來原因是什么的時候。最好的解決辦法就是調試了。調試允許我們逐行地執行代碼,并可以實時觀察內存中的數據,從而能夠比較輕易地查出問題的原因。

調試工作的第一步肯定是添加斷點,這里由于我們要調試登陸部分的問題,所以斷點可以加在登錄按鈕的點擊時間里。添加斷點的方法也很簡單,只需要在相應代碼行的左邊點擊一下就可以了。

添加斷點.png

如果想要取消這個斷點,對著它再次點擊就可以了。

添加好了斷點,接下來就可以對程序進行調試了,點擊Android Studio頂部工具欄中的Debug按鈕(最右邊的按鈕),就會使用調試模式來啟動程序。

調試按鈕.png

在輸入框中輸入賬號和密碼,并點擊Login按鈕,這時Android Studio就會自動打開Debug窗口。

Debug窗口.png

接下來每按一次F8鍵,代碼就會向下執行一行,并且通過Variables視圖還可以看到內存中的數據。

Variables視圖.png

調試完之后點擊Debug窗口中的Stop按鈕(最下邊的按鈕)來結束調試即可。

結束調試按鈕.png

這種調試方式雖然完全可以正常工作,但在調試模式下,程序的運行效率將會大大地降低,如果你的斷點加在一個比較靠后的位置,需要執行很多的操作才能運行到這個斷點,那么前面這些操作就都會有一些卡頓的感覺。

Android還提供了另外一種調試的方式,可以讓程序隨時進入到調試模式。

這次不需要選擇調試模式來啟動程序了,就使用正常的方式來啟動程序。把賬號和密碼輸入好,然后點擊Android Studio頂部工具欄的Attach debugger to Android process按鈕(最左邊的按鈕)


動態調試按鈕.png

此時會讓彈出一個進程選擇提示框

進程選擇提示框.png

這里目前只列出了一個進程,也就是我們當前程序的進程,選中這個進程,然后點擊Ok按鈕,就會讓這個進程進入到調試模式了。

Debug窗口.png

接下來在程序中點擊Login按鈕,Android Studio同樣也會自動打開Debug窗口,之后的流程就都是相同的了。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,881評論 18 139
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,118評論 25 708
  • 畫了好久
    一航航航航閱讀 225評論 3 3
  • 昨晚和女兒外出吃完飯逛超市,每次孩子只要外出逛店都要我隨侯身旁。逛好我徑直來到收銀臺排隊,孩子還想在邊上逛逛,我說...
    俠骨丹心9號閱讀 179評論 0 0
  • 這周據說有兩個臺風要登陸浙江,周一也確實下了點雨,可惜杯水車薪,僅過了一天太陽公公就王者歸來重掌乾坤了,我很失望。...
    Sheepy閱讀 375評論 0 4