2018年Android的保活方案效果统计

1、常见保活方案

**一、监听广播:**监听全局的静态广播,好比时间更新的广播、开机广播、解锁屏、网络状态、解锁加锁亮屏暗屏(3.1版本),高版本须要应用开机后运行一次才能监听这些系统广播,目前此方案失效。能够更换思路,作APP启动后的保活(监听广播启动保活的前台服务)android

**二、定时器、JobScheduler:**假如应用被系统杀死,那么定时器则失效,此方案失效。JobService在5.0,5.1,6.0做用很大,7.0时候有必定影响(能够在电源管理中给APP受权)c++

**三、双进程(NDK方式Fork子进程)、双Service守护:**高版本已失效,5.0起系统回收策略改为进程组。双Service方案也改为了应用被杀,任何后台Service没法正常状态运行面试

**四、提升Service优先级:**只能必定程度上缓解Service被立马回收bash

2、保活

  • 一、AIDL方式单进程、双进程方式保活Service网络

  • 二、下降oom_adj的值:常驻通知栏(可经过启动另一个服务关闭Notification,不对oom_adj值有影响)、使用”1像素“的Activity覆盖在getWindow()的view上、循环播放无声音频(黑科技,7.0下杀不掉)app

  • 三、监听锁屏广播:使Activity始终保持前台ide

  • 四、使用自定义锁屏界面:覆盖了系统锁屏界面。oop

  • 五、经过android:process属性来为Service建立一个进程测试

  • 六、跳转到系统白名单界面让用户本身添加app进入白名单ui

3、复活

  • 一、JobScheduler:原理相似定时器,5.0,5.1,6.0做用很大,7.0时候有必定影响(能够在电源管理中给APP受权)

  • 二、推送互相唤醒复活:极光、友盟、以及各大厂商的推送

  • 三、同派系APP广播互相唤醒:好比今日头条系、阿里系

方案实现效果统计

一、双进程守护方案(基于onStartCommand() return START_STICKY)

  • 一、原生5.0、5.1:原生任务栏滑动清理app,Service会被杀掉,而后被拉起,接着一直存活

  • 二、金立F100(5.1):一键清理直接杀掉整个app,包括双守护进程。不手动清理状况下,经测试能锁屏存活至少40分钟

  • 三、华为畅享5x(6.0):一键清理直接杀掉整个app,包括双守护进程。不手动清理下,锁屏只存活10s。结论:双进程守护方案失效。

  • 四、美图m8s(7.1.1):一键清理直接杀掉整个app,包括双守护进程。不清理状况下,锁屏会有被杀过程(9分钟左右被杀),以后从新复活,以后不断被干掉而后又从新复活。结论:双守护进程可在后台不断拉起Service。

  • 五、原生7.0:任务栏清除APP后,Service存活。使用此方案后Service照样存活。

  • 六、LG V30+(7.1.2):不加双进程守护的时候,一键清理没法杀掉服务。加了此方案以后也不能杀掉服务,锁屏存活(测试观察大于50分钟)

  • 七、小米8(8.1):一键清理直接干掉app而且包括双守护进程。不清理状况下,不加守护进程方案与加守护进程方案Service会一直存活,12分钟左右closed。结论:此方案没有起做用

**结论:**除了华为此方案无效以及未更改底层的厂商不起做用外(START_STICKY字段就能够保持Service不被杀)。此方案能够与其余方案混合使用

二、监听锁屏广播打开1像素Activity(基于onStartCommand() return START_STICKY)
  • 一、原生5.0、5.1:锁屏后3s服务被干掉而后重启(START_STICKY字段起做用)

  • 二、华为畅享5x(6.0):锁屏只存活4s。结论:方案失效。

  • 三、美图m8s(7.1.1):同原生5.0

  • 四、原生7.0:同美图m8s。

  • 五、LG V30+(7.1.2):锁屏后状况跟不加状况一致,服务一致保持运行,结论:此方案不起做用

  • 六、小米8(8.1):关屏过2s以后app所有被干掉。结论:此方案没有起做用

**结论:**此方案无效果

