I am trying to start a service but I get an error.
I have no idea how to solve this specific problem.
The class I am starting the service form:
package com.example.test;
public class Purchase extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
final Intent i = new Intent(this,MyHostApduService.class);
Livedate<User> user = *something*;
user.observe(Purchase.this, new Observer<User>() {
#Override
public void onChanged(#Nullable User user) {
i.putExtra("user name", user.getUserName());
i.putExtra("credit card", user.getCreditCard());
i.putExtra("context", Purchase.class);
i.putExtra("mail", user.getMail());
}
});
Toast.makeText(Purchase.this, "YOU CAN ONLY MAKE A PURCHASE AFTER GETTING CLOSE TO THE OTHER DEVICE", Toast.LENGTH_SHORT).show();
this.startService(i);
}
}
the service:
public int onStartCommand(Intent intent, int flags, int startId) {
creditCard = intent.getExtras().getString("credit card");
return START_STICKY;
}
the error:
java.lang.RuntimeException: Unable to start service com.example.test.MyHostApduService#fe81f37 with Intent { cmp=com.example.test/.MyHostApduService }:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.os.Bundle.getString(java.lang.String)' on a null object reference
The intent that you are passing to the service does not have the property "credit card". This is because you are starting the service with an empty intent (no extras). The code i.putExtra("credit card", user.getCreditCard()); is only executed when there is a change to the LiveData. When the activity first starts, there is nothing triggering LiveData's onChanged.
I would recommend reading about event loops here, there are a few good articles out there if you search for "android event loop".
Effectively what is happening is that your method "onCreate" and "onChanged" are both callbacks. "onCreate" will execute completely before "onChanged" can start. This means you start the service with no arguments getting the null pointer exception. As a general rule you don't want to use an object across multiple callbacks if avoidable.
One possible way around this is to start the service when new data comes in. But be careful what happens if multiple changes in the user data are possible.
package com.example.test;
public class Purchase extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_purchase);
Livedate<User> user = *something*;
user.observe(Purchase.this, new Observer<User>() {
#Override
public void onChanged(#Nullable User user) {
final Intent i = new Intent(this,MyHostApduService.class);
i.putExtra("user name", user.getUserName());
i.putExtra("credit card", user.getCreditCard());
i.putExtra("context", Purchase.class);
i.putExtra("mail", user.getMail());
Purchase.this.startService(i);
Toast...
}
});
}
}
Related
I wonder why this is happening. My MainActivity starts a service and passes itself to the service so the service can call one of the MainActivity's methods with the data it obtained (I'm fully aware that that's not the way things should be done). However, when this method is called, every field in the Activity is null and trying to call findViewById() results in Attempt to invoke virtual method 'android.view.View android.view.View.findViewById(int)' on a null object reference.
This means the Activity died, but how come? The Activity's lifecycle is not over. Why did the garbage collector took it away?
Please, take a look at the code:
MainActivity
public class MainActivity extends Activity implements Serializable{
private RecyclerView mRecyclerView;
private MyAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, Servicio.class);
intent.putExtra("ACTIVITY", this);
startService(intent);
}
public void loadList(List<Data>){
mList = (LinkedList<Data> dataList;
mRecyclerView = (RecyclerView) this.findViewById(R.id.rv);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(linearLayoutManager);
mAdapter = new MyAdapter(this, mList);
mRecyclerView.setAdapter(mAdapter);
}
}
Service
public class MyService extends Service {
private Thread mThread;
private Activity mActivity;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mThread = new Thread(new Runnable() {
#Override
public void run() {
execute();
}
});
}
#Override
public int onStartCommand(Intent intent, int flags, int startId)
mActivity = (Activity) intent.getSerializableExtra("ACTIVITY");
mThread.start();
return super.onStartCommand(intent, flags, startId);
}
public void execute(){
dataObtained = doStuff();
((MainActivity) mActivity).loadList(dataObtained);
}
}
However, if I use sendBroadcast() in the service to send back the data and retrieve it in the Activity by registering a BroadcastReceiver in it, the Activity remains fully operational and I can call findViewById(). How? When did the lifecycle ended before but not in this case?
You are doing it in wrong way mActivity = (Activity) intent.getSerializableExtra("ACTIVITY"); is it working?
You can use LocalBroadcastManager or Otto for communicating between Service & Activity
You can use broadcasts to communicate, message handlers, but not this. This is just plain bad practice (and therefore there is no 'good' answer to satisfy you). Not to mention the security issue that your success using this technique would present for Android as a whole (what I am saying here is that this is most likely NOT possible).
You should reconsider being too zealous about maintaining the Serialisation of the Activity, because ultimately you wouldn't be on StackOverflow if it were easy, plus no good programmer can really help you because no good programmer does this.
OK, I got it solved. It happens that, when serializing objects, you don't get the same object in the receiving activity. Both thisand mActivity objects had different IDs.
I am working on unity android project.
I have called the android side methods from unity like this
AndroidJavaObject aObj = new AndroidJavaObject("com.mypackage.UnityBridge",param1,param2);
aObj.Call("callme");
And on Android side
public class UnityBridge{
public UnityBridge(final String param1, final int param2) {
activity = UnityPlayer.currentActivity;
this.param1= param1;
this.param2= param2;
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
// INITIALIZATION OF ANDROID CLASS CONSTRUCTOR HERE
}
});
}
public void callme() {
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
if(obj!= null)
{
// ANDROID METHOD CALL HERE
}
}
});
}
This much is working perfectly.
If I want to call Activity specific methods like onPause(), onResume(), so there is a method in unity to do so.
void OnApplicationPause(bool pauseStatus) {
// HERE I CAN CALL activity specific **onPause()** and **onResume()** based on pauseStatus
}
Is there anything in unity from which I can give call to onNewIntent(Intent i) of Android. If not then how is it possible to call onNewIntent()
Please help to resolve this.
I wrote a post how this problem can be solved without overriding Unity activity (its about onActivityResult but the principle is the same): https://medium.com/#tarasleskiv/unity-android-plugins-and-onactivityresult-callback-abef4b6bbc87#.6d7sl8z81
Basically you create a custom empty activity and start it just to receive these callbacks and finish it immediately after that.
Check also this post about how to create activity for onNewIntent callback: https://blog.getsocial.im/android-unity-app-and-the-intent-issue/
Here is the example code of the intermediate activity yo have to create:
public class MyDeepLinkingActivity extends Activity
{
private static String TAG = "GetSocial GetSocialDeepLinkingActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
Log.v(TAG, "Forward the intent");
// Do with intent what you need to
Log.v(TAG, "Returning to main activity");
//start main activity
Intent newIntent = new Intent(this, getMainActivityClass());
this.startActivity(newIntent);
finish();
}
private Class<?> getMainActivityClass() {
String packageName = this.getPackageName();
Intent launchIntent = this.getPackageManager().getLaunchIntentForPackage(packageName);
try {
return Class.forName(launchIntent.getComponent().getClassName());
} catch (Exception e) {
Log.e(TAG, "Unable to find Main Activity Class");
return null;
}
}
}
Looks like there is no built it way to get onNewIntent callback in Unity. However i can suggest to export google android project from unity and override this method in main unity activity
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
//some code
}
Or you can create android plugin with your own activity which extends unity activity, but keep in mind that some plugins could stop working if you do this.
I would like to get voice recognition using just one method.
In order to do that i've created 3 classes
the main class
public class Index extends Activity {
private Button boton;
private EditText texto;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_index);
boton = (Button)findViewById(R.id.boton);
texto = (EditText) findViewById(R.id.texto);
boton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
texto.setText(IVRecognition.getInstancia().getComando(Index.this));
}
});
}
}
the intermediate
public class IVRecognition {
//*******************singleton********************
private static IVRecognition instancia;
private IVRecognition (){}
public static IVRecognition getInstancia(){
if (instancia==null) instancia = new IVRecognition();
return instancia;
}
//************************************************
public static String resultado = null;
public String getComando(Context content){
Intent intent = new Intent(content, VRecognition.class);
content.startActivity(intent);
//pause here untill VRecognition.onActivityResult is executed
return resultado;
}
}
and the recognition one
public class VRecognition extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startRecognition();
}
public void startRecognition (){
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,this.getPackageName());
startActivityForResult(intent, 1 /*VOICE_RECOGNITION_REQUEST_CODE*/);
}
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if(requestCode == 1 /*VOICE_RECOGNITION_REQUEST_CODE*/ && resultCode == RESULT_OK){
ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
IVRecognition.getInstancia().resultado = result.get(0);
}
this.finish();
}
}
The problem is that when I call VRecognition activity using content.startActivity(intent); the execution of the aplication keeps on going, so the variable called resultado has null value until onActivityResult is executed, which results in a null return value.
Hope you can help me. Cheers
Ian's answer is good. But from your comment, I'd recommend using an IntentService and the BroadcastManager. That way you don't need the intermediate activity. You call the startService(intent) from any activity that wants the VR result (and implements BroadcastReceiver). Then the IntentService calls startActivityForResult(intent,1) and Broadcasts the result.
More info:
http://developer.android.com/training/run-background-service/index.html
It sounds like you want to pause execution until voice recognition is complete. You may want to rethink this; you're calling getComando() from your UI thread, so your application UI will be frozen until recognition is complete. In the (probably quite likely) event that recognition takes more than five seconds, the system will pop up an Application Not Responding dialog. (Also, since you're implementing getComando() by starting another activity within your process, blocking the UI thread in getComando() would prevent recognition from ever running.)
The right way to do this is to use a completion callback. For instance, you could create an IVRecognitionListener interface:
public interface IVRecognitionListener {
public void onRecognitionComplete(String resultado);
}
and pass an instance of that to getComando(). Then instead of just setting IVRecognition.resultado in onActivityResult(), you could call onRecognitionComplete() to notify the caller of the result.
Im just wondering what would be the best and possibly easiest way to do this. I have two activites LoginAcitivty and Main Activity.
Ive coded an AsyncTask in my MainActivity as an inner class which sends updates to a web service. When i click the logout button of the MainActivity this returns the app to the Login Activity. Is it possible to still run the ASyncTask even though there is a different activity running or is there another way to do something like this?
Any suggestions would be greatly appreciated
Thanks
The Asynctask is tied to the "Entity" that created it, in your case it would be the MainActivity, so it will not survive the destroy of your activity (I trust you call the finis() method of the main activity once the user logs out)
What you can do is use a service that runs in background and use the async task to poll your server:
The service shall look like this:
public class PollService extends Service {
#Override
public void onCreate() {
super.onCreate();
}
public void onStart(Intent intent, int startId) {
(new PollAsyncTask(this)).execute();
}
//callback used to retrieve the result from the asynctask
void callBack(String result) {
//here is your logic, taking the result back from the async task
//eventually re-run the asynctask
(new PollAsyncTask(this)).execute();
}
#Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
}
The AsyncTask shall look like this:
private class PollAsyncTask extends AsyncTask<String, Void, String> {
private PollService caller;
PollAsyncTask(PollService caller) {
this.caller = caller;
}
#Override
protected String doInBackground(String... params) {
//do your polling here and return something meaningful to the service,
return SOMETHING_REFERRING_TO_THE_1_OF_3;
}
#Override
protected void onPostExecute(String result) {
//Give the result back to the caller:
this.caller.callBack(result);
}
#Override
protected void onPreExecute() {//nothing special here}
#Override
protected void onProgressUpdate(Void... values) {//nothing special here}
}
That way your async task will poll your server whatever activity is currently in foreground.
The service shall be started by your first activity when it is run the first time (i.e. in the onCreate method):
#Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState==null) {//only the first time
Intent serviceIntent = new Intent();
serviceIntent.setAction("com.yourcompany....PollService");
startService(serviceIntent);
}
}
Hope this helps.
For what I understand, you have an inner Class in your MainActivity.
So just make your AsyncTask in a separate Class and you can call it from both Activites.
Like: new YourAsyncTask().execute();
Greetings.
I have an Activity with a private OnSharedPreferenceChangeListener, the listener's work is defined on the onCreate() method of the Activity. The listener is registered to the sharedPreferences of the application.
The change itself is triggered by a Service in response to an sms received intent.
Will the listener receive the callback when the Activity itself has died? are there cases where it will not?
The listener is defined (roughly):
private OnSharedPreferenceChangeListener _sharedPreferenceListener;
public void onCreate(Bundle bundle){
...
_prefs = PreferenceManager.getDefaultSharedPreferences(this);
_prefs.registerOnSharedPreferenceChangeListener(_sharedPreferenceListener);
...
_sharedPreferenceListener = new SharedPreferences.OnSharedPreferenceChangeListener(){ /*doing some work here*/};
...
}
please igonre the logic here if correct or not, assume that the code works, my main concern is how the listener reacts to changes in the lifecycle of the activity.
Thanks,
actually, since the listener doesn't know anything about the activity (and as such you can use it anywhere , not just in an activity), you will get notified no matter where you use it.
Also, since you can't know for sure what it does with the context , you should use the application context instead in this case (so that you won't have memory leaks, though I doubt it needs a reference to the activity).
Of course, if the listener itself is referenced by weak reference, and the activity doesn't have any reference to itself on any other class, the listener can be GC-ed too. You can see in the code of Android (or at least of API 19) that in the class "android.app.SharedPreferencesImpl" (example link here) , you have a WeakHashMap of listeners, so it might mean that the activity that hosts the listener can be GC-ed and so the listener will stop from being called. Here is the relavant code of Android:
private final WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners =
new WeakHashMap<OnSharedPreferenceChangeListener, Object>();
...
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
synchronized(this) {
mListeners.put(listener, mContent);
}
}
So, as I've written, best if you just put the application context in case you wish to keep listening to this event.
Or, in case you do wish to stop listening to this event, just unregister it when the activity is being destroyed.
to prove it, you can simply run your app...
here's my proof app:
MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
preferences.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) {
android.util.Log.d("AppLog", "changed!");
}
});
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
startActivity(new Intent(MainActivity.this, Activity2.class));
}
}, 1000);
finish();
}
}
Activity2.java
public class Activity2 extends Activity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activity2);
//if you call here System.gc(); , you have a good chance that the listener won't be called
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//this may or may not cause the listener to write to the log
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(Activity2.this);
preferences.edit().putBoolean("test", true).commit();
}
}, 1000);
}
}
Will the listener receive the callback when the Activity itself has died?
-> No, it won't. Because when your activity dies, the _prefs and _sharedPreferenceListener fields will be destroyed.
You could check this question for more details on OnSharedPreferenceChangeListener :
SharedPreferences.onSharedPreferenceChangeListener not being called consistently
You must un-register the listener in onDestroy() of activity, else Activity object will stay in memory.