0

我正在开发一个应用程序,该应用程序应不时跟踪用户位置(例如每 20 秒),并在每分钟将这些位置发送到 Web 服务。为此,我创建了一个生产者消费者结构,其中每个任务都是一个由 AlarmManagers/BroadcastReceivers 启动的 android 服务。该应用程序还有一个使用 Phonegap 和 JQuery Mobile 开发的 UI,用于执行辅助任务并将一些数据发送到服务(例如用户名)。当负责网络通信的服务陷入 getInputStream() 方法并引发 Android Not Responding 对话框时,就会出现问题。我知道不应该在 UI 线程中执行冗长的操作,但我不确定如何根据我的方法解决这个问题,有什么线索吗?

下面是我的应用清单和一些代码:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.fcl"
          android:versionCode="1"
              android:versionName="1.0" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="17" />

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"   />
<uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BATTERY_STATS" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme"
    android:configChanges="orientation|keyboardHidden" 
    android:allowBackup="false">
    <activity
        android:name="com.android.fcl.MainActivity"
        android:label="@string/app_name" 
        android:screenOrientation="portrait">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <service 
        android:name="com.android.fcl.GPSLoggerService"
        android:label="@string/gps_logger_service">
    </service>
    <service 
        android:name="com.android.fcl.GPSHttpService"
        android:label="@string/gps_http_service">
    </service>
    <receiver android:name="com.android.fcl.GPSLoggerReceiver" />
    <receiver android:name="com.android.fcl.GPSHttpReceiver" />
</application>

</manifest>

Javascript:

function goToPage() {
    window.ServiceControl.startService();
    $.mobile.changePage('address.html', {transition: 'flip'});
    $.mobile.hidePageLoadingMsg();
}

活动:

public class MainActivity extends DroidGap {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        super.init();

        if (android.os.Build.VERSION.SDK_INT > 9) {
            StrictMode.ThreadPolicy policy = new    StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);
        }

        ServiceControl service = ServiceControl.getInstance();
        service.setActivity(this);
        service.setView(appView);
        appView.addJavascriptInterface(service, "ServiceControl");

        ServiceStorage serviceStorage = ServiceStorage.getInstance();
        serviceStorage.createDatabase(this);
        super.loadUrl("file:///android_asset/www/login.html");
    }    
}

服务控制.java:

public class ServiceControl {

    private AlarmManager gpsAlarmManager;
    private PendingIntent gpsPendingIntent;
    private AlarmManager httpAlarmManager;
    private PendingIntent httpPendingIntent;

    public void startService() {
        if (this.activity != null) {
            Intent gpsIntent = new Intent(this.activity, GPSLoggerReceiver.class);
            Intent httpIntent = new Intent(this.activity, GPSHttpReceiver.class);
            if (this.gpsAlarmManager == null) {
                gpsAlarmManager = (AlarmManager) activity.getSystemService(Service.ALARM_SERVICE);
                gpsPendingIntent = PendingIntent.getBroadcast(activity, 0, gpsIntent, 0);
                gpsAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), GPS_MIN_TIME, gpsPendingIntent);
            }
            if (this.httpAlarmManager == null) {
                httpAlarmManager = (AlarmManager) activity.getSystemService(Service.ALARM_SERVICE);
                httpPendingIntent = PendingIntent.getBroadcast(activity, 0, httpIntent, 0);
                httpAlarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), HTTP_MIN_TIME, httpPendingIntent);
            }
    }
}

广播接收器:

public class GPSHttpReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, GPSHttpService.class);
        context.startService(service);
    }
}

服务:

public class GPSHttpService extends Service {

    private PowerManager pm = null;
    private PowerManager.WakeLock wl = null;

    private HttpURLConnection httpConnection = null;

    private synchronized void setWakeLock() {
        ServiceControl serviceControl = ServiceControl.getInstance();
        DroidGap activity = serviceControl.getActivity();
        pm = (PowerManager) activity.getSystemService(Context.POWER_SERVICE);
        wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
        wl.acquire();
    }

    private String sendPostMessage(String message) {
        byte [] buffer = null;
        String response = null;
    URL url = null;
        DataOutputStream outputStream = null;
        InputStreamReader inputStream = null;

        buffer = message.getBytes();
        try {
            url = new URL(URI);
            httpConnection = (HttpURLConnection) url.openConnection();
            httpConnection.setDoOutput(true);
            httpConnection.setDoInput(true);
            httpConnection.setInstanceFollowRedirects(false);
            httpConnection.setRequestMethod(FCLString.POST_REQUEST_METHOD);
            httpConnection.setRequestProperty(FCLString.CONTENT_TYPE_PROPERTY, FCLString.CONTENT_TYPE_APP_JSON);
            httpConnection.setRequestProperty(FCLString.CHARSET_PROPERTY, FCLString.UTF8_FORMAT);
            httpConnection.setConnectTimeout(CONN_TIMEOUT);
            httpConnection.setRequestProperty(FCLString.CONTENT_LENGTH_PROPERTY, String.valueOf(buffer.length));
            httpConnection.setUseCaches(false);
            outputStream = new DataOutputStream(httpConnection.getOutputStream());
            outputStream.write(buffer);
            outputStream.flush();
            outputStream.close();
            inputStream = new InputStreamReader(httpConnection.getInputStream());
            response = GPSMessage.readResponseStream(inputStream);
       } catch (MalformedURLException e) {
           Log.d(TAG, FCLString.MALFORMED_URL);
       } catch (IOException e) {
           Log.d(TAG, FCLString.IO_EXCEPTION);
           Log.d(TAG,  e.getMessage());
       } finally {
           if (httpConnection != null) {
               httpConnection.disconnect();
            }
       }
       return response;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        ServiceStorage serviceStorage = ServiceStorage.getInstance();
        JSONObject json= null;
        String message = null;
        String response = null;
        boolean status = false;

        super.onStartCommand(intent, flags, startId);
    setWakeLock();
        json = serviceStorage.getRecords();
        if ((json != null) && (json.length() > 0)) {
            message = GPSMessage.prepareMessage(json);
            if (message != null) {
                if (hasInternetConnection()) {
                    response = sendPostMessage(message);
                    status = GPSMessage.evaluateResponse(response);
                    if (status) {
                        serviceStorage.removeRecords(json);
                    }
                } 
            }
        }
        this.stopSelf();
        return Service.START_NOT_STICKY;
     }
}
4

2 回答 2

3

From the reference doc on Android services:

Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.

于 2013-05-03T00:58:07.530 回答
0

您可以考虑使用IntentService而不是仅使用常规Service,因为您的工作似乎相对独立。这将负责在单独的线程上完成您的工作。

于 2013-05-03T00:42:22.023 回答