Hi I make Android application for Xamarin. I have created a simple application in the Android studio. so any answer welcome either Java or C#
I have a service(GPS service) and 2 Activities.
MainActivity - GPS service are well connected with the broadcast. I hope MainActivity -> Another activity real time GPS point.(It is also okay to send from the GPS service to another activity.) but it is fail...app is dead..
MainActivity code
private void RegisterService()
{
_gpsServiceConnection = new GPSServiceConnection(_binder);
_gpsServiceIntent = new Intent(Android.App.Application.Context, typeof(GPS.GPSService));
BindService(_gpsServiceIntent, _gpsServiceConnection, Bind.AutoCreate);
}
private void RegisterBroadcastReceiver()
{
IntentFilter filter = new IntentFilter(GPSServiceReciever.LOCATION_UPDATED);
filter.AddCategory(Intent.CategoryDefault);
_receiver = new GPSServiceReciever();
RegisterReceiver(_receiver, filter);
}
private void UnRegisterBroadcastReceiver()
{
UnregisterReceiver(_receiver);
}
public void UpdateUI(Intent intent)
{
LatLng_txt.Text = intent.GetStringExtra("Location");
Lat = intent.GetDoubleExtra("Lat", 0.0);
Lng = intent.GetDoubleExtra("Lng", 0.0);
}
protected override void OnResume()
{
base.OnResume();
RegisterBroadcastReceiver();
}
protected override void OnPause()
{
base.OnPause();
UnRegisterBroadcastReceiver();
}
[BroadcastReceiver]
internal class GPSServiceReciever : BroadcastReceiver
{
public static readonly string LOCATION_UPDATED = "LOCATION_UPDATED";
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action.Equals(LOCATION_UPDATED))
{
Instance.UpdateUI(intent);
}
}
}
GPS Service code
public void OnLocationChanged(Location location)
{
try
{
_currentLocation = location;
if (_currentLocation == null)
{
_location = "Unable to determine your location.";
}
else
{
_location = String.Format("{0}, {1}", _currentLocation.Latitude, _currentLocation.Longitude);
Geocoder geocoder = new Geocoder(this);
IList<Address> addressList = geocoder.GetFromLocation(_currentLocation.Latitude,
_currentLocation.Longitude, 10);
Address addressCurrent = addressList.FirstOrDefault();
if (addressCurrent != null)
{
StringBuilder deviceAddress = new StringBuilder();
for (int i = 0; i < addressCurrent.MaxAddressLineIndex; i++)
{
deviceAddress.Append(addressCurrent.GetAddressLine(i)).AppendLine(",");
}
_address = deviceAddress.ToString();
}
else
{
_address = "Unable to determine the address.";
}
IList<Address> source = geocoder.GetFromLocationName(_sourceAddress, 1);
Address addressOrigin = source.FirstOrDefault();
var coord1 = new LatLng(addressOrigin.Latitude, addressOrigin.Longitude);
var coord2 = new LatLng(addressCurrent.Latitude, addressCurrent.Longitude);
var distanceInRadius = Utils.HaversineDistance(coord1, coord2, Utils.DistanceUnit.Miles);
_remarks = string.Format("Your are {0} miles away from your original location.", distanceInRadius);
Intent intent = new Intent(this, typeof(MainActivity.GPSServiceReciever));
intent.SetAction(MainActivity.GPSServiceReciever.LOCATION_UPDATED);
intent.AddCategory(Intent.CategoryDefault);
intent.PutExtra("Location", _location);
intent.PutExtra("Lat", _currentLocation.Latitude);
intent.PutExtra("Lng", _currentLocation.Longitude);
SendBroadcast(intent);
}
}
catch
{
_address = "Unable to determine the address.";
}
}
Is not there a good way?
I understood your problem.But dont know more about GPS etc.I have faced the same problem when I was creating Music App.
Two activities were there and one service.And successfully got real time song position and song data from both activities.
My MainActivity has
ServiceConnection sc=null;
public static PlayerService ps;
And gets its value in onCreate of MainActivity
sc=new ServiceConnection(){
#Override
public void onServiceConnected(ComponentName p1, IBinder p2)
{
PlayerService.Getters getters=(PlayerService.Getters) p2;
ps=getters.getService();
}
#Override
public void onServiceDisconnected(ComponentName p1)
{
// TODO: Implement this method
}
};
Then PlayerService.Getters class is
public class Getters extends Binder
{
public PlayerService getService()
{
return PlayerService.this;
}
}
PlayerService has
#Override
public IBinder onBind(Intent p1)
{
return new Getters();
}
getService of Getters gives the object of PlayerService to my MainActivity.
Now I can get real time values of service variables and methods using static ps from multiple activities.
In order to send data or information from Service to Activity, you'll need to use Messenger API. This API will allow you to create an inter process communication (IPC) i.e. a communication link between two or more processes. In Android, Activity and Service are two separate processes, so you can use the IPC technique to establish a communication link in between them.
In the IPC technique, there are two ends, the Server end and the Client end. The Service acts as the Server and Activity acts as the Client.
Note: Service will only be able to communicate with one Activity at a time.
Messenger allows for the implementation of message-based communication across processes by help of Handlers.
Handler is a that allows you to send and process these messages.
Steps for implementing a Messenger:
Step 1. Service implements a Handler which receives the callbacks from the Activity
Step 2. The Handler then creates a Messenger object which further on creates an IBinder that the Service returns to the Activity.
Step 3. Activity then uses the IBinder to instantiate the Messenger, which the Activity uses to send messages to the Service.
Step 4. The Service receives the messages in the Handler created in the 1st step.
Lets now understand it with an example:
Create a Handler in the Service like this:
class ServiceHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
default:
super.handleMessage(msg);
}
}
}
Now, add the Messenger object along with onBind() method to the Service as mentioned in 2nd step above:
final Messenger messenger = new Messenger(new ServiceHandler());
#Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
In the Activity, we will create a ServiceConnection to fetch the iBinder from the Service to instantiate the Messenger object as mentioned in the 3rd step above.
Messenger messenger;
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder iBinder) {
messenger = new Messenger(iBinder);
}
public void onServiceDisconnected(ComponentName className) {
}
};
Bind the Service to the Activity by help of the ServiceConnection created above:
bindService(new Intent(this, MessengerService.class), serviceConnection,
Context.BIND_AUTO_CREATE);
To send messages to the Service from the Activity, use the send() method of the Messenger object.
If you want to receive messages from the Service in the Activity, you need to create a Messenger in the Activity along with a Handler and use the replyTo parameter of the Messenger to receive messages to the respective Handler.
Related
I need write a service which will update the list in MainActivity every 30sec. I use MVVM with ViewModel and LiveData and so my Service class looks like this:
public class ArticleJobService extends JobService {
public static String TAG = "ArticleJobService";
private Context context = this;
#Override
public boolean onStartJob(JobParameters jobParameters) {
Log.d(TAG, "onStartJob");
MainActivity.PAGE_NUMBER++;
LiveData<List<Article>> liveArticles = ArticleRepository.getInstance(getApplication()).getArticles(MainActivity.PAGE_NUMBER);
liveArticles.observeForever(new Observer<List<Article>>() {
#Override
public void onChanged(#Nullable List<Article> articles) {
Log.d(TAG, "onStartJob - onChanged!!!!!!");
liveArticles.removeObserver(this);
NotificationUtils.showNotification(context, articles.get(0).getSectionName(), articles.get(0).getWebTitle());
jobFinished(jobParameters, true);
}
});
return true;
}
}
Class for my notification:
public static void showNotification(Context context, String section, String title) {
PendingIntent contentPendingIntent = PendingIntent.getActivity
(context, REQUEST_CODE, new Intent(context, MainActivity.class),
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationManager manager =
(NotificationManager) context.getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
manager.createNotificationChannel(createNotificationChannel(context));
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(section)
.setContentText(title)
.setContentIntent(contentPendingIntent)
.setSmallIcon(R.drawable.app_icon)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setDefaults(NotificationCompat.DEFAULT_ALL)
.setAutoCancel(true);
manager.notify(0, builder.build());
}
When Onchanged in JobService works I get the list and show a notification. Notification opens MainActivity which makes new call to api as it always did. What changes do I have to make in order the MainActivity to show the list that I got from the service??? I really can't tie this up together.
I heard of IPC but wouldn't do that, I want some simpler practice which I sure exists which I just don't know about.
Also, there are two cases: Notification came and MainActivity is open, app is open but MainActivity is not in the foreground and app is on the background or closed. How should I handle each of these cases?
See also piece of code from MainActivity onCreate:
mArticleViewModel = ViewModelProviders.of(this).get(ArticleViewModel.class);
mArticleViewModel.getArticleList(PAGE_NUMBER).observe(this, articles -> {
Log.d(TAG, "List<Result> onChanged!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
mProgressBar.setVisibility(View.GONE);
mProgressBarMain.setVisibility(View.GONE);
mIsLoading = false;
mArticles = articles;
Please provide the best practices for this task, I know it's very common I just do it first time and using LiveData makes it way more complicated.
Here is Also Repository code:
public static ArticleRepository getInstance(Application application){
if(INSTANCE == null){
return new ArticleRepository(application);
}
return INSTANCE;
}
private ArticleRepository(Application application) {
Log.d(TAG, "ArticleRepository constructor");
mContext = application;
mArticles = new MutableLiveData<>();
ArticleRoomDatabase db = ArticleRoomDatabase.getInstance(application);
mArticleDao = db.articleDao();
}
public LiveData<List<Article>> getArticles(int page) {
Log.d(TAG, "getArticles");
if (NetworkUtils.isOnline(mContext)) {
Log.d(TAG, "isOnline");
mArticles = loadFromNetwork(page);
} else {
Log.d(TAG, "is NOT Online");
mArticles = loadFromDB(page);
}
}
You have this problem specifically because your Repository implementation is incorrect.
public LiveData<List<Article>> getArticles(int page) {
Log.d(TAG, "getArticles");
if (NetworkUtils.isOnline(mContext)) {
Log.d(TAG, "isOnline");
mArticles = loadFromNetwork(page);
} else {
Log.d(TAG, "is NOT Online");
mArticles = loadFromDB(page);
}
}
If you check the code for NetworkBoundResource, the trick is that you have a single LiveData that binds together the ability to both load from network, and to load from database.
In your case, you are replacing the database's auto-updating query results whenever you have network access - which is why you can't update the MainActivity.
The easiest way (without using a MediatorLiveData) is to have two separate functions on Repository: one for fetchFromNetwork, and one for fetchFromDatabase. The MainActivity should always fetch from database, while the Service always triggers load from network (and inserts it directly into database via a Dao).
This way, the observe function in MainActivity will receive the latest data when Service inserts the data into DB on background thread.
I am working on datasync service, i'm getting the json response from webserver. my question is how to pass that response to the activity from which i'm calling the service.
You can use EventBus and send the data to the desired Activity by Subscribing to the Event.
For Eg:
compile 'org.greenrobot:eventbus:3.0.0'
Eventbus Class
public class DataSyn {
public final List<YourModel> YourModel;
public DataSyn(List<YourModel> YourModel) {
this.YourModel = YourModel;
}
}
Send Data from your response :
EventBus.getDefault().post(new DataSyn(yourdataList));
Subscribe and Receive the Data wherever you need:
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onDataRecevied(DataSyn event) {
if (event.YourModel != null) {
populateData(event.YourModel);
}
}
Above is the most easiest way to share data
You can subscribe to a broadcast receiver in your activity with receiving a message, or you may look in push notification field...
I'm trying to create a native extension which can receive broadcasts, sent from a native android am as intent broadcasts.
The sending part works, I've tested this with a native app that has a broadcast receiver, but I cant get it to work in the native extension.
Here's what I have so far:
Here the java side of the ANE
public class ReceiverPhidget extends BroadcastReceiver {
private FREContext mFREContext;
public ReceiverPhidget(FREContext mFREContext) {
this.mFREContext = mFREContext;
}
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(IntentsKeys.INTENT_PHIDGET_CONNECTED)){
//Send listener in ANE project with message that phidget connected (not must)
System.out.println("Phidget connected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_CONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_DISCONNECTED)){
//Send listener in ANE project with message that phidget disconnected (not must)
System.out.println("Phidget disconnected");
mFREContext.dispatchStatusEventAsync("Yes", Keys.KEY_DISCONNECTED);
} else
if (action.equals(IntentsKeys.INTENT_PHIDGET_GAIN_TAG)){
//Send listener with data in ANE project with message that phidget gain receive
String message = intent.getStringExtra(IntentsKeys.INTENT_PHIDGET_EXTRA_DATA);
System.out.println("Phidget gain message: " + message);
Log.d("TAG FOUND", message);
mFREContext.dispatchStatusEventAsync(message, Keys.KEY_TAG_GAIN);
}
}
public static IntentFilter getIntentFilter(){
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_CONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_DISCONNECTED);
intentFilter.addAction(IntentsKeys.INTENT_PHIDGET_GAIN_TAG);
return intentFilter;
}
}
And the FREExtension
public class ReceiverExtension implements FREExtension {
private ReceiverPhidget mReceiverPhidget;
private ReceiverExtensionContext mContext;
#Override
public void initialize() {
mReceiverPhidget = new ReceiverPhidget(mContext);
mContext.getActivity().registerReceiver(mReceiverPhidget, ReceiverPhidget.getIntentFilter());
}
#Override
public FREContext createContext(String s) {
return mContext = new ReceiverExtensionContext();
}
#Override
public void dispose() {
mContext.getActivity().unregisterReceiver(mReceiverPhidget);
}
}
And here is the flash library side of the ANE
package nl.mediaheads.anetest.extension {
import flash.events.EventDispatcher;
import flash.events.StatusEvent;
import flash.external.ExtensionContext;
public class RFIDController extends EventDispatcher {
private var extContext:ExtensionContext;
private var channel:int;
private var scannedChannelList:Vector.<int>;
public function RFIDController() {
extContext = ExtensionContext.createExtensionContext(
"nl.mediaheads.anetest.exntension.RFIDController", "");
extContext.addEventListener(StatusEvent.STATUS, onStatus);
}
private function onStatus(event:StatusEvent):void {
if (event.level == EventKeys.KEY_TAG_GAIN) {
dispatchEvent (new TagEvent(TagEvent.TAG_GAINED, event.code) );
}
}
}
}
And here is my test mobile project class to test the ANE
package
{
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.text.TextField;
import nl.mediaheads.anetest.extension.RFIDController;
[SWF(width="1280", height="800", frameRate="60", backgroundColor="#ffffff")]
public class AneTestApp extends Sprite
{
private var tf:TextField;
private var rc:RFIDController;
public function AneTestApp()
{
super();
// support autoOrients
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.color = 0xFFFFFF;
addEventListener(Event.ADDED_TO_STAGE, onAdded);
}
private function onAdded(event:Event):void {
//
tf = new TextField();
tf.width = 200;
tf.height = 50;
tf.x = 10;
tf.y = 64;
tf.mouseEnabled = false;
tf.background = true;
tf.backgroundColor = 0xF50000;
addChild(tf);
rc = new RFIDController();
tf.text = "test 1";
this.addEventListener( TagEvent.TAG_GAINED , onTagAdded);
tf.text = "test 2";
//
}
private function onTagAdded(event:TagEvent):void
{
tf.text = event.params;
}
}
}
I have signed the ANE accordingly, I also signed the test app it's self.
I have a Log.d in the java part of the ANE which should pop up on log cat but it doesn't, also the textfield just becomes blank as soon as I initialized the RFIDController even without added the event listener.
If you need any more code or information to help me solve this problem feel free to ask.
I could really use some help because I'm completely lost, I've followed multiple tutorials and guide on how to do this, I should have done everything correctly, but I clearly have not.
UPDATE: 1
The extension xml
<extension xmlns="http://ns.adobe.com/air/extension/3.5">
<id>nl.mediaheads.anetest.exntension.RFIDController</id>
<versionNumber>0.0.1</versionNumber>
<platforms>
<platform name="Android-ARM">
<applicationDeployment>
<nativeLibrary>AneTest.jar</nativeLibrary>
<initializer>nl.mediaheads.anetest.ReceiverExtension</initializer>
<finalizer>nl.mediaheads.anetest.ReceiverExtension</finalizer>
</applicationDeployment>
</platform>
</platforms>
</extension>
UPDATE 2:
I fixed it, it was an context issue together with that flash somehow clean my custom event so I used status event to parse from the flash side of the ANE to the air application itself.
Currently you are creating your receiver at the initialisation point of the extension which will most likely be called before the context creation, so your context may be null at that point and causing your errors.
Try moving the creation of your ReceiverPhidget to the constructor of your ReceiverExtensionContext. Something like the following (I haven't tested this):
public class ReceiverExtensionContext extends FREContext
{
private ReceiverPhidget mReceiverPhidget;
public ReceiverExtensionContext()
{
mReceiverPhidget = new ReceiverPhidget( this );
getActivity().registerReceiver( mReceiverPhidget, ReceiverPhidget.getIntentFilter() );
}
#Override
public Map<String, FREFunction> getFunctions()
{
Map<String, FREFunction> functionMap = new HashMap<String, FREFunction>();
return functionMap;
}
#Override
public void dispose()
{
getActivity().unregisterReceiver( mReceiverPhidget );
}
}
This question already has answers here:
How can I update information in an Android Activity from a background Service
(5 answers)
Closed 9 years ago.
I have an app that receives gcm push notifications. I also have a check in place that if the app is currently open, it does not create the notification. In Android, is it possible in my push service to tell the current activity (IF Available/connected), that a new notification has arrived, refresh your list with the new content? If this is possible, I believe I am on the right path with using IBinders on my service. The thing about that is I am confused on how the Service Calls the Activity (I understand vice verse). Thanks in advance if anyone could help!
Just to be clear. I am trying to tell the activity about a new push message.
Service
public class LocalBinder extends Binder {
GcmIntentService getService() {
return GcmIntentService.this;
}
}
#Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private final IBinder mBinder = new LocalBinder();
Client (Activity)
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. Because we have bound to a explicit
// service that we know is running in our own process, we can
// cast its IBinder to a concrete class and directly access it.
mBoundService = ((GcmIntentService.LocalBinder)service).getService();
// Tell the user about this for our demo.
Toast.makeText(MainActivity.this, "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.
// Because it is running in our same process, we should never
// see this happen.
mBoundService = null;
Toast.makeText(MainActivity.this, "Disconnected",
Toast.LENGTH_SHORT).show();
}
};
void doBindService() {
// Establish a connection with the service. We use an explicit
// class name because we want a specific service implementation that
// we know will be running in our own process (and thus won't be
// supporting component replacement by other applications).
bindService(new Intent(MainActivity.this,
GcmIntentService.class), mConnection, Context.BIND_AUTO_CREATE);
mIsBound = true;
}
void doUnbindService() {
if (mIsBound) {
// Detach our existing connection.
unbindService(mConnection);
mIsBound = false;
}
}
#Override
protected void onDestroy() {
super.onDestroy();
doUnbindService();
}
Yes it's possible.
In your service class have a variable of type YourActivity
MyActivity myActivity = null;
In your onServiceConnected method set this myActivity variable on the service, obtained through the binder.
mBoundService.myActivity = MyActivity.this; // or something similar
Now your service has a pointer to your activity!!! YEah!!
Inside your activity, create a function, the body of this function should refresh the UI with data.
Finally, when the service detects new data, call:
if (myActivity)
myActivity.refreshMyData();
When unbinding, remember to set the myActivity variable to null, otherwise the previous code will fail.
I've already bound an activity to my service following this tutorial.
http://developer.android.com/guide/components/bound-services.html
I'm able to call service functions, but what if I want to for example, change some of my textviews or disable some of the toggle buttons because of work done on the service (and from the service). Would there be an easy to way to do this?
You can use messages to send information between activities and services. This is an easy way to send simple data, but may not be the best option if you need to send data very frequently, or send complicated data. This is an example of some code I have in one of my apps with a service and an activity which communicate:
Code in the activity:
//this is where you set what you want to happen
class IncomingHandler extends Handler {
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
//this switch reads the information in the message (usually just
//an integer) and will do something depending on which integer is sent
case 1: do_something();
case 2: do_something_2(); //etc.
default:
super.handleMessage(msg);
}
}
}
final Messenger myMessenger = new Messenger(new IncomingHandler());
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
myService = new Messenger(service);
myCallbackText = (TextView)findViewById(R.id.tv01); //This is a text view which will display status information as needed
myCallbackText.setText("Attached.");
try {
Message msg = Message.obtain(null,
1);
msg.replyTo = mMessenger; //here we send an instance of our messenger implementation as the replyTo address
mService.send(msg);
msg = Message.obtain(null,
3, this.hashCode(), 0);
mService.send(msg); //send a message with the value "3"
} catch (RemoteException e) {
//nothing you can do if the server isn't active
}
Toast.makeText(Service_testActivity.this, R.string.remote_service_connected,
Toast.LENGTH_SHORT).show();//confirmation that the connection happened successfully
}
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 = (TextView)findViewById(R.id.tv01);//same textview as before
mCallbackText.setText("Disconnected.");
Toast.makeText(Service_testActivity.this, R.string.remote_service_disconnected,
Toast.LENGTH_SHORT).show();
}
};
Code in the service:
In the service, you will want to have code (very similar to the code in the activity) to receive a message and save the msg.replyTo field as a Messenger object. There is an example somewhere which will have you make an object and then use an IncomingHandler like this:
ArrayList<Messenger> mClients = new ArrayList<Messenger>();
class IncomingHandler extends Handler {
#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;
default:
super.handleMessage(msg);
}
}
}
This can allow your service to keep track of multiple clients at once and send messages to specified clients. To send a message simply use something like this:
mClients.get(1).send(Message.obtain(null, 3, new Random().nextInt(), 0));
//sends a message to the first client saved in the list