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窗口,之后的流程就都是相同的了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,458评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,030评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,879评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,278评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,296评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,019评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,633评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,541评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,068评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,181评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,318评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,991评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,670评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,183评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,302评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,655评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,327评论 2 358

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,678评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,226评论 25 707
  • 画了好久
    一航航航航阅读 221评论 3 3
  • 昨晚和女儿外出吃完饭逛超市,每次孩子只要外出逛店都要我随侯身旁。逛好我径直来到收银台排队,孩子还想在边上逛逛,我说...
    侠骨丹心9号阅读 178评论 0 0
  • 这周据说有两个台风要登陆浙江,周一也确实下了点雨,可惜杯水车薪,仅过了一天太阳公公就王者归来重掌乾坤了,我很失望。...
    Sheepy阅读 371评论 0 4