Android N 多窗口布局 省电模式 全新通知功能

今年google 16 i/o 强势推出 android n
迄今为止还没有给他命名、有兴趣的可以登录网站 https://www.android.com/versions/name-n/
会中提到android N 运行环境有很大提升,30%到600%提升。应用安装提速75%。
AndroidN 新增Vulkan 现代3D图形API、实现高帧率游戏,即时应用

OK、好东西用了才知道、那么如何运行Android N呢

会学到什么

多窗口布局互动
确保后台服务继续工作,而打盹模式激活
更新通知,以利用新功能

需要什么

Android的工作室版本2.1+
示例代码
测试设备(或仿真器)与Android N和谷歌的API
使用USB线连接你的测试设备

获取示例代码

通过命令行从github上clone

$ git clone https://github.com/googlecodelabs/getting-ready-for-android-n

因为该实例需要使用OpenWeatherMap 获取天气数据、多以在运行代码之前要获取OpenWeatherMap API密钥 获取链接 https://home.openweathermap.org/

运行示例应用程序

OK 打开你下载好的项目、
重要提醒:将你的 OpenWeatherMap API key 添加到 app/build.gradle
Important: Add your OpenWeatherMap API key to app/build.gradle,
将这行代码替换 .
运行程序、验证示例程序可以显示天气

支持多窗口布局

个人觉得最酷炫的是支持multi-window layouts

支持多窗口首先确保我们布局放得下大小windows。首先熟悉几个功能

默认情况下,所有application和activity有个resizeable属性可以用作multi-window功能、
在清单中可以禁用这个功能

        <activity
            android:name=".MainActivity"
            ...
            android:resizeableActivity="false">

        </activity>

尝试一下这两个模式

  1. 启动 sunshine app
  2. 通过长按按钮启动multi-windows 模式
  3. 禁用multi-windows app是全屏的
  4. 将resizeable 置为 true 再试试

注意:如果root activity resizeble是false、则不起作用
A task’s root activity properties are applied to all additional activities launched in the task.
If the root activity of a task is resizeable, the system treats all other activities in the task as resizeable; if the root activity isn’t resizeable, it won’t.
这里写图片描述

适配小窗口
支持 multi-windows 得考虑 size 和 density的问题
在实例中、用了很多pixel显示天气
这里写图片描述

如果想把左边蓝色缩小、我们可以用下面的方法

在 values-sw220dp 创建refs.xml、这样尺寸就限制在220dp到下一维度400dp
sw220dp/refs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- Replace today's forecast layout with the same layout used for every day -->
    <item type="layout" name="list_item_forecast_today">@layout/list_item_forecast</item>
</resources>

sw400dp/refs.xml

<item type="layout" name="list_item_forecast_today">@layout/list_item_forecast_today_big</item>

这样list_item_forecast_today 就适应了两个布局

这里写图片描述

总结:
适应不同大小的应用程序多画面模式,建议先设计最小的大小。
创建220dp 尽量删除无用元素

添加窗口背景
这里写图片描述

添加android:windowBackgroundFallback会覆盖 windowBackground

<style name="AppTheme" parent="@style/Theme.AppCompat.Light.DarkActionBar">
        ...
        <item name="android:windowBackgroundFallback">@color/sunshine_dark_blue</item>
</style>

打开ForecastFragment .class openPreferredLocationInMap 方法
添加

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addFlags(Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT);

效果如下
这里写图片描述
注意:FLAG_ACTIVITY_LAUNCH_ADJACENT只适用于多画面模式。

优化电池效率

现在的android手机、有时候充的电还没用的快…蛋疼 现在优化会好些吗?
在Android N,我们继续这些努力让android 6打瞌睡功能更牛逼,让设备更能保存点亮。任何时候屏幕在一段时间内,设备是不插电,瞌睡适用于CPU和网络限制应用程序。即使在自己的口袋里携带他们的设备也能省点。
轻量级打瞌睡模式
这里写图片描述

静止的:
这里写图片描述

那么有了这些功能、app功能会受到限制、特别是网络活动
为了确保您的应用程序在后台继续正常运行,一些服务可能需要修改

  1. (如检查电子邮件,或备份照片),迁移到theJobScheduler或GcmNetworkManager api。这些api提供更大的灵活性为调度工作,并确保你的工作是维护窗口期间处理。
  2. 实时通知(如接收即时消息),使用高优先级GCM通知。这些有能力立即醒来一个应用的低功耗状态。

