Research Scheme of APP Resident Memory for Android Application Preservation

Preface

One of the things that bloggers remember in those days is that they have done a step-by-step activity, which has helped me speed up baldness. We know that in Android version 4.4, Android supports built-in step-counting sensors in hardware, such as Wechat Motion and other software, which calls the Sensor.TYPE_STEP_COUNTER sensor service in Android to get the number of steps per day.

At that time, the mobile version of users was generally on the low side, so they needed to take notes by hand. Fortunately, I was smart. Referring to the principle of micro-signal shaking, I got the acceleration value in a certain period of time through the acceleration sensor SENSOR_TYPE_LINEAR_ACCELERATION, and finally got the step number through a calculation formula. But the acceleration values of different models are not the same, and super power consumption. The most difficult problem is that the application is particularly vulnerable to killing, because memory and power consumption is high, when the application runs in the background, it will be recycled preferentially by the system, and the user kills the process when he manually cleans it up, leading to the inability to remember the steps.

 

I. Common methods of life preservation

1. Monitoring Broadcasting Mode

By monitoring the static broadcasting of the whole situation, such as boot-up broadcasting, unlock screen broadcasting, network status broadcasting, etc., to start the background service of the application. At present, the high version of Android system has failed, because the Android system stipulates that applications must run once after the system is booted to listen to these system broadcasts. Generally, the application process is killed and broadcasts are not received.

2. Improving Service Priority

There are many ways to improve service priority, such as onStartCommand returning START_STICKY to enable service to automatically start, pop-up notification and configure service priority when system memory is enough. These ways can only alleviate service recovery to a certain extent, but as long as users clean up one key or system recovery is the same. Invalid.

3. Double service pull up

After testing, as long as the current application is killed, no back-end service can run or pull up.

3. Two-process pull-up

This method uses NDK to create a child process in the underlying fork to achieve mutual pull with the parent process. Android 4.x is still very effective, but the system recycling strategy of the high version Android system has changed to the form of process group. If the system wants to recover an application, it will kill all processes belonging to the same process group and cause the two processes to be unable to pull up.  

II. Multiprocess Audio Preservation Scheme

To sum up, the above method only improves the ability of the backstage operation of APP. In the case that users do not actively clean up or kill, it is still very good to test the survival effect of APP. But Gu Du magically survived by clicking on a button to clean up, because it played a silent music loop in the background. As follows:

The code is as follows:

public class PlayerMusicService extends Service {
    private final static String TAG = "PlayerMusicService";
    private MediaPlayer mMediaPlayer;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    @Override
    public void onCreate() {
        super.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;//note 1
    }
    private void startPlayMusic(){
        if(mMediaPlayer != null){
            mMediaPlayer.start();
        }
    }
    private void stopPlayMusic(){
        if(mMediaPlayer != null){
            mMediaPlayer.stop();
        }
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        stopPlayMusic();
        // Note 2
        Intent intent = new Intent(getApplicationContext(),PlayerMusicService.class);
        startService(intent);
    }
}

AndroidManifest.xml

<service android:name=".service.PlayerMusicService"
          android:enabled="true"
          android:exported="true"
          android:process=":music_service"/>//Multiprocess

Note 1: In the onStartCommand method, START_STICKY is returned. Its effect is that when the Service process is kill ed, the system tries to recreate the Service.

Note 2: Restart yourself in the onDestory method, which calls onDestory when the Service is destroyed and executes the restart code.

Open the background broadcast audio service, the program in each model test as follows:
1. Huawei Mate8(7.0), one-click cleanup still survives, surviving more than 12 hours in the background black screen mode; but if the user only chooses to clean up the application, it will also be killed, which is consistent with the "Gudong" survival effect.
2. Samsung C9(6.0), a key to clean up the latest applications, the successful survival;
3. Hua4X (6.0): Clean up the latest application with one key and survive successfully.
4. Samsung Note4(5.0): Clean up the latest application with one button and survive successfully.

 

