Android 的保活的两种解决方案
做Android 保活的背景
由于之前做一个项目的时候需要让进程一直在后台活下去保持不被杀死的状态,因此也是各种百度各种苦苦寻找,本来是想着靠Service来做保活的,因为我的手机是6.0系统的,之前试过的各种依靠Service的方式均以失败告终,因此决定站在另一个角度上来解决问题,–>Android的进程。
方案一:双进程守护
其实诸如类似360杀毒软件之类的产品本身原理是通过一个一个的遍历进程,如果存活就杀死从而达到清理软件的作用的,所以我们是可以拿到自己进程和创建新的进程的。而通过AIDL的接口则可以实现跨进程通信,因此,使用双进程并通过进程间的通信是一种可行的解决方案。因此方案一是通过双进程守护来解决这个Android应用保活的。
首先是一个AIDL接口,两边的Service都要通过继承Service_1.Stub来实现AIDL接口中的方法,这里做一个空实现,目的是为了实现进程通信。接口声明如下:
package com.ph.myservice;
interface Service_1 {
String getName();
}
然后是两个Service,为了保持连接,内部写一个内部类实现ServiceConnection的接口,当外部杀了其中一个进程的时候,会进入onDisConnection中,那么此时要做的就是start和bind另一个进程,因为Service的启动是可以多次的,所以这样是没问题的,代码如下:
package com.ph.myservice; import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Toast; import java.util.List; public class LocalService extends Service {
private ServiceConnection conn;
private MyService myService; @Override
public IBinder onBind(Intent intent) {
return myService;
} @Override
public void onCreate() {
super.onCreate();
init(); } private void init() {
if (conn == null) {
conn = new MyServiceConnection();
}
myService = new MyService();
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(getApplicationContext(), "本地进程启动", Toast.LENGTH_LONG).show();
Intent intents = new Intent();
intents.setClass(this, RemoteService.class);
bindService(intents, conn, Context.BIND_IMPORTANT);
return START_STICKY;
} class MyService extends Service_1.Stub { @Override
public String getName() throws RemoteException {
return null;
}
} class MyServiceConnection implements ServiceConnection { @Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("获取连接"); } @Override
public void onServiceDisconnected(ComponentName name) {
Toast.makeText(LocalService.this, "远程连接被干掉了", Toast.LENGTH_SHORT).show();
LocalService.this.startService(new Intent(LocalService.this,
RemoteService.class));
LocalService.this.bindService(new Intent(LocalService.this,
RemoteService.class), conn, Context.BIND_IMPORTANT); } } }
远程服务类如下:
package com.ph.myservice; import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.widget.Toast; public class RemoteService extends Service {
private MyBinder binder;
private ServiceConnection conn; @Override
public void onCreate() {
super.onCreate();
// System.out.println("远程进程开启");
init(); } @Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(getApplicationContext(), "远程进程启动", Toast.LENGTH_LONG).show();
Intent intents = new Intent();
intents.setClass(this, LocalService.class);
bindService(intents, conn, Context.BIND_IMPORTANT);
return START_STICKY;
} private void init() {
if (conn == null) {
conn = new MyConnection();
}
binder = new MyBinder();
} @Override
public IBinder onBind(Intent intent) {
return binder;
} static class MyBinder extends Service_1.Stub { @Override
public String getName() throws RemoteException {
return "远程连接";
}
} class MyConnection implements ServiceConnection { @Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("获取远程连接");
} @Override
public void onServiceDisconnected(ComponentName nme) {
Toast.makeText(RemoteService.this, "本地连接被干掉了", Toast.LENGTH_SHORT).show();
RemoteService.this.startService(new Intent(RemoteService.this,
LocalService.class));
RemoteService.this.bindService(new Intent(RemoteService.this,
LocalService.class), conn, Context.BIND_IMPORTANT);
}
} }
布局文件里要加上声明
<service android:name=".LocalService" />
<service
android:name=".RemoteService"
android:process=":remote" />
实际情况我个人测试,在5.0以下的模拟器上是没问题的,不管多次从系统的进程里kill掉,也还是会重新启动tos,但是5.0以上这种方法是无效的,5.0以上Android应该是意识到了这种双进程守护的方式,因此修改了一下源码,让这种双进程保活应用的方式无效。因此,针对5.0以上,我们采用另一种方案。
方案二:JobScheduler执行任务调度保活
JobScheduler这个类是21版本google新出来的api,我们看他的文档可以知道大致这个类的作用如下:
框架将智能当你收到你的回调,并尝试批并尽可能推迟。通常如果你不指定期限在你的工作,它可以运行在任何时候根据作业调度器的当前状态的内部队列,然而它可能是延迟只要直到下一次设备被连接到一个电源。
这个任务其实是在设备空闲期执行的,而且系统设计的这个api不会很耗电,本意是用来执行一些任务调度的,但是我们设想一下,如果用这个类来执行我们的开启双进程,那么也是一定会在设备空闲期执行的,因此我们写一个类继承JobService,在onstart里声明创建JobScheduler对象,并设置多就执行一次和开机自启动,这样就能确保及时在息屏状态,也能够执行重启进程,所以我们在JobService的onStopJob方法里判断我们的进程是否被回收了,如果被回收了就重启进程,这样子就可以实现5.0以上的进程保活了。具体代码如下:
package com.ph.myservice; import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.widget.Toast; import java.util.List; /**
* Created by 86119 on 2017/1/6.
*/ @SuppressLint("NewApi")
public class JobHandlerService extends JobService {
private JobScheduler mJobScheduler; @Override
public int onStartCommand(Intent intent, int flags, int startId) {
System.out.println("服务被创建"); // startService(new Intent(this, LocalService.class));
// startService(new Intent(this, RemoteService.class)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder = new JobInfo.Builder(startId++,
new ComponentName(getPackageName(), JobHandlerService.class.getName())); builder.setPeriodic(5000); //每隔5秒运行一次
builder.setRequiresCharging(true);
builder.setPersisted(true); //设置设备重启后,是否重新执行任务
builder.setRequiresDeviceIdle(true); if (mJobScheduler.schedule(builder.build()) <= 0) {
//If something goes wrong
System.out.println("工作失败");
} else {
System.out.println("工作成功");
}
}
return START_STICKY;
} @Override
public boolean onStartJob(JobParameters params) { Toast.makeText(this, "服务启动", Toast.LENGTH_SHORT).show();
// || isServiceRunning(this, "com.ph.myservice.RemoteService") == false
System.out.println("开始工作");
// if (!isServiceRunning(getApplicationContext(), "com.ph.myservice") || !isServiceRunning(getApplicationContext(), "com.ph.myservice:remote")) {
// startService(new Intent(this, LocalService.class));
// startService(new Intent(this, RemoteService.class));
// } /* boolean serviceRunning = isServiceRunning(getApplicationContext(), "com.ph.myservice");
System.out.println("进程一" + serviceRunning); boolean serviceRunning2 = isServiceRunning(getApplicationContext(), "com.ph.myservice:remote");
System.out.println("进程二" + serviceRunning2);*/
return false;
} @Override
public boolean onStopJob(JobParameters params) {
if (!isServiceRunning(this, "com.ph.myservice.LocalService") || !isServiceRunning(this, "com.ph.myservice.RemoteService")) {
startService(new Intent(this, LocalService.class));
startService(new Intent(this, RemoteService.class));
}
return false;
} // 服务是否运行
public boolean isServiceRunning(Context context, String serviceName) {
boolean isRunning = false;
ActivityManager am = (ActivityManager) this
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> lists = am.getRunningAppProcesses(); for (ActivityManager.RunningAppProcessInfo info : lists) {// 获取运行服务再启动
System.out.println(info.processName);
if (info.processName.equals(serviceName)) {
isRunning = true;
}
}
return isRunning; } }
package com.ph.myservice; import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
openJobService();
} else {
openTwoService();
} } private void openTwoService() {
startService(new Intent(this, LocalService.class));
startService(new Intent(this, RemoteService.class));
} private void openJobService() { Intent intent = new Intent();
intent.setClass(MainActivity.this, JobHandlerService.class);
startService(intent); }
}
经过我6.0系统的华为真机测试是没有问题的,就算处于息屏状态进程也还是活着的,不管过多久打开屏幕还是会tos,并且关机了开机也会tos。以上就是我的Android保活的两种方案啦,其实还有更深入的几种方案,但是涉及到ndk层和Linux层我目前也没做,就不在这里说啦。
Android 的保活的两种解决方案的更多相关文章
- PHP中实现MySQL嵌套事务的两种解决方案
PHP中实现MySQL嵌套事务的两种解决方案 一.问题起源 在MySQL的官方文档中有明确的说明不支持嵌套事务: Transactions cannot be nested. This is a co ...
- Android 抗锯齿的两种方法
Android 抗锯齿的两种方法 (其一:paint.setAntiAlias(ture);paint.setBitmapFilter(true)) 在Android中,目前,我知道有两种出现锯齿 ...
- javascript文件夹选择框的两种解决方案
javascript文件夹选择框的两种解决方案 解决方案1:调用windows 的shell,但会有安全问题. * browseFolder.js * 该文件定义了BrowseFolder()函数,它 ...
- android emulator启动的两种方法详解
android emulator启动的两种方法详解 转https://blog.csdn.net/TTS_Kevin/article/details/7452237 对于android学习者,模 ...
- Android中BroadcastReceiver的两种注册方式(静态和动态)详解
今天我们一起来探讨下安卓中BroadcastReceiver组件以及详细分析下它的两种注册方式. BroadcastReceiver也就是"广播接收者"的意思,顾名思义,它就是用来 ...
- Android中Fragment的两种创建方式
fragment是Activity中用户界面的一个行为或者是一部分.你可以在一个单独的Activity上把多个Fragment组合成为一个多区域的UI,并且可以在多个Activity中再使用.你可以认 ...
- Android请求服务器的两种方式--post, get的区别
android中用get和post方式向服务器提交请求_疯狂之桥_新浪博客http://blog.sina.com.cn/s/blog_a46817ff01017yxt.html Android提交数 ...
- Android TextView里显示两种颜色
今天介绍一个小技巧,在Android的TextView里设置两种颜色,直接上代码: TextView TV = (TextView)findViewById(R.id.mytextview01); S ...
- Android访问WebService的两种方法
首先解释一下WebService:WebService是一种基于SOAP协议的远程调用标准.通过WebService可以将不同操作系统平台,不同语言.不同技术整合到一起.详细见:http://baik ...
随机推荐
- 关于IT术语---ip、uv、pv、tps、qps、rps
涉及到IT方面的几条术语,这里要好好说道说道: 只要和网站打交道,难免会经常听到一系列的转有名词 >>> 系统今日UV多少.PV多少.QPS多少之类的问题.这里就对这些常见的术语 ...
- POJ 1981 最大点覆盖问题(极角排序)
Circle and Points Time Limit: 5000MS Memory Limit: 30000K Total Submissions: 8346 Accepted: 2974 ...
- ELK之Elasticsearch
安装并运行Elasetisearch cd elasticsearch-<version> ./bin/elasticsearch 如果你想把 Elasticsearch 作为一个守护进程 ...
- 17-比赛1 F - 较小元素 Weak in the Middle (set)
Seg-El has last chance to make the final changes in order to prevent the destruction of Krypton. He ...
- Java线程和多线程(六)——守护线程
当我们在Java中创建线程的时候,这个线程在默认的情况下是一个用户线程,并且,如果这个线程在运行,那么JVM就不会终结这个应用.和用户线程不同,当一个线程被标记为守护线程的时候,JVM在用户线程结束的 ...
- Java语言基础---变量与数据类型
变量的作用域 java用一对大括号“{}”作为语句块的范围,称为作用域.作用域中的变量不能重复定义:离开作用域,变量所分配的内存空间将被JVM所收回. 基本数据类型的包装类 java为基础数据类型提供 ...
- TP5 中出现 No input file specified
之前用php5.4 更新至php7之后原tp5项目出现 No input file specified 修改方法: 打开public目录下的.htaccess文件,把:RewriteRule ^(.* ...
- Python语法之com[1][:-7]
strCom = com[0] + ": " + com[1][:-7] 如上应该是一个字符串合成,最后的[1][:-7],我理解是去除com[1]的最后7个字符. 比如com[0 ...
- ScrollView中ViewPager无法正常滑动问题
本文主要介绍如何解决ViewPager在ScrollView中滑动经常失效.无法正常滑动问题. 解决方法只需要在接近水平滚动时ScrollView不处理事件而交由其子View(即这里的ViewPage ...
- USACO Section2.2 Runaround Numbers 解题报告 【icedream61】
runround解题报告---------------------------------------------------------------------------------------- ...