我想调用一个反复查询Parse.com数据库并监视特定属性的服务。这就是我到目前为止所得到的:
public class Battle extends Service {
@Override
public int onStartCommand(Intent intent,int flags,int startId)
{
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
ParseUser currentUser = ParseUser.getCurrentUser();
username = currentUser.getString("username");
findinBackground();
return START_STICKY;
}
private void findinBackground(){
//public void run() {
// TODO Auto-generated method stub
while(true)
{
query = ParseUser.getQuery();
query.whereEqualTo("isAttacking", username);
query.findInBackground(new FindCallback<ParseUser>() {
public void done(List<ParseUser> objects, ParseException e) {
if ((e == null)&(objects.size() != 0))
{
// The query was successful.
ParseUser attacker = objects.get(0);
String attackerName = attacker.getUsername();
Log.i("ambustest",attackerName);
makeToast(attackerName);
}
else
{
Log.i("fd","Something went wrong.");
}
}
});
}
}
}
此代码编译正常,但在运行时停止响应。这是我的logcat:
答案 0 :(得分:1)
您需要在单独的线程上调用服务
@Override
public int onStartCommand(Intent intent,int flags,int startId)
{
Toast.makeText(this, "Service Started", Toast.LENGTH_LONG).show();
ParseUser currentUser = ParseUser.getCurrentUser();
username = currentUser.getString("username");
new Thread()
{
public void run() {
findinBackground();
}
}.start();
return START_STICKY;
}
应该注意的是,Intent Service会在一个单独的线程上自动调用,但常规服务不是。
答案 1 :(得分:0)
服务与UI在同一个线程上运行。如果您想进行耗时的操作,则需要在单独的线程中关闭它们。
答案 2 :(得分:0)
最佳解决方案是具有处理程序的远程服务,该处理程序会报告您的客户(活动)有关更改的信息。
http://developer.android.com/reference/android/app/Service.html
您的服务将在单独的过程中运行
首先,您需要一个AIDL - 作为与服务和客户端进行通信的接口
// IRemoteService.aidl
package de.contecon.android.util.abstractservice;
interface IRemoteService {
void registerCallback(IRemoteServiceCallback mCallback);
void unregisterCallback(IRemoteServiceCallback mCallback);
}
您的服务可能如下所示
//RemoteService.java
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY_COMPATIBILITY;
}
@Override
public void onCreate() {
// While this service is running, it will continually increment a
// number. Send the first message that is used to perform the
// increment.
mHandler.sendEmptyMessage(REPORT_MSG);
}
@Override
public IBinder onBind(Intent intent) {
// Select the interface to return. If your service only implements
// a single interface, you can just return it here without checking
// the Intent.
if (IRemoteService.class.getName().equals(intent.getAction())) {
return mBinder;
}
//Example for a second Binder
// if (IRemoteServiceSecondary.class.getName().equals(intent.getAction())) {
// return mBinderSec;
// }
return null;
}
private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {
@Override
public void registerCallback(IRemoteServiceCallback mCallback) throws RemoteException {
if (mCallback != null) mCallbacks.register(mCallback);
}
@Override
public void unregisterCallback(IRemoteServiceCallback mCallback) throws RemoteException {
if (mCallback != null) mCallbacks.unregister(mCallback);
}
};
/**
* Our Handler used to execute operations on the main thread. This is used
* to schedule increments of our value.
*/
private final Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
// It is time to bump the value!
case REPORT_MSG: {
// Up it goes.
int value = ++mValue;
// Broadcast to all clients the new value.
final int N = mCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
try {
mCallbacks.getBroadcastItem(i).valueChanged(value);
} catch (RemoteException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
}
mCallbacks.finishBroadcast();
// Repeat every 1 second.
sendMessageDelayed(obtainMessage(REPORT_MSG), 1*1000);
} break;
default:
super.handleMessage(msg);
}
}
};
和您的客户
/**
* This implementation is used to receive callbacks from the remote
* service.
*/
private IRemoteServiceCallback mCallback = new IRemoteServiceCallback.Stub() {
/**
* This is called by the remote service regularly to tell us about
* new values. Note that IPC calls are dispatched through a thread
* pool running in each process, so the code executing here will
* NOT be running in our main thread like most other things -- so,
* to update the UI, we need to use a Handler to hop over there.
*/
public void valueChanged(int value) {
mHandler.sendMessage(mHandler.obtainMessage(BUMP_MSG, value, 0));
}
};
private static final int BUMP_MSG = 1;
private Handler mHandler = new Handler() {
@Override public void handleMessage(Message msg) {
switch (msg.what) {
case BUMP_MSG:
mCallbackText.setText("Received from service: " + msg.arg1);
break;
default:
super.handleMessage(msg);
}
}
};
public void startService() {
// Make sure the service is started. It will continue running
// until someone calls stopService().
// We use an action code here, instead of explictly supplying
// the component name, so that other packages can replace
// the service.
startService(new Intent(
"your.action.uri.code.REMOTE_SERVICE"));
}
public void stopService() {
// Cancel a previous call to startService(). Note that the
// service will not actually stop at this point if there are
// still bound clients.
stopService(new Intent(
"your.action.uri.code.REMOTE_SERVICE"));
}
/**
* Class for interacting with the main interface of the service.
*/
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
// This is called when the connection with the service has been
// established, giving us the service object we can use to
// interact with the service. We are communicating with our
// service through an IDL interface, so get a client-side
// representation of that from the raw service object.
mService = IRemoteService.Stub.asInterface(service);
mCallbackText.setText("Attached.");
// We want to monitor the service for as long as we are
// connected to it.
try {
mService.registerCallback(mCallback);
} catch (RemoteException e) {
// In this case the service has crashed before we could even
// do anything with it; we can count on soon being
// disconnected (and then reconnected if it can be restarted)
// so there is no need to do anything here.
}
// As part of the sample, tell the user what happened.
Toast.makeText(RemoteServiceBinding.this, "service connected",
Toast.LENGTH_SHORT).show();
}
public void onServiceDisconnected(ComponentName className) {
// This is called when the connection with the service has been
// unexpectedly disconnected -- that is, its process crashed.
mService = null;
mCallbackText.setText("Disconnected.");
// As part of the sample, tell the user what happened.
Toast.makeText(RemoteServiceBinding.this,"service disconnected",
Toast.LENGTH_SHORT).show();
}
};
private void bindService(){
// Establish a couple connections with the service, binding
// by interface names. This allows other applications to be
// installed that replace the remote service by implementing
// the same interface.
bindService(new Intent(IRemoteService.class.getName()),
mConnection, Context.BIND_AUTO_CREATE);
bindService(new Intent(IRemoteServiceSecondary.class.getName()),
mSecondaryConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
mCallbackText.setText("RemoteServiceBinding.");
}
private void unbindService(){
if (mIsBound) {
// If we have received the service, and hence registered with
// it, then now is the time to unregister.
if (mService != null) {
try {
mService.unregisterCallback(mCallback);
} catch (RemoteException e) {
// There is nothing special we need to do if the service
// has crashed.
}
}
// Detach our existing connection.
unbindService(mConnection);
unbindService(mSecondaryConnection);
mIsBound = false;
mCallbackText.setText("Unbinding.");
}
}
的AndroidManifest.xml
<service
android:name=".service.RemoteService"
android:process=":remote"
android:enabled="true" >
<intent-filter>
<!-- These are the interfaces supported by the service, which
you can bind to. -->
<action android:name="de.your.path.util.abstractservice.IRemoteService" />
<!-- This is an action code you can use to select the service
without explicitly supplying the implementation class. -->
<action android:name="your.action.uri.code.REMOTE_SERVICE" />
</intent-filter>
</service>