Desarrollo de componentes Android (4)--Encapsulación de componentes Keep-Alive de procesos

prefacio

En el artículo anterior, encapsulamos el componente de solicitud de red lib_neworky el componente de carga de imágenes.Hoy lib_image_loaderencapsularemos un componente de mantenimiento de procesolib_pull_alive

Artículos relacionados:

Desarrollo de componentes de Android (1)--Construcción de servicios privados de Maven

Desarrollo de componentes de Android (2)--Encapsulación de componentes de solicitud de red

Desarrollo de componentes de Android (3)--Encapsulación de componentes de carga de imágenes

Desarrollo de componentes.png

En la antigüedad, había muchas tecnologías negras. Por ejemplo MarsDaemon, el uso de vigilancia de proceso dual para mantenerse con vida era muy hermoso en ese momento, pero desafortunadamente 8.0se abandonó cuando llegó el momento.

Otro ejemplo es 1像素Activityel método keep-alive que apareció más tarde. No es exagerado decir que es un pícaro. Si todos usan estas operaciones, porque el consumo de energía aumenta considerablemente, afectará directamente la vida útil del teléfono móvil. . Por lo tanto, para solucionar la aparición de este fenómeno, los principales fabricantes de teléfonos móviles han impuesto restricciones a estos comportamientos no autorizados a nivel del sistema: 后台进程即使你是要黑科技让进程优先级很高,也可能被杀死, por lo que prefiero llamarlo aplicación 求生en lugar de aplicación 保活.

La intención original de este método es buena, reducir el consumo de energía del dispositivo, reducir la memoria, evitar que el teléfono se caliente, etc., pero para algunas aplicaciones que realmente necesitan realizar operaciones de mantenimiento, se puede describir como miserable.

Así 求生que han surgido nuevas medidas.

Cómo sobrevivir con gracia

Después de Android 6.0, el sistema ha lanzado una solución de optimización de la batería, que eliminará estratégicamente algunos procesos que consumen mucha energía.

Entonces, algunas personas pueden querer preguntar, ¿cómo se guardan las aplicaciones que consumen mucha energía, como WeChat y QQ?

看下图

WeChat qq lista blanca.awebp

Se puede ver que el sistema ha puesto WeChat y QQ en ellos 白名单¿Cómo se hace esto?

De hecho, esta es una negociación entre WeChat y el fabricante, y sus propias aplicaciones están configuradas en su lista blanca de optimización de energía. La próxima vez que un producto me pregunte por qué pueden hacerlo, les mostraré esta imagen. .

Entonces, ¿podemos dejar que el fabricante nos agregue a la lista? UH uh uh. .

El chico tiene grandes ideas.webp

Afortunadamente, los fabricantes de teléfonos móviles no bloquearon el camino, dejándonos un camino de regreso.

  • 1. Primero use el siguiente código para detectar si nuestro proceso está en la lista blanca:
@RequiresApi(api = Build.VERSION_CODES.M)
private boolean isIgnoringBatteryOptimizations() {
    boolean isIgnoring = false;
    PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
    if (powerManager != null) {
        isIgnoring = powerManager.isIgnoringBatteryOptimizations(getPackageName());
    }
    return isIgnoring;
}
  • 2. Si no: llame al siguiente código para solicitar la inclusión en la lista blanca
