java - 线程在后台使用 JobService 时停止运行

标签 java android java-threads background-thread

我正在创建一个 sleep 定时器应用程序,它会随着时间的流逝逐渐降低系统音量。如果用户将时间设置为 30 分钟,则音量将在 15 分钟和 7.5 分钟等处减小。

我目前将音量衰减发送到 JobService,它在我的手机屏幕上显示时工作正常,但一旦我锁定手机并将其留在后台,它将工作一段时间几分钟的顶部。我也尝试过使用 ServiceIntentService,两者都产生了相似的结果。

我想知道这些线程是否认为它们已完成,即使它们没有完成,因为我正在使用一个调用自身的 handle.postDelayed

为什么它不会在后台运行超过几分钟?我是否需要在 onStartJob 内部设置一个检查器来查看 startVolumeDecrement 是否已完成?

以下是相关代码(完整代码在文章底部):

主 Activity .java

package com.example.dreamquiet;

//imports
/.../

public class MainActivity extends AppCompatActivity {

    //Methods:
    @Override
    protected void onCreate(Bundle savedInstanceState) {    //method that does it all!
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startDecay()
    }

    public void startDecay(){
        //this is used to pass the time to the job.
        PersistableBundle bundle = new PersistableBundle();
        bundle.putInt("totsleep", totalSleepTime);

        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);

        JobInfo jobInfo = new JobInfo.Builder(11, new ComponentName(this, StartDecayJob.class))
                // only add if network access is required
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                .setExtras(bundle)  //this is used to pass the time to the job.
                .build();

        jobScheduler.schedule(jobInfo);
    }

    public void stopDecay(){
        JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
        jobScheduler.cancelAll();
    }

StartDecayJob.java

package com.example.dreamquiet;

//bunch of imports
/..../

public class StartDecayJob extends JobService {
    protected int counter;
    protected int totalSleepTime;
    protected boolean isDone;
    final Handler handleDecay = new Handler();


    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        totalSleepTime = jobParameters.getExtras().getInt("totsleep");

        startVolumeDecrement();
        return true;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        handleDecay.removeCallbacksAndMessages(null); //wipe out the current startVolumeDecrement
        super.onDestroy();
        stopSelf();
        Toast.makeText(this, "Goodnight :)", Toast.LENGTH_LONG).show();
        // return true to restart the job
        return false;
    }

    //the method that lowers the volume over time.
    protected void startVolumeDecrement() {
        //get the audio
        final AudioManager phoneVolume = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
        int currentVolume = phoneVolume.getStreamVolume(AudioManager.STREAM_MUSIC);
        //get the vector. Yes it's ugly. enjoy it.
        Vector decayVector = new Vector();
        decayVector = populateDecay(totalSleepTime, decayVector, currentVolume);
        final Vector decay = decayVector;
        counter = 0; //Reset the counter --> safe :P

        final Runnable volDecay = new Runnable(){
            public void run(){
                if(counter >= decay.size()-1 || phoneVolume.getStreamVolume(AudioManager.STREAM_MUSIC) == 0){
                    //if current volume isn't muted then MUTE IT!
                    if(phoneVolume.getStreamVolume(AudioManager.STREAM_MUSIC) > 0){
                        counter--;
                        handleDecay.postDelayed(this, ((int)decay.get(decay.size()-1)));
                    }
                    //If using spotify, it will pause it! :)
                    Intent pauseSpotify = new Intent("com.spotify.mobile.android.ui.widget.PLAY");
                    pauseSpotify.setPackage("com.spotify.music");
                    sendBroadcast(pauseSpotify);
                    return;     //return is redundant but why not...
                }
                //decrease volume then recall function
                phoneVolume.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
                handleDecay.postDelayed(this, ((int) decay.get(counter++)));
            }
        };
        handleDecay.postDelayed(volDecay, ((int) decay.get(counter++)));    //start the initial counter
    }

}

我的 list 文件

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.dreamquiet" >
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission>  <!--preventitive measures-->

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="preloaded_fonts"
            android:resource="@array/preloaded_fonts" />
        <!-- intent defined in this service tag below -->

        <service android:name=".StartDecayJob"
            android:label="decay"
            android:permission="android.permission.BIND_JOB_SERVICE"/> <!--Gotta see if process will work -->

    </application>

</manifest>

Full code here

最佳答案

因为当你锁定你的手机操作系统时,如果许多服务在不同的应用程序中运行并且我在构建测验应用程序时遇到同样的问题,那么你的手机操作系统将暂停正在运行的服务。所以解决方案是你可以使用 WAKE LOCK 它解决了这个问题。

Activity 中:

    private var wakeLock: PowerManager.WakeLock? = null

启动唤醒锁:

    var powerManager = getSystemService(POWER_SERVICE) as PowerManager
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "ExampleApp:Wakelock Started")
    wakeLock?.acquire()

释放唤醒锁最重要的一步:

  if (wakeLock?.isHeld!!) {
            L.e("ExampleApp", "Wakelock Released")
            wakeLock?.release();
        }

最后在 list 中添加权限:

  <uses-permission android:name="android.permission.WAKE_LOCK" />

关于java - 线程在后台使用 JobService 时停止运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59540742/

相关文章:

Javafx:如果服务消息更新(标签绑定(bind)到消息),标签变为空白

java - Java 实体的 id 字段的 Long 与 Integer

Java Swing JButton

android - 为什么解析 StackOverflowException 会抛出 OOM?

android - MapView Activity 关闭后,我是否需要停止使用 GPS?

java - 多个线程在java中使用同一个对象是否复制它?

java - 在 Java 中设置哈希表数组

java - 如何知道 ms 中的 ping

android - NestedScrollView 高度不匹配父级

java - 如何使用线程中断方法停止线程