III. Through the Job Scheduler Scheme

Summary

Job Scheduler is an API introduced by Google in Android 5.0 that can perform specific tasks when conditions are met. Usually, even if the APP is forced to stop, the scheduled tasks will still be performed.

principle

The task is executed in the onStartJob method that overrides the parent JobService, uses the JobInfo Builder method to set conditions and bind the component name of the subclass that implements JobService, and then calls the scheme method of the system service JobScheduler. In this way, even if the application process is killed before the task is executed, the task will not be executed because the system service JobScheduler will use the bindService AsUser method to start up the sub-service that implements JobService and execute its onStartJob method.

New class: AliveJobService.java

@TargetApi(21)
public class AliveJobService extends JobService {
    private static final int MESSAGE_ID_TASK = 0x01;
    // Inform the compiler that this variable cannot be optimized
    private volatile static Service mKeepAliveService = null;
    public static boolean isJobServiceAlive(){
        return mKeepAliveService != null;
    }
    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            if(SystemUtils.isAPPALive(getApplicationContext(), Contants.PACKAGE_NAME)){
                //APP Lives
            }else{
                Intent intent = new Intent(getApplicationContext(), SportsActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
                //APP was killed and restarted
            }
            jobFinished( (JobParameters) msg.obj, false ); // Notify the end of system task execution
            return true;
        }
    });
    @Override
    public boolean onStartJob(JobParameters params) {
        mKeepAliveService = this;
        Message msg = Message.obtain(mHandler, MESSAGE_ID_TASK, params);
        mHandler.sendMessage(msg);
        // Returning false, the system assumes that the task has been completed by the time the method returns.
        // Returning true, the system assumes that the task is about to be performed
        return true;
    }
    @Override
    public boolean onStopJob(JobParameters params) {
        mHandler.removeMessages(MESSAGE_ID_TASK);
        return false;
    }
}
 

JobScheduler management class performs system tasks: JobScheduler Manager. Java

public class JobSchedulerManager {
    private static final int JOB_ID = 1;
    private static JobSchedulerManager mJobManager;
    private JobScheduler mJobScheduler;
    private static Context mContext;
 
    private JobSchedulerManager(Context ctxt){
        this.mContext = ctxt;
        mJobScheduler = (JobScheduler)ctxt.getSystemService(Context.JOB_SCHEDULER_SERVICE);
    }
    public final static JobSchedulerManager getJobSchedulerInstance(Context ctxt){
        if(mJobManager == null){
            mJobManager = new JobSchedulerManager(ctxt);
        }
        return mJobManager;
    }
    @TargetApi(21)
    public void startJobScheduler(){
        if(AliveJobService.isJobServiceAlive() || isBelowLOLLIPOP()){
            return;        // If JobService has been started or API < 21, return
        }
        JobInfo.Builder builder = new JobInfo.Builder(JOB_ID,new ComponentName(mContext, AliveJobService.class));
        builder.setPeriodic(3000); // Set up to perform tasks every 3 seconds
        builder.setPersisted(true);  // When setting up the device to restart, perform the task
        builder.setRequiresCharging(true); // When the charger is inserted, perform the task
        JobInfo info = builder.build();
        mJobScheduler.schedule(info);  
    }
    @TargetApi(21)
    public void stopJobScheduler(){
        if(isBelowLOLLIPOP())
            return;
        mJobScheduler.cancelAll();
    }
    private boolean isBelowLOLLIPOP(){
        return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; // API< 21
    }
}

Remember to add permissions: android:permission="android.permission.BIND_JOB_SERVICE"

Doze sleep pattern

In Android M(6.0), Google proposed an energy-saving way to extend battery life. Its core idea is that after the mobile phone is out of screen, unplugged or stationary for a period of time, the mobile phone will automatically enter Doze mode, and the mobile phone in Doze mode will stop all non-system applications such as WalkLocks, network access and noise. Clocks, GPS/WIFI scans, including Job Sheduler activities.

