簡體   English   中英

如何使android服務在后台運行?

[英]How to keep android services running in background?

我剛為我的大學期末項目開發Android應用程序。 最近,我嘗試創建一個Android應用程序,即使我的活動已被清除或關閉(例如通過最近的活動),它也可以在后台運行服務。

我做了什么,但仍然失敗了:

  • 試圖返回START_STICKY;
  • 嘗試使用AlamManager重新計划服務的重新啟動。

我注意到此問題僅在新設備​​內發生。 我的服務可以在模擬器中正常運行,但不能在真實設備中運行。 我使用的是Xiomi Redmi Note 3(Lolipop),並且對內置任務管理器非常了解。 但是,即使我為我的應用啟用/允許自動啟動,Android仍然會殺死我的后台服務。

我測試了其他幾個應用程序,例如whatssup甚至來自playstore的幾個不受歡迎的應用程序(我已經允許自動啟動權限)。它們可以像往常一樣自動“重新啟動”服務。

我已經對此進行了大量研究,到目前為止,還沒有找到真正的解決方案。

到目前為止,這是我的服務代碼:

package com.muzaffar.myApps.App;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.IBinder;
import android.os.SystemClock;
import android.widget.Toast;

import com.muzaffar.myApps.Lib.PhoneInfo;

import java.util.concurrent.TimeUnit;

public class myAppsServices extends Service {

    private static final int FIRST_RUN_TIMEOUT_MILISEC = 5 * 1000;
    private static final int SERVICE_STARTER_INTERVAL_MILISEC = 1 * 1000;
    private static final int SERVICE_TASK_TIMEOUT_SEC = 10;
    private final int REQUEST_CODE = 1;

    private AlarmManager serviceReStarterAlarmManager = null;
    private MyTask asyncTask = null;

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

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

        // Start of timeout-autostarter for our service (watchdog)
        startServiceReStarter();

        // Start performing service task
        serviceTask();

        /*Toast.makeText(this, "Service Started!", Toast.LENGTH_LONG).show();*/
    }

    /***
     *       _____                     _
     *      / ____|                   (_)
     *     | (___    ___  _ __ __   __ _   ___  ___  ___
     *      \___ \  / _ \| '__|\ \ / /| | / __|/ _ \/ __|
     *      ____) ||  __/| |    \ V / | || (__|  __/\__ \
     *     |_____/  \___||_|     \_/  |_| \___|\___||___/
     *
     *      http://patorjk.com/software/taag/#p=display&h=1&v=0&c=c&f=Big&t=Shared%20Pref
     */

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Toast.makeText(getApplicationContext(), "Services Has Been Started!", Toast.LENGTH_SHORT).show();


        return START_STICKY;
    }

    /***
     *      _____                 _     _  __ _  _  _  __  __
     *     |  __ \               | |   | |/ /(_)| || ||  \/  |
     *     | |  | |  ___   _ __  | |_  | ' /  _ | || || \  / |  ___
     *     | |  | | / _ \ | '_ \ | __| |  <  | || || || |\/| | / _ \
     *     | |__| || (_) || | | || |_  | . \ | || || || |  | ||  __/
     *     |_____/  \___/ |_| |_| \__| |_|\_\|_||_||_||_|  |_| \___|
     *
     *
     */

    private void StopPerformingServiceTask() {
        asyncTask.cancel(true);
    }

    @Override
    public void onDestroy() {
        // performs when user or system kill our service
        /*Toast.makeText(getApplicationContext(),"Services Has Been Destroyed!",Toast.LENGTH_SHORT).show();*/
        StopPerformingServiceTask();
    }

    private void serviceTask() {
        asyncTask = new MyTask();
        asyncTask.execute();
    }

    class MyTask extends AsyncTask<Void, Void, Void> {
        @Override
        protected Void doInBackground(Void... params) {
            try {
                for (;;) {
                    TimeUnit.SECONDS.sleep(SERVICE_TASK_TIMEOUT_SEC);

                    // check does performing of the task need
                    if(isCancelled()) {
                        break;
                    }

                    // Initiating of onProgressUpdate callback that has access to UI
                    publishProgress();
                }

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Void... progress) {
            super.onProgressUpdate(progress);
            //Toast.makeText(getApplicationContext(), "Please dont kill me, Im tired dying...T.T", Toast.LENGTH_LONG).show();
        }
    }

    // We should to register our service in AlarmManager service
    // for performing periodical starting of our service by the system
    private void startServiceReStarter() {
        Intent intent = new Intent(this, ServiceStarter.class);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(this, this.REQUEST_CODE, intent, 0);

        if (pendingIntent == null) {
            /*Toast.makeText(this, "Some problems with creating of PendingIntent", Toast.LENGTH_LONG).show();*/
        } else {
            if (serviceReStarterAlarmManager == null) {
                serviceReStarterAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
                serviceReStarterAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,
                        SystemClock.elapsedRealtime() + FIRST_RUN_TIMEOUT_MILISEC,
                        SERVICE_STARTER_INTERVAL_MILISEC, pendingIntent);
            }
        }
    }
}

