My IntentService is responsible for synching the local SQLite data to Firebase collection when Wifi network is available, however if I am trying this exact same code from my repository class its working perfectly but not working(Firestore add) from background IntentService.
Below is my code, can anybody help me with this. I'm stuck :(
public class DataSyncService extends IntentService {
private NotesDao mDao;
private FirebaseFirestore mFirestore;
public DataSyncService() {
super("DataSyncService");
}
#Override
protected void onHandleIntent(Intent intent) {
mDao = Room.databaseBuilder(getApplicationContext(), NotesDatabase.class, Constants.DATABASE_NAME)
.build()
.getNotesDao();
FirebaseApp firebaseApp = FirebaseApp.initializeApp(getApplicationContext());
if (firebaseApp != null) {
mFirestore = FirebaseFirestore.getInstance(firebaseApp);
sync();
}
}
/**
* Sync the current local persistence data with the network
*/
public void sync() {
Thread thread = new Thread(new Runnable() {
#Override
public void run() {
final List<Notes> offlineData = mDao.fetch();
for (Notes notes :
offlineData) {
Map<String, Object> note = new HashMap<>();
note.put(Constants.NOTE_TITLE, notes.getTitle());
note.put(Constants.NOTE_DESCRIPTION, notes.getDescription());
note.put(Constants.NOTE_TIMESTAMP, notes.getTimestamp());
note.put(Constants.NOTE_IMAGE_URL, notes.getImageUri());
mFirestore.collection(Constants.NOTES_COLLECTION).document(notes.getId()).set(note, SetOptions.merge());
}
}
});
thread.start();
}
}
Related
On PostsByLabelViewModel it have MutableLiveData<String> token this token is changed every scrolling of recyclerView, I need to observe on it in the PostsByLabelViewModel not from UI, because I tried to change it in recyclerView.addOnScrollListener and the app is freezing and hanged. Here's the code:
public class PostsByLabelViewModel extends ViewModel {
public static final String TAG = "PostsByLabelViewModel";
public MutableLiveData<PostList> postListMutableLiveData = new MutableLiveData<>();
public MutableLiveData<String> finalURL = new MutableLiveData<>();
public MutableLiveData<String> token = new MutableLiveData<>();
public void getPostListByLabel() {
Log.e(TAG, finalURL.getValue());
PostsByLabelClient.getINSTANCE().getPostListByLabel(finalURL.getValue()).enqueue(new Callback<PostList>() {
#Override
public void onResponse(Call<PostList> call, Response<PostList> response) {
PostList list = response.body();
if (list.getItems() != null) {
Log.e(TAG, list.getNextPageToken());
token.setValue(list.getNextPageToken());
postListMutableLiveData.setValue(list);
}
}
#Override
public void onFailure(Call<PostList> call, Throwable t) {
}
});
}
}
I see there's an observe method on the ViewModel and I tried to use it like this
token.observe(PostsByLabelViewModel.this, new Observer<String>() {
#Override
public void onChanged(String s) {
token.setValue(s);
}
});
but I got runtime error
error: incompatible types: PostsByLabelViewModel cannot be converted to LifecycleOwner
token.observe(PostsByLabelViewModel.this, new Observer<String>() {
So how can I observe on the token on every change?
I need to observe on it in the PostsByLabelViewModel not from UI.
You might use observeForever. Just don't forget to call removeObserver when it is no longer needed.
...the app is freezing and hanged.
You're calling PostsByLabelClient.getINSTANCE().getPostListByLabel(finalURL.getValue()).enqueue on the main thread. Move it to a background thread.
I am having troubles understanding how to find a solution for my problems with threads in Android. So basically the current (simplified) code below is running on the main thread and that's causing me some issues because methods calculateMeanMagnitude() and predict() are slow and hence block the UI as expected.
What I would like to do is to compute those two functions in a separate thread and once I am done call updateData() in my UI thread.
I am really not sure how to do this both from a syntax point of view but also how to avoid busy waiting before updateData() since that would also block the UI thread.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
}
private double calculateMeanMagnitude(ArrayList<SensorReading> accReadings, boolean isAcc) {
return 0.0;
}
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
Bundle data = intent.getExtras();
if (data == null) return;
if (data.containsKey(Constants.WindowBroadcastExtraName)) {
ScanResult scan = (ScanResult) data.getSerializable(Constants.WindowBroadcastExtraName);
if (scan != null) {
double meanMagnitude = calculateMeanMagnitude(scan.getAccReadings(), true);
float[] predictions = predict(meanMagnitude);
updateData(isStill, predictions, scan.getLocationScans());
}
}
}
};
}
Call your calculateMeanMagnitude() and predict() method in separate thread and once the both of them finished send s broadcast from that thread for updating your UI
register this receiver in on resume of your Activity class
IntentFilter = new IntentFilter();
mIntentFilter.addAction(any action string);
registerReceiver(mReceiver, mIntentFilter);
Asyntask for executing your methods on separate thread
private class Find extends AsyncTask<Void, Void, YourReusltfromthesemethods> {
#Override
protected YourReusltfromthesemethods doInBackground(Void... voids) {
calculateMeanMagnitude();
predict();
}
#Override
protected void onPostExecute(YourReusltfromthesemethods result) {
super.onPostExecute(aVoid);
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("any action string");
broadcastIntent.putExtra("Data", "Broadcast Data");
sendBroadcast(broadcastIntent);
}
}
}
And in onReceiver method of you receiver update your ui
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals("any action string")){
Bundle data = intent.getExtras();
if (data == null) return;
if (data.containsKey(Constants.WindowBroadcastExtraName)) {
ScanResult scan = (ScanResult) data.getSerializable(Constants.WindowBroadcastExtraName);
if (scan != null) {
double meanMagnitude = calculateMeanMagnitude(scan.getAccReadings(), true);
float[] predictions = predict(meanMagnitude);
updateData(isStill, predictions, scan.getLocationScans());
}
}
}
}
};
I am trying to use realm database to display my api data. I want to display the company name, however the data is saids it is inserted in the log but cant seem to display the data on the UI. Here is the code..
Any help would be greatly appreciated with this problem. The variables are at the top and the problem is when it hits on success, ive written the code "write to DB", but it doesnt display the data but tells me the data has been inserted.
// Variables for the search input field and results TextViews.
private EditText mCompanyInput;
private TextView mTitleText;
private TextView mDescriptionText;
private TextView mOfficerText;
private TextView mTitleText1;
private TextView mDescriptionText1;
private OkHttpClient okHttpClient;
private static final String TAG = "MainActivity";
private Request request;
private String url = "https://api.companieshouse.gov.uk/search/companies?q=";
Button save;
TextView log;
Realm realm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCompanyInput = findViewById(R.id.companyInput);
log = findViewById(R.id.log);
mDescriptionText = findViewById(R.id.descriptionText);
mOfficerText = findViewById(R.id.officerText);
mTitleText1 = findViewById(R.id.titleText1);
mTitleText = findViewById(R.id.titleText);
mDescriptionText1 = findViewById(R.id.descriptionText1);
save = findViewById(R.id.searchButton);
realm = Realm.getDefaultInstance();
save.setOnClickListener(this);
}
public void onClick(View view){
okHttpClient = new OkHttpClient();
request = new Request.Builder().url(url).header("Authorization", "k6DNRbTp-AnQWn51JBz5VuPiTl8jv4_etdzoMyhf") .method("GET", null).build();
Log.d(TAG, "onClick:"+url);
okHttpClient.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
Log.i(TAG, e.getMessage());
}
#Override
public void onResponse(Call call, Response response) throws IOException {
Log.i(TAG,response.body().string());
Log.d(TAG, "onResponse:"+response.code());
}
});
writeToDB(mCompanyInput.getText().toString().trim(), (mDescriptionText.getText().toString().trim()));
showData();
}
public void showData(){
RealmResults<Company> guests = realm.where(Company.class).findAll();
// Use an iterator to invite all guests
String op="";
for (Company guest : guests) {
op+=guest.getName();
op+=guest.getAppointments();
}
log.setText(op);
}
public void writeToDB(final String mTitleText1, final String mDescriptionText1){
realm.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm bgRealm) {
Company user = new Company(mTitleText1, mDescriptionText1);
bgRealm.insert(user);
}
}, new Realm.Transaction.OnSuccess() {
#Override
public void onSuccess() {
writeToDB(mCompanyInput.getText().toString().trim(), (mOfficerText.getText().toString().trim()));
showData();
// Transaction was a success.
Log.v("Database", "Data Inserted");
}
}, new Realm.Transaction.OnError() {
#Override
public void onError(Throwable error) {
// Transaction failed and was automatically canceled.
Log.e("Database", error.getMessage());
}
});
}
#Override
protected void onDestroy() {
super.onDestroy();
realm.close();
}
Why are you calling writeToDB() from the onSuccess() method? This will cause recursion and keep writing the same data into the realm. It's correct to call showData() from onSuccess(), but there's not much point calling it directly from onClick().
I think your problem though is that you're trying to update the UI from a thread: it's called from an async transaction thread and not the main thread. See this answer (and there are others you can find easily once you know the problem: Updating UI / runOnUiThread / final variables: How to write lean code that does UI updating when called from another Thread.
I try to implement a Android AIDL communication strategy.
I have an Activity and a Service.
My Activity can successfully "talk" to my Service, but the reverse process does not seem to work.
To summarize, as the Activity and the Service run in different processes, they cannot share any data throw the IBinder interface.
So the onServiceConnected() method receive an AIDL interface instead.
This interface is implemented Service-side and is aimed at being used (called) Activity-side.
I use this interface to register() another AIDL.
This new AIDL is implemented Activity-side and called Service-side through the AIDL interface.
It act like a listener.
Unfortunatly, the method of this new AIDL does not seem to be called.
The Service run in its own process thanks to the following line in AndroidManifest.xml:
AndroidManifest.xml
<service android:name=".DemoService" android:process=":DemoServiceProcess" />
I have 2 AIDL files, one knowing the other.
IAidlActivity.aidl
package app.test.aidldemo;
interface IAidlActivity {
void publish(int count);
}
IAidlService.aidl
package app.test.aidldemo;
import app.test.aidldemo.IAidlActivity;
interface IAidlService {
void startCounter();
void register(IAidlActivity activity);
}
The Service implements onBind() and run a handler in charge of incrementing a counter.
DemoService.java
package app.test.aidldemo;
import [...]
public class DemoService extends Service
{
protected IAidlActivity aidlActivity;
#Override
public IBinder onBind(Intent intent)
{
return new IAidlService.Stub() {
#Override
public void startCounter() {
DemoService.this.startJob();
}
#Override
public void register(IAidlActivity activity) {
DemoService.this.aidlActivity = activity;
}
};
}
public void startJob() {
final Handler handler = new Handler();
handler.post(new Runnable() {
protected int count = 0;
#Override
public void run() {
if (count < 500) {
count++; // increment counter
try { // then publish it to view
DemoService.this.aidlActivity.publish(count); // interface, implemented activity-side
} catch (RemoteException e) {}
handler.postDelayed(this, 2000); // 2sec.
}
}
});
}
}
The Activity only consist of a TextView.
It start the bounding with the Service and update the view from time to time.
It is also supposed to update the view when publish() is called.
But that does not happen.
MainActivity.java
package app.test.aidldemo;
import [...]
public class MainActivity extends Activity {
protected TextView view;
protected ServiceConnection connection;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
view = new TextView(this);
setContentView(view);
appendToView("Let's go!");
connection = new ServiceConnection() {
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
IAidlService aidlService = IAidlService.Stub.asInterface(service);
appendToView("IAidlService accessed");
IAidlActivity.Stub aidlActivity = new IAidlActivity.Stub() {
#Override
public void publish(int count) {
appendToView("*** Hey, new count is: " + count + "!! ***");
}
};
appendToView("IAidlActivity created");
try {
aidlService.register(aidlActivity);
aidlService.startCounter(); // interface, implemented service-side
}
catch (RemoteException e) { appendToView(e.toString()); }
}
#Override
public void onServiceDisconnected(ComponentName name) {}
};
Intent intent = new Intent(MainActivity.this, DemoService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
#Override
public void onDestroy() {
unbindService(connection);
super.onDestroy();
}
public void appendToView(String text) {
view.append(text + "\n");
}
}
I also try some variations like:
run the appendToView("*** Hey... into runOnUiThread()
delay the bindService() by using another handler + postDelayed()
My fallback technique would be to only use IAidlService and have a "watcher" Activity-side to constantly check the counter.
But I would rather understand why it is not working, and what is the correct way to use AIDL.
Summary
2 statements to change.
DemoService.java
final Handler handler = new Handler(DemoService.this.getMainLooper());
MainActivity.java
public void appendToView(String text) {
view.post(new Runnable() {
#Override
public void run() {
view.append(text + "\n");
}
});
}
I am working on an android application with push notification feature using GCM. I have created a class called PushNotificationService which extends GCMListenerService. Inside the onMessageReceived(String from, Bundle data) I am able to get the message in the push notification.
Now, I want to access a method inside my MainActivity class whenever a particular message is received in the push.
Below is my code :-
PushNotificationService.java
public class PushNotificationService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle data) {
// TODO Auto-generated method stub
super.onMessageReceived(from, data);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task"))
{
//call method from MainActivity.class
}
}
}
MainActivty.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void beginTask()
{
Log.d("GCM","Message Received from Server");
finish();
}
}
I want the beginTask() method to execute whenever the message "Begin Task" is received.
I know one approach is via Service->Interface->Activity architecture but I am not able to use this as I never create an object of PushNotificationService.
Please help.
UPDATE :-
I am now using Otto Library and below is my code.
Added new MyBus.java
public class MyBus extends Bus {
private static Bus bus;
//isRegistered is used to track the current registration status
private static boolean isRegistered;
private Handler handler = new Handler(Looper.getMainLooper());
public MyBus() {
if (bus == null) {
//ANY will allow event bus to run even with services
//and broadcast receivers
bus = new Bus(ThreadEnforcer.ANY);
}
}
#Override
public void register(Object obj) {
//The bus is registered when an activity starts
bus.register(obj);
isRegistered = true;
}
#Override
public void unregister(Object obj) {
//The bus is unregistered when an activity goes to background
bus.unregister(obj);
isRegistered = false;
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
//post the event in main thread or background thread
bus.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
}
public boolean isRegistered(){
return isRegistered;
}
}
PushNotificationService.java
public class PushNotificationService extends GcmListenerService {
#Override
public void onMessageReceived(String from, Bundle data) {
// TODO Auto-generated method stub
super.onMessageReceived(from, data);
MyBus myBus = new MyBus();
myBus.register(myBus);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task"))
{
myBus.post(message);
}
}
}
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Subscribe
public void beginTask()
{
Log.d("GCM","Message Received from Server");
}
}
The problem is still not solved. The beginTask() inside MainActivity.java is still not getting called.
Use eventBus libraries to facilitate this process...
I use Otto for this process
http://square.github.io/otto/
Here is an another eventBus library https://greenrobot.github.io/EventBus/
Steps:
1.Create an event from the service
2.Add a listener in the activity
3.If the activity is running the method will be executed
**EDIT 1 : **
I have abstracted the otto bus like this.
package com.mypackage.eventBus;
import android.os.Handler;
import android.os.Looper;
import com.squareup.otto.Bus;
import com.squareup.otto.ThreadEnforcer;
/**
* Created by gowtham on 10/6/15.
*/
public class MyBus extends Bus {
private static Bus bus;
//isRegistered is used to track the current registration status
private static boolean isRegistered;
private Handler handler = new Handler(Looper.getMainLooper());
public MyBus() {
if (bus == null) {
//ANY will allow event bus to run even with services
//and broadcast receivers
bus = new Bus(ThreadEnforcer.ANY);
}
}
#Override
public void register(Object obj) {
//The bus is registered when an activity starts
bus.register(obj);
isRegistered = true;
}
#Override
public void unregister(Object obj) {
//The bus is unregistered when an activity goes to background
bus.unregister(obj);
isRegistered = false;
}
#Override
public void post(final Object event) {
if (Looper.myLooper() == Looper.getMainLooper()) {
//post the event in main thread or background thread
bus.post(event);
} else {
handler.post(new Runnable() {
#Override
public void run() {
bus.post(event);
}
});
}
}
public boolean isRegistered(){
return isRegistered;
}
}
create an instance of the above object and try posting event
EDIT 2 for Jcarlo's comment
Follow these steps to find the state of the activity.
In your activity's onResume call MyBus.getInstance().register(this).
In your activity's onPause call MyBus.getInstance().unregister(this).
In your GCM IntentService before posting the message
if(MyBus.getInstance().isRegistered()){
//app is alive
//post data
}else{
//show notification
}
Hope this helps
You can use LocalBroadcastManager. Create a LocalBroadcastManager object mBroadcaster = LocalBroadcastManager.getInstance(this); on onCreate of your GCMListener and send broadcast with
#Override
public void onCreate() {
super.onCreate();
mBroadcaster = LocalBroadcastManager.getInstance(this);
}
#Override
public void onMessageReceived(String from, Bundle data) {
super.onMessageReceived(from, data);
String message = data.getString("message");
if(message.equalsIgnoreCase("Begin Task")) {
Intent i = new Intent();
i.setAction("yourPackageName");
i.putExtra("DATA", yourData);
mBroadcaster.send(i);
}
}
Then you can receive message in MainActivity using a BroadcastReceiver.
BroadCastReceiver mBroadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
beginTask();
}
};
Also you need to register and unregister the receiver in onStart and onStop of your activity
#Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter();
filter.addAction("yourPackageName);
LocalBroadcastManager.getInstance(this).registerReceiver((mBroadcastReceiver), filter);
}
#Override
protected void onStop() {
super.onStop();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mBroadcastReceiver);
}