当前位置:   article > 正文

Android进程保活全攻略(中)_android service 监听锁屏解锁

android service 监听锁屏解锁

在上一篇博客Android进程保活全攻略(上)

中介绍了进程保活的背景和一些方法的思路和实现方式,本篇博客我将承接上篇博客,继续进行介绍。

9) 1像素悬浮层 **思路:**1像素悬浮层是传说的QQ黑科技,监控手机锁屏解锁事件,在屏幕锁屏时启动1个像素的 Activity,在用户解锁时将 Activity 销毁掉。注意该 Activity 需设计成用户无感知。通过该方案,可以使进程的优先级在屏幕锁屏时间由4提升为最高优先级1。 保活强度: 前台进程,跟前台服务差不多。需要权限,不敌force-stop 实现代码: 首先定义 Activity,并设置 Activity 的大小为1像素:

public class MainActivity extendsAppCompatActivity {
    private static final StringTAG="keeplive";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
        Window window = 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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

其次,从 AndroidManifest 中通过如下属性,排除 Activity 在 RecentTask 中的显示:

<activity
    android:name=".KeepAliveActivity"
    android:excludeFromRecents="true"
    android:exported="false"
    android:finishOnTaskLaunch="false"
    android:launchMode="singleInstance"
    android:process=":live"
    android:theme="@style/LiveActivityStyle"
    >
</activity>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

最后,控制 Activity 为透明:

<stylename="LiveActivityStyle">
   <itemname="android:windowBackground">@android:color/transparent</item>
   <itemname="android:windowFrame">@null</item>
   <itemname="android:windowNoTitle">true</item>
   <itemname="android:windowIsFloating">true</item>
   <itemname="android:windowIsTranslucent">true</item>
   <itemname="android:windowContentOverlay">@null</item>
   <itemname="android:windowAnimationStyle">@null</item>
   <itemname="android:windowDisablePreview">true</item>
   <itemname="android:windowNoDisplay">true</item>
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Activity 启动与销毁时机的控制:

public class KeepLiveReceiver extendsBroadcastReceiver {
    privateContextmContext;

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (action.equals(Intent.ACTION_SCREEN_OFF)) {
            KeepLiveManeger.getInstance(mContext).startKeepLiveActivity();
        } else if (action.equals(Intent.ACTION_USER_PRESENT)) {
            KeepLiveManeger.getInstance(mContext).destroyKeepLiveActivity();
        }
        KeepLiveManeger.getInstance(mContext).startKeepLiveService();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

10) 应用间互相拉起 **思路:**app之间知道包名就可以相互唤醒了,比如你杀了我qq,只要微信还在就能确保随时唤醒qq。还有百度全系app都通过bdshare实现互拉互保,自定义一个广播,定时发,其他app收广播自起等

11) 心跳唤醒 **思路:**微信保活技术,依赖系统特性:长连接网络回包机制 **保活强度:**不敌force-stop,需要网络,API level >= 23的doze模式会关闭所有的网络 代码实现:

public class HeartbeatService extends Service implements Runnable {
    private Thread mThread;
    public int count = 0;
    private boolean isTip = true;
    private static String mRestMsg;
    private static String KEY_REST_MSG = "KEY_REST_MSG";