我的活動代碼

package com.muzaffar.myApps.App;

import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.muzaffar.spycare.App.SpycareServices;
import com.muzaffar.spycare.App.Util;
import com.muzaffar.spycare.R;
import com.muzaffar.spycare.Receiver.DeviceAdmin;

/**
 * Created by OligoCoco on 11/2/2016.
 */

public class Main_Activity extends AppCompatActivity implements View.OnClickListener {

    //Default PIN
    public static final String DEFAULT_PIN = "1234";

    //Import Instance Shared Pref
    Util myPref=new Util(Main_Activity.this);

    //ImportButton
    Button btn_setting, btn_Stealth, btn_Test_Page;
    public static Button btn_device_manager;
    TextView txt_Start_Stop;

    private boolean isRunning = false;
    private EditText SecretCode;
    String device_admin;

    //Enable Device Admin
    private ComponentName deviceAdmin;
    private DevicePolicyManager devicePolicyManager;
    private static final int REQUEST_CODE_ENABLE_ADMIN = 1;

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

        /*Initialize*/
        deviceAdmin = new ComponentName(getApplicationContext(), DeviceAdmin.class);
        devicePolicyManager = (DevicePolicyManager) getSystemService(DEVICE_POLICY_SERVICE);

        /*Initialize Button*/
        txt_Start_Stop = (TextView) findViewById(R.id.app_start_stop);
            /*Return default status of txt_Start_Stop*/
            showServiceStatus();
        btn_setting = (Button) findViewById(R.id.app_setting);
        btn_setting.setOnClickListener(this);
        btn_device_manager = (Button) findViewById(R.id.app_device_manager);
            device_admin = myPref.getSavedShared("device_admin");
            if(device_admin==null || device_admin.equals("false") || device_admin.equals("")){
                btn_device_manager.setText(R.string.app_device_manager);
                btn_device_manager.setClickable(true);
                btn_device_manager.setEnabled(true);
            }else{
                btn_device_manager.setText(R.string.app_device_manager_activated);
                btn_device_manager.setClickable(false);
                btn_device_manager.setEnabled(false);
            }
        btn_device_manager.setOnClickListener(this);
        btn_Stealth = (Button) findViewById(R.id.app_start_stealth);
        btn_Stealth.setOnClickListener(this);
        SecretCode = (EditText) findViewById(R.id.app_secret_code);
        SecretCode.setTextColor(Color.LTGRAY);
        SecretCode.setText(myPref.getSavedShared("Secret_Code"));

        //Start Services
        startMonitoring();

