The problem is that when accessing TaskTimerApplication.TEST from the activity, it is still "Creation", but in the service it is "Modification" like it should be. I have already made 100% sure that my service is executing its code before the activity is accessing the data. Below is a simple test scenario that presents the problem in a more obvious way.
Application code:
public class TaskTimerApplication extends Application {
// Static properties
private static final String TAG = "Application";
public static final boolean DEBUG = true;
public static String TEST = "Declaration";
#Override
public void onCreate() {
super.onCreate();
TEST = "Creation";
Log.v(TAG, "Created");
}
}
Service code:
public class TaskService extends Service {
public static final String TAG = "TaskService";
#Override
public void onCreate() {
TaskTimerApplication.TEST = "Modification";
Log.d(TAG, TaskTimerApplication.TEST);
}
}
My full, non-test code can be seen at my GitHub project. In the full code, the TASKS ArrayList stays empty in the activity, but is properly filled in the service.
Your problem lies in your manifest:
<service android:description="#string/service_description" android:name=".TaskService" android:label="#string/service_label" android:process=":TaskService"></service>
There is usually no good reason to waste RAM and CPU by putting your service in a separate process. Moreover, if you do put the service in a separate process, then your activity and your service will not be in the same process and therefore will not share a common Application object instance.
My recommendation is to drop the android:process attribute.
Related
I am aware that if I use android:processin the manifest then the class that extends Application will be called twice. Once, for the app and a second time for the new process.
The problem is I have code in MyApplication (extends Application) which should not be called twice. I want to have a separate process but I only want the code in MyApplication#onCreate to ever be ran once per load.
I tried setting a flag in Shared Preferences but it doesn't work, probably because of the different processes issue
Any ideas?
Root cause
From SharedPreferences documentation:
Note: This class does not support use across multiple processes.
That explains why your SharePreferences does not work.
Solution 1: Using Context.MODE_MULTI_PROCESS, but it is deprecated in API level 23
public static final int MODE_MULTI_PROCESS
This constant was deprecated in API level 23.
MODE_MULTI_PROCESS does not work reliably in some versions of Android,
and furthermore does not provide any mechanism for reconciling
concurrent modifications across processes. Applications should not
attempt to use it. Instead, they should use an explicit cross-process
data management approach such as ContentProvider.
Solution 2: Using ContentProvider
2.1. Write your own ContentProvider
We just need a way to store a boolean variable that indicates the onCreate() method of MyApplication is called for the first time or not, using this solution seems inefficient.
2.2. Use pre-defined ContentProvider from Android
The app must declare Read/Write external storage permissions and asking runtime permissions as well. By the way, it confuses users when the first time they open the app.
Solution 3: We can use the below flow to implement
Declare a BroadcastReceiver that runs in the same process as your app. Because they run on the same process, so they have the same process id.
The component (activity, service, receiver, provider) that runs on a separate process (private or global), so they will have a different process id.
Implementation
Step 1. Create a class that extends from BoardcastReceiver, named AppReceiver.
public class AppReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Messenger messenger = intent.getParcelableExtra(MyApplication.EXTRA_MESSENGER);
int processId = intent.getIntExtra(MyApplication.EXTRA_PROCESS_ID, -1);
boolean isOnCreateCalledFirstTime = processId == Process.myPid();
Message message = Message.obtain();
message.arg1 = isOnCreateCalledFirstTime ? 1 : 0;
try {
messenger.send(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
Step 2. Add that class to AndroidManifest.xml
<receiver
android:name=".AppReceiver"
android:enabled="true"
android:exported="true" />
Step 3. Modify your MyApplication class
public class MyApplication extends Application {
public static final String EXTRA_MESSENGER = "EXTRA_MESSENGER";
public static final String EXTRA_PROCESS_ID = "EXTRA_PROCESS_ID";
#Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent();
String pkg = getPackageName();
intent.setComponent(new ComponentName(pkg, pkg + ".AppReceiver"));
Handler handler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(#NonNull Message msg) {
boolean isOnCreateCalledFirstTime = msg.arg1 != 0;
if (isOnCreateCalledFirstTime) {
// TODO: First time onCreate() is called
} else {
// TODO: Next time onCreate() is called
}
return true;
}
});
Messenger messenger = new Messenger(handler);
intent.putExtra(EXTRA_MESSENGER, messenger);
intent.putExtra(EXTRA_PROCESS_ID, Process.myPid());
sendBroadcast(intent);
}
}
I need to create a session and change it at times. In a specific activity should recover it and compare it to a different variable and modify the value of this session. I tried to create a class for this, but the change of activity, the value back to null. I need it to remain until the application is closed.
below:
import android.app.Application;
public class Util extends Application {
private static String idCorrente;
public void onCreate() {
super.onCreate();
idCorrente="0";
}
public static String getIdCorrente() {
return idCorrente;
}
public static void setIdCorrente(String id) {
Util.idCorrente = id;
}
}
I do not know exactly the right way to do it.
You need to store the data on the device somehow. I would recommend reading the Storage Options page of the Android Developers Guide.
Specifically, I think you will find SharedPreferences well-suited for your application.
As far as I know, there is no system API for me to get user idle time. When I say user idle time, I mean user have some interaction on the touch screen within my app. Therefore, I want to track it by my self. The way come up to my mind is to extends Activity and override onuserinteraction method to save the last user active time.
But the challenge is that my app have multiple processes. I am not sure the following way is the correct and efficient way.
I want to use SharedPreference with MODE_WORLD_WRITEABLE flag to store the user last active time. To avoid the performance issue, I also cache the last active time within the activity in each activity. And I only save the new time to SharedPrefernces if the diff time > 1 second.
Is this way efficient compared to using aidl? Actually, is aidl also share variable using file? If yes, I think the two ways should have similar performance, right? Thanks.
Instead writing it down every time, from everywhere, make this a global function in your App:
public class MyApp extends Application {
private static SharedPreferences sPreference;
private static final long MIN_SAVE_TIME = 1000;
private static final String PREF_KEY_LAST_ACTIVE = "last_active";
private static final String PREF_ID_TIME_TRACK = "time_track";
public static void saveTimeStamp(){
if(getElapsedTime() > MIN_SAVE_TIME){
sPreference.edit().putLong(PREF_KEY_LAST_ACTIVE, timeNow()).commit();
}
}
public static long getElapsedTime(){
return timeNow() - sPreference.getLong(PREF_KEY_LAST_ACTIVE,0);
}
private static long timeNow(){
return Calendar.getInstance().getTimeInMillis();
}
#Override
public void onCreate() {
super.onCreate();
sPreference = getSharedPreferences(PREF_ID_TIME_TRACK,MODE_PRIVATE);
}
}
Add Application class to manifest: <application android:name="com.example.MyApp"
Place saving functionality in an abstract Activity class:
public abstract class TimedActivity extends Activity {
#Override
public void onUserInteraction() {
super.onUserInteraction();
MyApp.saveTimeStamp();
}
public long getElapsed(){
return MyApp.getElapsedTime();
}
}
Now, extend all your activities from this class, all of them will be auto-save time, and will be able to use getElapsed().
I have a real problem using my app that involve 2 processes. One process its executing a Service (p1) and the other the GUI (p2).
I have a class in p2 that implements the use of an object (iThing) that is custom memory managed (and its static). It has to be like this bacause of Android OS implementation of destroying the views whenever he wants.
public class Connections{
public static int iGlobalCounter=0;
public static Object iThing;
public static void start(){
iGlobalCounter++;
Log.d("PROCESS", "UP: "+iGlobalCounter);
if (iGlobalCounter<=1){
//Create the object "iThing"
}
}
public static int stop(){
iGlobalCounter--;
Log.d("PROCESS", "DOWN: "+iGlobalCounter);
if (iGlobalCounter<=0){
//Destroy the object "iThing"
}
}
}
The main GUI (in p2), starts and stops the variable on the onCreate / onDestroy (for all views in my app)
public class MyMainClass extends Activity{
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Connections.start();
}
#Override
public void onDestroy(){
super.onDestroy();
Connections.stop();
}
}
Finally in p1 I have the service, which also needs the variable, so, it does the same
public class MyMainService extends Service{
#Override
public void onCreate() {
super.onCreate();
Connections.start();
}
#Override
public void onDestroy(){
super.onDestroy();
Connections.stop();
}
}
The problem is that if I use only p2 (GUI), it goes all well, but when I execute the service (in p1), the counter doesn't increment from the last state, but from 0, resulting in destroying the object when leaving the service, not the app.
if do this navigation, I get the following counters:
MyMainClass (1) --> OtherClass (2) --> AnotherClass (3) --> MyMainService (1)
My question is if there is a way of having a multi-process global variable? As it seems that every process takes its own static variables and are not "real static". A solution could be using SharedPreferences to save the state, but not really nice solution, as it hasn't to be saved when leaving the app.
Thanks,
PAU
I think that you should extend Application class and put your globalVariable there.
You can store your global data in shared memory (see MemoryFile).
To synchronize access to the file, I think the best approach is to implement some sort of spinlock using the same memory file.
In and case, I don't know a simply way of doing this.
You have the following options which you can look into for sharing data between different processes,
Message Queue,
Named Pipes,
Memory mapped files
WCF on Named Pipes or MSMQ
So my problem is as follows: I have 2 services running in different processes and would like to keep it this way. One is busing data from databases to bound applications and the second is polling for incoming data through sockets. I feel keeping these in independent process would be better. The problem is that I would like to have a shared preferences between the two services and would like to implement OnSharedPreferenceChangeListener to update setting needed for polling and busing data. I can't implement OnSharedPreferenceChangeListener in the services since they run on different processes. I could implement this on the PreferenceActivity but how do I communicate to the services for immediate update? I do not want to use AIDL and worry about binding. There is the possibility of creating broadcast receivers and sending out intents but these seems like a big work around if the settings menu grows. Any other great ideas out there?
all right here is your answer...
for the preference of this example lets take 3 classes - 2 services service A and B (href A,B) and Settings(preferenceActivity)
initialize the two services as
public class ServiceA/B extends serice implements OnSharedPreferenceChangeListener{
#Overside
public void onCreate(....){
Settings.getPrefs(this).registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onResume() {
super.onResume();
Settings.getPrefs(this).registerOnSharedPreferenceChangeListener(this);
}
#Override
protected void onPause() {
super.onPause();
// Unregister the listener whenever a key changes
Settings.getPrefs(this)
.unregisterOnSharedPreferenceChangeListener(this);
}
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
System.out.println("++"+key+"++");
if(key == "KEYA"||key == "KEYC")
Do_what_ever_you_want();
if (key == "KEYB")
do_anything();
}
do_anything(){}
Do_what_ever_you_want();
}
Shared preference Part.
public class Settings extends PreferenceActivity implements
OnSharedPreferenceChangeListener{
public static final String PREFS_PRIVATE = "PREFS_PRIVATE";
public static final String MASTERKEY = "!##$%^&*";
public static final String KEYA = "KEYA";
public static final String KEYB = "KEYB";
public static final String KEYC = "KEYC";
--- the create and get methods for getting and sharing data in the prefs... .....
// get them from just a google search.
}
I have this system implemented in one of my applicaiton... and deployed... so fiddle around these basics and let me know how it is goes...
Rajesh...
I've created a simple SharedPreferences based on ContentProvider, which can be used accross processes, you can find them from my bitbucket https://bitbucket.org/ABitNo/zi/src/783b6b451ba1/src/me/abitno/zi/provider/preference