7

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

我尝试了什么:

  • 当我手动关闭数据时,应用程序正在打开它并且它可以工作
  • 我也尝试了 WakeLock,但它没有帮助。
  • 闹钟按预期工作,即使手机进入睡眠状态数小时

在摩托罗拉 Atrix Android 2.3.3 上测试。我不能依赖 Wifi。在现实生活中,它将每周同步。我们怎样才能使它成为可能?

警报管理器:

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" />
4

3 回答 3

16

首先,您需要将该HttpPost代码BroadcastReceiverIntentService. 永远不要在主应用程序线程上进行网络 I/O,并在主应用程序线程onReceive()上调用。例如,如果您花费的时间过长,Android 将在您的 Internet 操作中途终止您的代码。

其次,鉴于IntentService,您需要使用WakeLock. 这可能会引导您使用myWakefulIntentService,它可以处理这两个问题。或者,使用WakefulBroadcastReceiver,它具有相同的目的。

三、删除turnOnInet()getMobileDataEnabled()。你不需要它们,它们不可靠,尤其turnOnInet()是对用户怀有敌意——如果用户想要打开移动数据,他们就会打开它。

现在,鉴于所有这些,在您onHandleIntent()IntentService()(或您doWakefulWork()WakefulIntentService)中,如果您没有立即连接 Internet,作为临时解决方法,SystemClock.sleep()请稍等片刻,然后再试一次,循环重复几次。如果您发现您在一段时间后可以访问 Internet,那么您可以考虑变得更复杂(例如,侦听连接更改广播而不是轮询,尽管这会使您远离WakefulIntentService并进入Service具有自己的后台线程和用于管理的状态机WakeLock)。或者,只要坚持sleep()——如果你把这个后台线程捆绑几秒钟,它不太可能是世界末日。但是,如果您在适当的时间后仍未获得连接,请不要无限期地尝试,因为您可能无法获得连接的原因有很多,包括 Android 4.0+ 上的用户驱动的带宽管理。

于 2012-08-19T22:37:44.233 回答
5

我建议稍微改变一下这种方法,这一点也不错,确保您始终保持同步甚至很好,唯一的问题是您没有给用户决定的机会,因为如果我,作为用户,决定关闭我的数据我只是不想让任何人打开它。这可能有几个原因,其中任何一个都应该足够了,但是说你离开了这个国家并且你没有国际数据计划,并且意外或默认你激活了数据漫游。如果我发现某些应用程序打开了我的数据并花费了敏感金额,我会非常生气,而且我会很个人化。

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

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

希望能帮助到你。让我们了解您的最新进展。

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

于 2012-08-18T10:28:45.757 回答
1

不是一个确切的答案 - 但这种方法绝对会破坏电池寿命。睡眠的全部意义在于节省电池电量,无论功能多么有用,这个应用程序都只会让客户烦恼。

我的建议是,如果在不使用应用程序时绝对有必要连接到互联网 - 设置手机唤醒时的触发器。如果不是完全有必要,最好在每次打开应用程序时简单地重新连接到互联网。

于 2012-08-20T23:29:08.377 回答