三、故意在后台播放无声的音乐(基于onStartCommand() return START_STICKY)

  • 一、原生5.0、5.1:锁屏后3s服务被干掉而后重启(START_STICKY字段起做用)

  • 二、华为畅享5x(6.0):一键清理后服务依然存活,须要单独清理才可杀掉服务,锁屏8分钟后依然存活。结论:此方案适用

  • 三、美图m8s(7.1.1):同5.0

  • 四、原生7.0:任务管理器中关闭APP后服务被干掉,大概过3s会从新复活(同仅START_STICKY字段模式)。结论:看不出此方案有没有其做用

  • 五、LG V30+(7.1.2):使用此方案先后效果一致。结论:此方案不起做用

  • 六、小米8(8.1):一键清理能够杀掉服务。锁屏后保活超过20分钟

**结论:**成功对华为手机保活。小米8下也成功突破20分钟

四、使用JobScheduler唤醒Service(基于onStartCommand() return START_STICKY)

  • 一、原生5.0、5.1:任务管理器中干掉APP,服务会在周期时间后从新启动。结论:此方案起做用

  • 二、华为畅享5x(6.0):一键清理直接杀掉APP,过12s左右会自动重启服务,JobScheduler起做用

  • 三、美图m8s(7.1.1):一键清理直接杀掉APP,没法自动重启

  • 四、原生7.0:同美图m8s(7.1.1)

  • 五、小米8(8.1):同美图m8s(7.1.1)

**结论:**只对5.0,5.一、6.0起做用

五、混合使用的效果,而且在通知栏弹出通知

  • 一、原生5.0、5.1:任务管理器中干掉APP,服务会在周期时间后从新启动。锁屏超过11分钟存活

  • 二、华为畅享5x(6.0):一键清理后服务依然存活,须要单独清理才可杀掉服务。结论:方案适用。

  • 三、美图m8s(7.1.1):一键清理APP会被杀掉。正常状况下锁屏后服务依然存活。

  • 四、原生7.0:任务管理器中关闭APP后服务被干掉,过2s会从新复活

  • 五、小米8(8.1):一键清理能够杀掉服务,锁屏下后台保活时间超过38分钟

  • 六、荣耀10(8.0):一键清理杀掉服务,锁屏下后台保活时间超过23分钟

**结论:**高版本状况下可使用弹出通知栏、双进程、无声音乐提升后台服务的保活几率

实现具体过程

1、双进程实现方案

使用AIDL绑定方式新建2个Service优先级(防止服务同时被系统杀死)不同的守护进程互相拉起对方,并在每个守护进程的ServiceConnection的绑定回调里判断保活Service是否须要从新拉起和对守护线程进行从新绑定。

一、新建一个AIDL文件

KeepAliveConnection
interface KeepAliveConnection  {
}
复制代码

二、新建一个服务类StepService,onBind()方法返回new KeepAliveConnection.Stub()对象,并在ServiceConnection的绑定回调中对守护进程服务类GuardService的启动和绑定。

/**
 * 主进程 双进程通信
 *
 * @author LiGuangMin
 * @time Created by 2018/8/17 11:26
 */
public class StepService extends Service {

    private final static String TAG = StepService.class.getSimpleName();
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Logger.d(TAG, "StepService:创建连接");
            boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
            if (!isServiceRunning) {
                Intent i = new Intent(StepService.this, DownloadService.class);
                startService(i);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            // 断开连接
            startService(new Intent(StepService.this, GuardService.class));
            // 从新绑定
            bindService(new Intent(StepService.this, GuardService.class), mServiceConnection, Context.BIND_IMPORTANT);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new KeepAliveConnection.Stub() {
        };
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground(1, new Notification());
        // 绑定创建连接
        bindService(new Intent(this, GuardService.class), mServiceConnection, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

}
复制代码

三、对守护进程GuardService进行和2同样的处理

/**
 * 守护进程 双进程通信
 *
 * @author LiGuangMin
 * @time Created by 2018/8/17 11:27
 */
public class GuardService extends Service {
    private final static String TAG = GuardService.class.getSimpleName();
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Logger.d(TAG, "GuardService:创建连接");
            boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
            if (!isServiceRunning) {
                Intent i = new Intent(GuardService.this, DownloadService.class);
                startService(i);
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            // 断开连接
            startService(new Intent(GuardService.this, StepService.class));
            // 从新绑定
            bindService(new Intent(GuardService.this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new KeepAliveConnection.Stub() {
        };
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        startForeground(1, new Notification());
        // 绑定创建连接
        bindService(new Intent(this, StepService.class), mServiceConnection, Context.BIND_IMPORTANT);
        return START_STICKY;
    }

}
复制代码

四、在Activity中在启动须要保活的DownloadService服务后而后启动保活的双进程

public class MainActivity extends AppCompatActivity {
    private TextView mShowTimeTv;
    private DownloadService.DownloadBinder mDownloadBinder;
    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mDownloadBinder = (DownloadService.DownloadBinder) service;
            mDownloadBinder.setOnTimeChangeListener(new DownloadService.OnTimeChangeListener() {
                @Override
                public void showTime(final String time) {
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            mShowTimeTv.setText(time);
                        }
                    });
                }
            });
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, DownloadService.class);
        startService(intent);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
        //双守护线程,优先级不同
        startAllServices();
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        mShowTimeTv = findViewById(R.id.tv_show_time);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
    }

    /**
     * 开启全部守护Service
     */
    private void startAllServices() {
        startService(new Intent(this, StepService.class));
        startService(new Intent(this, GuardService.class));
    }
}
复制代码