我们的样例应用程序使用AlarmManger定期醒来和获取天气数据,同时在后台运行。在本节中,我们将它升级到使用GcmNetworkManager。我们的应用程序中醒来的时候允许操作系统更好的去控制,并确保我们的应用程序在这个网络访问事件。

在SunshineSyncService.java定位现有alarm-based调度代码:

public static void ScheduleAlarm(Context context) {
    Log.i(TAG, "Scheduling alarm, interval: " + AlarmManager.INTERVAL_HALF_HOUR / 60000 + " min");
    Intent intent = new Intent(context, SyncAlarmReceiver.class);
    final PendingIntent pIntent = PendingIntent.getBroadcast(
            context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    long firstMillis = System.currentTimeMillis(); // Perform initial sync immediately
    AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    // Perform subsequent syncs every 30 minutes
    alarm.setInexactRepeating(AlarmManager.RTC_WAKEUP, firstMillis,
            AlarmManager.INTERVAL_HALF_HOUR, pIntent);
}

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

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.i(TAG, "Broadcast received, sending sync intent to SunshineSyncService");
        Intent i = new Intent(context, SunshineSyncService.class);
        context.startService(i);
    }
}

基于GcmNetworkManager替换上面的代码

dependencies {
    // ...
    compile 'com.google.android.gms:play-services-gcm:8.4.0'
}

封装一个后台任务
sync/SunshineJobScheduler.java

public class SunshineJobScheduler extends GcmTaskService {

    @Override
    public int onRunTask(TaskParams taskParams) {
        return 0;
    }

}

如前所述,GcmNetworkManager和JobScheduler工作通过定义任务或工作和一组约束决定应该如何运行。
让我们为这个任务定义两个约束条件——一个经常运行的同时连接到一个充电器,和一个在电池不经常运行。
sync/SunshineJobScheduler.java

public static final int MINUTES_AS_SEC = 60;
public static final int HOURS_AS_SEC = 60*60;
public static final String TASK_TAG_CHARGING = "sync_charging";
public static final String TASK_TAG_BATTERY = "sync_battery";

/**
 * Task specification for running jobs while connected to a charger. In this case, we allow
 * execution to occur frequently, as power usage is not constrained.
 */
private static final Task CHARGING_TASK = new PeriodicTask.Builder()
        .setService(SunshineJobScheduler.class)
        .setPeriod(30*MINUTES_AS_SEC)
        .setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
        .setRequiresCharging(true)
        .setPersisted(true)
        .setUpdateCurrent(true)
        .setTag(TASK_TAG_CHARGING)
        .build();

/**
 * Task specification for running jobs while on battery. In this case, we execute jobs less
 * frequently to conserve battery life.
 *
 * Note that additional restrictions may be put on this task by the system if the device is
 * in Doze or App Standby mode due to inactivity. However, we should still try to be friendly
 * to the battery even when these aren't in effect.
 */
private static final Task BATTERY_TASK = new PeriodicTask.Builder()
        .setService(SunshineJobScheduler.class)
        .setPeriod(6 * HOURS_AS_SEC)
        .setRequiredNetwork(Task.NETWORK_STATE_CONNECTED)
        .setRequiresCharging(false)
        .setPersisted(true)
        .setUpdateCurrent(true)
        .setTag(TASK_TAG_BATTERY)
        .build();

setPersisted(true)调用确保跨设备重新启动该任务将被保留下来

现在我们已经定义了任务,我们需要系统安排他们。创建一个新的ScheduleTasks()静态方法,并调用GcmNetworkManager.schedule()我们定义的每个任务。

sync/SunshineJobScheduler.java

private static final String TAG = "SunshineJobScheduler";

/**
 * Method to schedule tasks. Called from either MainActivity.onCreate() for interactive
 * sessions, or onInitializeTasks() in the event Play Services is restarted.
 *
 * Note that since all jobs are flagged as "persisted" in their specifications (above), these
 * will automatically persist across reboots.
 *
 * @param context
 */
