精通安卓性能优化-第七章(二)

关闭Broadcast Receiver

为了保存电量,应用应该避免执行无目的的代码。在上面的实例中,当用户界面不在最前的时候去更新Textview的text是没有价值的,仅仅无必要的从电池提取能量。
除了ACTION_BATTERY_CHANGED的sticky intent包含电池信息,Android定义了其他的四个intent,你的应用可以使用:
(1) ACTION_BATTERY_LOW
(2) ACTION_BATTERY_OKAY
(3) ACTION_POWER_CONNECTED
(4) ACTION_POWER_DISCONNECTED
尽管简单的在应用manifest中声明一个receiver,不能收到ACTION_BATTERY_CHANGED的broadcast intent(这个receiver需要显式的在代码中调用registerReceiver()),其他的intent允许在应用的manifest里面注册,如Listing 7-2。

Listing 7-2 在Manifest中声明receiver

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.apress.proandroid.ch07" android:versionCode="1"
        android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".BatteryInfoActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".BatteryReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BATTERY_LOW" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.BATTERY_OKAY" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
            </intent-filter>
        </receiver>
    </application>
</manifest>

Listing 7-3给出了broadcast receiver的一个简单实现。我们定义了一个BatteryReceiver处理所有的四个action。

Listing 7-3 BatteryReceiver实现

public class BatteryReceiver extends BroadcastReceiver {
    private static final String TAG = "BatteryReceiver";

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        String text;

        // 在这里处理4个action
        if (Intent.ACTION_BATTERY_LOW.equals(action)) {
            text = "Low power";
        } else if (Intent.ACTION_BATTERY_OKAY.equals(action)) {
            text = "Power okay (not low anymore)";
        } else if (Intent.ACTION_POWER_CONNECTED.equals(action)) {
            text = "Power now connected";
        } else if (Intent.ACTION_POWER_DISCONNECTED.equals(action)) {
            text = "Power now disconnected";
        } else {
            return;
        }

        Log.i(TAG, text);
        Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
    }
}

就像现在,应用可以被认为有一个严重的缺陷。事实上,其中任何一个action发生应用将启动(如果还没有start ready)当。尽管这可能是设计行为,在许多情况下可能需要的行为不同。比如在这个情况下,我们可以说只有当应用在最前的时候显示toast有意义,总是显示的话可能会干扰其他应用,使得用户体验变差。
当应用没有在运行,或者在后台运行,我们希望关闭这些Toast。两种方式去实现:
(1) 可以在应用中添加一个标志位,在activity的onResume里面设置为True,在onPause()设置为false,修改receicer的onReceive()方法去检测这个标志
(2) 我们可以仅当应用是foreground应用的时候开启broadcast receiver
尽管第一种方式工作的很好,不会阻止应用从四种Action触发的时候启动。这最终导致没有必要的指令被执行,依然会从电池提取电量。另外,你需要在多个文件里面修改这个标志,因为你的应用会定义多个activity。
第二种方法更好一些,我们可以保证指令仅当我们有一个明确的目的时候去执行,因此电量仅为了合理的理由消耗。为了达到这个目的,我们需要在应用中做两件事:
(1) 默认关闭receiver
(2) 在onResume()开启receiver并且在onPause()关闭

关闭和开启Broadcast Receiver

Listing 7-4显示了如何在应用的manifest文件中关闭broadcast receiver。
Listing 7-4 在Manifest中关闭Broadcast Receiver

...
<receiver android:name=".BatteryReceiver" android:enable="false" >
...

NOTE:<application>标签有自己的enabled属性。broadcast receiver仅在application和receiver的enabled属性都为true的时候才会开启,其他的情况下关闭。

Listing 7-5给出了如何在onResume()开启和在onPause()关闭broadcast receiver。

Listing 7-5 开启和关闭Broadcast Receiver

public class BatteryInfoActivity extends Activity {
    
    ...
    
    private void enableBatteryReceiver(boolean enabled) {
        PackageManager pm = getPackageManager();
        ComponentName receiverName = new ComponentName(this, BatteryReceiver.class);
        int newState;

        if (enabled) {
            newState = PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
        } else {
            newState = PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
        }

        pm.setComponentEnabledSetting(receiverName, newState, PackageManager.DONT_KILL_APP);
    }

    ...

    @Override
    protected void onPause() {
        super.onPause();
        unregisterReceiver(mBatteryChangedReceiver);

        enableBatteryReceiver(false);  // 关闭battery receiver

        // 当应用没在前台为了保存电能注销receiver
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mBatteryChangedReceiver == null) {
            createBatteryReceiver();
        }

        registerReceiver(mBatteryChangedReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));

        enableBatteryReceiver(true);  // 开启battery receiver
    }

    ...

}

仅在确实需要的时候开启broadcast receiver在电能消耗上有很大的不同。尽管这是开发的时候很容易忽视的一个方面,需要特别注意receiver,他们仅在需要的时候被开启。

网络

许多应用在设备和服务器之间传递数据,或者设备之间传递数据。就像电池状态,应用需要从设备的网络连接获取信息。ConnectivityManager类提供可供应用访问网络信息的API。Android通常有多种数据连接可用:
(1) Bluetooth
(2) Ethernet
(3) Wi-Fi
(4) WiMAX
(5) Mobile(EDGE, UMTS, LTE)
Listing 7-6给出了如何获取活动的链接以及所有的链接的信息。
Listing 7-6 网络信息

