保活:如何让我们的app在Android系统不被杀死 保证存活,简单做法就是提升程序的优先级,看完本文一些流氓锁机你也会了哦.但锁机源码我不打算提供 为了防止某些恶心的人直接复制然后在市面上搞破坏
android 进程优先级如下:
1. 前台进程;Foreground process
1. 用户正在交互的Activity(onResume())
2. 当某个Service绑定正在交互的Activity。
3. 被主动调用为前台Service(startForeground())
4. 组件正在执行生命周期的回调(onCreate()/onStart()/onDestroy())
5. BroadcastReceiver 正在执行onReceive();
2. 可见进程;Visible process
1. 我们的Activity处在onPause()(没有进入onStop())
2. 绑定到可见的Activity的Service。
3. 服务进程;Service process
1. 简单的startService()启动。
4. 后台进程;Background process
1. 对用户没有直接影响的进程----Activity出于onStop()的时候。
5. 空进程; Empty process
1. 不含有任何的活动的组件。(android设计的,为了第二次启动更快,采取的一个权衡)
前提:需要root
本人在window环境下做示范
数字对应含义如下
主要有这么几个优先级(oom_adj值) 转载至参考文献:
Adjustment used in certain places where we don’t know it yet. (Generally this is something that is going to be cached, but we don’t know the exact value in the cached range to assign yet.)
This is a process only hosting activities that are not visible, so it can be killed without any disruption.
CACHED_APP_MIN_ADJ = 9
缓存进程,也就是空进程
SERVICE_B_ADJ = 8
不活跃的进程
The B list of SERVICE_ADJ – these are the old and decrepit services that aren’t as shiny and interesting as the ones in the A list.
This is the process of the previous application that the user was in. This process is kept above other things, because it is very common to switch back to the previous app. This is important both for recent task switch (toggling between the two top recent apps) as well as normal UI flow such as clicking on a URI in the e-mail app to view in the browser, and then pressing back to return to e-mail.
This is a process holding the home application – we want to try avoiding killing it, even if it would normally be in the background, because the user interacts with it so much.
This is a process holding an application service – killing it will not have much of an impact as far as the user is concerned.
This is a process with a heavy-weight application. It is in the background, but we want to try to avoid killing it. Value set in system/rootdir/init.rc on startup.
This is a process currently hosting a backup operation. Killing it is not entirely fatal but is generally a bad idea.
可感知的进程,比如那种播放音乐
This is a process only hosting activities that are visible to the user, so we’d prefer they don’t disappear.
- FOREGROUND_APP_ADJ = 0
前台进程
This is the process running the current foreground app. We’d really rather not kill it!
This is a process that the system or a persistent process has bound to, and indicated it is important.
This is a system persistent process, such as telephony. Definitely don’t want to kill it, but doing so is not completely fatal.
The system process runs at the default adjustment.
Special code for native processes that are not being managed by the system (so don’t have an oom adj assigned by the system).
在Android-18及以下空进程不叫CACHED_APP_MIN_ADJ ,叫HIDDEN_APP_MIN_ADJ,有这么点不一样,但是值都一样。
本人在window环境下做示范
此时你会看到控制台输出很多信息 如何你需要查找你的app进程pid比较麻烦的会可以把输出信息输出到文本 ,然后在文本中查找包名
adb shell ps > print.txt (输出到print.txt文本中)
tip:由于监听锁屏和点亮广播在高版本 无法静态注册(无法再清单文件中写一个广播)
- "android:windowBackground">@android:color/transparent
的主题 不然会显示一片黑色的东西
来看看具体代码:
public class EmptyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//这里注意一定要activity在清单继承一个含有
// - @android:color/transparent
主题
// 不然会不会透明 显示一片黑色
//大家待会往下看就知道为什么
MyApp application = (MyApp) getApplication();
//注册一个引用 方便 解锁的时候销毁让广播类销毁
application.putActivity(this);
//我们的activity是绘制在window之下的 所以我们直接修改window参数
//window显示的时候位于锁屏界面之下
Window window = getWindow();
//放置在窗口左边
window.setGravity(Gravity.LEFT);
//得到布局参数
WindowManager.LayoutParams attributes = window.getAttributes();
attributes.height = 1;//设置窗口大小 为1
attributes.width =1;
attributes.x = 0;//位置设置
attributes.y = 0;
//设置参数
window.setAttributes(attributes);
}
}
主题样式
<style name="MyStyle" parent="Theme.AppCompat">
<item name="android:windowBackground">@android:color/transparent
- "android:windowFrame"
>@null
- "android:windowNoTitle"
>true
- "android:windowIsFloating">true
- "android:backgroundDimEnabled">false
- "android:windowContentOverlay">@null
- "android:windowIsTranslucent">true
- "android:windowAnimationStyle">@null
- "android:windowDisablePreview">true
- "android:windowNoDisplay">false
style>
/**
* Created by FMY on 2017/4/2.
*/
public class MyApp extends Application {
WeakReference weakReference;
public void putActivity(Activity activity){
weakReference = new WeakReference<>(activity);
}
public void finishActivity(){
if (weakReference != null) {
Activity activity1 = weakReference.get();
if (activity1 != null) {
activity1.finish();
}
}
}
@Override
public void onCreate() {
super.onCreate();
//注册接收锁屏和点亮屏幕广播接收者
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
intentFilter.addAction(Intent.ACTION_USER_PRESENT);
intentFilter.setPriority(10000);
registerReceiver(new OffSreenBroadcast(), intentFilter);
}
}
public class OffSreenBroadcast extends BroadcastReceiver {
private static final String TAG = "OffSreenBroadcast";
@Override
public void onReceive(Context context, Intent intent) {
if (intent==null||intent.getAction()==null) {
return;
}
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
Log.e(TAG, "屏幕关了启动屏幕 开启一个activity: " +intent.getAction() );
Intent intent1 = new Intent(context, EmptyActivity.class);
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent1);
}
if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
Log.e(TAG, "屏幕开了 销毁" +intent.getAction() );
MyApp app = (MyApp) context;
app.finishActivity();
}
}
}
- 开启两个c进程守护 因为5.0以上卸载或者关闭程序会同一uid的进程一起关掉所以我这里就不做此方法阐述(需要JNI/NDK基本知识)
- 开启双服务 同样关闭程序的时候会把两个服务杀死 但是简单所以我们这里就介绍此种方式
tip:
大致思路:开启两个服务相互aidl互相绑定,如果其中一个被杀死会回调另一个服务的onServiceDisconnected 此时重新绑定即可
两个服务代码:
1. OneService :
public class OneService extends Service {
public OneService() {
}
private static final String TAG = "OneService";
@Override
public void onCreate() {
super.onCreate();
}
class MyConnet implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
new Thread(){
@Override
public void run() {
super.run();
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "onServiceConnected: ");
}
}
}.start();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: ");
Intent intent1 = new Intent(OneService.this,TwoService.class);
//BIND_IMPORTANT 表示这个服务非常重要 提高优先级
bindService(intent1,new OneService.MyConnet(), Context.BIND_AUTO_CREATE);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: " );
Intent intent1 = new Intent(this,TwoService.class);
//BIND_IMPORTANT 表示这个服务非常重要 提高优先级
bindService(intent1,new MyConnet(), Context.BIND_IMPORTANT);
//开启通知栏变为前台
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("前台服务开启中");
builder.setContentTitle("one服务");
builder.setSmallIcon(R.mipmap.ic_launcher);
//开启前台
startForeground(13,builder.build());
//被杀掉后重启
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: " );
return new MyIBinder();
}
class MyIBinder extends IMyAidlInterface.Stub
{
@Override
public void basicTypes() throws RemoteException {
}
}
}
public class TwoService extends Service {
private static final String TAG = "Two Server";
public TwoService() {
}
@Override
public void onCreate() {
super.onCreate();
}
class MyConnet implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG, "onServiceConnected: " );
new Thread(){
@Override
public void run() {
super.run();
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "onServiceConnected: ");
}
}
}.start();
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG, "onServiceDisconnected: " );
Intent intent1 = new Intent(TwoService.this, OneService.class);
//BIND_IMPORTANT 表示这个服务非常重要 提高优先级
bindService(intent1,new MyConnet(), Context.BIND_IMPORTANT);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: " );
Intent intent1 = new Intent(this,OneService.class);
//BIND_IMPORTANT 表示这个服务非常重要 提高优先级
bindService(intent1,new MyConnet(), Context.BIND_AUTO_CREATE);
//开启通知栏变为前台
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setContentTitle("前台服务开启中");
builder.setContentTitle("one服务");
builder.setSmallIcon(R.mipmap.ic_launcher);
//开启前台
startForeground(13,builder.build());
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: " );
return new MyIBinder();
}
class MyIBinder extends IMyAidlInterface.Stub
{
@Override
public void basicTypes() throws RemoteException {
}
}
}
MainActivity :
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this,OneService.class));
startService(new Intent(this,TwoService.class));
}
}
清单文件:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.youjiayou.fmy.maputil">
<permission android:name="android.permission.STATUS_BAR" />
<uses-permission android:name="android.permission.STATUS_BAR" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.REORDER_TASKS" />
<uses-permission android:name="android.permission.REAL_GET_TASKS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
intent-filter>
activity>
<receiver android:name=".BootBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.HOME" />
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
<action android:name="android.intent.action.TEST_GKP" />
intent-filter>
receiver>
<service
android:name=".MyService"
android:enabled="true"
android:process=":oneServer"
android:exported="true" >
service>
<service
android:process=":twoServer"
android:name=".LanchService"
android:enabled="true"
android:exported="true">
service>
application>
manifest>
在MIUI8 中我在服务中开启了一个悬浮窗,然后我发现一个有趣的现象,按下手机的最近任务键,然后点击清理所有任务按钮,发现服务和悬浮窗没有死,这我不惊讶,惊讶的是我在最近任务手动滑动当前进程去关掉,悬浮窗和服务才死去.也就说说我们可以学习qq黑科技在屏幕上留一个像素点悬浮窗.然后你懂的
import static android.content.ContentValues.TAG;
public class MyService extends Service {
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
/**
* 悬浮窗会隐式持有此activity
*/
WindowManager systemService = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
//设置不透明
layoutParams.format = PixelFormat.RGBA_8888;
//设置悬浮窗没有触摸反馈 让用户不明白悬浮窗这回事
layoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
//宽度
layoutParams.width =1;
//高度
layoutParams.height = 1;
//设置window type
layoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
View inflate = LayoutInflater.from(this).inflate(R.layout.hover_view, null);
inflate.findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("fmy", "悬浮窗被点击了");
Toast.makeText(MyService.this, "阿斯达斯", Toast.LENGTH_LONG).show();
}
});
systemService.addView(inflate, layoutParams);
new Thread(){
@Override
public void run() {
super.run();
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, "run: " );
}
}
}.start();
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
}
前面我们说了点击最最近任务按钮然后 清理所有任务就可以杀死所有相关服务.但是在android5.0中有一个新API却提供了便利JobScheduler.
JobScheduler是一把双刃剑可以省电也可以利用其保活,类似AlarManager.
我们使用JobScheduler创建一个周期任务,间隔10毫秒执行任务.(实际不可能 ,必须和手机的心率对齐,执行任务必须在窗口期执行,添加的队列存在android系统中,正因如此所以….)
JobSchedulerService2 :
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class JobSchedulerService2 extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Toast.makeText(this,"asd",Toast.LENGTH_LONG).show();
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
}
public class MainActivity extends AppCompatActivity {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo.Builder builder2= new JobInfo.Builder( 1,
new ComponentName( getPackageName(),
JobSchedulerService2.class.getName() ) );
builder2.setPeriodic(10);
//Persisted 重启后继续
// builder2.setPersisted(true);
jobScheduler.schedule(builder2.build());
}
}
清单文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.hover">
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.BIND_JOB_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
intent-filter>
activity>
<service
android:name=".JobSchedulerService2"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE">
service>
application>
manifest>
上面的代码执行后哪怕杀死进程也会执行
就是把apk放入 system/app中即可
下面大家简单即可
public void onclick(View view) {
DataOutputStream dataOutputStream = null;
BufferedReader err = null;
try {
//得到su权限
Process process = Runtime.getRuntime().exec("su root");
//输出命令
dataOutputStream = new DataOutputStream(process.getOutputStream());
//获取外部sd路径
File externalStorageDirectory = Environment.getExternalStorageDirectory();
//得到apk文件路径
File apkFile = new File(externalStorageDirectory.getAbsoluteFile()+File.separator+"youjiayou.apk");
//移动文件命令
String command = "cp " + apkFile.getAbsolutePath() + " system/app\n";
//挂载为system可写 不然无法写入文件 = = 这些坑我是一点点 踩过的
dataOutputStream.writeBytes("mount -o remount rw /system\n");
//输出命令
dataOutputStream.write(command.getBytes("UTF-8"));
//挂载为可写不然无法生效
dataOutputStream.writeBytes("chmod 777 /system/app/youjiayou.apk\n");
//挂载为可读
dataOutputStream.writeBytes("mount -o remount ro /system\n");
//刷新输出
dataOutputStream.flush();
//重启
dataOutputStream.writeBytes("reboot\n");
//退出
dataOutputStream.writeBytes("exit\n");
//刷新输出
dataOutputStream.flush();
//等候命令结束
process.waitFor();
String line;
String msg = "";
//读取控制台返回的数据
err = new BufferedReader(new InputStreamReader(process.getInputStream()));
//读取数据
while ((line = err.readLine()) != null) {
msg += line;
}
Log.e("Fmy", "结果 " +msg);
} catch (Exception e) {
// e.printStackTrace();
Log.e("Fmy", "发生异常" + e.getMessage());
} finally {
try {
if (dataOutputStream != null) {
dataOutputStream.close();
}
if (err != null) {
err.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
开启自启需要用户手动去授权,但是在miui系统中接收到开机广播在开机几十秒后.在这里大家可以监听
这里当你开机和拔掉手机耳机的时候会发出广播
让用户手动开启悬浮窗权限.WindowManager.LayoutParams. type=xxxx就可以让悬浮窗在整个android系统视图最上层. = =当然大家千万别再给开机权限了不然更恶心
示例apk:正在制作稍后上传
文献1
文献2
文献3