我会直接介绍一些我发现的事实/数据,如果您遇到/解决了类似的问题,请帮我。

我每5分钟将数据发送回服务器,除非用户借助wakeful broadcast receiver通过intent service手动将其关闭。另外,我在执行操作之前先获得了wifi锁(我已经尝试过不带锁的功能;也不起作用)

现在,在从常规数据网络(2G/3G/4G)发送回数据的设备上,这可以正常工作,但是在连接到wifi网络的设备上,我莫名其妙地将getActiveNetworkInfo()称为null(即使直接点击网络URL也会得到hostname not found error),这肯定会在以下情况下发生HTC One(安装了v.4.4.2)其他设备也可以正常工作。

  • 我们都知道以前的官方连接管理器在返回太多正确/错误条件时遇到了问题,即使网络不可用/也不可用。我非常怀疑他们是否再次出现,或者只是一些定制OEM jing-bang
  • 警报被触发>>锁定后启动wakeful broadcast receiver >> get wifi locks >> sleep the thread for 3 secs inside the onReceive`,以等待wifi恢复连接>>然后完成我的服务。
  • 正在考虑强制使用移动数据连接而不是wifi的可能性。 (通过使用startUsingNetworkFeature())是否有人对此有解决方案?
  • 最佳答案

    希望我还不晚,像你一样,我遇到了一个问题。我的应用程序需要以一定的时间间隔(30m/1h/2h/4h)将数据(从内部存储文件)发送到服务器。为了使getActiveNetworkInfo()得到正确的结果,您需要唤醒wifi(因为在5-15分钟的电话 sleep 后wifi会关闭)。这给我带来了很多麻烦,但是这是我的解决方案的工作原理:

    首先,我有一个WakefulBroadcastReceiver,当我需要唤醒手机时会被调用:

    /** Receiver for keeping the device awake */
    public class WakeUpReceiver extends WakefulBroadcastReceiver {
    
        // Constant to distinguish request
        public static final int WAKE_TYPE_UPLOAD = 2;
    
        // AlarmManager to provide access to the system alarm services.
        private static AlarmManager alarm;
        // Pending intent that is triggered when the alarm fires.
        private static PendingIntent pIntent;
    
        /** BroadcastReceiver onReceive() method */
        @Override
        public void onReceive(Context context, Intent intent) {
            // Start appropriate service type
            int wakeType = intent.getExtras().getInt("wakeType");
            switch (wakeType) {
            case WAKE_TYPE_UPLOAD:
                Intent newUpload = new Intent(context, UploadService.class);
                startWakefulService(context, newUpload);
                break;
            default:
                break;
            }
        }
    
        /** Sets alarms */
        @SuppressLint("NewApi")
        public static void setAlarm(Context context, int wakeType, Calendar startTime) {
            // Set alarm to start at given time
            alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
            Intent intent = new Intent(context, WakeUpReceiver.class);
            intent.putExtra("wakeType", wakeType);
            pIntent = PendingIntent.getBroadcast(context, wakeType, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT);
            // For android 4.4+ the method is different
            if (android.os.Build.VERSION.SDK_INT >=
                    android.os.Build.VERSION_CODES.KITKAT) {
                alarm.setExact(AlarmManager.RTC_WAKEUP,
                        startTime.getTimeInMillis() + 5000, pIntent);
            } else {
                alarm.set(AlarmManager.RTC_WAKEUP,
                        startTime.getTimeInMillis() + 5000, pIntent);
            }
            // The + 5000 is for adding symbolic 5 seconds to alarm start
        }
    }
    

    请注意public static void setAlarm(Context, int, Calendar)函数,我使用该函数设置警报(请参阅主应用程序)。

    接下来,服务本身不是IntentService,而是Service:
    /** Service for uploading data to server */
    public class UploadService extends Service {
    
        private static final String serverURI = "http://your.server.com/file_on_server.php";
    
        private Looper mServiceLooper;
        private ServiceHandler mServiceHandler;
    
        WifiLock wfl;
    
        private Intent currentIntent;
    
        private SharedPreferences sharedPrefs;
        private int updateInterval;
        private boolean continueService;
    
        /** Service onCreate() method */
        @Override
        public void onCreate() {
            super.onCreate();
    
            // Initialize wifi lock
            wfl = null;
    
            // Initialize current Intent
            currentIntent = null;
    
            // Create separate HandlerThread
            HandlerThread thread = new HandlerThread("SystemService",
                    Process.THREAD_PRIORITY_BACKGROUND);
            thread.start();
            // Get the HandlerThread's Looper and use it for our Handler
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
    
            // Get shared variables values if set
            sharedPrefs = getSharedPreferences("serviceVars", MODE_PRIVATE);
            updateInterval = sharedPrefs.getInt("sendInterval", 60); // in your case 5
            continueService = sharedPrefs.getBoolean("bgServiceState", false);
        }
    
        /** Service onStartCommand() method */
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            // If continuing, set new alarm
            if (continueService) {
                Calendar nextStart = Calendar.getInstance();
                nextStart.set(Calendar.SECOND, 0);
                // Set alarm to fire after the interval time
                nextStart.add(Calendar.MINUTE, updateInterval);
                WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD,
                        nextStart);
            }
    
            // Get current Intent and save it
            currentIntent = intent;
    
            // Acquire a wifi lock to ensure the upload process works
            WifiManager wm = (WifiManager) getSystemService(Context.WIFI_SERVICE);
            wfl = wm.createWifiLock(WifiManager.WIFI_MODE_FULL, "WifiLock");
            if (!wfl.isHeld()) {
                wfl.acquire();
            }
    
            // For each start request, send a message to start a job and give
            // start ID so we know which request we're stopping when we finish
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            mServiceHandler.sendMessage(msg);
            // If service gets killed, it will restart
            return START_STICKY;
        }
    
        /** Handler that receives messages from the thread */
        private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            /** Executes all service operations */
            @Override
            public void handleMessage(Message msg) {
                // First wait for 5 seconds
                synchronized (this) {
                    try {
                        wait(5 * 1000);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                // Start checking if internet is enabled
                boolean hasInternet = false;
                long endTime = System.currentTimeMillis() + 55 * 1000;
                // Check every second (max 55) if connected
                while (System.currentTimeMillis() < endTime) {
                    if (hasInternet(UploadService.this)) {
                        hasInternet = true;
                        break;
                    }
                    synchronized (this) {
                        try {
                            wait(1000);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
                // Connected to internet or 60 (5 + 55) seconds passed
                if (hasInternet) {
                    // Connect to server
                    connectToServer(serverURI, fileName);
                } else {
                    // Can't connect, send message or something
                }
    
                // Stop service
                stopSelf(msg.arg1);
            }
        }
    
        /** Checks if phone is connected to Internet */
        private boolean hasInternet(Context context) {
            ConnectivityManager cm = (ConnectivityManager) context
                    .getSystemService(Context.CONNECTIVITY_SERVICE);
            NetworkInfo ni = null;
            if (cm != null) {
                ni = cm.getActiveNetworkInfo();
            }
            return ni != null && ni.getState() == NetworkInfo.State.CONNECTED;
        }
    
        /** Service onDestroy() method */
        @Override
        public void onDestroy() {
            // Release wifi lock
            if (wfl != null) {
                if (wfl.isHeld()) {
                    wfl.release();
                }
            }
    
            // Release wake lock provided by BroadcastReceiver.
            WakeUpReceiver.completeWakefulIntent(currentIntent);
    
            super.onDestroy();
        }
    
        /** Performs server communication */
        private void connectToServer(String serverUri, String dataFileName) {
            // this function does my network stuff
        }
    
        /** Service onBind() method - Not used */
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

    我也有一个BOOT BroadcastReceiver,所以如果设置了警报然后重新启动了电话,它将再次运行服务:
    /** Receiver for (re)starting alarms on device reboot operations */
    public class BootReceiver extends BroadcastReceiver {
        WakeUpReceiver alarm = new WakeUpReceiver();
    
        /** BroadcastReceiver onReceive() method */
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
                WakeUpReceiver.setAlarm(context, WakeUpReceiver.WAKE_TYPE_UPLOAD,
                        Calendar.getInstance());
            }
        }
    }
    

    最后,但并非最不重要的一点是,在Main Activity中,我通过启动接收器来启动服务:
    // Start upload service now
    Calendar timeNow = Calendar.getInstance();
    WakeUpReceiver.setAlarm(this, WakeUpReceiver.WAKE_TYPE_UPLOAD, timeNow);
    
    // Enable BootReceiver to (re)start alarm on device restart
    getPackageManager().setComponentEnabledSetting(
            new ComponentName(this, BootReceiver.class),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP);
    

    当我停止服务时,我也停止了BOOT接收器:
    // Disable BootReceiver to (re)start alarm on device restart
    getPackageManager().setComponentEnabledSetting(
            new ComponentName(this, BootReceiver.class),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP);
    

    此外,请不要忘记添加适当的权限和组件来体现:
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    
    <receiver
        android:name=".BootReceiver"
        android:enabled="false" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
    <receiver android:name=".WakeUpReceiver" />
    
    <service
        android:name=".UploadService"
        android:enabled="true"
        android:label="ServerUpload"
        android:launchMode="singleInstance" />
    

    我想我涵盖了所有内容,如果这可以帮助任何人解决他们的问题,我将感到很高兴。

    10-07 19:28
    查看更多