java - 使用多个服务或在单个服务中执行所有操作哪个更好?

标签 java android multithreading sockets

我正在开发一个基于套接字的应用程序,在该应用程序中我不断更新房间的状态,例如有多少盏灯打开和关闭。我有 7 个此类房间,我需要更新每个房间的状态。

所以我的问题是我应该为每个房间创建一个单独的服务还是应该在单个服务中执行所有操作?就性能而言,哪种方式更方便。

这是我的单人间服务等级。

public class UpdateUiService extends Service 
{
    @Override
    public void onCreate() {
        super.onCreate();
        intent = new Intent(BROADCAST_ACTION);
        try {
            s = new Socket("192.168.1.19,502);
            i = s.getInputStream();
            o = s.getOutputStream();
            System.out.println("connected");
        } catch (UnknownHostException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    @Override
    public void onStart(Intent intent, int startId) {
        handler.removeCallbacks(sendUpdatesToUI);
        handler.postDelayed(sendUpdatesToUI, 1000); // 1 second

    }

    private Runnable sendUpdatesToUI = new Runnable() {
        public void run() {
            DisplayLoggingInfo();
            handler.postDelayed(this, Integer.parseInt(interval_dinning));
        }
    };

    private void DisplayLoggingInfo() {
        try {
            byte[] data1 = new byte[1024], packet1 = 
            { 
                (byte) 0x00,(byte) 0x00,(byte) 0x00, 
                (byte) 0x00,(byte) 0x00,(byte) 0x06, 
                (byte) 0x01,(byte) 0x01,(byte) 0x00,
                (byte) 0x00,(byte) 0x00,(byte) 0x19
            };

            o.write(packet1);
            i.read(data1, 0, 1024);

            byte_to_hex = ConversionMethods.bytesToHex(data1).substring(18, 26);

            char[] arr = byte_to_hex.toCharArray();
            for (int i = 0; i < arr.length - 1; i += 2) {
                char temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
            }

            swapped_result = new String(arr);
            result = ConversionMethods.hexStringToNBitBinary(swapped_result, 32);
            int counter_ = 0;
            for( int i=0; i<result.length(); i++ ) 
            {
                if( result.charAt(i) == '1' )
                {
                    counter_++;        
                }  
            }
            status=Integer.toString(counter_);

        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

        intent.putExtra("counter", String.valueOf(++counter));
        intent.putExtra("status", status);
        sendBroadcast(intent);
    } 
}

我正在启动此服务并在我想要显示此值的 Activity 中获取广播 Intent 。这是update UI from background service的引用链接.

如何使用多线程实现这一点。我的最终目标是读取套接字并获取状态,仅此而已。

请为我指出正确的方向。任何建议和想法将不胜感激。

谢谢

最佳答案

问题

a)从您的代码中看不出来是什么在启动您的服务。是启动一次还是多次。

根据当前代码,看起来它会向 ip 发送一些内容,读取结果,发送一个广播,仅此而已。

那么,问题是,您需要只更新一次灯光状态还是需要不断/定期更新它们?

想法

a) 在这种情况下,如果您只需要更新一次灯光状态,并且它是从 UI 触发并更新 UI,那么使用 AsyncTask 会更好。这是专门为此设计的。

您是否想要 7 个并发 AsyncTask(如果您想并行更新灯光状态),或者您可以拥有一个 AsyncTask,它将串行更新灯光状态并在每个灯光更新后向 UI 线程报告,这取决于您。已更新。

b) 在这种情况下,如果您需要持续跟踪灯光状态,那么您最好选择一项服务。但是,您需要在此服务中有一个长时间运行的线程。因此,您应该在 onStart 中启动一个线程。

一般来说,它应该(假设每 10 秒一次)调用一些方法来引发所有通信等等。

在该方法中,您可以激发 X 个线程(每个房间一个),并在这些线程中执行所有操作(写入套接字、读取、解析等),或者您可以在第一个线程中执行所有这些操作。您在这里可以选择与 AsyncTask 相同的选择来并行或串行执行此操作。

c) 此外,您可能希望保持所有套接字处于 Activity 状态并重用它们,以便在需要更新灯光状态时不会每 5 秒重新连接一次。

一般评论

a) 您正在使用已弃用的 onStart()。您应该使用 onStartCommand()

b)我知道它可能是一个原型(prototype),但是您展示的这段代码质量相当低。如果你不清理它,你将来会有很多 bug 需要追查:

您的代码如下:

  • 很多神奇的数字
  • 函数命名错误(例如 DisplayLoggingInfo,它不显示任何内容,而是读/写套接字、执行一些转换并发送广播)
  • 长方法 (DisplayLoggingInfo)