When the screen of the mobile phone entering Doze mode is lit, moved or charged, it will immediately return to normal from Doze mode, and the system will continue to execute the live items "frozen" by Doze mode. In other words, the Doze mode does not kill the process, but stops the process-related power consumption activities and puts it into a "dormant" state. After Android N(7.0), Google further optimized Doze's dormancy mechanism, and enhanced its application scenarios and usage rules. In Android 6.0, Doze needs to be placed in parallel for a period of time before it can be turned on, and in 7.0, it can be turned on at any time.

Therefore, Job Sheduler's wake-up is very effective for Android 5.0; for Android 6.0, although Google introduced Doze mode, it is usually difficult to really enter Doze mode, so Job Sheduler's wake-up is still effective; for Android 7.0, Job Sheduler's wake-up will have a certain impact, we can in battery management. Open a green channel for APP to prevent it from using Job Sheduler after Doze mode.

test result
Samsung C9(6.0): One-click cleaning and force stop can wake up APP;
Samsung Note4(5.0): One-click cleaning and force stop can wake up APP;
Huawei Glory 4X(6.0): One click of clean-up and force stop can wake up APP;
Huawei Mate8(7.0): Failure (possibly shielded by Huawei);


IV. Huawei Pushes SDK

Android Market Push Including Getui, Millet, Aurora Huawei Push, 360, Meizu, etc. Huawei's push Service lets a back-end Service run in a separate process, and the main program does not need resident memory. When the background Service receives the push message, it broadcasts the notification to the main process and triggers the related callback interface. Usually, the forcibly stopped APP cannot receive broadcasting, but Huawei Push's back-end Service can forcibly pull up the APP because it uses the Intent.FLAG_INCLUDE_STOPPED_PACKAGES tag to implement it when broadcasting.

MyHwPushReceiver.java

This class inherits the Huawei Receive (com.huawei.android.pushagent.api.PushEventReceive), which is used to receive token from the server, get the connection status of the server, and receive notifications and transmissions from the server. It is important to note that onToken method and onPushMsg method must be implemented, and try not to open threads and handle handlers in MyHwPushReceiver class.

public class MyHwPushReceiver extends PushEventReceiver{
    private final static String TAG = "MyHwPushReceiver";
    @Override
    public void onToken(Context context, String token, Bundle bundle) {
        Log.i(TAG,"Connect to Huawei push server. token="+token);
    }
    @Override
    public boolean onPushMsg(Context context, byte[] msgBytes, Bundle bundle) {
        Log.i(TAG,"Receive through message:"+new String(msgBytes,"UTF-8"));
        // Start the application
        return false;
    }
    @Override
    public void onPushState(Context context, boolean connectState) {
        Log.i(TAG,"Connect to Huawei Push Server:"+(connectState?"connected":"disconnected"));
    }

    @Override
    public void onEvent(Context context, Event event, Bundle bundle) { 
        //Click to open the notification bar
        super.onEvent(context, event, bundle);
    }
}

HwPushManager.java

 public class HwPushManager {
    private static HwPushManager mPushManager;
    private Context mContext; 
    private HwPushManager(Context mContext){
        this.mContext = mContext;
    }
    public static  HwPushManager getInstance(Context mContext){
        if(mPushManager == null){
            mPushManager = new HwPushManager(mContext);
        }
        return mPushManager;
    }
    public void startRequestToken(){
        //Request Token from the server
        PushManager.requestToken(mContext);
    }
        //Whether to receive the passing message from the server
    public void isEnableReceiveNormalMsg(boolean isEnable){
        PushManager.enableReceiveNormalMsg(mContext,isEnable);
    }
        //Whether to receive self-rendering messages
    public  void isEnableReceiverNotifyMsg(boolean isEnable){
        PushManager.enableReceiveNotifyMsg(mContext,isEnable);
    }
}