2、监听到锁屏广播后使用“1”像素Activity提高优先级

一、该Activity的View只要设置为1像素而后设置在Window对象上便可。在Activity的onDestroy周期中进行保活服务的存活判断从而唤醒服务。"1像素"Activity以下

public class SinglePixelActivity extends AppCompatActivity {
    private static final String TAG = SinglePixelActivity.class.getSimpleName();

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Window mWindow = getWindow();
        mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
        WindowManager.LayoutParams attrParams = mWindow.getAttributes();
        attrParams.x = 0;
        attrParams.y = 0;
        attrParams.height = 1;
        attrParams.width = 1;
        mWindow.setAttributes(attrParams);
        ScreenManager.getInstance(this).setSingleActivity(this);
    }

    @Override
    protected void onDestroy() {
        if (!SystemUtils.isAppAlive(this, Constant.PACKAGE_NAME)) {
            Intent intentAlive = new Intent(this, DownloadService.class);
            startService(intentAlive);
        }
        super.onDestroy();
    }
}
复制代码

二、对广播进行监听,封装为一个ScreenReceiverUtil类,进行锁屏解锁的广播动态注册监听

public class ScreenReceiverUtil {
    private Context mContext;
    private SreenBroadcastReceiver mScreenReceiver;
    private SreenStateListener mStateReceiverListener;

    public ScreenReceiverUtil(Context mContext) {
        this.mContext = mContext;
    }

    public void setScreenReceiverListener(SreenStateListener mStateReceiverListener) {
        this.mStateReceiverListener = mStateReceiverListener;
        // 动态启动广播接收器
        this.mScreenReceiver = new SreenBroadcastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        filter.addAction(Intent.ACTION_USER_PRESENT);
        mContext.registerReceiver(mScreenReceiver, filter);
    }

    public void stopScreenReceiverListener() {
        mContext.unregisterReceiver(mScreenReceiver);
    }

    /**
     * 监听sreen状态对外回调接口
     */
    public interface SreenStateListener {
        void onSreenOn();

        void onSreenOff();

        void onUserPresent();
    }

    public class SreenBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (mStateReceiverListener == null) {
                return;
            }
            if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
                mStateReceiverListener.onSreenOn();
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
                mStateReceiverListener.onSreenOff();
            } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁
                mStateReceiverListener.onUserPresent();
            }
        }
    }
}

复制代码

三、对1像素Activity进行防止内存泄露的处理,新建一个ScreenManager类

public class ScreenManager {
    private static final String TAG = ScreenManager.class.getSimpleName();
    private static ScreenManager sInstance;
    private Context mContext;
    private WeakReference<Activity> mActivity;

    private ScreenManager(Context mContext) {
        this.mContext = mContext;
    }

    public static ScreenManager getInstance(Context context) {
        if (sInstance == null) {
            sInstance = new ScreenManager(context);
        }
        return sInstance;
    }

    /** 得到SinglePixelActivity的引用
     * @param activity
     */
    public void setSingleActivity(Activity activity) {
        mActivity = new WeakReference<>(activity);
    }

    /**
     * 启动SinglePixelActivity
     */
    public void startActivity() {
        Intent intent = new Intent(mContext, SinglePixelActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);
    }

    /**
     * 结束SinglePixelActivity
     */
    public void finishActivity() {
        if (mActivity != null) {
            Activity activity = mActivity.get();
            if (activity != null) {
                activity.finish();
            }
        }
    }
}
复制代码