        //Request on potret
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    }

    @Override
    public void onStop(){
        super.onStop();
        myPref.saveToPref("Secret_Code",SecretCode.getText().toString());
        final int flags = DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY;
        if (devicePolicyManager.isAdminActive(deviceAdmin)) {
            devicePolicyManager.resetPassword(myPref.getSavedShared("Secret_Code"), flags);
        }
    }


    /***
     *      _____                _              __  __
     *     |  __ \              (_)            |  \/  |
     *     | |  | |  ___ __   __ _   ___  ___  | \  / |  __ _  _ __    __ _   __ _   ___  _ __
     *     | |  | | / _ \\ \ / /| | / __|/ _ \ | |\/| | / _` || '_ \  / _` | / _` | / _ \| '__|
     *     | |__| ||  __/ \ V / | || (__|  __/ | |  | || (_| || | | || (_| || (_| ||  __/| |
     *     |_____/  \___|  \_/  |_| \___|\___| |_|  |_| \__,_||_| |_| \__,_| \__, | \___||_|
     *                                                                        __/ |
     *                                                                       |___/
     */


    /*Device Manager*/
    private void lock() {
        devicePolicyManager.setPasswordQuality(deviceAdmin, DevicePolicyManager.PASSWORD_QUALITY_NUMERIC);
        devicePolicyManager.setPasswordMinimumLength(deviceAdmin, 4);
        final int flags = DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY;
        devicePolicyManager.resetPassword(myPref.getSavedShared("Secret_Code"), flags);

    }

    @Override
    protected void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == REQUEST_CODE_ENABLE_ADMIN && resultCode == RESULT_OK) {
            btn_device_manager.setText(this.getString(R.string.app_device_manager_activated));
            btn_device_manager.setClickable(false);
            btn_device_manager.setEnabled(false);
            lock();
        }else{
            btn_device_manager.setText(this.getString(R.string.app_device_manager));
            btn_device_manager.setClickable(true);
            btn_device_manager.setEnabled(true);
        }
    }

    /***
     *       _____             _                      __  __        _    _                 _
     *      / ____|           | |                    |  \/  |      | |  | |               | |
     *     | |     _   _  ___ | |_  ___   _ __ ___   | \  / |  ___ | |_ | |__    ___    __| |
     *     | |    | | | |/ __|| __|/ _ \ | '_ ` _ \  | |\/| | / _ \| __|| '_ \  / _ \  / _` |
     *     | |____| |_| |\__ \| |_| (_) || | | | | | | |  | ||  __/| |_ | | | || (_) || (_| |
     *      \_____|\__,_||___/ \__|\___/ |_| |_| |_| |_|  |_| \___| \__||_| |_| \___/  \__,_|
     *
     *
     */

    /*Function to listen which button has been clicked and response correspondingly*/
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.app_device_manager:
                defaultPIN();
                if (devicePolicyManager.isAdminActive(deviceAdmin)) {
                    //If device admin is active

                } else {
                    // Launch the activity to have the user enable our admin.
                    Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
                    intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, deviceAdmin);
                    startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);
                    myPref.saveToPref("device_admin", "true");
                }
                break;
            case R.id.app_setting:
                defaultPIN();
                Intent a = new Intent(getApplicationContext(), Setting_Activity.class);
                startActivity(a);
                break;
            case R.id.app_start_stealth:
                defaultPIN();
                Intent b = new Intent(Intent.ACTION_MAIN);
                b.addCategory(Intent.CATEGORY_HOME);
                b.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(b);
                break;
        }
    }

    /*Function used to start the monitoring process*/
    private void startMonitoring() {
        //startService(new Intent(this, SpycareServices.class));
        startService(new Intent(this, SpycareServices.class));
    }

    /*Function used to stop the monitoring process*/
    private void stopMonitoring() {
        //stopService(new Intent(this, SpycareServices.class));
        stopService(new Intent(this, SpycareServices.class));
    }

    /*Function to change button status*/
    private void showServiceStatus(){
        // Show the current service state
        /*if(isMyServiceRunning(SpycareServices.class,getApplicationContext())){*/
        if(isMyServiceRunning(SpycareServices.class,getApplicationContext())){
            txt_Start_Stop.setText(this.getString(R.string.stop_label));
            txt_Start_Stop.setTextColor(Color.BLUE);
            isRunning = true;
        }else{
            txt_Start_Stop.setText(this.getString(R.string.start_label));
            txt_Start_Stop.setTextColor(Color.RED);
            isRunning = false;
            //If service not running
            startMonitoring();
            //Return
            Intent a = new Intent(getApplicationContext(), Main_Activity.class);
            startActivity(a);
        }
    }

    /*Function to check if certain services is running*/
    private boolean isMyServiceRunning(Class<?> serviceClass,Context context) {
        ActivityManager manager = (ActivityManager)context. getSystemService(Context.ACTIVITY_SERVICE);
        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }

    /*Function to place default PIN*/
    public void defaultPIN(){
        String compA = myPref.getSavedShared("Secret_Code");
        if(compA.isEmpty()){
            Toast.makeText(getApplicationContext(), "PIN Cannot be empty, Applying default PIN "+DEFAULT_PIN, Toast.LENGTH_SHORT).show();
            myPref.saveToPref("Secret_Code",DEFAULT_PIN);

            //Set DMP Pass
            final int flags = DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY;
            if (devicePolicyManager.isAdminActive(deviceAdmin)) {
                devicePolicyManager.resetPassword(DEFAULT_PIN, flags);
            }

            SecretCode.setText(myPref.getSavedShared("Secret_Code"));

        }
    }

}

服務始終在后台運行。

如果萬一您需要繼續運行服務甚至被殺死,那么您只需要通過onStartCommand()返回START_STICKY即可;

即使它被某種方式殺死, START_STICKY也會重新創建服務。

另外,請確保您不會在“活動生命周期”內終止服務。

您的代碼看起來不錯。 您的服務正在從其他地方被殺死。 可能來自活動。 檢查您的代碼或對該Activity類進行更新。

在您的onstartCommand位置:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Toast.makeText(getApplicationContext(), "Services Has Been Started!", Toast.LENGTH_SHORT).show();


    return START_STICKY;
}

嘗試這個:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}

有關更多知識,請訪問此鏈接- 服務

我想我剛剛找到了解決這個問題的方法。 對於Xiomi設備,我們需要:

  • 從內置安全性應用程序授予自動啟動自動啟動權限。
  • 實現RECEIVE_BOOT_COMPLETED廣播接收器,當接收到該意圖時將啟動服務

我不知道為什么這是保持它運行的解決方案,但是我最好的猜測可能是因為XIOMI已經修改了他們的操作系統。

經過測試並使用Xiomi Redmi Note 3 Pro。

UPDATE

如果您卸載該應用程序,然后再安裝回去,它將不再起作用。 后台服務不再自動重新啟動。

您是否嘗試過以其他過程運行服務。 可以通過以下方式完成:

<service ...
         android:process=":separate" >
    ...
</service>

“:processname”是用於創建新進程名稱的約定。 默認進程名為您的程序包(例如“ com.myapp”),與此示例分開的進程可以是“ com.myapp:separate”

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM