VOIP アプリケーションの一種であるアプリケーションを実装しています。つまり、私のアプリケーションは一種のネットワーク アプリケーションです。ここで、アプリケーションに 2 つの部分を実装したいと考えています。1 つは GUI 部分で、もう 1 つはネットワーク部分です。GUI パーツには、アクティビティとユーザー インタラクションの処理のみが含まれます。マイ ネットワーク パーツは、着信ネットワーク データの処理や、GUI インタラクションに基づくネットワークへのデータ送信など、ネットワーク関連のすべてのアクティビティを処理する必要があります。着信データがあるときはいつでも、参照がネットワークモジュールにないアクティビティを更新したいと思います。では、他のクラスからアクティビティを更新する最良の方法は何でしょうか? 私の場合、他のクラスは Network クラスです。要するに、そのようなシナリオのアーキテクチャはどうあるべきかを尋ねたいと思いますか? つまり、ネットワーク部分は別のスレッドで実行し、そこから GUI を更新する必要がありますか?
4 に答える
アクティビティに送信する必要があるデータのタイプ/サイズに応じて、いくつかのオプションのいずれかを使用できます。
- ここで説明するいずれかの方法を使用してください。
- を使用して
BroadcastReceiver
: に登録し、ネットワーク コードを処理するでActivity
一致するIntent
を起動します。Service
- に
Activity
バインドしてから、 s を送信Service
する a を渡します。Handler
Message
私はこのようなアプリを作成しましたが、Handler メソッドを好みます。実際、私はすべての大変な作業を行い、変更の通知が必要なアクティビティで単純に拡張するために、Abstract Activity クラスを作成しました。
次のコードを使用するには、Activity を取得して UpdatableActivity を拡張し、dataUpdated() メソッドをオーバーライドします。このメソッドは、データが更新されたことを Service がハンドラーに通知するときに呼び出されます。Service コードでは、 update() メソッドで更新を行うコードを配置します (または、既存のコードを呼び出すように変更します)。これにより、アクティビティは this.updateService() を呼び出して更新を強制できます。サービスは sendMessageToUI() メソッドを呼び出して、関連するすべてのアクティビティにデータが更新されたことを通知できます。
抽象アクティビティは次のようになります。
public abstract class UpdatableActivity extends Activity {
public static final String TAG = "UpdatableActivity (Abstract)";
private final Messenger mMessenger = new Messenger(new IncomingHandler());
private Messenger mService = null;
private boolean mIsBound;
protected class IncomingHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (Constants.LOG_DEBUG) Log.d(TAG, "Service has notified us of an update: ");
switch (msg.arg1) {
case UpdateService.MSG_DATA_UPDATED:
dataUpdated();
break;
default: super.handleMessage(msg);
}
}
}
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
mService = new Messenger(service);
try {
Message msg = Message.obtain(null, UpdateService.MSG_REGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// In this case the service has crashed before we could even do anything with it
}
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been unexpectedly disconnected - process crashed.
mService = null;
}
};
/**Override this method in you acctivity to handle the update */
public abstract void dataUpdated();
void doBindService() {
if (Constants.LOG_DEBUG) Log.d(TAG, "Binding to service...");
bindService(new Intent(this, UpdateService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// If we have received the service, and hence registered with it, then now is the time to unregister.
if (mService != null) {
try {
Message msg = Message.obtain(null, UpdateService.MSG_UNREGISTER_CLIENT);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
// There is nothing special we need to do if the service has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
public void updateService() {
if (Constants.LOG_DEBUG) Log.d(TAG,"Updating Service...");
if (mIsBound) {
if (mService != null) {
try {
Message msg = Message.obtain(null, UpdateService.MSG_SET_INT_VALUE, UpdateService.MSG_DO_UPDATE, 0);
msg.replyTo = mMessenger;
mService.send(msg);
} catch (RemoteException e) {
if (Constants.LOG_ERROR) Log.e(TAG,Log.getStackTraceString(e));
}
}
} else {
if (Constants.LOG_DEBUG) Log.d(TAG, "Fail - service not bound!");
}
}
pu
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.doBindService();
}
@Override
protected void onDestroy() {
super.onDestroy();
try {
doUnbindService();
} catch (Throwable t) {
if (Constants.LOG_ERROR) Log.e(TAG, "Failed to unbind from the service", t);
}
}
}
サービスは次のようになります。
public class UpdateService extends Service {
public static final String TAG = "UpdateService";
public static final int MSG_DATA_UPDATED = 0;
public static final int MSG_REGISTER_CLIENT = 1;
public static final int MSG_UNREGISTER_CLIENT = 2;
public static final int MSG_DO_UPDATE = 3;
public static final int MSG_SET_INT_VALUE = 4;
private static boolean isRunning = false;
private Handler handler = new IncomingHandler();
private final Messenger mMessenger = new Messenger(handler);
private ArrayList<Messenger> mClients = new ArrayList<Messenger>(); // Keeps track of all current registered clients.
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
class IncomingHandler extends Handler { // Handler of incoming messages from clients.
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mClients.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mClients.remove(msg.replyTo);
break;
case MSG_SET_INT_VALUE:
switch (msg.arg1) {
case MSG_DO_UPDATE:
if (Constants.LOG_DEBUG) Log.d(TAG,"UI has asked to update");
update();
break;
}
break;
default:
super.handleMessage(msg);
}
}
}
private void sendMessageToUI() {
if (Constants.LOG_DEBUG) Log.d(TAG, "Notifying "+mClients.size()+" UI clients that an update was completed");
for (int i=mClients.size()-1; i>=0; i--) {
try {
// Send data as an Integer
mClients.get(i).send(Message.obtain(null, MSG_SET_INT_VALUE, MSG_DATA_UPDATED, 0));
} catch (RemoteException e) {
// The client is dead. Remove it from the list; we are going through the list from back to front so this is safe to do inside the loop.
mClients.remove(i);
}
}
}
public static boolean isRunning()
{
return isRunning;
}
@Override
public void onCreate() {
super.onCreate();
isRunning = true;
if (Constants.LOG_DEBUG) Log.d(TAG, "Service Started");
update();
}
@Override
public void onDestroy() {
if (Constants.LOG_DEBUG) Log.d(TAG, "Service Destroyed");
isRunning = false;
}
private void update() {
/**Your code to do an update goes here */
}
}
はい、個人的には、ネットワークと UI は別のスレッドにすべきだと思います。私が 2 つの間で通信する方法は、おそらく推奨される適切な方法ではありませんが、私にとってはうまくいきます。それは、アプリケーション クラスにグローバル変数を作成することです。これが少し役立つことを願っています
メインの UI スレッドに直接投稿し、
ハンドラー mHandler = new Handler(Looper.getMainLooper());
mHandler.post(new Runnable() {...});