Penetrating message, that is, for the information transmission channel, does not care about the message body format and content, it is only responsible for the transmission of messages, does not do any processing of messages. When the client receives the transmitted message, it is up to the client itself to decide how to process the message, such as silently processing the message in the background to communicate. Knowing the way to display messages to users, so it makes up for the inadequacy of the notification bar message client can not capture the relevant actions.

 AndroidManifest.xml

In addition to the custom MyHwPushReceiver class, other official Demo copies can be pushed directly from Huawei. To test the feasibility of Huawei's push guarantee, we sent a through message to KeepAppAlive on the Huawei Developer Alliance Web page. Generally speaking, we all set up a timer on our own server to push through messages to the client at regular intervals.

Note: Some Huawei mobile phones may also need to open self-startup permissions; how to integrate Huawei push SDK, just look at official documents directly; non-Huawei mobile phones need to install "Huawei Mobile Service. apk" to use Huawei push (a pit).

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
  <!-- Third Party Relevance :Receive Push Messages (registration, Push News, Push Connection status, label, LBS Reporting Results) Broadcasting -->
        <receiver android:name=".receiver.MyHwPushReceiver" >
            <intent-filter>
                <action android:name="com.huawei.android.push.intent.REGISTRATION" />
                <action android:name="com.huawei.android.push.intent.RECEIVE" />
                <action android:name="com.huawei.android.push.intent.CLICK" />
                <action android:name="com.huawei.intent.action.PUSH_STATE" />
                <action android:name="com.huawei.android.push.plugin.RESPONSE" />
            </intent-filter>
            <meta-data android:name="CS_cloud_ablitity" android:value="@string/hwpush_ability_value"/>
        </receiver>
        <!-- PushSDK:PushSDK Receiving External Request Event Entry -->
        <receiver
            android:name="com.huawei.android.pushagent.PushEventReceiver"
            android:process=":pushservice" >
            <intent-filter>
             <action android:name="com.huawei.android.push.intent.REFRESH_PUSH_CHANNEL" />
                <action android:name="com.huawei.intent.action.PUSH" />
                <action android:name="com.huawei.intent.action.PUSH_ON" />
                <action android:name="com.huawei.android.push.PLUGIN" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.PACKAGE_REMOVED" />
                <data android:scheme="package" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="com.huawei.android.pushagent.PushBootReceiver"
            android:process=":pushservice" >
            <intent-filter>
                <action android:name="com.huawei.android.push.intent.REGISTER" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
            <meta-data
                android:name="CS_cloud_version"
                android:value="\u0032\u0037\u0030\u0035" />
        </receiver>
        <!-- PushSDK:Push service -->
        <service
            android:name="com.huawei.android.pushagent.PushService"
            android:process=":pushservice" >
        </service>
        <!-- locale|layoutDirection Do not recreate after switching languages activity -->
        <activity
            android:name="com.huawei.android.pushselfshow.richpush.RichPushActivity"
            android:process=":pushservice"
            android:theme="@style/hwpush_NoActionBar"
            android:configChanges="orientation|screenSize|locale|layoutDirection"
            android:screenOrientation="portrait">
            <meta-data android:name="hwc-theme"
                android:value="androidhwext:style/Theme.Emui"/>
            <intent-filter>
                <action android:name="com.huawei.android.push.intent.RICHPUSH" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        <activity
       android:name="com.huawei.android.pushselfshow.permission.RequestPermissionsActivity"
            android:theme="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar"
            android:launchMode="singleTop"
            android:screenOrientation="portrait"
            android:configChanges="orientation|screenSize|locale|layoutDirection"
            android:exported="false">
        </activity>

test result

 

Reference link:

https://blog.csdn.net/andrexpert/article/details/75174586

https://blog.csdn.net/andrexpert/article/details/75045678

Keywords: Android Mobile Google Java

Added by peytonrm on Wed, 28 Aug 2019 13:40:01 +0300