在睡眠时打开Android上的互联网

pro*_*ook 6 android sleep-mode

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

我尝试了什么:

  • 当我手动关闭数据时,应用程序将其打开并运行
  • 我也试过WakeLock,但它没有帮助.
  • 警报按预期工作,即使手机进入睡眠状态数小时

在摩托罗拉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);
Run Code Online (Sandbox Code Playgroud)

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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" />
Run Code Online (Sandbox Code Playgroud)

Com*_*are 15

首先,你需要从中获取HttpPost代码BroadcastReceiver并将其转换为IntentService.永远不要在主应用程序线程上进行网络I/O,并在主应用程序线程onReceive()上调用.例如,如果您花费太长时间,Android将通过您的Internet操作中途终止您的代码.

其次,考虑到IntentService,你需要使用一个WakeLock.这可能会引导你使用我的WakefulIntentService,处理这两个问题.或者,使用WakefulBroadcastReceiver,具有相同的目的.

第三,删除turnOnInet()getMobileDataEnabled().您不需要它们,它们不可靠,特别turnOnInet()是用户恶意 - 如果用户想要移动数据,他们就会打开它.

现在,考虑到所有这一切,在你onHandleIntent()的你IntentService()(或你的doWakefulWork()你的WakefulIntentService),如果你没有互联网连接的时候了,作为临时解决方法,SystemClock.sleep()一秒钟,再试一次,在一个循环中重复几次.如果您发现稍微上网后可以访问互联网,那么您可以考虑变得更复杂(例如,监听连接更改广播而不是轮询,尽管这会让您远离WakefulIntentService并进入常规Service的自己的后台线程和国家机器WakeLock管理).或者,坚持下去sleep()- 如果你把这个后台线程绑定几秒钟,它就不太可能成为世界末日.但是,如果您在适度的时间之后没有获得连接,请不要无限期地尝试,因为您可能无法获得连接的原因有很多,包括Android 4.0+上的用户驱动带宽管理.


Jos*_*gia 5

我建议改变一下这个方法,这一点都不错,确保你总是同步,这是唯一的问题,你不会给用户提供决定的机会,因为如果我,作为用户,决定关闭我的数据我只是不希望任何人打开它.可能有几个原因,其中任何一个都应该足够,但是说你离开这个国家并且你没有国际数据计划,并且偶然或默认你已经激活了数据漫游.如果我发现某些应用程序将我的数据打开并花费了大量金钱,我会非常生气,而且我会是个人的.

一个更合适的方法和直接的解决方案是,每当用户打开你的应用程序或根据一些简单的条件(上次同步)访问某些wifi连接(ConnectivityManager是你的朋友)时,可以不时进行硬/完全同步超过一周,过时的保存数据,不一致等)并在其余情况下进行软同步(在后台更新数据).

此外,定期同步意味着在用户不使用应用程序的情况下浪费用户数据.最终,这会使您的应用程序成为每隔一段时间被系统关闭的完美候选者.

希望能帮助到你.随时向我们通报您的进度.

相关阅读:优化下载以实现高效的网络访问