    @Override
    public void run() {
        while (true) {
            try {
                if (count > 1) {

                    count = 1;
                    if (isTip) {
                        //判断应用是否在运行
                        ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
                        List<RunningTaskInfo> list = am.getRunningTasks(3);
                        for (RunningTaskInfo info : list) {
                            if (info.topActivity.getPackageName().equals("org.yhn.demo")) {
                                //通知应用,显示提示“连接不到服务器”
                                Intent intent = new Intent("org.yhn.demo");
                                intent.putExtra("msg", true);
                                sendBroadcast(intent);
                                break;
                            }
                        }

                        isTip = false;
                    }
                }
                if (mRestMsg != "" && mRestMsg != null) {
                    //向服务器发送心跳包
                    sendHeartbeatPackage(mRestMsg);
                    count += 1;
                }

                Thread.sleep(1000 * 3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private void sendHeartbeatPackage(String msg) {
        HttpGet httpGet = new HttpGet(msg);
        DefaultHttpClient httpClient = new DefaultHttpClient();
        // 发送请求
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (httpResponse == null) {
            return;
        }

        // 处理返回结果
        final int responseCode = httpResponse.getStatusLine().getStatusCode();
        if (responseCode == HttpStatus.SC_OK) {
            //只要服务器有回应就OK
            count = 0;
            isTip = true;
        } else {
            Log.i("@qi", "responseCode " + responseCode);
        }

    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public void onCreate() {
        super.onCreate();
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    public void onStart(Intent intent, int startId) {
        Log.i("@qi", "service onStart");
        //从本地读取服务器的URL,如果没有就用传进来的URL
        mRestMsg = getRestMsg();
        if (mRestMsg == null || mRestMsg == "") {
            mRestMsg = intent.getExtras().getString("url");
        }
        setRestMsg(mRestMsg);

        mThread = new Thread(this);
        mThread.start();
        count = 0;

        super.onStart(intent, startId);
    }

    public String getRestMsg() {
        SharedPreferences prefer = getSharedPreferences("settings.data", Context.MODE_PRIVATE);

        return prefer.getString(KEY_REST_MSG, "");
    }

    public void setRestMsg(String restMsg) {
        SharedPreferences prefer = getSharedPreferences("settings.data", Context.MODE_PRIVATE);
        SharedPreferences.Editor editor = prefer.edit();
        editor.putString(KEY_REST_MSG, restMsg);
        editor.commit();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

最后别忘了注册Server和GET_TASKS

<service
    android:name=".demo.HeartbeatService"
    android:label="QServer"
    android:persistent="true" >
    <intent-filter>
        <action android:name="HeartbeatService" />
    </intent-filter>
</service>
<uses-permission android:name="android.permission.GET_TASKS" />

<uses-permission android:name="android.permission.GET_TASKS" />
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

12) Native进程拉起 **思路:**开启native子进程,定时发intent **保活强度:**单杀可以杀死,force close 5.0以上无效,5.0以下部分手机无效,第三方软件下无效,且无法保证实时常驻 实现代码: 首先开启一个c进程,将需要保活的service名字传递进去

private static void start(Context context, Class<?> daemonClazzName, int interval) {
   String cmd = context.getDir(BIN_DIR_NAME, Context.MODE_PRIVATE)
      .getAbsolutePath() + File.separator + DAEMON_BIN_NAME;

   /* create the command string */
   StringBuilder cmdBuilder = new StringBuilder();
   cmdBuilder.append(cmd);
   cmdBuilder.append(" -p ");
   cmdBuilder.append(context.getPackageName());
   cmdBuilder.append(" -s ");
   cmdBuilder.append(daemonClazzName.getName());
   cmdBuilder.append(" -t ");
   cmdBuilder.append(interval);

   try {
      Runtime.getRuntime().exec(cmdBuilder.toString()).waitFor();
   } catch (IOException | InterruptedException e) {
      Log.e(TAG, "start daemon error: " + e.getMessage());
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

然后定时给自己主进程发一个intent,如果主进程挂掉了,就可以顺利拉起来保证存活。

while(sig_running)
{
   interval = interval < SLEEP_INTERVAL ? SLEEP_INTERVAL : interval;
   select_sleep(interval, 0);

   LOGD(LOG_TAG, "check the service once, interval: %d", interval);

   /* start service */
   start_service(package_name, service_name);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

但这只是一个没有主动权的消息轮询器,说是守护其实很勉强,而且,这是要建立在保证c进程不挂的基础上,才能轮询,但是就目前来看,只有5.0以下的非国产机才会有这样的漏洞。也就是说在force close的时候,系统忽略c进程的存在,5.0以上包括5.0的哪怕源生系统也会连同c进程一起清理掉,国产机就更不用说了。就算是这样,在5.0以下的非国产机上,如果安装了获取root权限的360\cm的话,也是可以直接清理掉,也就是说会失效。

native进程守护缺点非常明显,那就是守护是单向的,也就是说只能a保b,b保不了a;a保b也不是在b死了立刻拉起来,要等到了时间才会去拉。那如何解决这个native进程的缺点呢?那就是通过双进程守护,下一篇我将详细讲解如何通过linux层来实现双进程守护。

更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》

1.Android车载应用开发系统学习指南(附项目实战)

2.Android Framework学习指南,助力成为系统级开发高手

3.2023最新Android中高级面试题汇总+解析,告别零offer

4.企业级Android音视频开发学习路线+项目实战(附源码)

5.Android Jetpack从入门到精通,构建高质量UI界面

6.Flutter技术解析与实战,跨平台首要之选

7.Kotlin从入门到实战,全方面提升架构基础

8.高级Android插件化与组件化(含实战教程和源码)

9.Android 性能优化实战+360°全方面性能调优

10.Android零基础入门到精通,高手进阶之路

敲代码不易,关注一下吧。ღ( ´・ᴗ・` )

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/633754
推荐阅读
相关标签