private void showNetworkInfoToast() {
    ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);

    // 仅仅显示active链接
    NetworkInfo info = cm.getActiveNetworkInfo();
    if (info != null) {
        Toast.makeText(this, "Active: " + info.toString(), Toast.LENGTH_LONG().show();
    }

    // 显示所有链接
    NetworkInfo[] array = cm.getAllNetworkInfo();
    if (array != null) {
        String s = "All: ";
        for (NetworkInfo i : array) {
            s += i.toString() + "\n";
        }
        Toast.makeText(this, s, Toast.LENGTH_LONG).show();
    }
} 

NOTE: 应用需要ACCESS_NETWORK_STATE权限去获取网络信息。

因为聚焦于最大化电池使用时间,我们需要注意到如下事情:
(1) 后台数据设置
(2) 数据传输率

后台数据

用户可以设置指定是否允许后台数据传输,预期可以节省电池电量。如果你的应用不在前台的时候需要数据传输,需要检测标志位,如Listing 7-7所示。 Service通常需要在初始化任何传输前检查这个设置。

Listing 7-7 检测后台数据设置

private void transferData(byte[] array) {
    ConnectivityManager cm = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
    boolean backgroundDataSetting = cm.getBackgroundDataSetting();
    
    if(backgroundDataSetting) {
        // transfer data
    } else {
        // 兑现设置不传输数据
    }
}

因为这是一个自愿的检测,你的应用实际上可能忽略这个设置,在任何情况下传输数据。然而,因为这可能和用户的意愿抵触,潜在的降低前台数据传输的速度,并且影响电池使用时间,这样的行为可能导致用户卸载掉你的应用。
为了当后台的数据设置改变的时候接到通知,可以在Java代码中注册一个receiver,使用ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED去构建intent filer或者在应用的manifest文件使用andoird.net.conn.BACKGOUND_DATA_SETTING_CHANGED。因为这个设置控制后台数据传输,实际上比在onResume()开启receiver和在onPause()关闭receiver更有意义。

NOTE:getBackgoundDataSetting()方法在4.0弃用了,会always返回true。另外,当后台数据传输不可用network可能会被断开。

数据传输

传输率变化范围很广,GPRS数据传输通常少于100K每秒,而LTE或者Wifi连接通常达到几M。除了连接类型,NetworkInfo类还指定了连接的子类型。当连接是TYPE_MOBILE的时候这很重要。Android定义了如下的连接子类型(在TelephonyManager类):


子类型随着新技术创建和开发而添加。比如,LTE子类型在API 11添加,HSPAP子类型在API 13添加。如果你的代码依赖于这些值,保证你处理了这种情况,即应用存在他不知道的数值;否则可能导致应用不能传输数据。当有新的子类型添加,需要更新你的代码,所以请关注每一个版本的Android SDK。改变列表在http://d.android.com/sdk/api_diff/13/changes.html给出,比如。
直观上你的应用应该选择更快的连接。即使3G射频芯片消耗比Wifi射频芯片消耗更少的电量,Wifi传输率可能最终意味着WIFI传输能够减少电量消耗,因为传输可以在很短的时间内完成。

NOTE:因为数据计划现在通常允许有限数量的数据传输(比如,¥30/2GB每月),Wifi连接经常被作为第一选择。同样的,你的应用可以使用NetworkInfo.isRoaming()去了解设备当前是否在漫游,因为这可以添加额外的消费,当isRoaming()返回true的时候,需要避免传输数据。

Table 7-2给出了T-Mobile G1手机不同的组件内存消耗(也叫HTC Dream, or Era G1)。尽管这个手机现在有些老(在2008晚期发布),数值同样给出了每个组件提取多少电量一个很好的概述。

Table 7-2 Android G1手机电能消耗(源自: Google I/O 2009)


尽管精确的数值会随不同设备变化,了解你的应用使用多少电能很重要。因为G1有一个1150mAh的电池,下载和播放视频的应用(比如,YouTube)在使用3G连接的情况下,大约在3个小时清空电池:3G需要150mA,CPU需要90mA,LCD需要90mA,总共330mA,或者3个半小时的使用(假设没有其他应用运行)。
如果你可以控制什么样的数据传输,可以考虑在传输前压缩数据。尽管CPU需要在数据使用前需要解压缩(因此需要更多的电量),数据传输将更快,射频芯片(比如,3G,Wifi)可以更快的关闭,以保存电池的寿命。考虑的事情包括:
(1) 使用GZIP压缩文本数据,使用GZIPInputStream去访问数据
(2) 如果可能使用JPEG而不是PNG
(3) 使用匹配设备分辨率的资源(比如,如果需要resize到96*54,没有必要去下载1920*1080的图片)
传输速度越慢(比如,EDGE),压缩的重要性越大,因为你希望去减少射频芯片开启的时间。
因为Android运行在越来越多的设备上,从手机到平板,从机顶盒到网络笔记本,为所有这些设备产生资源很乏味。然而,使用合适的资源可以大幅增加电池的寿命,因此使你的应用更加合意。除了保存电能,更快的下载和上传将使你的应用响应更好。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值