@RequiresApi(api = Build.VERSION_CODES.M)
public void requestIgnoreBatteryOptimizations() {
    try {
        Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
        intent.setData(Uri.parse("package:" + getPackageName()));
        startActivity(intent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

申请时会弹出一个让用户选择的Dialog:

opciones de optimización de batería.awebp

窗口中会提示该操作可能是影响电池的使用,如果需要监听用户的按键,可以使用startActivityResultonActivityResult中监听

好了,白名单是加好了,那是不是就是万事大吉了呢?

手机厂商:哪有那么容易,就算你加入了电量优化白名单,你要是不按规矩来,在后台运行的进程还是会被我们杀掉?还有啥招式快快使出来吧

Acercar. gif eee。。

我们知道进程被杀死,是因为系统的后台管理系统把我们重启的路堵住了,为啥堵我啊?按我说可能系统看你这个进程不顺眼吧,哈哈。。

言归正传:

其实是你不在后台管理的自启动白名单中,自启动白名单就像一张通行证,你的应用需要在系统后台自启动,就要在白名单上,否则哪里来回哪里去吧

那白名单这么好,怎么才能加入TM呢?

要知道市面上手机厂家很多,每个厂家的系统都不一样,一个系统还有很多甚至几十个版本,这让我们怎么加入啊?

而大部分自启动操作可以在厂商的手机管家的设置里面设置:

最理想的做法:我们根据不同手机,甚至是不同的系统版本,给用户呈现一个图文操作步骤,并且提供一个按钮,直接跳转到指定页面进行设置

  • 我们先定义下面两个方法:
/**
 * 跳转到指定应用的首页
 */
private static void showActivity(Context context,@NonNull String packageName) {
	Intent intent = context.getPackageManager().getLaunchIntentForPackage(packageName);
	context.startActivity(intent);
}

/**
 * 跳转到指定应用的指定页面
 */
private void showActivity(Context context,@NonNull String packageName, @NonNull String activityDir) {
	Intent intent = new Intent();
	intent.setComponent(new ComponentName(packageName, activityDir));
	intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
	context.startActivity(intent);
}
  • 手机厂商判断:
华为:
public boolean isHuawei() {
    if (Build.BRAND == null) {
        return false;
    } else {
        return Build.BRAND.toLowerCase().equals("huawei") || Build.BRAND.toLowerCase().equals("honor");
    }
}
小米
public static boolean isXiaomi() {
    return Build.BRAND != null && Build.BRAND.toLowerCase().equals("xiaomi");
}
OPPO
public static boolean isOPPO() {
    return Build.BRAND != null && Build.BRAND.toLowerCase().equals("oppo");
}

VIVO
public static boolean isVIVO() {
    return Build.BRAND != null && Build.BRAND.toLowerCase().equals("vivo");
}
魅族
public static boolean isMeizu() {
    return Build.BRAND != null && Build.BRAND.toLowerCase().equals("meizu");
}
三星
public static boolean isSamsung() {
    return Build.BRAND != null && Build.BRAND.toLowerCase().equals("samsung");
}

  • 手机管家或者自启动界面启动方式:
华为:
private void goHuaweiSetting() {
    try {
        showActivity("com.huawei.systemmanager",
            "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
    } catch (Exception e) {
        showActivity("com.huawei.systemmanager",
            "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
    }
}
小米:

private void goXiaomiSetting() {
    showActivity("com.miui.securitycenter",
        "com.miui.permcenter.autostart.AutoStartManagementActivity");
}

OPPO:
private void goOPPOSetting() {
    try {
        showActivity("com.coloros.phonemanager");
    } catch (Exception e1) {
        try {
            showActivity("com.oppo.safe");
        } catch (Exception e2) {
            try {
                showActivity("com.coloros.oppoguardelf");
            } catch (Exception e3) {
                showActivity("com.coloros.safecenter");
            }
        }
    }
}

VIVO
private void goVIVOSetting() {
    showActivity("com.iqoo.secure");
}

魅族:
private void goMeizuSetting() {
    showActivity("com.meizu.safe");
}
三星:
private void goSamsungSetting() {
    try {
        showActivity("com.samsung.android.sm_cn");
    } catch (Exception e) {
        showActivity("com.samsung.android.sm");
    }
}

总结下上面我们所讲:

  • 1.为了不被电量优化,我们需要将应用添加进电量优化白名单中
  • 2.为了可以在被杀死后,自己可以启动自己,需要将应用自启动开关开启,可以使

用图文引导的方式:

参考下面这张图:

Inicio automático.awebp

保活增强:

我们都知道保活操作一般是使用一个前台服务来挂起我们的应用: 还有的保活操作是使用一个JobService来对让系统在某个条件符合下回调一个请求操作。

基于以上分析:

  • 笔者这边封装了一个保活组件-lib_pull_alive

Combinando: 前台服务+ JobService+ 电量优化白名单+ 引导用户应用自启动的方式Implementado un 求生esquema, el código es el siguiente:

keepAliveService.java
package com.anna.lib_keepalive.service;

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.os.Handler;
import android.os.Message;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.anna.lib_keepalive.forground.ForgroundNF;
import com.anna.lib_keepalive.utils.Utils;

/**
 * 创建一个JobService用于提高应用优先级
 */
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class KeepAliveService extends JobService {
    private static final String TAG = KeepAliveService.class.getSimpleName();
    private JobScheduler mJobScheduler;
    private static final int JOB_ID = 1;
    private ComponentName JOB_PG;
    private int NOTIFICATION_ID = 10;
    private ForgroundNF mForgroundNF;


    private Handler mJobHandler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            Log.d(TAG, "pull alive.");
            jobFinished((JobParameters) msg.obj, true);
            return true;
        }
    });
    @Override
    public void onCreate() {
        super.onCreate();
        mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        JOB_PG = new ComponentName(getPackageName(),KeepAliveService.class.getName());
        mForgroundNF = new ForgroundNF(this);
        Utils.requestIgnoreBatteryOptimizations(this);
    }
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public static void start(Context context){
        Intent intent = new Intent(context,KeepAliveService.class);
        context.startService(intent);
    }

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.d(TAG,"onStartJob");
        mJobHandler.sendMessage(Message.obtain(mJobHandler, 1, params));
        return true;
    }

    /**系统回调使用,说明触发了job条件
     * @param params
     * @return
     */
    @Override
    public boolean onStopJob(JobParameters params) {
        Log.d(TAG,"onStopJob");
        mJobHandler.sendEmptyMessage(1);
        return false;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        JobInfo job = initJob();
        mJobScheduler.schedule(job);
        startNotificationForGround();
        return START_STICKY;
    }

    /**
     * 大于18可以使用一个取消Notification的服务
     */
    private void startNotificationForGround(){
        if(Build.VERSION.SDK_INT<18){
            mForgroundNF.startForegroundNotification();
        }else{
            mForgroundNF.startForegroundNotification();
            Intent it = new Intent(this, CancelNotifyervice.class);
            startService(it);
        }
    }

    /**初始化Job任务
     * @return
     */
    private JobInfo initJob() {
        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID, JOB_PG);
        if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.N){
            builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS); //执行的最小延迟时间
            builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);  //执行的最长延时时间
            builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS,
                    JobInfo.BACKOFF_POLICY_LINEAR);//线性重试方案
        }else {
            builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS);
        }
        builder.setPersisted(false);
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE);
        builder.setRequiresCharging(false);
        return builder.build();
    }

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

}

