如何执行重复的 JSoup 任务,即使应用程序在 android 中处于后台也能正常工作

How can you execute a repeating JSoup task which will work even if the app is in background in android

对于我的应用程序,我需要由 jSoup 从网站解析的值,然后使用通知 returned 给用户,这些值每分钟都会更改,因此要与值保持同步我使用处理程序设置了一个任务,这在应用程序处于前台时效果很好,但是一旦用户进入主屏幕,应用程序就会 return 多个异常,例如java.net.UnknownHostException 或 java.net.SocketTimeoutException,在代码中,当 jSoup 连接到指定站点时,我已经尝试使用 Services 和 AsyncTasks 而不是线程,但它总是完全相同的问题,我还搜索了有类似经历的人,但我想我的问题很具体。

这是处理程序的代码:

private final static int INTERVAL = 1000 * 60;
    Handler mHandler = new Handler();
    Runnable mHandlerTask = new Runnable()
    {
        @Override
        public void run() {
            try {
                wakeLock.release();
            } catch (RuntimeException e) {
                e.printStackTrace();
            }
            if (!isUpdating) {
                isUpdating = true;
                App.shouldUpdate = true;
                System.out.println("update");
                final TinyDB tinydb = new TinyDB(getApplicationContext());
                                TextView priceEditText = loadedLayouts.get(1).findViewById(R.id.priceTextView);
                                TextView increaseEditText = loadedLayouts.get(1).findViewById(R.id.increaseTextView);
                                updatePricesStock(tinydb.getString("current_isin"), priceEditText, increaseEditText);
                             
                        }
                        wakeLock.acquire(2*60*1000L /*10 minutes*/);
                 
                }
                mHandler.postDelayed(mHandlerTask, INTERVAL);
            }
        }
    }; 

这是 updateStockPrices 方法的代码(我不会包括 updatePricesWarrant 和 updatePricesKnockout,因为它们本质上做同样的事情并且抛出同样的异常)

public void updatePricesStock(final String ISIN, TextView priceText, TextView increaseText) {

        final TinyDB tinydb = new TinyDB(getApplicationContext());
        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                TinyDB tinydb = new TinyDB(getApplicationContext());
                try {
                    Document doc = Jsoup.connect("https://www.ls-tc.de/de/aktie/" + ISIN).get();
                    System.out.println(ISIN);
                    increase = doc.selectFirst("#page_content > div > div:nth-child(1) > div > div.mpe_bootstrapgrid.col-md-8 > div > div:nth-child(3) > div > span:nth-child(3)").text().replace(" ", "");
                    tinydb.putString(ISIN + "notification_price", doc.selectFirst("#page_content > div > div:nth-child(1) > div > div.mpe_bootstrapgrid.col-md-8 > div > div:nth-child(3) > div > span:nth-child(1)").text() + "€");

                } catch (IOException e) {
                    e.printStackTrace();
                    tinydb.putString(ISIN + "notification_price", "Error Scraping Price");
                }
            }
        });
        if(!thread.isAlive()) {
            thread.start();
        }
        try{
            thread.join();
        }catch (Exception ex){
            ex.printStackTrace();
        }
        if(!thread.isAlive()) {
            notificationCompat = NotificationManagerCompat.from(getApplicationContext());
            System.out.println("done");
            System.out.println(tinydb.getString(ISIN + "notification_price"));
            priceText.setText(tinydb.getString(ISIN + "notification_price"));
            increaseText.setText(increase);
            System.out.println("Text Updated "  + priceText.getText().toString());
            if(tinydb.getBoolean(ISIN + "_notification_status")) {
                Notification notification = new NotificationCompat.Builder(getApplicationContext(), App.notificationChannel)
                        .setSmallIcon(R.drawable.ic_baseline_attach_money_24).setContentTitle(tinydb.getString(ISIN + "notification_name")).setContentText(tinydb.getString(ISIN + "notification_price")).setPriority(NotificationCompat.PRIORITY_MAX).setCategory(NotificationCompat.CATEGORY_STATUS).setOnlyAlertOnce(true).build();

                notificationCompat.notify(tinydb.getInt(ISIN + "_notification_id"), notification);
            }
            isUpdating = false;
        }
    }

最后这些是我得到的堆栈跟踪:


W/System.err: java.net.SocketTimeoutException: timeout
W/System.err:     at com.android.okhttp.okio.Okio.newTimeoutException(Okio.java:225)
        at com.android.okhttp.okio.AsyncTimeout.exit(AsyncTimeout.java:263)
W/System.err:     at com.android.okhttp.okio.AsyncTimeout.read(AsyncTimeout.java:217)
        at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:317)
        at com.android.okhttp.okio.RealBufferedSource.indexOf(RealBufferedSource.java:311)
W/System.err:     at com.android.okhttp.okio.RealBufferedSource.readUtf8LineStrict(RealBufferedSource.java:207)
W/System.err:     at com.android.okhttp.internal.http.Http1xStream.readResponse(Http1xStream.java:388)
        at com.android.okhttp.internal.http.Http1xStream.readResponseHeaders(Http1xStream.java:146)
W/System.err:     at com.android.okhttp.internal.http.HttpEngine.readNetworkResponse(HttpEngine.java:900)
        at com.android.okhttp.internal.http.HttpEngine.readResponse(HttpEngine.java:772)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:493)
W/System.err:     at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:429)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:560)
W/System.err:     at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
        at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
        at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:734)
W/System.err:     at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:706)
W/System.err:     at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:299)
W/System.err:     at org.jsoup.helper.HttpConnection.get(HttpConnection.java:288)
        at de.xliquid.stockwatchultimate.MainActivity.run(MainActivity.java:266)
W/System.err:     at java.lang.Thread.run(Thread.java:919)
W/System.err: Caused by: java.net.SocketException: socket is closed
        at com.android.org.conscrypt.ConscryptFileDescriptorSocket$SSLInputStream.read(ConscryptFileDescriptorSocket.java:588)
        at com.android.okhttp.okio.Okio.read(Okio.java:145)
        at com.android.okhttp.okio.AsyncTimeout.read(AsyncTimeout.java:213)
W/System.err:   ... 18 more



java.net.UnknownHostException: Unable to resolve host "www.onvista.de": No address associated with hostname
        at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:156)
W/System.err:     at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
        at java.net.InetAddress.getAllByName(InetAddress.java:1152)
        at com.android.okhttp.Dns.lookup(Dns.java:41)
W/System.err:     at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:178)
        at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:144)
        at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:86)
        at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:192)
        at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:144)
        at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:106)
        at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:400)
W/System.err:     at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:333)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:483)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:429)
        at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:560)
        at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
        at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
        at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:734)
        at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:706)
        at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:299)
        at org.jsoup.helper.HttpConnection.get(HttpConnection.java:288)
W/System.err:     at de.xliquid.stockwatchultimate.MainActivity.run(MainActivity.java:371)
        at java.lang.Thread.run(Thread.java:919)
W/System.err: Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
W/System.err:     at libcore.io.Linux.android_getaddrinfo(Native Method)
W/System.err:     at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:74)
        at libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:200)
        at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:74)
        at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:135)
W/System.err:   ... 22 more

此外,此应用仅供我使用,因此如果解决方案不是最干净的或耗电更快,我真的不会在意。

问题是节能模式,如果开启了phone在后台/空闲模式下不会做请求,不管唤醒锁,我通过添加权限解决了我的问题,所以即使 phone 处于节能待机状态,应用程序也可以请求数据。