四、对1像素的Style进行特殊处理,在style文件中新建一个SingleActivityStyle

<style name="SingleActivityStyle" parent="android:Theme.Holo.Light.NoActionBar">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowFrame">@null</item>
        <item name="android:windowNoTitle">true</item>
        <item name="android:windowIsFloating">true</item>
        <item name="android:windowContentOverlay">@null</item>
        <item name="android:backgroundDimEnabled">false</item>
        <item name="android:windowAnimationStyle">@null</item>
        <item name="android:windowDisablePreview">true</item>
        <item name="android:windowNoDisplay">false</item>
复制代码

五、让SinglePixelActivity使用singleInstance启动模式,在manifest文件中

<activity
            android:name=".activity.SinglePixelActivity"
            android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
            android:excludeFromRecents="true"
            android:finishOnTaskLaunch="false"
            android:launchMode="singleInstance"
            android:theme="@style/SingleActivityStyle" />
复制代码

六、在保活服务类DownloadService中对监听的广播进行注册和对SinglePixelActivity进行控制

public class DownloadService extends Service {
    public static final int NOTICE_ID = 100;
    private static final String TAG = DownloadService.class.getSimpleName();
    private DownloadBinder mDownloadBinder;
    private NotificationCompat.Builder mBuilderProgress;
    private NotificationManager mNotificationManager;

    private ScreenReceiverUtil mScreenListener;
    private ScreenManager mScreenManager;
    private Timer mRunTimer;

    private int mTimeSec;
    private int mTimeMin;
    private int mTimeHour;

    private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() {
        @Override
        public void onSreenOn() {
            mScreenManager.finishActivity();
            Logger.d(TAG, "关闭了1像素Activity");
        }

        @Override
        public void onSreenOff() {
            mScreenManager.startActivity();
            Logger.d(TAG, "打开了1像素Activity");
        }

        @Override
        public void onUserPresent() {
        }
    };
    private OnTimeChangeListener mOnTimeChangeListener;

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

//        注册锁屏广播监听器
        mScreenListener = new ScreenReceiverUtil(this);
        mScreenManager = ScreenManager.getInstance(this);
        mScreenListener.setScreenReceiverListener(mScreenListenerer);
        
        mDownloadBinder = new DownloadBinder();
        mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Logger.d(TAG, "onStartCommand");
        startRunTimer();
        return START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        return mDownloadBinder;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Logger.d(TAG, "onUnbind");
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        NotificationManager mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        if (mManager == null) {
            return;
        }
        mManager.cancel(NOTICE_ID);
        stopRunTimer();
//        mScreenListener.stopScreenReceiverListener();
    }


    private void startRunTimer() {
        TimerTask mTask = new TimerTask() {
            @Override
            public void run() {
                mTimeSec++;
                if (mTimeSec == 60) {
                    mTimeSec = 0;
                    mTimeMin++;
                }
                if (mTimeMin == 60) {
                    mTimeMin = 0;
                    mTimeHour++;
                }
                if (mTimeHour == 24) {
                    mTimeSec = 0;
                    mTimeMin = 0;
                    mTimeHour = 0;
                }
                String time = "时间为:" + mTimeHour + " : " + mTimeMin + " : " + mTimeSec;
                if (mOnTimeChangeListener != null) {
                    mOnTimeChangeListener.showTime(time);
                }
                Logger.d(TAG, time);
            }
        };
        mRunTimer = new Timer();
        // 每隔1s更新一下时间
        mRunTimer.schedule(mTask, 1000, 1000);
    }

    private void stopRunTimer() {
        if (mRunTimer != null) {
            mRunTimer.cancel();
            mRunTimer = null;
        }
        mTimeSec = 0;
        mTimeMin = 0;
        mTimeHour = 0;
        Logger.d(TAG, "时间为:" + mTimeHour + " : " + mTimeMin + " : " + mTimeSec);
    }

    public interface OnTimeChangeListener {
        void showTime(String time);
    }

    public class DownloadBinder extends Binder {
        public void setOnTimeChangeListener(OnTimeChangeListener onTimeChangeListener) {
            mOnTimeChangeListener = onTimeChangeListener;
        }
    }
}

复制代码

三、在后台播放音乐

一、准备一段无声的音频,新建一个播放音乐的Service类,将播放模式改成无限循环播放。在其onDestroy方法中对本身从新启动。