ForgroundNF.java
package com.anna.lib_keepalive.forground;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.os.Build;

import androidx.core.app.NotificationCompat;

import com.anna.lib_keepalive.R;

public class ForgroundNF {
    private static final int START_ID = 101;
    private static final String CHANNEL_ID = "app_foreground_service";
    private static final String CHANNEL_NAME = "前台保活服务";

    private Service service;
    private NotificationManager notificationManager;
    private NotificationCompat.Builder mNotificationCompatBuilder;
    public ForgroundNF(Service service){
        this.service = service;
        initNotificationManager();
        initCompatBuilder();
    }


    /**
     * 初始化NotificationCompat.Builder
     */
    private void initCompatBuilder() {
        mNotificationCompatBuilder = new NotificationCompat.Builder(service,CHANNEL_ID);
        //标题
        mNotificationCompatBuilder.setContentTitle("test keep alive");
        //通知内容
        mNotificationCompatBuilder.setContentText("test alive");
        mNotificationCompatBuilder.setSmallIcon(R.mipmap.ic_launcher_round);
    }

    /**
     * 初始化notificationManager并创建NotificationChannel
     */
    private void initNotificationManager(){
        notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
        //针对8.0+系统
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel  = new NotificationChannel(CHANNEL_ID,CHANNEL_NAME,NotificationManager.IMPORTANCE_LOW);
            channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
            channel.setShowBadge(false);
            notificationManager.createNotificationChannel(channel);
        }
    }

    public void startForegroundNotification(){
        service.startForeground(START_ID,mNotificationCompatBuilder.build());
    }

    public void stopForegroundNotification(){
        notificationManager.cancelAll();
        service.stopForeground(true);
    }

}

El código completo se puede ver en la demostración en github: github.com/ByteYuhb/an…

Resumir:

Este artículo es la cuarta parte del desarrollo basado en componentes, y el paquete del tercer componente funcional se cargó en Github, y el paquete de otros componentes se recomendará más adelante.

Desarrollo de componentes.png

Supongo que te gusta

Origin juejin.im/post/7121643256495996936
Recomendado
Clasificación