睡眠时在Android上打开互联网 [英] Turn on the Internet on Android when in sleep

查看:146
本文介绍了睡眠时在Android上打开互联网的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个需要同步到互联网的Android应用程序,但是一旦手机进入睡眠状态,我就无法访问互联网.仅当用户使用电池模式"时,即15分钟后关闭数据时,才会发生这种情况.我编写了一个测试应用程序,并打开了数据,但它仍然可以连接到服务器.

我尝试过的事情:

  • 当我手动关闭数据时,则应用程序将其打开并且可以正常工作
  • 我也尝试了WakeLock,但没有帮助.
  • 即使手机进入睡眠状态数小时,闹钟仍能正常工作

在Motorola Atrix Android 2.3.3上进行了测试.我不能依靠Wifi.在现实生活中,它将每周同步一次.我们如何使其成为可能?

AlarmManager:

alarm_manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, 
                        PendingIntent.FLAG_UPDATE_CURRENT);
alarm_manager.setRepeating(AlarmManager.RTC_WAKEUP, 
                        System.currentTimeMillis(), 15000, pending);

警报接收器:

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MYTAG", "RECEIVED getMobileDataEnabled: " + getMobileDataEnabled(context));  
        if (!isOnline(context)) {
            Log.d("MYTAG", "NO INET");
            if (turnOnInet(context)) {
                Log.d("MYTAG", "INET IS ON");
            }
        }

        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://xxx.xxx.xxx.xxx/ping/pong/moto/");
            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
                nameValuePairs.add(new BasicNameValuePair("short_code", "ROFL"));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                httpclient.execute(httppost);
                Log.d("MYTAG", "POST FINISHED");
            }
            catch (Exception e) {
                Log.e("MYTAG", "MYTAG", e);
            }
    }

    public boolean isOnline(Context context) {
        ConnectivityManager cm = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null){
            Log.d("MYTAG", "isAvailable: "+netInfo.isAvailable());
        }
        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        }
        return false;
    }

    public boolean turnOnInet(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "ConnectivityManager == NULL");
            return false;
        }
        try {
            Method setMobileDataEnabledMethod = mgr.getClass().getDeclaredMethod("setMobileDataEnabled", boolean.class);
            if (null == setMobileDataEnabledMethod) {
                Log.d("MYTAG", "setMobileDataEnabledMethod == null");
                return false;
            }    
            setMobileDataEnabledMethod.invoke(mgr, true);
        }
        catch(Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
        return true;
    }   


    private boolean getMobileDataEnabled(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "getMobileDataEnabled ConnectivityManager == null");
            return false;
        }
        try {
            Method method = mgr.getClass().getMethod("getMobileDataEnabled");
            return (Boolean) method.invoke(mgr);
        } catch (Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

解决方案

首先,您需要将HttpPost代码从BroadcastReceiver中取出并放入IntentService中. 从不在主应用程序线程上执行网络I/O,并且在主应用程序线程上调用onReceive().例如,如果您花费的时间太长,Android将在Internet操作过程中终止代码.

第二,给定IntentService,您需要使用WakeLock.这可能会引导您使用我的WakefulIntentService ,它可以解决这两个问题.或者,使用 WakefulBroadcastReceiver ,它具有相同的目的.

第三,删除turnOnInet()getMobileDataEnabled().您不需要它们,它们是不可靠的,尤其是turnOnInet()是对用户不利的-如果用户希望打开移动数据,他们会打开它.

现在,考虑到所有情况,如果您暂时没有Internet连接,则在IntentService()onHandleIntent()(或WakefulIntentServicedoWakefulWork())中,SystemClock.sleep()一秒钟,然后重试,一次循环重复几次.如果您发现稍候片刻即可访问Internet,则可以考虑变得更复杂(例如,侦听连接更改广播而不是轮询,尽管这样做会使您离开WakefulIntentService并进入常规的Service您自己的后台线程和用于WakeLock管理的状态机).或者,只需坚持使用sleep() -如果您将此后台线程占用几秒钟,这不太可能成为世界末日.不过,如果您在一段时间后仍无法连接,请不要无限期尝试,因为有很多原因导致您无法连接,包括Android 4.0+上的用户驱动带宽管理.

I have an Android app that needs to sync to the internet, but as soon as the phone goes to sleep I can't access the internet. It only happens when the user uses the "battery mode", when it turns off the data after 15 minutes. I wrote a test app and its turning the data on, but it still does connect to the server.

What I tried:

  • When I turn the data manually off, then the app is turning it on and it works
  • I also tried WakeLock, but it did not help.
  • The alarm works as expected, even when the phone goes to sleep for hours

Tested on Motorola Atrix Android 2.3.3. I can't rely on Wifi. In real life it will sync every week. How can we make it possible?

AlarmManager:

alarm_manager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent pending = PendingIntent.getBroadcast(this, 0, intent, 
                        PendingIntent.FLAG_UPDATE_CURRENT);
alarm_manager.setRepeating(AlarmManager.RTC_WAKEUP, 
                        System.currentTimeMillis(), 15000, pending);

AlarmReceiver:

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.d("MYTAG", "RECEIVED getMobileDataEnabled: " + getMobileDataEnabled(context));  
        if (!isOnline(context)) {
            Log.d("MYTAG", "NO INET");
            if (turnOnInet(context)) {
                Log.d("MYTAG", "INET IS ON");
            }
        }

        HttpClient httpclient = new DefaultHttpClient();
        HttpPost httppost = new HttpPost("http://xxx.xxx.xxx.xxx/ping/pong/moto/");
            try {
                List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(1);
                nameValuePairs.add(new BasicNameValuePair("short_code", "ROFL"));
                httppost.setEntity(new UrlEncodedFormEntity(nameValuePairs));
                httpclient.execute(httppost);
                Log.d("MYTAG", "POST FINISHED");
            }
            catch (Exception e) {
                Log.e("MYTAG", "MYTAG", e);
            }
    }

    public boolean isOnline(Context context) {
        ConnectivityManager cm = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo netInfo = cm.getActiveNetworkInfo();
        if (netInfo != null){
            Log.d("MYTAG", "isAvailable: "+netInfo.isAvailable());
        }
        if (netInfo != null && netInfo.isConnectedOrConnecting()) {
            return true;
        }
        return false;
    }

    public boolean turnOnInet(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "ConnectivityManager == NULL");
            return false;
        }
        try {
            Method setMobileDataEnabledMethod = mgr.getClass().getDeclaredMethod("setMobileDataEnabled", boolean.class);
            if (null == setMobileDataEnabledMethod) {
                Log.d("MYTAG", "setMobileDataEnabledMethod == null");
                return false;
            }    
            setMobileDataEnabledMethod.invoke(mgr, true);
        }
        catch(Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
        return true;
    }   


    private boolean getMobileDataEnabled(Context context) {
        ConnectivityManager mgr = (ConnectivityManager)context.getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mgr == null) {
            Log.d("MYTAG", "getMobileDataEnabled ConnectivityManager == null");
            return false;
        }
        try {
            Method method = mgr.getClass().getMethod("getMobileDataEnabled");
            return (Boolean) method.invoke(mgr);
        } catch (Exception e) {
            Log.e("MYTAG", "MYTAG", e);
            return false;
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />

解决方案

First, you need to get that HttpPost code out of the BroadcastReceiver and into an IntentService. Never do network I/O on the main application thread, and onReceive() is called on the main application thread. For example, if you take too long, Android will terminate your code partway through your Internet operation.

Second, given the IntentService, you need to use a WakeLock. That may steer you to use my WakefulIntentService, which handles both problems. Or, use WakefulBroadcastReceiver, which has the same purpose.

Third, delete turnOnInet() and getMobileDataEnabled(). You do not need them, they are unreliable, and in particular turnOnInet() is user-hostile -- if the user wanted mobile data on, they would have turned it on.

Now, given all of that, in your onHandleIntent() of your IntentService() (or your doWakefulWork() of your WakefulIntentService), if you do not have an Internet connection right away, as a temporary workaround, SystemClock.sleep() for a second and try again, repeating a few times in a loop. If you find that you are getting Internet access after a bit, then you can consider getting more sophisticated (e.g., listening for connectivity change broadcasts rather than polling, though this would drive you away from WakefulIntentService and into a regular Service with your own background thread and a state machine for WakeLock management). Or, just stick with the sleep() -- it's unlikely to be the end of the world if you tie up this background thread for a few seconds. If you do not get connectivity after a modest amount of time, though, please do not keep trying indefinitely, as there are any number of reasons why you might not get a connection, including user-driven bandwidth management on Android 4.0+.

这篇关于睡眠时在Android上打开互联网的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