以前做过一个项目是这样的。手机端监控服务器的状态,首页显示的是监控的服务器的总数及报错数量,然后点击进入后,是列表界面,可以看到报错的服务器列表及好的服务器列表,然后点击列表中的某个可以具体看这个服务器的详情内容,比如监控哪几个进程,哪个进程报错了等信息。所以一共是三个界面:1、首页,2、列表界面,3、详情界面
需求:
-
当服务器有报错信息,或者是服务器由错误变成正确的时候,都会有信息推送到手机端。消息推送到达率尽可能要高,因为是监控服务器,所以不像一般的软件通知可有可无。
-
当用户是把软件关掉的状态下接受到信息是没关系,反正点击推送信息,都会再打开应用;但是当用户处于上述三个界面的某个界面的时候,都需要立即刷新当前所属界面的信息。比如当前客户是在首页的话,那信息推送过来后,首页就要被刷新;如果是在列表界面,有信息的话,列表界面就要被刷新;如果在详情界面,那详情界面就要被刷新。**
需求一处理:
前提说明,关于推送,我这边因为也做了个iOS小项目。也写了iPhone的推送,所以就粗略说下二者的推送(希望大家能指正,让我学习下):
- 苹果推送是利用它自带的APNS推送,相当于后台其实推送的信息是推送到了苹果的APNS,再发送到设备从而收到推送信息。举例来说,就是我们在iPhone上把QQ关了。这时候QQ是真的都关了,但是我们还会收到QQ的信息通知,但这时候并不是说QQ在后台活着,收到了信息,而是QQ服务器先把信息发给了APNS,然后APNS发送给iPhone设备,因为iPhone内部有系统进程会来接受APNS发送来的信息,然后层显出来通知。同理什么其他应用的通知也是都这个流程,所以这时候iPhone手机里面等于只要存活着一个能接受到APNS发送过来信息的系统进程,就可以实现呈现不同软件的通知信息了。
- Android上本来也有类似的苹果的推送,就是谷歌的推送GCM,大家都知道国内不能用这个,因为国内的手机厂商都是对原生安卓系统进行了Google内容的剔除。那国内的推送是怎么实现呢,就只能是每个应用自己有个接受信息的进程。
所以在Android设备上想要确保推送率,就是说如何让你接受监控服务器信息进程能尽可能的存活在手机中,从而能够接收到服务器发来的信息。
大家可以看下下面这边链接:
Android 进程保活招式大全
如果自己觉得写接受信息的进程太麻烦,就可以直接使用市面上现场的第三方推送,我大概分了下是这几种:
-
厂商推送:小米推送,华为推送。
因为是小米手机和华为手机收合入了自家的推送进程,不会被杀死,所以比如在小米手机上正确的合入了小米推送,消息就会准确被推送到手机上。 -
软件商推送:腾讯信鸽推送,百度云推送,阿里云推送等
腾讯,百度这些软件厂商也做起了推送,当然别想着合入了腾讯信鸽推送就可以像微信,QQ那样就能一直收到信息。 -
其他第三方推送:极光推送,友盟推送,LeanCloud等
我这边用的是LeanCloud的混合推送,因为混合推送是内部帮忙集成了小米推送和华为推送,及自带的LeanCloud推送,尽量确保了推送的到达率。阿里云推送也有个混合推送,也是同样的道理。
LeanCloud混合推送
设置完推送,记得对不同手机开启不同权限开关
Paste_Image.png需求二处理:
(ps:可能大家有更好的需求二的处理方法,希望大家下面回复我。可能我那时候这么处理并不是特别好。)
推送的讨论完了。我们来看下我们的第二个需求,因为推送信息的接受及处理,是在自定义BroadcastReceiver的onReceive(Context context, Intent intent) 方法中获取到信息。所以我想到了context.startActivity方法来启动相应的界面。而且这个界面还不能被重复的开了多个,不然按个返回按钮,就又出现那个老的界面了。在这个应用中,这个界面startActivity多次也只有这一个Activity实例。那这个界面不就等于被刷新了么。
我们分步来解决第二个需求:
1.如何知道当前是处于那个界面,是首页、列表界面,还是详情界面?
String className = ((ActivityManager.RunningTaskInfo) ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getRunningTasks(1).get(0)).topActivity.getClassName();
if ("com.monitorapp.module.home.ui.Act_Main".equals(className)) {
Intent intent_main = new Intent(context, Act_Main.class);
intent_main.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent_main);
} else if ("com.monitorapp.module.servers.ui.Act_ServerList".equals(className)) {
Intent intent_list = new Intent(context, Act_ServerList.class);
intent_list.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent_list);
} else if ("com.monitorapp.module.servers.ui.Act_MonitorDetail".equals(className)) {
Intent intent_detail = new Intent(context, Act_MonitorDetail.class);
intent_detail.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent_detail);
}
没错,我们可以获取到顶部的Activity的类名。然后我们把这个类名与这三个界面的类名进行比较就可以了。然后和哪个界面的Activity类名相同,就去启动相应的Activity就可以。
2.如何startActivity的时候不会开多个该Activity的实例。这就与Activity的启动方式有关。
Activity启动方式:
- standard 标准模式,默认启动模式:同一个Activity每次都会创建一个新的实例
- singleTop: 同一个Activity可以实例化多次,但是栈顶只能出现一个,当栈顶不存在要启动的Activity实例时,系统会创建一个新的Activity实例,并且放入栈顶,当栈顶存在要启动的Activity实例时,系统会调用onNewIntent()方法,把Intent对象传递给已经存在的Activity实例,重用栈顶的Activity
- singleTask: 同一个Activity实例在栈中只能有一个,当栈中不存在要启动的Activity实例时,系统会创建一个新的Activity实例,并放入栈顶,当栈中已存在Activity的实例时,系统会调用已存在的Activity的onNewIntent(),把Intent对象传递给已经存在的Activity实例(并不会创建新的实例),并且不允许栈的上方存在其他的Activity实例,他上方的Activity实例将会被移出栈并销毁
- singleInstance: 当使用这种模式启动Activity时,系统会分配一个单独的任务,并将Activity的实例放入栈的底端,它不允许其他Activity实例和它共享一个栈
大家也可以看下相关介绍:
Activity任务栈以及启动模式
好了。我们也了解了Activity的启动方式,当时我用的是singleInstance.但是对于这个需求的实现,singleTop和singleTask都可以使用。然后因为这时候这么设置了Activity的启动方式后。再去startActivity,这时候就会去调用改Activity的onNewIntent方法,这时候我们只要在各自的Activity的onNewIntent方法中写上相关接口调用及界面的数据刷新功能就可以了。
<activity
android:name=".module.home.ui.Act_Main"
android:launchMode="singleInstance"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".module.monitor.ui.Act_ServerList"
android:launchMode="singleInstance"
>
</activity>
<activity
android:name=".module.monitor.ui.Act_MonitorDetail"
android:launchMode="singleInstance"
>
</activity>
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//调用相关接口,然后进行界面刷新
}
网友评论