public static void ScheduleTasks(Context context) {
    GoogleApiAvailability googleAvailability = GoogleApiAvailability.getInstance();
    int resultCode = googleAvailability.isGooglePlayServicesAvailable(context);
    if (resultCode == ConnectionResult.SUCCESS) {
        Log.i(TAG, "Scheduling tasks");
        GcmNetworkManager gcmNetworkManager = GcmNetworkManager.getInstance(context);

        // Run every 30 minutes while the device is on a charger
        gcmNetworkManager.schedule(CHARGING_TASK);

        // ... And run every 6 hours while the device is on battery power
        gcmNetworkManager.schedule(BATTERY_TASK);
    } else {
        // We display a user actionable error inside MainActivity. We'll just abort and log the
        // error here.
        Log.e(TAG, "Google Play Services is not available, unable to schedule jobs");
    }
}

配置manifest
ACTION_TASK_READY,为了让持久化设备重启后任务被重塑。

<!-- GcmJobService instance used for background sync -->
<service
    android:name=".sync.SunshineJobScheduler"
    android:exported="true"
    android:permission="com.google.android.gms.permission.BIND_NETWORK_TASK_SERVICE">
    <intent-filter>
        <action android:name="com.google.android.gms.gcm.ACTION_TASK_READY" />
    </intent-filter>
</service>

我们的GcmNetworkManager实现相应的工作,我们可以删除旧的实现。
删除SunshineSyncService。SyncAlarmReceiver SunshineSyncService。BootBroadcastReceiver以及他们在AndroidManfiest.xml相关条目。
测试,尝试启动应用程序。你可能想尝试不同的值用于setPeriod CHARGING_TASK()和BATTERY_TASK。如果使用一个模拟器,可以使用模拟器控制来模拟不同的充电和网络状态。

Q: What’s the difference between JobScheduler and GcmNetworKManager?
A: GcmNetworkManager is a wrapper around JobScheduler, that provides backwards compatibility for devices older than API 21. Your app doesn’t need to use GCM messaging in order to take advantage of it.
Q: How do I ensure my jobs are scheduled after a restart?
A: Use the setPersisted() option when creating a task specification.
Q: What happens if I don’t migrate to JobScheduler or GcmNetworkManager?
A: On devices running Marshmallow or later, your app may lose network access if Doze or App Standby modes are activated by the system. To avoid this, you need to make sure your app’s background activities only occur during maintenance windows. To do this, make sure you’re using JobScheduler/GcmNetworkManager (recommended) or a SyncAdapter.

支持增强的通知

Android N notifications 有一下几点
1. 列表内容通知分组在phones和tablets使用、不仅仅 android wear
2. 通知可以响应文本输入
3. 刷新和操作更美了

这里写图片描述

NotificationCompat Android支持库引入N,我们可以通过分组通知达到更好的用户体验

下面是notification group部分

使用一个支持库版本大于24.0.0-alpha2

compile 'com.android.support:appcompat-v7:21.0.2'
compile 'com.android.support:appcompat-v7:24.0.0-alpha2'

打开SunshineSyncEngine.java
SunshineSyncEngine.java
添加如下代码

private static final String FORECAST_NOTIFICATION_GROUP = "FORECAST_NOTIFICATION_GROUP";

new NotificationCompat.Builder
Builder调用setGroup(String)
SunshineSyncEngine.java

.setGroup(FORECAST_NOTIFICATION_GROUP)

所有通知组需要一个summary.创建额外的summary通知代码
SunshineSyncEngine.java

if (numNotifications > 0) {
    // Always build a summary if you have notifications in a group.
    // The summary will be displayed by the system when needed.
    NotificationCompat.Builder summaryBuilder =
        createSummaryNotificationBuilder(context,
            "Sunshine Forecasts", "");
    summaryBuilder.setGroupSummary(true);
    mNotificationManager.notify(WEATHER_NOTIFICATION_ID,  
        summaryBuilder.build());
}

部署代码,将看到在系统栏通知分组只占用一个图标。单摘要通知是可扩展的,group内部数量低于2,摘要通知消失

下面是关于Remote Input部分

