My application has a few activities, and a background service. My question is, if I have the context variable in the service, how can I tell which activity is currently open? I need to do this to direct the next action my service takes. For example,
if (context is activity_1) {
//take this action
} else if (context is activity_2) {
//do this instead...
}
That's the basic gist of what I'm trying to do.
Help much appreciated.
You could set a SharedPreferences entry in each onResume() method of your activities and read that value from the service. To keep it clean you could write an Activity that does that and then extend all your activities from it:
public class MyActivity extends Activity {
private static final String PREFS_NAME = "MyPrefsFile";
#Override
protected void onResume() {
getContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.edit()
.putInt("activtiyIdRunning", getActivityId() )
.commit();
super.onResume();
}
abstract protected int getActivityId();
}
public class MyConcreteActivity1 extends MyActivity {
#Override
protected int getActivityId() {
return 1;
}
// your normal code
}
in your service than just call:
int currentActivity = getContext().getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
.getInt("activityIdRunning",-1);
Related
I have created an volley list in this i have problem to get data from adapter to activity and this activity to another activity. I have received error cannot cast activity.java to anotherActivity.java below is my code. Please help me anyone thanks.
My Interface itemclick in Adapter class
private OnItemClickGetPlaylist mListener;
public interface OnItemClickGetPlaylist{
public void OnPlaylistItemClick(String playlistName,int numOfItems,String imageViewForPlaylist);
}
public void setOnClickListenerOnPlaylist(OnItemClickGetPlaylist listener)
{
mListener = listener;
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String id = playlist.getId_playlist_identify();
String PlaylistName = playlist.getTitile_of_playlist();
String imageOfPlaylist = playlist.getImage_of_playlist();
int numOfPlaylistSongs = getItemCount();
SendIdToDatabase(id);
if (mListener != null)
{
mListener.OnPlaylistItemClick(PlaylistName,numOfPlaylistSongs,imageOfPlaylist);
}
else {
Toast.makeText(context, "mListeren is null" + mListener, Toast.LENGTH_SHORT).show();
}
}
});
After get data handle OnPlaylistItemClick click in Activity below Codes
public void OnItemClickHandleInPlaylistActivity(String playlistName,int numOfItems,String imageViewForPlaylist)
{
//here is the adapter item click in activity now i want to send that data to another activity without any intent please help me.
// i have implement below code but it give me cannot cast activity to another activity error.
((anotherActivity) getApplicationContext()).OnItemClickInMusicActivity(playlistName,numOfItems,imageViewForPlaylist);
}
See the solution at https://stackoverflow.com/a/47637313/2413303
public class MyApplication extends Application {
private static MyApplication INSTANCE;
DataRepository dataRepository; // this is YOUR class
#Override
public void onCreate() {
super.onCreate();
INSTANCE = this;
dataRepository = new DataRepository();
}
public static MyApplication get() {
return INSTANCE;
}
}
The DataRepository should expose LiveData:
public class DataRepository {
private final MutableLiveData<MyData> data = new MutableLiveData<>();
public LiveData<MyData> getMyData() {
return data;
}
public void updateText(String text) {
MyData newData = data.getValue()
.toBuilder() // immutable new copy
.setText(text)
.build();
data.setValue(newData);
}
}
Where the Activity subscribes to this:
public class MyActivity extends AppCompatActivity {
DataRepository dataRepository;
TextView textView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyApplication app = (MyApplication)getApplicationContext();
dataRepository = app.getDataRepository();
setContentView(R.layout.main_activity);
textView = findViewById(R.id.textview);
dataRepository.getMyData().observe(this, new Observer() {
#Override
public void onChange(MyObject myObject) {
textView.setText(myObject.getText());
}
}
}
So to update this text, you need to get the DataRepository class, and call updateText on it:
DataRepository dataRepository = MyApplication.get().dataRepository();
dataRepository.updateText("my new text");
Make sure that the data in DataRepository is properly persisted somewhere, or at least can be obtained again after process death. You might want to use a database for example (but not shared preferences).
If you don't want to use Intents, maybe you can use a publish/subscribe architecture. There is a library called eventbus (org.greenrobot:eventbus) very easy to use which could achieve what you want.
Use an Application Class instead.
public class MyApplicationClass extends Application{
#Override
public void onCreate() {
super.onCreate();
///do something on create
}
getterMethods(){...}
setterMethods(){...}
}
then add android:name=".MyApplicationClass" to manifest file
Now you can access the methods in class by
MyApplicationClass applicationClass = (MyApplicationClass)getApplicationContext();
int id = applicationClass .getterMethod().getID;
String playListName = applicationClass .getterMethod().getPlayListName();
and vice versa for Setter();
after that you can use it for data getting and setting Data throughout the application.
Hope it helps :)
References:
https://guides.codepath.com/android/Understanding-the-Android-Application-Class
I find the best to use callbacks.
in ClassA:
Create interface
MyCallback callback;
viod setCallback(MyCallback callback){
this.callback = callback;
}
viod onStop(){
callback = null;
}
interface MyCallback{
void doSomething(Params params);
}
in ClassB:
implement MyCallback
public class ClassB **implements ClassA.MyCallback**
set reference in onCreate
ClassA classA = new ClassA();
classA.setCallback(this);
// override method doSomething
#override
void doSomething(Params params){
//do your thing with the params…
}
when the job is done inside class A call:
callback.doSomething(params);
destroy reference inside class B in onStop()
classA.onStop();
I have a method in onResume() which fetches user's data and should get called when user launch the app. This is working fine.
The problem is that for example after opening 'Settings' when I tap/click on the back arrow, the method in onResume() gets called again and user data starts getting fetched again.
What I want is, I want that method to get called only when user launches the app and not every time the user transition back from settings to main activity.
Here's the onResume() in MainActivity.java:
#Override
protected void onResume() {
super.onResume();
fetchUserData();
}
Here's how I transition to Settings.java:
Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
startActivity(settingsIntent);
Please let me how can I restrict the fetchUserData() to get called only when user launches the app and not again when user transition back to main activity from any other activity by tapping/clicking on back arrow.
Sorry, if question seems to be badly formatted. I'm still a beginner here.
if you want the method to be called only once when the activity opens move it inside OnCreate() method.OnResume() can be called several times.You can see the documentation of acttivity lifecycle here
You could adapt the following code, by setting a flag/indicator so that only wanted returns are processed (eg set resume_state to RESUMESTATE_NOTHING except when starting an intent after which you want to fetchUserData:-
public class AisleListByCursorActivity extends AppCompatActivity {
public final static int RESUMESTATE_NOTHING = 0;
public final static int RESUMESTATE_AISLEADD = 1;
public final static int RESUMESTATE_AISLESTOCK = 2;
public final static int RESUMESTATE_AISLEDELETE =3;
public final static int RESUMESTATE_AISLEUPDATE = 4;
public int resume_state = RESUMESTATE_NOTHING;
public ShopsCursorAdapter currentsca;
public AislesCursorAdapter currentaca;
public ShopListSpinnerAdapter currentslspa;
public long currentshopid;
private final static String THIS_ACTIVITY = "AisleListByCursorActivity";
private final ShopperDBHelper shopperdb = new ShopperDBHelper(this,null, null, 1);
private ListView aisleslistview;
private Cursor csr;
private int shopid = 0;
protected void onResume() {
super.onResume();
switch (resume_state) {
case RESUMESTATE_AISLEADD:case RESUMESTATE_AISLEUPDATE: {
Cursor csr = shopperdb.getAislesPerShopAsCursor(currentshopid);
currentaca.swapCursor(csr);
resume_state = RESUMESTATE_NOTHING;
break;
}
default: {
resume_state = RESUMESTATE_NOTHING;
}
}
}
........
public void aalbcadd(View view) {
resume_state = RESUMESTATE_AISLEADD;
Intent intent = new Intent(this,AisleAddActivity.class);
intent.putExtra("Caller",THIS_ACTIVITY);
intent.putExtra("SHOPID", currentshopid);
startActivity(intent);
}
Create method in OnStart() or onCreate() rather than onResume(). for reference - please see this link for better understanding of Android LifeCycle http://developer.android.com/reference/android/app/Activity.html
If you want your method to be called only once you must put that method in onCreate method of Activity. Because onResume is always called you come back to the activity as per Android lifecycle. So just replace you method fetchUserData(); into onCreate like below:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);
fetchUserData();
}
#Override
protected void onResume() {
super.onResume();
}
I have two activities. I want to pas an integer to the other. this is how i do it
public void gotoSecondActivity(View v) {
Intent intent = new Intent(this, SecondActivity.class);
intent.putIntegerArrayListExtra("myInt", myVariable);
startActivity(intent);
}
and then i retrieve it in the onCreate function as follows
var = getIntent().getIntExtra("myInt", 0);
This works. But it only gets the value AT THE moment when i press the button to go to next activity
However, myVariable is an integer that keeps updating and changing. EVEN when I am on my secondActivity, the integer should be running in background and changing.
Is there a way to constantly pass this integer?
I have even tried to make it static and read it like var = MainActivity.myVariable;
You need to implement some sort of observer pattern. Use BroadcastReceiver, or event bus like otto or GreenRobot.
update (more details):
build.gradle
dependencies {
compile 'de.greenrobot:eventbus:2.2.1'
}
Event.java
public class Event {
public int value;
public Event() {}
}
MainActivity.java
public class MainActivity extends Activity {
private Event event = new Event();
// inside a loop
event.value = newValue;
EventBus.getDefault().post(event);
}
SecondActivity.java
public class SecondActivity extends Activity {
#Override
public void onResume() {
super.onResume();
EventBus.getDefault().register(this);
}
#Override
public void onPause() {
EventBus.getDefault().unregister(this);
super.onPause();
}
public void onEvent(Event event) {
// do something
}
}
But more importantly, what type of value are you sending over, and how do you plan on updating it?
You should be better off using an IntentService or some background process.
Why don't you define the variable at Application level and you access to it at any moment?
Your solution of making it static should do the trick, just be sure to not store the variable locally but always fetch it from the static field, this will be the simplest way to do this.
Currently im using a simple Fragment with a attached AsyncTask and setRetainInstance(true) to handle runtime configuration changes and a callback interface to the MainActivity straight from the AsyncTask. (following this example). This works fine so far.
But my Problem is that the data onProgressUpdate passes, once the fragment is detached (when switching to the home-screen for example) is lost. My soloution would be to create buffer variables inside the Fragment which store the lost data from the AsyncTask until the fragment is attached again.
public class MyFragment extends SherlockFragment {
static interface TaskCallbacks {
void onPreExecute();
void onProgressUpdate(MyUpdateBundle p);
void onCancelled();
void onPostExecute();
}
private TaskCallbacks mCallbacks;
private WebFetcherTask mTask;
public List<MyUpdateBundle> updateBuffer;
public MyFragment() {
this.updateBuffer = new ArrayList<MyUpdateBundle>();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallbacks = (TaskCallbacks) activity;
if(updateBuffer.size() > 0)
{
for(MyUpdateBundle update : updateBuffer)
mCallbacks.onProgressUpdate(update);
updateBuffer.clear();
}
}
#Override
public void onDetach() {
super.onDetach();
mCallbacks = null;
}
...
private class MyAsyncTask extends AsyncTask<Void, MyUpdateBundle, Void> {
...
#Override
protected void onProgressUpdate(MyUpdateBundle... uB) {
if (mCallbacks != null) {
for(MyUpdateBundle u : uB)
mCallbacks.onProgressUpdate(u);
}
else
{
for(MyUpdateBundle u : uB)
updateBuffer.add(p);
}
}
...
This seems to me the most cleanest soloution, beside saving the data (sinch this is very slow) or using StickyBroadcasts (doesn't seems a clean approach to me). I think that a Service would be a good alternative, but I'm not sure if I would end up in the same problem as here: prevent data from been lost when everthing is unable to recieve. However when I want to re-send the UpdateBundles within the onAtach() methode of the fragment, the buffer is always empty.
I've tryed so far:
volatile statement on the updateBuffer List
Collections.synchronizedList on the updateBuffer List
ensured that the fragment is no create again/twice
put the updateBuffer within the AsyncTask
...
But before I put too much time into this, I would like to know if my approach is even possible and when how.
Thanks in Advance!
Anyway I fixed it. I attached the AsyncTask to the application task and completly removed the Fragment.
It's also a great way to handle any configuration changes or vanished activitys. I don't need to save any data, I just read my buffers and pass them to the callback and have the exact same state as before!
I have an activity with multiple AsyncTask's, but when i press back button, the Activity is reloaded and the AsyncTask's are executed again. what should i do to Back to the previous activity and not reload the activity and asynctask ? please help.
public class LugarActivity extends SherlockActivity {
CargarDatos cargarDatos;
CargarComentarios cargarComentarios;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lugar);
cargarDatos = new CargarDatos();
cargarCometarios = new CargarComentarios();
loadData();
}
public void loadData(){
cargarDatos.execute();
}
public void loadOtherData(){
cargarComentarios.execute();
}
public class CargarDatos extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
#Override
protected void onPostExecute(String html) {
loadOtherData();
}
}
public class CargarComentarios extends AsyncTask<Integer, Integer, String>{
#Override
protected String doInBackground(Integer... params) {
// here download data
}
}
}
FIXED!
i fixed the problem with Singleton class:
public class DataManager {
private static DataManager instance = null;
protected static boolean isShowingTheView = false;
protected DataManager() { }
public static synchronized DataManager getInstance() {
if (instance == null) {
instance = new DataManager();
}
return instance;
}
}
in the activity i add this code:
DataManager dataManager = new DataManager();
if(!dataManager.isShowingTheView){
loadData();
dataManager.isShowingTheView = true;
}else{
finish();
}
and finally i override the onDestroy() method
#Override
public void onDestroy(){
dataManager.isShowingTheView = false;
super.onDestroy();
}
Remove loadData() from onCreate and call somewhere else.
Use Fragments
http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
A fragment can stay in memory during a configuration change and therefore you can run your asynctask inside itself. You can then query the fragment for any state information you require from your tasks and update your Activity accordingly.
If your Activity is destroyed before the other activity starts, using the back button will call onCreate again, instead of onRestart or onResume.
See here for details.
As Kuffs already mentions, using Fragments is the way to go.
Uglier solution, you could also set a shared preference holding a boolean once your AsyncTask is launched (or on its onPostExecute) so that it won't launch again after checking for that preference on your Activity's onCreate.