public class PlayerMusicService extends Service {
    private final static String TAG = PlayerMusicService.class.getSimpleName();
    private MediaPlayer mMediaPlayer;

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

    @Override
    public void onCreate() {
        super.onCreate();
        Logger.d(TAG, TAG + "---->onCreate,启动服务");
        mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
        mMediaPlayer.setLooping(true);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                startPlayMusic();
            }
        }).start();
        return START_STICKY;
    }

    private void startPlayMusic() {
        if (mMediaPlayer != null) {
            Logger.d(TAG, "启动后台播放音乐");
            mMediaPlayer.start();
        }
    }

    private void stopPlayMusic() {
        if (mMediaPlayer != null) {
            Logger.d(TAG, "关闭后台播放音乐");
            mMediaPlayer.stop();
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopPlayMusic();
        Logger.d(TAG, TAG + "---->onCreate,中止服务");
        // 重启本身
        Intent intent = new Intent(getApplicationContext(), PlayerMusicService.class);
        startService(intent);
    }
}
复制代码

二、 在保活的DownloadServie服务类的onCreate方法中对PlayerMusicService进行启动

Intent intent = new Intent(this, PlayerMusicService.class);
 startService(intent);
复制代码

三、在Manifest文件中进行注册

<service
            android:name=".service.PlayerMusicService"
            android:enabled="true"
            android:exported="true"
            android:process=":music_service" />
复制代码

四、使用JobScheduler唤醒Service

一、新建一个继承自JobService的ScheduleService类,在其onStartJob回调中对DownloadService进行存活的判断来重启。

public class ScheduleService extends JobService {
    private static final String TAG = ScheduleService.class.getSimpleName();

    @Override
    public boolean onStartJob(JobParameters params) {

        boolean isServiceRunning = ServiceAliveUtils.isServiceAlice();
        if (!isServiceRunning) {
            Intent i = new Intent(this, DownloadService.class);
            startService(i);
            Logger.d(TAG, "ScheduleService启动了DownloadService");
        }
        jobFinished(params, false);
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}
复制代码

二、 在DownloadService服务类中进行JobScheduler的注册和使用

/**
     * 使用JobScheduler进行保活
     */
    private void useJobServiceForKeepAlive() {
        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        if (jobScheduler == null) {
            return;
        }
        jobScheduler.cancelAll();
        JobInfo.Builder builder =
            new JobInfo.Builder(1024, new ComponentName(getPackageName(), ScheduleService.class.getName()));
        //周期设置为了2s
        builder.setPeriodic(1000 * 2);
        builder.setPersisted(true);
        builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
        int schedule = jobScheduler.schedule(builder.build());
        if (schedule <= 0) {
            Logger.w(TAG, "schedule error!");
        }
    }
复制代码

三、在manifest文件中进行权限设置

<service
            android:name=".service.ScheduleService"
            android:enabled="true"
            android:exported="true"
     android:permission="android.permission.BIND_JOB_SERVICE" />
复制代码

关于推送类拉活

根据华为官方文档集成HUAWEI Push

  • 一、华为畅玩5X(6.0):APP所有进程被杀死时能够被拉起。

  • 二、华为nove 3e(8.0):APP所有进程被杀死时没法被拉起,能收到推送。

  • 三、华为荣耀10(8.1):同2

**结论:**理论状况下,华为推送应该能够拉起华为机器才对,感受是我没花钱的缘由

补充:ServiceAliveUtils 类以下
public class ServiceAliveUtils {

    public static boolean isServiceAlice() {
        boolean isServiceRunning = false;
        ActivityManager manager =
            (ActivityManager) MyApplication.getMyApplication().getSystemService(Context.ACTIVITY_SERVICE);
        if (manager == null) {
            return true;
        }
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if ("demo.lgm.com.keepalivedemo.service.DownloadService".equals(service.service.getClassName())) {
                isServiceRunning = true;
            }
        }
        return isServiceRunning;
    }
}
复制代码

做者:minminaya 连接:www.jianshu.com/p/b5371df6d…

阅读更多

作后台是选择Java 、Go ,仍是 PHP?

NDK项目实战—高仿360手机助手之卸载监听

AndroidUtils:Android开发不得不收藏的Utils

(Android)面试题级答案(精选版)

Google开发者大会:你不得不知的Tensorflow小技巧

相信本身,没有作不到的,只有想不到的

在这里得到的不只仅是技术!

相关文章
相关标签/搜索