通知栏输入文字、要做一下几个个步骤
1. 您需要声明一个类,它将接收一个intent。目的将包含的信息由用户输入的文本。在这个示例中我们要重复使用BroadcastReceiver。
2. MessageReplyReceiver类,您将看到以下意图额外的定义。将存储的回复文本意图转发给接收方是关键。
MessageReplyReceiver.java

    public static final String EXTRA_REMOTE_REPLY = "extra_remote_reply";
    // public static final String REPLY_ACTION = BuildConfig.APPLICATION_ID + ".ACTION_MESSAGE_REPLY";
    // public static final String EXTRA_FORECAST = "extra_forecast";
  1. 取消REPLY_ACTION和EXTRA_FORECAST静态类变量的备注
    REPLY_ACTION和EXTRA_FORECAST静态类变量。REPLY_ACTION将用于声明RemoteInput行动的通知。EXTRA_FORECAST包含预测信息,需要重建的通知。

  2. 将MessageReplyReceiver onReceive方法中的代码解除注释、
    将检索一个接收器可以处理的意图。
    SunshineSyncEngine.java

RemoteInput remoteInput = new RemoteInput.Builder(
                             MessageReplyReceiver.EXTRA_REMOTE_REPLY)
                             .setLabel("Take note")
                             .build();

5.创建一个Pending intent通知用户提交的回复
SunshineSyncEngine.java

RemoteInput remoteInput = new RemoteInput.Builder(
                             MessageReplyReceiver.EXTRA_REMOTE_REPLY)
                             .setLabel("Take note")
                             .build();
  1. 创建action
    SunshineSyncEngine.java
NotificationCompat.Action actionReplyByRemoteInput =
    new NotificationCompat.Action.Builder(
        R.mipmap.ic_launcher,
        "Take note",
        replyIntent)
        .addRemoteInput(remoteInput)
        .build();

7.现在添加远程输入行动通知。
SunshineSyncEngine.java

builder.addAction(actionReplyByRemoteInput);

总结如下

        NotificationCompat.Builder builder =
                new NotificationCompat.Builder(context)
                        .setColor(resources.getColor(R.color.sunshine_light_blue))
                        .setSmallIcon(iconId)
                        .setLargeIcon(largeIcon)
                        .setContentTitle(forecast.mNotificationTitle)
                        .setContentText(forecast.mNotificationContentText)
                        .setGroup(FORECAST_NOTIFICATION_GROUP);
        RemoteInput remoteInput = new RemoteInput.Builder(
                MessageReplyReceiver.EXTRA_REMOTE_REPLY)
                .setLabel("Take note")
                .build();

        PendingIntent replyIntent = PendingIntent.getBroadcast(context,
                // This field should be unique per notification.
                forecast.mDaysSinceEpoch,
                MessageReplyReceiver.getMessageReplyIntent(forecast),
                PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Action actionReplyByRemoteInput =
                new NotificationCompat.Action.Builder(
                        R.mipmap.ic_launcher,
                        "Take note",
                        replyIntent)
                        .addRemoteInput(remoteInput)
                        .build();
        builder.addAction(actionReplyByRemoteInput);

部署应用程序、发送文本发现进度指示器没有提醒、因为我们差了一步骤
onReceive消息中你会注意到NotificationManager变量不能使用。除非你通知用户使用否则该系统将不知道远程接收输入
MesageReplyReceiver.java

NotificationManager notificationManager =
    (NotificationManager) context.getSystemService(
         Context.NOTIFICATION_SERVICE);
NotificationCompat.Builder notificationBuilder =
    SunshineSyncEngine.createForecastNotificationBuilder(
        context, forecast);
notificationManager.notify(forecast.mDaysSinceEpoch,
    notificationBuilder.build());

提示:NotificationCompat.Builder创建对象是昂贵的。如果你打算经常更新相同的通知需要创建和缓存它们。这将避免不必要的对象创建请求。

这里写图片描述

OK、这些功能慢慢体验吧、你的应用已经运行android n新功能了

  1. 互动的多窗口布局
  2. 确保后台服务继续工作,打瞌睡模式处于活动状态
  3. 利用新功能更新通知

google原文地址、注意带上梯子
https://codelabs.developers.google.com/codelabs/getting-ready-for-android-n/index.html?index=..%2F..%2Fio2016#6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值