i am trying to have an activity launched from a service.
My problem is hiding/showing this activity.
The activity is started like so
overlay = new BubbleOverlay();
Intent intent = new Intent(this, overlay.getClass()).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
Activity:
public class BubbleOverlay extends Activity {
private boolean active = false;
private View mainView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.overlay_layout);
mainView = findViewById(R.id.main_overlay_layout);
if(mainView == null)
Log.d("BubbleOverlay", "onCreate: MainView is null");
}
public void setActive(boolean value){
active = value;
}
public void hide(){
active = false;
mainView.setVisibility(View.INVISIBLE);
}
public void show(){
active = true;
mainView.setVisibility(View.VISIBLE);
}
I am trying to toggle the visibility of the mainView from the service.
Handler handler = new Handler();
Runnable runnable_longClick = new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
overlay.hide();
}
};
handler.postDelayed(runnable_longClick, 5000);
this produces the following error:
07-28 05:51:56.549 21690-21690/com.derp.derp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.derp.derp, PID: 21690
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setVisibility(int)' on a null object reference
at com.derp.derp.BubbleOverlay.hide(BubbleOverlay.java:37)
at com.derp.derp.BubbleService$4.run(BubbleService.java:181)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6044)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
The reason you're getting the Null Pointer Exception is the mainView has not been initialised before its use. This happens because the Activity's onCreate() method which holds the object's initialise statement has not been called (with respect to the overlay object you created inside your Service). The onCreate() method will only be called once the Activity is created.
In order to call the hide() method of the Activity from the Service you need to create a proper communication channel between the Activity and the Service i.e. you need to bind the Service to your Activity. I know, this approach requires a bit of coding efforts but its the best approach and it will guarantee you flawless and smooth functioning for your app.
Related
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...
}
});
}
}
Everytime the application go in background, and the RAM start filling out, the application start loosing variables (if im not wrong, this is the normal behavior of Android, it loose the instances of variables)
When I bring back the app from background the application crash, because I loose the MainActivity.
There is a way to relaunch this activity if there is not activity at all?
Is an ambiguous question but I found this question but I don't think is the best choice
ActivityManager mngr = (ActivityManager) getSystemService( ACTIVITY_SERVICE );
List<ActivityManager.RunningTaskInfo> taskList = mngr.getRunningTasks(10);
if(taskList.get(0).numActivities == 1 &&
taskList.get(0).topActivity.getClassName().equals(this.getClass().getName())) {
Log.i(TAG, "This is last activity in the stack");
}
Im loosing the full activity, well I can get the activity
#Override
public void onAttach(Activity activity) {
LocalNotification.registerReceiver(reloadReceiver, LocalNotification.RELOAD_CARTMG);
try {
mListener = (OnFragmentInteractionListener) activity;
MainActivity ma = (MainActivity)activity; //THIS IS LOOSE
ma.openMyList = false;
ma.openMyAddress = false;
ma.hideSearchButton();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnFragmentInteractionListener");
}
super.onAttach(activity);
}
This is the Log inside the app
com.XXXX.mg E/Uncaught Exception detected in thread {}: Unable to start activity ComponentInfo{com.XXXX.mg/com.XXXX.view.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.XXXX.view.MainActivity.showShoppingCartIcon()' on a null object reference
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.XXXX.mg/com.XXXX.view.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.XXXX.view.MainActivity.showShoppingCartIcon()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2426)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.XXXX.view.MainActivity.showShoppingCartIcon()' on a null object reference
at com.XXXX.view.shoppingCart.ShoppingCartFragment.onDetach(ShoppingCartFragment.java:411)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1202)
at android.support.v4.app.FragmentManagerImpl.removeFragment(FragmentManager.java:1349)
at android.support.v4.app.BackStackRecord.popFromBackStack(BackStackRecord.java:915)
at android.support.v4.app.FragmentManagerImpl.popBackStackState(FragmentManager.java:1722)
at android.support.v4.app.FragmentManagerImpl$3.run(FragmentManager.java:593)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617)
at android.support.v4.app.FragmentController.execPendingActions(FragmentController.java:339)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:602)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1260)
at android.app.Activity.performStart(Activity.java:6261)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2389)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
at android.app.ActivityThread.-wrap11(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5443)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
I'm not sure if your app crashes just because you loose your MainActivity, you should be coding expecting this kind of behaviour, using onSaveInstanceState like #pablobu suggested is the right approach.
Can we see the logs? If you're loosing the state of your instances that's fine, you should save those states and restore them Recreating an Activity is it because of Fragments? you can also save those too, Handling Fragments Tutorial this is a good read.
Please, can you give us your logcat output, that might help a lot for us to see whats really going on.
EDIT
With your Logcat edit, try to take a look to the fragment lifecycle, onAttach executes first than onActivityCreated(), so if your Activity is already destroyed, your onAttach method will not have your activity, you should wait until your Activity is recreated .
Furthermore, you have your
super.onAttach(activity);
at the end of your code block, put it before your code block.
/**
This method was deprecated in API level 23.
Use onAttach(Context) instead.
*/
#Override
public void onAttach(Context context) {
super.onAttach(context);
MainActivity mainActivity;
if (context instanceof Activity) {
mainActivity = (MainActivity) context;
try {
mListener = (OnFragmentInteractionListener) mainActivity;
/**
Since you are getting a reference and accesing attributes you should be careful
with NullPointerException, check if not null first.
Or better yet refactor a little your code using an interface to handle this behaviour or use
the one you already created and just tell the activity what to do.
*/
/*if(mainActivity != null) {
ma.openMyList = false;
ma.openMyAddress = false;
ma.hideSearchButton();
}*/
} catch (ClassCastException e) {
throw new ClassCastException(mainActivity.getClass().getSimpleName() + " must implement OnFragmentInteractionListener");
}
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//since your Listener is global use mListener behaviour that I suggested here.
if (mListener != null){
mListener.openListOfSomething(false);
mListener.hideSearch();
}
}
You can use the Bundle savedInstatceState, wich is given in OnCreate and similar methods:
private String aVariable;
#Override
public void onCreate (Bundle savedInstantceState) {
super.onCreate(savedInstanceState);
// Get the variable from savedInstanceState, if it isn't empty.
// If it is empty, it could be the first Activity run.
try {
aVariable = savedInstanceState.get("Key");
} catch (NullPointerException e) {
aVariable = "Baum";
}
}
#Override
public void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putString(aVariable, "Key");
}
These methods set the Variable "aVariable" to "Baum" on the first run
(NullPointerException, because there is no String to "Key").
Later, when the Activity is going to be destroyed, onSaveInstanceState will be called. There you save your variables (aVariable) under a Key ("Key").
i am trying to put a list in my app with a listener on each element where clicking on it opens a new activity with a parameter given by the field.
It sounds quite easy but as i moved from listview to recyclerview i am facing an error
The first time i click on an item, and only the first one, it takes 5/6 seconds to open and throws an exception like the following
RuntimeException: Performing stop of activity that is not resumed
on the activity that keeps the list
This is the code i am using to set up the listener in the viewholder
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final AdapterRequestListController controller;
// each data item is just a string in this case
public TextView elapsedTime;
public View rootView;
public ViewHolder(View v, AdapterRequestListController controller) {
super(v);
elapsedTime = (TextView) v.findViewById(R.id.rlr_reply_title);
rootView = v;
this.controller = controller;
v.setOnClickListener(this);
}
#Override
public void onClick(View v) {
controller.openReply(getAdapterPosition());
}
}
I looked for the problem in the controller (that is actually my activity) but nothing changed, ao i moved to the adapter and the very strange thing is that if i put the listener in the onbind method of the adapter it does not happen
I don't like to put it there for many reasons (tons of listener updated every time it scrolls)
Any suggestions or ideas? Thanks!
This is the complete exception
E/ActivityThread: Performing stop of activity that is not resumed: {it.mb.s/it.mb.s.working.ActivityVI}
java.lang.RuntimeException: Performing stop of activity that is not resumed: {it.mb.s/it.mb.s.working.ActivityVI}
at android.app.ActivityThread.performStopActivityInner(ActivityThread.java:3465)
at android.app.ActivityThread.handleStopActivity(ActivityThread.java:3550)
at android.app.ActivityThread.-wrap20(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1373)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
UPDATE 2
this is the method that actually runs the intent
#Override
public void openReply(int position) {
//TODO
Log.d(TAG, position + " fired");
Intent intent = new Intent(this, ActivityViewReply.class);
Bundle bundle = new Bundle();
bundle.putSerializable(ActivityViewReply.REQUEST_FIELD_NAME, meRequests.get(position));
intent.putExtras(bundle);
startActivity(intent);
}
Try flag for your intent .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
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'm getting an NPE when I start an activity in my application. It doesn't happen right away when I boot my phone and start debugging on it. After several dozen new builds are pushed to my phone it eventually starts crashing with this error every single time. I can remedy it temporarily by opening a few other activities first before I activate the errant one.
Any ideas what could cause this? It's a somewhat complicated Activity with a DrawerLayout, and a fragment that contains a SwipeRefreshLayout with a ListView.
Exception
java.lang.RuntimeException: Unable to start activity ComponentInfo{}: java.lang.NullPointerException: Attempt to read from field 'boolean android.support.v4.app.BackStackRecord.mAddToBackStack' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2298)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2360)
at android.app.ActivityThread.access$800(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1279)
Activity Code
public class TastingsActivity extends BaseActivity implements TastingListFragment.Callbacks {
public static final String TAG = TastingsActivity.class.getSimpleName();
private TastingListFragment mTastingListFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tastings);
//create fragment
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
TastingListFragment fragment = new TastingListFragment();
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
fragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.container, fragment).commit();
}
}
#Override
protected int getSelfNavDrawerItem() {
return NAVDRAWER_ITEM_TASTINGS;
}
//================================================================================
// Handlers
//================================================================================
#Override
public void onTastingSelected(Tasting tasting) {
Intent intent = new Intent(this, TastingActivity.class);
intent.putExtra(TastingDetailsFragment.EXTRA_TASTING_ID, tasting.getId());
startActivity(intent);
}
}