借鉴部分
背景:从产品的角度来说,任何一个应用程序的PM都希望自己的应用程序在用户手机中的留存率高些些,之前我接触到的一个业务需求也是如此,要求提升应用程序在国内第三方厂商ROM中的存活率。
如前篇所述踩坑篇,保活策略只在android原生系统中起作用,在国内第三方厂商ROM中是没有效果的。为了提高app在第三方ROM中的存活率,调研了一些方法,最终选择某移动社交应用的1像素保活方案,因为这个方案人家没有开源,所以只能自己动手。针对国内第三方厂商ROM在退屏后应用很快被杀的调查发现,原生系统在内存充足的情况下,用户执行熄屏的操作不会立即kill app,但是第三方ROM会,推测是修改了ROM在熄屏时执行了内存清理工作,不论此时手机内存是否充足都执行清理。对于没有加入到厂家ROM清理白名单中的app,不可避免的被杀。当然有的人会不同意,说像QQ,微信之类的就没有在熄屏后被杀,用户也没有手动将其加入系统白名单中。这种情况是QQ和微信已经加入到系统的白名单中了,当然不需要用户手动添加了。不过这已经经 是技术人员可以解决的了,需要双方的商务洽谈了。
我在使用国内第三方ROM MIUI时发现,如果保持当前应用在前台存活,执行熄屏,分别等待1分钟,5分钟,10分钟后,再次打开屏幕时,app会存活,即使被杀掉也会重新启动。基与此并结合1像素保活方案,我在应用程序启动时注册一个监听器监听屏幕亮屏熄屏,当用户启动我的应用程序时,并没有按退出的应用程序,切换到其他应用程序使我的应用程序进入后台待机模式,只要我方应用没有被杀死,当用户熄屏时,会启动OnepxActivity,这样人为的制造了一个系统欺骗,告诉ROM,用户执行熄屏时,当前UI是我的应用程序。这样,当用户亮屏时,因为有监听,OnepxActivity也会销毁,达到一个用户无感知同时又提高了我方的应用在第三方ROM中的存活率。
原创部分:
(1)前台服务器创建:
1,在前台服务器的创建(server.class):在类中,我创建了一个线程,是因为在像服务器做得到请求的时候,需要将程序放到线程中实现在onStartCommand函数中调用像素的广播。
包com.example.administrator.qshAndroid.web;
导入android.annotation.TargetApi;
导入android.app.Notification;
导入android.app.NotificationManager;
导入android.app.Service;
导入android.content.Context;
导入android.content.Intent;
导入android.content.IntentFilter;
导入android.os。*;
导入android.widget.Toast;
import com.example.administrator.demo.R;
import com.example.administrator.qshAndroid.utils.OnePixelReceiver;
import org.json.JSONException;
import org.json.JSONObject;
/ **
*由管理员在2017年12月27日创建。
* /
公共类服务器扩展服务{
公共静态最终诠释NOTIFICATION_ID = 0x11;
私人静态PowerManager.WakeLock mWakeLock;
private String memberLoginBM =“”;
private String getCode =“”;
private OnePixelReceiver mOnepxReceiver;
String infoBack =“”;
int dataBack = 0;
公共服务器(){
}
//必须实现的,绑定改服务时回调该方法
@覆盖
公共IBinder onBind(意图意图){
抛出新的UnsupportedOperationException(“尚未实现”);
}
//服务器被创建时回调
@覆盖
public void onCreate(){
// acquireWakeLock(本);
super.onCreate();
}
//服务器断开连接时回调
@覆盖
public boolean onUnbind(Intent intent){
Toast.makeText(server.this,“onUnbind!”,Toast.LENGTH_SHORT).show();
返回true;
}
//服务器被关闭前回调
@覆盖
public void onDestroy(){
stopForeground(true); //停止前台服务 - 参数:表示是否移除之前的通知
Toast.makeText(server.this,“destory!”,Toast.LENGTH_SHORT).show();
意图localIntent =新的意图();
localIntent.setClass(this,server.class); //销毁时重新启动服务
this.startService(localIntent);
}
@覆盖
public void onRebind(Intent intent){
Toast.makeText(server.this,“onRebind!”,Toast.LENGTH_SHORT).show();
super.onRebind(意向);
}
@覆盖
公共int onStartCommand(意图intent,诠释标志,诠释startId){
//注册监听屏幕的广播,启动像素保活
mOnepxReceiver = new OnePixelReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction( “android.intent.action.SCREEN_OFF”);
intentFilter.addAction( “android.intent.action.SCREEN_ON”);
intentFilter.addAction( “android.intent.action.USER_PRESENT”);
registerReceiver(mOnepxReceiver,intentFilter);
memberLoginBM = intent.getStringExtra(“allMessage”);
getCode = intent.getStringExtra(“code”);
new Thread(new MyThread2())。start();
返回START_STICKY;
}
公共静态类InnerService延伸服务{
@覆盖
公共IBinder onBind(意图意图){
返回null;
}
@覆盖
public void onCreate(){
super.onCreate();
//发送与KeepLiveService中ID相同的通知,然后将其取消并取消自己的前台显示
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle(“Foreground Service2”);
builder.setContentText( “----------------------------”);
startForeground(NOTIFICATION_ID,builder.build());
new Handler()。postDelayed(new Runnable(){
@覆盖
public void run(){
stopForeground(真);
NotificationManager manager =(NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
stopSelf();
}
},100);
}
@覆盖
公共int onStartCommand(意图intent,诠释标志,诠释startId){
//标记(TAG,“VMDaemonService-> onStartCommand”);
/ **
*这里返回值是使用系统服务的机制自动重新启动,不过这种方式以下两种方式不适用:
* 1.服务第一次被异常杀死后会在5秒内重启,第二次被杀死会在10秒内重启,第三次会在20秒内重启,一旦在短时间内服务被杀死达到5次,则系统不再拉起。
* 2.进程被取得Root权限的管理工具或系统工具通过forestop停止掉,无法重启。
* 3.一些定制化比较高的第三方系统也不适用
* /
返回START_STICKY;
}
}
//子线程接收数据,主线程修改数据
公共类MyThread2实现了Runnable {
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
@覆盖
public void run(){
infoBack = serverPick.executeHttpGet(memberLoginBM,getCode);
JSONObject jsonObject = null;
尝试{
jsonObject = new JSONObject(infoBack);
dataBack = jsonObject.getInt(“success”);
Notification.Builder builder = new Notification.Builder(server.this);
引用代码= “1”;
if(dataBack == 1){
if(Build.VERSION.SDK_INT <18){
startForeground(NOTIFICATION_ID,new Notification()); // API <18,此方法能有效隐藏Notification上的图标
} else {
// API 18以上,发送通知并将其置为前台后,启动InnerService
builder.setSmallIcon(R.mipmap.ic_launcher);
//builder.setTicker("Foreground Service Start“);
builder.setContentTitle( “企商汇”);
builder.setContentText( “你有新的业务信息没有读取!”);
builder.setDefaults(Notification.DEFAULT_SOUND);
startForeground(NOTIFICATION_ID,builder.build());
startService(new Intent(server.this,InnerService.class));
了Thread.sleep(10000);
跑();
//继续;
}
} else if(dataBack == 2){
if(Build.VERSION.SDK_INT <18){
startForeground(NOTIFICATION_ID,new Notification()); // API <18,此方法能有效隐藏Notification上的图标
} else {
//Notification.Builder builder = new Notification.Builder(server.this);
builder.setSmallIcon(R.mipmap.ic_launcher);
//builder.setTicker("Foreground Service Start“);
builder.setContentTitle( “企商汇”);
builder.setContentText( “你暂时没有新的业务信息!”);
//builder.setDefaults(Notification.DEFAULT_SOUND);
startForeground(NOTIFICATION_ID,builder.build());
startService(new Intent(server.this,InnerService.class));
了Thread.sleep(10000);
跑();
// Toast.makeText(server.this,“你暂时没有新的业务信息!”,Toast.LENGTH_SHORT).show();
}
} else {
if(Build.VERSION.SDK_INT <18){
startForeground(NOTIFICATION_ID,new Notification()); // API <18,此方法能有效隐藏Notification上的图标
} else {
builder.setSmallIcon(R.mipmap.ic_launcher);
//builder.setTicker("Foreground Service Start“);
builder.setContentTitle( “企商汇”);
builder.setContentText( “服务器,通讯错误,请重启应用程序!”);
//builder.setDefaults(Notification.DEFAULT_SOUND);
startForeground(NOTIFICATION_ID,builder.build());
startService(new Intent(server.this,InnerService.class));
了Thread.sleep(10000);
跑();
}
}
catch(JSONException e){
e.printStackTrace();
catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
2,在前台AndroidMainfest.xml文件中注册服务器
<service android:name =“com.example.administrator.qshAndroid.web.server”>
<意图滤波器>
<action android:name =“com.example.administrator.qshAndroid.web.server”/>
</意图滤波器>
</服务>
3,在另一个活动中启动服务器
Intent intent2 = new Intent(menu.this,server.class);
intent2.putExtra( “allMessage”,allMessage);
intent2.putExtra( “代码”, “2”);
startService(intent2);
如图4所示,在1中创建的像素BroadcastReceiver.java
包com.example.administrator.qshAndroid.utils;
导入android.content.BroadcastReceiver;
导入android.content.Context;
导入android.content.Intent;
导入android.content.IntentFilter;
/ **
*由管理员于2018-1-9创建。
* /
公共类OnePixelReceiver扩展BroadcastReceiver {
私人静态OnePixelReceiver接收器;
@覆盖
public void onReceive(Context context,Intent intent){
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { //屏幕关闭启动1像素Activity
Intent it = new Intent(context, OnePiexlActivity.class);
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(it);
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { //屏幕打开 结束1像素
context.sendBroadcast(new Intent("finish activity"));
}
}
public static void register1pxReceiver(Context context) {
if (receiver == null) {
receiver = new OnePixelReceiver();
}
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_ON));
}
public static void unregister1pxReceiver(Context context) {
context.unregisterReceiver(receiver);
}
}
5,像素的活性(OnePiexActivity.java)
包com.example.administrator.qshAndroid.utils;
导入android.app.Activity;
导入android.content.BroadcastReceiver;
导入android.content.Context;
导入android.content.Intent;
导入android.content.IntentFilter;
导入android.os.Bundle;
导入android.os.PowerManager;
导入android.view.Gravity;
导入android.view.Window;
导入android.view.WindowManager;
/ **
*由管理员于2018-1-9创建。
* /
公共类OnePiexlActivity扩展活动{
私人BroadcastReceiver endReceiver;
@覆盖
保护无效的onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
//设置1个像素
窗口窗口= getWindow();
window.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x = 0;
params.y = 0;
params.height = 1;
params.width = 1;
window.setAttributes(PARAMS);
//结束该页面的广播
endReceiver = new BroadcastReceiver(){
@覆盖
public void onReceive(Context context,Intent intent){
完();
}
};
registerReceiver(endReceiver,new IntentFilter(“finish”));
//检查屏幕状态
checkScreen();
}
@覆盖
保护无效onResume(){
super.onResume();
checkScreen();
}
/ **
*检查屏幕状态isScreenOn为true屏幕“亮”结束该活动
* /
private void checkScreen(){
PowerManager pm =(PowerManager)OnePiexlActivity.this.getSystemService(Context.POWER_SERVICE);
boolean isScreenOn = pm.isScreenOn();
if(isScreenOn){
完();
}
}
}
如图6所示,设置像素为透明:
<style name =“OnePixelActivity”parent =“android:Theme.Holo.Light.NoActionBar”> //无标题
<item name =“android:windowIsTranslucent”> true </ item> //透明
如图7所示,在XML注册中
OnePiexActivity.java
<activity android:name =“com.example.administrator.qshAndroid.utils.OnePiexlActivity”
机器人:screenOrientation = “画像”
机器人:主题= “@风格/ OnePixelActivity”> </活动>
逻辑流程:在主活动中,调用我们的前台服务器,在前台服务器中调用像素广播,像素广播调用像素的活动,这样在锁屏的时候,调用活动,开启的时候关闭即可就这样,恭喜。你,你的Android的被保活了。