Is there any way to directly communicate with a WallpaperService from an Activity? It doesn't look like I can use the normal service communication classes because the onBind method is declared final in the WallpaperService class I'm extending. Worth noting that I'm referring to my WallpaperService not any.
Any workarounds if this isn't possible?
My solution was to use local sockets. I created an instance of a LocalServerSocket in the constructor of my wallpaper's Engine. Here's a quick implementation. Server runs on a separate thread and is directly tied to the lifecycle of MyEngine. The thread will stop when continueSocket is set to false. This happens onDestroy. Problem is that LocalServerSocket.accept() blocks until there's something to do. The workaround is to send a message to our own server so it will run through the loop again and check continueSocket (which is now false), closing the server. Check the closeSocketServer method. I have it running in onDestroy in the example but you might want to use it elsewhere like onSurfaceDestroyed and add your own sanity checks.
public class MyWallpaperService extends WallpaperService {
#Override
public Engine onCreateEngine() {
return new MyEngine();
}
private class MyEngine extends Engine {
private boolean continueSocket = true;
MyEngine() {
new Thread() {
#Override
public void run() {
try {
LocalServerSocket server = new LocalServerSocket("MyAddress");
Log.d("SERVER READY", "Server is ready.");
while(continueSocket) {
LocalSocket receiver = server.accept();
if(receiver != null) {
InputStream input = receiver.getInputStream();
byte[] data = IOUtils.toByteArray(input);
Log.d("GOT DATA", new String(data));
}
}
server.close();
} catch (IOException ex) {
Log.wtf("IOEXCEPTION", ex);
}
}
}.start();
}
#Override
public void onDestroy() {
closeSocketServer();
super.onDestroy();
}
private void closeSocketServer() {
continueSocket = false;
try {
LocalSocket socket = new LocalSocket();
socket.connect(new LocalSocketAddress("MyAddress"));
socket.getOutputStream().write(new byte[0]);
socket.getOutputStream().close();
socket.close();
} catch (IOException ex) {
//
}
}
}
}
And in my Activity it can be as simple as this...
try {
LocalSocket sender = new LocalSocket();
sender.connect(new LocalSocketAddress("MyAddress"));
String data = "Hello world!";
Log.d("SENT DATA", data);
sender.getOutputStream().write(data.getBytes());
sender.getOutputStream().close();
sender.close();
} catch (IOException ex) {
Log.wtf("IOEXCEPTION", ex);
}
Logcat ends up looking like this:
D/SERVER READY﹕ Server is ready. (when the wallpaper starts up)
D/SENT DATA﹕ Hello world! (when the activity sends data)
D/GOT DATA﹕ Hello world! (when the wallpaper gets the data)
In your WallpaperService onCreateEngine:
IntentFilter intentFilter = new IntentFilter("your.package.your.action");
MyBroadcastReceiver mReceiver = new MyBroadcastReceiver(mRenderer);
LocalBroadcastManager.getInstance(getApplicationContext())
.registerReceiver(mReceiver, intentFilter);
In mRenderer's class:
public void receiveCommand(int i) {
Log.d("got", String.valueOf(i));
}
Receiver class:
public class MyBroadcastReceiver extends BroadcastReceiver {
private final MyRenderer _mRenderer;
public MyBroadcastReceiver(MyRenderer mRenderer) {
_mRenderer = mRenderer;
}
#Override
public void onReceive(Context context, Intent intent) {
_mRenderer.receiveCommand(intent.getExtra("msg", -1));
}
}
Now call from activity:
Intent in = new Intent();
in.setAction("your.package.your.action");
in.setExtra("msg", 42);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(in);
Related
I am working with images to send on the device, that works fine when I am using Async Task it works well but the problem is when I do it via Service it doesn't work and I get this error, "Unable to start Service with intent caused by "NetworkOnMainThreadException"". I am actually passing the byte[] from MainActivity to Service
I need to use service because OnReceive method of BroadcastReceiver cannot respond to Async task.
Thank you!
In the Mainfest.xml
<service android:name=".SendImageClientService"/>
MainActivity
private BroadcastReceiver wifiStateReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int wifiStateExtra = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
WifiManager.WIFI_STATE_UNKNOWN);
if(wifiStateExtra==WifiManager.WIFI_STATE_ENABLED){
sendingDrawableImage();
}else if(wifiStateExtra==WifiManager.WIFI_STATE_DISABLED){
Toast.makeText(context, "Please Check Your Internet Connection", Toast.LENGTH_SHORT).show();
}
}
};
private void sendingDrawableImage() {
drawable = (BitmapDrawable) imageView.getDrawable();
bitmap = drawable.getBitmap();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream);
byte[] array = byteArrayOutputStream.toByteArray();
Intent serviceIntent=new Intent(this,SendImageClientService.class);
serviceIntent.putExtra("byte",array);
this.startService(serviceIntent);
}
#Override
protected void onStart() {
super.onStart();
IntentFilter intentFilter = new
IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
registerReceiver(wifiStateReceiver, intentFilter);
}
Service class
public class SendImageClientService extends Service {
Handler handler = new Handler(Looper.getMainLooper());
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
byte[] bytesss=intent.getByteArrayExtra("byte");
Socket socket = new Socket("ip_address_here", 8888);
OutputStream out = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
dataOutputStream.write(bytesss);
handler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(SendImageClientService.this, "Image sent", Toast.LENGTH_SHORT).show();
}
});
dataOutputStream.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
}
}
the documentation says:
Caution: A service runs in the main thread of its hosting process; the
service does not create its own thread and does not run in a separate
process unless you specify otherwise. If your service is going to
perform any CPU-intensive work or blocking operations, such as MP3
playback or networking, you should create a new thread within the
service to complete that work. By using a separate thread, you can
reduce the risk of Application Not Responding (ANR) errors, and the
application's main thread can remain dedicated to user interaction
with your activities.
https://developer.android.com/guide/components/services
so, you need to create new thread in your service for a long operation.
Also, you need to know, that Services are using for a long background tasks and IntentServices are using for a short background tasks. But IntentService work in separate thread.
You can use something like this:
new Handler().post(runnable)
where runnable is your long operation action (like internet action or database action). Handler is a specific class for working with thread in android.
Edit.
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
try {
handler.post(new Runnable() {
#Override
public void run() {
byte[] bytesss=intent.getByteArrayExtra("byte");
Socket socket = new Socket("ip_address_here", 8888);
OutputStream out = socket.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(out);
dataOutputStream.write(bytesss);
dataOutputStream.close();
out.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
return START_STICKY;
}
In my app, I am getting my messages instantly from my server via pusher. I have created a service designated to handle connections and firing broadcast messages to other activities in my app.
The problem that I face now is to have this service run in a new thread to have it still run even when my app goes to the background. I've found from this that I should create and connect it to the "service thread", but I cannot find examples for it with pusher.
If anyone can, could you please provide an example to do so? If not, insights to writing code with these "service threads" would be helpful as well. Thanks in advance for the help :D
PusherService.java
public class PusherService extends Service {
private static final String TAG = "PusherService";
private Pusher pusher = new Pusher("myKey");
private Channel channel = pusher.subscribe("cafe_channel");
private JSONObject pusherJSONObj;
private Order order;
public PusherService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//this service will run until we stop it
setupPusher();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
}
private void setupPusher() {
Log.d(TAG, System.currentTimeMillis()+"");
channel.bind("customer_order", new SubscriptionEventListener() {
#Override
public void onEvent(String channelName, String eventName, final String data) {
Intent broadcastIntent = new Intent();
try {
pusherJSONObj = new JSONObject(data);
order = new Order(pusherJSONObj);
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "success");
broadcastIntent.putExtra("order", order);
} catch (JSONException e) {
e.printStackTrace();
Log.d("Pusher", "conversion failed");
broadcastIntent.setAction("customer_order");
broadcastIntent.putExtra("message", "JSON conversion error");
}
sendBroadcast(broadcastIntent);
}
});
pusher.connect();
}
}
OrdersActivity.java
private BroadcastReceiver pusherReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equalsIgnoreCase("customer_order")) {
adapter.newOrder((Order) intent.getParcelableExtra("order"));
}
}
};
It turns out that multithreading on one process does not solve my problem.
So instead, I split the service into a new process, which will keep the service running independent of the status of the main thread & process. Tested and found that service does not stall when my activities go background.
I'm developing an Android application.
This application will have a server to start a DatagramSocket as a server. It will wait for incoming message. When the socket get a message I will process it.
To start a UDP Server socket I'm going to use a Local Service. This service will have a worker thread where I'm going to listen to incoming messages.
This is my unfinished Local Service implementation:
public class UDPSocketBackgroundService extends Service
{
private static final String TAG = "UDPSocketBackgroundService";
private ThreadGroup myThreads = new ThreadGroup("UDPSocketServiceWorker");
private Handler mServiceHandler;
#Override
public void onCreate()
{
super.onCreate();
Log.v(TAG, "in onCreate()");
}
#Override
public IBinder onBind(Intent arg0)
{
try
{
new Thread(myThreads, new UDPServerThread("X", 8888)).start();
}
catch (IOException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
And this is my also unfinished Worker Thread implementation:
public class UDPServerThread extends Thread
{
private static final int MESSAGE_SIZE = 256;
protected DatagramSocket socket = null;
protected boolean end = false;
public UDPServerThread(String serverName, int port) throws IOException
{
super(serverName);
socket = new DatagramSocket(port);
}
public void run()
{
while (!end)
{
try
{
byte[] buf = new byte[MESSAGE_SIZE];
// Wait an incoming message.
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
// TODO: Notify Service with packet received
}
catch (IOException e)
{
// TODO Mensaje de error.
e.printStackTrace();
}
}
}
}
Those classes have their own file (they are on different files).
Here:
socket.receive(packet);
//TODO: Notify Service with packet received
How can I notify service that we have received a packet? I want to send to service that packet also.
Here there is an example on how to communicate from Main thread to worker thread. But, I don't need that, I'm looking for an example on how to communicate from worker thread to service.
I've found this example, but I don't understand it very well because on that example both classes are declare it on the same file.
As you can see, I'm a newbie on Android development.
If you know a better approach, please tell me.
When you create the UDPServerThread, you could pass in a reference to the UDPSocketBackgroundService and then call a method on it (processPacket() for example) when packets are received. This processPacket() method will need to use some sort of synchronization.
Here's a small code excerpt of the related functions:
public class UDPSocketBackgroundService extends Service
{
....
#Override
public IBinder onBind(Intent arg0)
{
try
{
new Thread(myThreads, new UDPServerThread(this, "X", 8888)).start();
// Notice we're passing in a ref to this ^^^
}
...
}
public void processPacket(DatagramPacket packet)
{
// Do what you need to do here, with proper synchronization
}
}
public class UDPServerThread extends Thread
{
private static final int MESSAGE_SIZE = 256;
protected DatagramSocket socket = null;
protected boolean end = false;
protected UDPSocketBackgroundService = null;
public UDPServerThread(UDPSocketBackgroundService service, String serverName, int port) throws IOException
{
super(serverName);
this.service = service;
socket = new DatagramSocket(port);
}
...
public void run()
{
while (!end)
{
try
{
byte[] buf = new byte[MESSAGE_SIZE];
// Wait an incoming message.
DatagramPacket packet = new DatagramPacket(buf, buf.length);
socket.receive(packet);
service.processPacket(packet);
}
...
}
...
}
}
Notice that going this approach, the UDPSocketBackgroundService is now "tightly coupled" with the UDPServerThread. Once you get this working, you may consider refactoring it with a more elegant design where there is less coupling, but for now this should get you going :)
This is a common question, and I have read up on the various ways of handling it, but each on seems to fall short for what I am trying to do, which is essentially be a good OO-Citizen.
I have an Activity that invokes a CommunicationManager, which basically polls a TCP socket for data. When the CommunicationManager receives data, it throws a custom event (containing the string it just fetched), which is handled by the Activity. I am doing this, A) because other classes will depend on that data, not just the Activity, and B) because the polling is asynchronous, and should fire an event when it receives results.
My problem lies in that I need to surface those results into a TextView on the UI. I have the polling mechanism all set up, it fires every 1000ms, and invokes the event handler on the Activity. However, the UI never updates.
Assumedly this is a thread issue and the UI thread is not the one getting the change to the TextView, but how do I do this?? I have tried using a Handler, but am not sure where to put it, and when I did get it compiling it never updated the UI.
This seems relatively trivial if everything was done within the Activity, but adding in this other class (CommunicationManager) and the event is making it very confusing for me.
Here is what I have so far:
ACTIVITY (polling is invoked by clicking a button on the UI):
public void onClick(View v) {
if (v.getId() == R.id.testUDPBtn) {
statusText.setText("");
commMgr = new CommunicationManager();
commMgr.addEventListener(this);
MediaPositionPollThread poller = new MediaPositionPollThread(commMgr);
poller.startPolling();
}
}
#Override
public void handleMediaPositionFoundEvent(MediaPositionFoundEvent e) {
statusText.append(e.userData);
}
THREAD:
class MediaPositionPollThread extends Thread {
private CommunicationManager commManager;
private static final String TAG = "MediaPositionPollThread";
private boolean isPolling = false;
public MediaPositionPollThread(CommunicationManager cm) {
commManager = cm;
}
public void startPolling() {
isPolling = true;
this.run();
}
public void stopPolling() {
isPolling = false;
}
#Override
public void run() {
while (isPolling) {
try {
commManager.getCurrentMediaPosition();
Thread.sleep(1000);
}
catch (InterruptedException e) {
Log.d(TAG, "EXCEPTION: " + e.getMessage());
}
}
}
}
COMMUNUCATION MANAGER:
public void getCurrentMediaPosition() {
PrintWriter outStream;
BufferedReader inStream;
String resultString = "";
try {
outStream = new PrintWriter(tcpSocket.getOutputStream(), true);
outStream.println("GET?current_pts");
inStream = new BufferedReader(new InputStreamReader(tcpSocket.getInputStream()));
resultString = inStream.readLine();
fireEventWithData(resultString);
} catch (Exception e) {
e.printStackTrace();
}
}
public synchronized void addEventListener(MediaPositionFoundEventListener listener) {
_listeners.add(listener);
}
public synchronized void removeEventListener(MediaPositionFoundEventListener listener) {
_listeners.remove(listener);
}
private synchronized void fireEventWithData(String outputString) {
MediaPositionFoundEvent evt = new MediaPositionFoundEvent(this);
evt.userData = outputString;
Iterator<MediaPositionFoundEventListener> i = _listeners.iterator();
while(i.hasNext()) {
((MediaPositionFoundEventListener) i.next()).handleMediaPositionFoundEvent(evt);
}
}
So I have the Activity making a thread that gets executed every second, calling CommunicationManager >> getCurrentMediaPosition, which in turn fires the MediaPositionFoundEvent, which is handled by the Activity and updates the TextView (statusText) on the screen.
Everything works except the screen not updating. I have tried runOnUiThread, and a Handler, but am obviously not getting it right.
Thanks in advance for any insight or solutions!
In your Activity class, add a private Handler _handler,
Initialize it in your onCreate Activity method,
and change your handleMediaPositionFoundEvent method to
#Override public void handleMediaPositionFoundEvent(MediaPositionFoundEvent e) {
_handler.post(new Runnable(){
public void run(){
statusText.append(e.userData);
});
}
}
It looks like your blocking the UI thread with your custom Thread. Please update this method to call start() vs run().
public void startPolling() {
isPolling = true;
this.start();
}
I just want to know how to launch an Activity in a try/catch bloc, i made this
public void onReceive(Context context, Intent intent) {
SipAudioCall incomingCall = null;
try {
Intent monIntent = new Intent(this,dialog.class);
startActivity(monIntent);
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onRinging(SipAudioCall call, SipProfile caller) {
But i have errors:
constructor Intent(IncomingCallReceiver, Class<dialog>) is not defined
Method startActivity(Intent) is undefined for the type IncomingCallReceiver
I want to show an alertdialog when having a call.
How can i solve this problem?
Thank you very much.
Whole class:
public class IncomingCallReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
SipAudioCall incomingCall = null;
try {
SipAudioCall.Listener listener = new SipAudioCall.Listener() {
#Override
public void onRinging(SipAudioCall call, SipProfile caller) {
try {
call.answerCall(30);
} catch (Exception e) {
e.printStackTrace();
}
}
};
SIPCommunicator wtActivity = (SIPCommunicator) context;
incomingCall = wtActivity.manager.takeAudioCall(intent, listener);
incomingCall.answerCall(30);
incomingCall.startAudio();
incomingCall.setSpeakerMode(true);
if(incomingCall.isMuted()) {
incomingCall.toggleMute();
}
wtActivity.call = incomingCall;
wtActivity.updateStatus(incomingCall);
} catch (Exception e) {
if (incomingCall != null) {
incomingCall.close();
}
}
}
}
The problem you are facing is not the try catch block, it's the fact that you are starting activity in a listener implementation. Substitute
Intent monIntent = new Intent(this,dialog.class);
with
Intent monIntent = new Intent(<Name of this class>.this,dialog.class);
and
startActivity(monIntent);
with
<Name of this class>.this.startActivity(monIntent);
By <Name of this class> I mean the head class where you are writing your code.
By the looks of it, you are launching a new thread from within a try/catch block. This is bad practice as the thread can fail should an exception be being thrown.
Instead, declare a variable before the value, and assign to it on the line preceding the catch statement.
Also assign to it if the exception is raised, but a different value.
Then launch the thread outside the try/catch block, based on the successful evaluation of the variable.
Use callbacks to trap failed threads, not exceptions.
int x = 0;
try
{
/* Do some logic here */
x = 1; // we have success
}
catch(Exception e)
{
x = -1; // failure
}
if (x > 0)
{
Intent monIntent = new Intent(this,dialog.class);
startActivity(monIntent);
...
}
As a newbie I would like to state my view:-
Try putting some error messages to find out where the error is occurring. Also check the log file.
Example of try catch:-
try
{
//some code
//Toast message on success
Toast.makeText(this, R.string.msgStatusUpdatedSuccefully, Toast.LENGTH_LONG).show();
}
catch (TwitterException e)
{
//Toast message on failure
Toast.makeText(this, R.string.msgStatusUpdateFailed, Toast.LENGTH_LONG).show();
e.printStackTrace();
}
I would check if my imports are all fine for all the api's that I am using.
Also does your main public class looks like the following to resolve the undefined error:-
public class StatusActivity extends Activity implements OnClickListener{}