更新 1

这是适合您的示例应用程序。 请注意,这是一个原型(prototype)。您可能有兴趣添加更多检查,将其分离到更多类等等。

MyService.java

package com.example.servicesample;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import java.lang.Thread;
import android.support.v4.content.LocalBroadcastManager;

public class MyService extends Service implements Runnable {
    public static final String ROOM_STATUS_BROADCAST = "com.example.room_status_broadcast";
    public static final String ROOM_STATUS_BROADCAST_EXTRA_ROOM_NUMBER = "roomnumber";
    public static final String ROOM_STATUS_BROADCAST_EXTRA_STATUS = "status";

    static final int NUM_ROOMS = 7;
    static final int TIME_FOR_A_REST = 5000; //ms

    Thread mThread = null;
    Boolean mRunning = false;

    @Override
    public void onCreate() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        start();

        return  START_STICKY;
    }

    @Override
    public void onDestroy() {
        stop();
    }

    private synchronized void start()
    {
        if (mThread != null)
            return;

        mRunning = true;
        mThread = new Thread(this);
        mThread.start();
    }

    private synchronized void stop()
    {
        if (mThread == null)
            return;

        mRunning = true;

        try
        {
            mThread.join();
        } catch (InterruptedException e) {}
        mThread = null;
    }


    public void run()
    {
        while (mRunning)
        {
            for (int i = 0; i < NUM_ROOMS; i++)
                updateRoomStatus(i);

            try
            {
                Thread.sleep(TIME_FOR_A_REST);
            } catch (InterruptedException e) {}             
        }
    }

    Boolean getRoomStatus(int roomNumber)
    {
        // Do real communication here (instea of just assigning true)
        // It makes sense to move all communication to a separate class from here
        Boolean newRoomStatus = true;

        return newRoomStatus;
    }

    void updateRoomStatus(int roomNumber)
    {
        Boolean newRoomStatus = getRoomStatus(roomNumber);
        broadcastRoomStatus(roomNumber, newRoomStatus);
    }

    void broadcastRoomStatus(int roomNumber, Boolean newRoomStatus)
    {
        Intent intent = new Intent(ROOM_STATUS_BROADCAST);
        intent.putExtra(ROOM_STATUS_BROADCAST_EXTRA_ROOM_NUMBER, roomNumber);
        intent.putExtra(ROOM_STATUS_BROADCAST_EXTRA_STATUS, newRoomStatus);
        LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
    }

}

MyActivity.java

package com.example.servicesample;

import android.os.Bundle;
import android.app.Activity;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.Menu;
import com.example.servicesample.MyService;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.IntentFilter;

public class MainActivity extends Activity {

    private IntentFilter mIntentFilter = new IntentFilter(MyService.ROOM_STATUS_BROADCAST);


    private BroadcastReceiver mReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            MainActivity.this.receivedBroadcast(intent);       
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startMyService();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }   

    void startMyService()
    {
        // You can move this code to be executed on a button click or something else
        // It will start a service
        startService(new Intent(this, MyService.class));
    }   

    @Override
    protected void onResume()
    {
        super.onResume();

        LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, mIntentFilter);
    }

    @Override
    protected void onPause()
    {
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);

        super.onPause();
    }   

     private void receivedBroadcast(Intent i) {
         Integer roomNumber = i.getIntExtra(MyService.ROOM_STATUS_BROADCAST_EXTRA_ROOM_NUMBER, 0);
        Boolean roomStatus = i.getBooleanExtra(MyService.ROOM_STATUS_BROADCAST_EXTRA_STATUS, false);

        // Let's do here whatever we want with received status (as example, update UI)
        Log.d("SomeTag", "Room number "+roomNumber.toString() + " got new status " + roomStatus.toString());
     }  

}

AndroidManifest.xml

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

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.servicesample.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <service android:name=".MyService"/>

    </application>

</manifest>

关于java - 使用多个服务或在单个服务中执行所有操作哪个更好?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14869079/

相关文章:

java - 在 Java 中对象和对它的引用

java - 在android studio中遇到通货膨胀异常

c - 有什么方法可以更改链接以避免 HPUX 上的 LD_PRELOAD?

java - android 2.3.6版本中的NoClassDefFoundError(HttpResponseCahe)

java - 将 .dll 导入与 Java 中的文件相同的目录中

android - Material 按钮颜色

c# - 在 Parallel.For 完成作业后引发事件

python - 两个进程之间的IPC示例,opencv(cv::Mat对象)c++作为服务器,python作为客户端

java - 为什么 Java 崩溃(退出代码 134)?

java - 如何在我的 Android 应用程序中添加对 Android 4.4 的支持