FragmentTransaction in WebViewClient.shouldOverrideUrlLoading throws IllegalStateException - java

I seem to be fighting with a race condition, the cause of which I can't seem to pin down. When executing the below code, I intermittently get the stack trace below.
Is there some obvious rule of the Fragment lifecycle I am disobeying? I am not clear on what would explicitly forbid me from performing a transaction here to handle the event.
I am using a WebViewClient to detect external URLs clicked within a local .html document - as in, URLs which point to a non-local host. I am using Otto's EventBus to post those actions to an Activity. When the Activity receives those events, I want to show those external URLs in a different Fragment, by calling FragmentTransaction.replace()
DefaultWebViewClient.java
#Override
public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
boolean shouldOverride;
if (urlIsLocal(url)) {
shouldOverride = super.shouldOverrideUrlLoading(view, url);
} else {
// trigger an event for the fragment to swap out
// return true to tell the webview not to load it...
EventBus.getInstance().post(new LoadExternalUrlEvent(url));
shouldOverride = true;
}
return shouldOverride;
}
FragmentActivity.java
#Subscribe
public void onLoadExternalUrlEvent(LoadExternalUrlEvent externalLoadEvent) {
final BrowserFragment browserFragment = new BrowserFragment();
Bundle args = new Bundle();
args.putSerializable(BrowserFragment.ARG_LOAD_EXTERNAL_URL_EVENT, externalLoadEvent);
browserFragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, browserFragment, BrowserFragment.FRAGMENT_TAG)
.addToBackStack(null).commit();
}
LoadExternalUrlEvent.java
public class LoadExternalUrlEvent implements Serializable {
private static final long serialVersionUID = 1L;
public final String url;
public LoadExternalUrlEvent(String url) {
this.url = url;
}
#Override
public String toString() {
return "LoadExternalUrlEvent [url=" + url + "]";
}
}
EventBus.java
import com.squareup.otto.Bus;
public class EventBus {
private static Bus _INSTANCE;
public static synchronized Bus getInstance() {
if (null == _INSTANCE) {
_INSTANCE = new Bus();
}
return _INSTANCE;
}
}
Stack trace
java.lang.RuntimeException: Could not dispatch event: class <omitted>.LoadExternalUrlEvent to handler [EventHandler public void <omitted>Activity.onLoadExternalUrlEvent(<omitted>LoadExternalUrlEvent)]: Can not perform this action after onSaveInstanceState
at com.squareup.otto.Bus.throwRuntimeException(Bus.java:456)
at com.squareup.otto.Bus.dispatch(Bus.java:386)
at com.squareup.otto.Bus.dispatchQueuedEvents(Bus.java:367)
at com.squareup.otto.Bus.post(Bus.java:336)
at <omitted>DefaultWebViewClient.shouldOverrideUrlLoading(DefaultWebViewClient.java:51)
at com.android.webview.chromium.WebViewContentsClientAdapter.shouldOverrideUrlLoading(WebViewContentsClientAdapter.java:293)
at com.android.org.chromium.android_webview.AwContentsClientBridge.shouldOverrideUrlLoading(AwContentsClientBridge.java:96)
at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5356)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1360)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1378)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
at <omitted>Activity.run(<omitted>Activity.java:162)
at <omitted>Activity.onLoadExternalUrlEvent(<omitted>Activity.java:156)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.squareup.otto.EventHandler.handleEvent(EventHandler.java:89)
at com.squareup.otto.Bus.dispatch(Bus.java:384)
... 15 more
A/libc(2689): Fatal signal 6 (SIGABRT) at 0x00000a81 (code=-6), thread 2689

I discovered the problem.
Because I was calling EventBus.register() in Activity.onCreate() I was getting multiple instances of the Activity on my backstack which would act as responders to these events.
The solution is to either register your Activity as late as possible with
#Override
protected void onResume() {
super.onResume();
EventBus.getInstance().register(this);
}
#Override
protected void onPause() {
EventBus.getInstance().unregister(this);
super.onPause();
}
or to declare your Activity as a single instance with
android:launchMode="singleTask"

Related

Trusted web activity Unable to start activity ComponentInfo Service Intent must be explicit: Intent

I have integrated Trusted Web Activity in my Android App when launching TwalauncherActivity as main Activity, App is not opening, it's stopping suddenly & closing automatically.
I have followed this link to add Trusted web Activity.
Logcat:-
FATAL EXCEPTION: main
Process: com.therightdoctors.cathlab, PID: 2906
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.therightdoctors.cathlab/com.therightdoctors.cathlab.TwaLauncherActivity}: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=android.support.customtabs.action.CustomTabsService }
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2646)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2707)
at android.app.ActivityThread.-wrap12(ActivityThread.java)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1460)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)
Caused by: java.lang.IllegalArgumentException: Service Intent must be explicit: Intent { act=android.support.customtabs.action.CustomTabsService }
TwaLauncherActivity.java:-
package com.therightdoctors.dpmpatient;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsIntent;
import android.support.v7.app.AppCompatActivity;
public class TwaLauncherActivity extends AppCompatActivity
implements TwaSessionHelper.TwaSessionCallback {
private static final String TWA_ORIGIN = "https://dpmp.therightdoctors.com";
private static final String TARGET_URL = TWA_ORIGIN;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_twa_launcher);
Uri originUri = Uri.parse(TWA_ORIGIN);
TwaSessionHelper twaSessionHelper = TwaSessionHelper.getInstance();
twaSessionHelper.setTwaSessionCallback(this);
twaSessionHelper.bindService(this, originUri);
}
#Override
protected void onDestroy() {
super.onDestroy();
TwaSessionHelper twaSessionHelper = TwaSessionHelper.getInstance();
twaSessionHelper.setTwaSessionCallback(null);
}
public void openTwa() {
TwaSessionHelper twaSessionHelper = TwaSessionHelper.getInstance();
// Set an empty transition from TwaLauncherActivity to the TWA splash screen.
CustomTabsIntent customTabsIntent = twaSessionHelper.createIntentBuilder()
.setStartAnimations(this, 0, 0)
.build();
Uri openUri = Uri.parse(TARGET_URL);
twaSessionHelper.openTwa(this, customTabsIntent, openUri);
}
#Override
public void onTwaSessionReady() {
openTwa();
}
#Override
public void onTwaSessionDestroyed() {
}
#Override
public void onTwaOpened() {
finishAndRemoveTask();
}
}
The linked demo is outdated (the link is currently broken too, as the demo was moved here)
This is the current code for the update LauncherActivity, inside the Support Library.
The latest version of the Support Library supporting TWAs is being hosted in JitPack temporarily.
It will be merged into the official support library in the near future.

How can I implement in fragment with my interface in another fragment

I created a interface in a fragment like following:
public interface SGFCallBackInterface {
void itemSelected(Item item);
}
private SGFCallBackInterface mCallbackInterface;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SGFCallBackInterface) {
mCallbackInterface = (SGFCallBackInterface) context;
} else {
throw new RuntimeException(context.toString() + " must implement SelectGdsFragment.SGFCallBackInterface");
}
}
public void setSGFCallBackInterface(SGFCallBackInterface mCallbackInterface) {
this.mCallbackInterface = mCallbackInterface;
}
and I would implement that in another fragment like the following
public class SaleMenageFragment extends Fragment implements SelectGdsFragment.SGFCallBackInterface {
...
SelectGdsFragment selectGdsFragment = new SelectGdsFragment();
selectGdsFragment.setSGFCallBackInterface(SaleMenageFragment.this);
...
}
#Override
public void onItemSelected(Item item) {
...
}
But it's still not working, this is the error log:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.leo.test, PID: 3853
java.lang.RuntimeException: com.leo.test.Control.IndexActivity#2a66aece must implement SelectGdsFragment.SGFCallBackInterface
at com.leo.test.Control.Fragment.SaleManagement.SelectGdsClsFragment.onAttach(SelectGdsFragment.java:42)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1019)
at android.support.v4.app.BackStackRecord.setLastIn(BackStackRecord.java:779)
at android.support.v4.app.BackStackRecord.calculateFragments(BackStackRecord.java:819)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:660)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1617)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:517)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
I think the error log means my IndexActivity is not implementing SelectGdsFragment.SGFCallBackInterface.
But I don't want to implement that in IndexActivity.
I want to implement that in SaleMenageFragment.
How can I do that?
I am afraid you have to implement it in your Activity
See the doc
Communicating with Fragments
Hope it helps!
Just remove the part in onAttach(Context context) method like this:
public interface SGFCallBackInterface {
void itemSelected(Item item);
}
private SGFCallBackInterface mCallbackInterface;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof SGFCallBackInterface) {
mCallbackInterface = (SGFCallBackInterface) context;
} // this wont throw an exception if the activity does not implement that interface
}
public void setSGFCallBackInterface(SGFCallBackInterface mCallbackInterface) {
this.mCallbackInterface = mCallbackInterface;
}

Call Interface function from main class from sub object

Solution:
The following worked fine, but I had two Activities already using my helper class, but forgot to add the Interface to one Activity.
So I got the TypeCastException.
But I did not realize I always looked at the wrong Activity, so I could not find the issue.
I currently write my first android service, which works fine.
Now I realized, that I have to bind unbind, check binding all over and so I thought to put all into a subclass and only do an interface with callbacks for the only functions I need.
But now i am stuck to call my interface functions
onTcBlueServiceConnected()
onTcBlueServiceDisconnected()
I simply want to prevent to write the same code over and over again.
Each activity I have to use the service, I extend from this helper class, instead of the AppCompatActivity.
Maybe I chose the wrong path, but lost with the right words for a search.
As far as I understand bound services, I have to bind from each activity I access the service, and unbind of course when it is destroyed.
So I thought this is a good approach
public abstract class TcBlueServiceHelper extends AppCompatActivity {
private static String LOG_TAG = "TcBlueServiceHelper";
TcBlueService mTcBlueService;
TcBlueService.TcBlueBinder myBinder;
boolean mServiceBound = false;
Context mCallingContext;
public TcBlueServiceHelper(Context callingContext) {
mCallingContext = callingContext;
}
public TcBlueServiceHelper(){
mCallingContext = this;
}
public void bindServiceSave(){
if (mServiceBound == false) {
Intent intent = new Intent(this, TcBlueService.class);
startService(intent);
bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
}
public void unbindServiceSave(){
if (mServiceBound) {
unbindService(mServiceConnection);
mServiceBound = false;
}
}
public TcBlueService.TcBlueBinder getBinder(){
return myBinder;
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
#Override
public void onServiceDisconnected(ComponentName name) {
mServiceBound = false;
((ITcBlueServiceHelper)mCallingContext).onTCBServiceDisconnected(name);
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (TcBlueService.TcBlueBinder) service;
mTcBlueService = myBinder.getService();
mServiceBound = true;
((ITcBlueServiceHelper)mCallingContext).onTCBServiceConnected(name, service);
}
};
public interface ITcBlueServiceHelper{
public void onTCBServiceConnected(ComponentName name, IBinder service);
public void onTCBServiceDisconnected(ComponentName name);
}
}
My first thought to cast result in a "ClassCastException"
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.ClassCastException: de.mobacomp.android.freightweight.StartActivity cannot be cast to de.mobacomp.android.freightweight.TcBlueServiceHelper$ITcBlueServiceHelper
at de.mobacomp.android.freightweight.TcBlueServiceHelper$1.onServiceConnected(TcBlueServiceHelper.java:72)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1068)
at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1085)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4424)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
at dalvik.system.NativeStart.main(Native Method)

Shared Preferences Null Pointer Exception

I am working on adding Shared Preferences to my Note taking Android App and I am running into a peculiar null pointer exception. I am currently testing settings for font size and typeface in my app and have been able to successfully save the shared preferences for both but am having trouble with the retrieval part. When the settings are changed, they successfully change the font and font size on a Fragment for the remainder of time the app is opened but I cannot restore them if the app is restarted.
First weird Null Pointer is in the onCreate.
OnCreate:
//create a new note fragment if one has not been created yet
mNoteFragment = (NoteFragment) getFragmentManager().findFragmentById(R.id.container);
if (mNoteFragment == null) {
mNoteFragment = new NoteFragment();
getFragmentManager().beginTransaction()
.replace(R.id.container, mNoteFragment).commit();
}
//restore SharedPreferences
SharedPreferences sharedPrefs = getPreferences(0);
int stylePref = sharedPrefs.getInt(SharedPreferanceConstants.PREF_FONT_SIZE, 2);
String fontPref = sharedPrefs.getString(SharedPreferanceConstants.PREF_TYPEFACE, "");
Log.e("STYLEID", String.valueOf(stylePref));
Log.e("FONTTYPE", fontPref);
onStyleChange(null , stylePref); //NULL POINTER EXCEPTION HERE
onFontChange(null, fontPref);
The logs output "3" and "Impact" which are the correct values for size and font which indicates that stylePref and fontPref are not null.
The next Null Pointer is below.:
#Override
public void onStyleChange(CustomStyleDialogFragment dialog, int styleId) {
Log.d("NOTEFRAGMENT", String.valueOf(mNoteFragment));
mNoteFragment.setCustomStyle(styleId); //NULL POINTER EXCEPTION HERE
}
#Override
public void onFontChange(CustomStyleDialogFragment dialog, String fontName) {
mNoteFragment.setCustomFont(fontName);
}
I have tested logging the value of styleId and got "3" so that doesn't seem to be the issue. mNoteFragment isnt null either based on the log.
Here is the third NP Exception in the NoteFragment.java. This is where I ultimately set the EditText view to the desired settings. I had no trouble changing font and size without shared preferences so I am not sure the issue here.
public void setCustomFont(String fontName) {
if(fontName.equals("Helvetica")) {
mEditText.setTypeface(mHelvetica);
}
else if(fontName.equals("Helvetica-Neue")) {
mEditText.setTypeface(mHelveticaNeue);
}
else if(fontName.equals("Impact")) {
mEditText.setTypeface(mImpact);
}
else {
mEditText.setTypeface(Typeface.DEFAULT);
}
}
public void setCustomStyle(int styleId) {
if(styleId == 1) {
mEditText.setTextAppearance(getActivity(), android.R.style.TextAppearance_Small);
}
else if(styleId == 2) {
mEditText.setTextAppearance(getActivity(), android.R.style.TextAppearance_Medium);
}
else if(styleId == 3) {
//NULL POINTER EXCEPTION HERE
mEditText.setTextAppearance(getActivity(), android.R.style.TextAppearance_Large);
}
}
And of course, here is the logcat. Thanks in advance!
11-18 10:31:53.030 18966-18966/com.richluick.blocnotes I/Process﹕ Sending signal. PID: 18966 SIG: 9
11-18 10:35:32.838 19248-19248/com.richluick.blocnotes D/STYLEID﹕ 3
11-18 10:35:32.838 19248-19248/com.richluick.blocnotes D/FONTTYPE﹕ Impact
11-18 10:35:32.839 19248-19248/com.richluick.blocnotes D/ERROR﹕ NoteFragment{422972f8 id=0x7f090001}
11-18 10:35:32.840 19248-19248/com.richluick.blocnotes D/AndroidRuntime﹕ Shutting down VM
11-18 10:35:32.840 19248-19248/com.richluick.blocnotes W/dalvikvm﹕ threadid=1: thread exiting with uncaught exception (group=0x4197dd40)
11-18 10:35:32.842 19248-19248/com.richluick.blocnotes E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: com.richluick.blocnotes, PID: 19248
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.richluick.blocnotes/com.richluick.blocnotes.BlocNotes}: java.lang.NullPointerException
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2198)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
at android.app.ActivityThread.access$800(ActivityThread.java:139)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5097)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.NullPointerException
at com.richluick.blocnotes.NoteFragment.setCustomStyle(NoteFragment.java:86)
at com.richluick.blocnotes.BlocNotes.onStyleChange(BlocNotes.java:139)
at com.richluick.blocnotes.BlocNotes.onCreate(BlocNotes.java:61)
at android.app.Activity.performCreate(Activity.java:5248)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1110)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2162)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2257)
            at android.app.ActivityThread.access$800(ActivityThread.java:139)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1210)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5097)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
            at dalvik.system.NativeStart.main(Native Method)
NoteFragment.java
public class NoteFragment extends Fragment {
public EditText mEditText;
private Typeface mHelvetica;
private Typeface mHelveticaNeue;
private Typeface mImpact;
private static final String TEXT = "text";
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString(TEXT, mEditText.getText().toString());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_note, container, false);
setHasOptionsMenu(true);
//restore state of app when activity is destroyed and restarted
mEditText = (EditText) rootView.findViewById(R.id.editText);
if (savedInstanceState != null) {
mEditText.setText(savedInstanceState.getString(TEXT));
}
//Store the font assets as variables
mHelvetica = Typeface.createFromAsset(getActivity().getAssets(), "fonts/Helvetica_Reg.ttf");
mHelveticaNeue = Typeface.createFromAsset(getActivity().getAssets(), "fonts/HelveticaNeue_Lt.ttf");
mImpact = Typeface.createFromAsset(getActivity().getAssets(), "fonts/impact.ttf");
return rootView;
}
/**
* This is a setter method for setting the font the user has selected from the spinner
*
* param fontName the name of the font the user selected
* #return void
* */
public void setCustomFont(String fontName) {
if(fontName.equals("Helvetica")) {
mEditText.setTypeface(mHelvetica);
}
else if(fontName.equals("Helvetica-Neue")) {
mEditText.setTypeface(mHelveticaNeue);
}
else if(fontName.equals("Impact")) {
mEditText.setTypeface(mImpact);
}
else {
mEditText.setTypeface(Typeface.DEFAULT);
}
}
/**
* This is a setter method for setting the font style the user has selected from custom menu
*
* param styleId the integer id of the font stlye selected (SMALL, MEDIUM, LARGE)
* #return void
* */
public void setCustomStyle(int styleId) {
if(styleId == 1) {
mEditText.setTextAppearance(getActivity(), android.R.style.TextAppearance_Small);
}
else if(styleId == 2) {
mEditText.setTextAppearance(getActivity(), android.R.style.TextAppearance_Medium);
}
else if(styleId == 3) {
Log.d("EDITTEXT", String.valueOf(mEditText));
mEditText.setTextAppearance(getActivity(), android.R.style.TextAppearance_Large);
}
}
#Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Inflate the menu; this adds items to the action bar if it is present.
super.onCreateOptionsMenu(menu, inflater);
inflater = getActivity().getMenuInflater();
inflater.inflate(R.menu.bloc_notes, menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_erase) {
mEditText.setText("");
}
return super.onOptionsItemSelected(item);
}
}
In your SomeActivity.onCreate() you are creating an instance of NoteFragment, but even if you commit it via a transaction, it won't be inflated and created right away. Which basically means, the NoteFragment.onCreateView() was not executed and the fragments UI is not yet created. However, your Activity.onCreate() assumes, that mFragment.mEditText is already set, which is not the case, so you run into a NullPointerException.
Take a look at the FragmentTransation.commit method: http://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html#commit()
Schedules a commit of this transaction. The commit does not happen immediately; it will
be scheduled as work on the main thread to be done the next time that thread is ready.
Your mEditText in your NoteFragment is null at this time, hence the NPE.
Just init your member mEditText in onCreate() or onActivityCreated() of the fragment, not onCreateView(). That should ensure that mEditText is not null when you call your setters.
Whatever happens, you should be protecting against a null mEditText in your setter functions.
EDIT this is what I meant:
public void setCustomFont(String fontName) {
if(mEditText != null){
if(fontName.equals("Helvetica")) {
mEditText.setTypeface(mHelvetica);
}
else if(fontName.equals("Helvetica-Neue")) {
mEditText.setTypeface(mHelveticaNeue);
}
else if(fontName.equals("Impact")) {
mEditText.setTypeface(mImpact);
}
else {
mEditText.setTypeface(Typeface.DEFAULT);
}
}
}
EDIT 2: Oncreate will not be a good place. Basically you want to call your setter after the fragmentmanager has committed your change, so that mEditText has been initialized.
EDIT 3 - Try this with your initial code:
getFragmentManager().beginTransaction().replace(R.id.container, mNoteFragment).commit();
getFragmentManager().executePendingTransactions();
Okay so I am not sure why this works but I was able to figure out the anwer based on this question:
onCreateView() in Fragment is not called immediately, even after FragmentManager.executePendingTransactions()
I added the following line
getFragmentManager().executePendingTransactions();
and then moved everthing to the onStart method
#Override
protected void onStart() {
super.onStart();
//create a new note fragment if one has not been created yet
mNoteFragment = (NoteFragment) getFragmentManager().findFragmentById(R.id.container);
if (mNoteFragment == null) {
mNoteFragment = new NoteFragment();
getFragmentManager().beginTransaction().replace(R.id.container, mNoteFragment).commit();
getFragmentManager().executePendingTransactions();
}
//restore SharedPreferences
SharedPreferences sharedPrefs = getPreferences(0);
int stylePref = sharedPrefs.getInt(SharedPreferanceConstants.PREF_FONT_SIZE, 2);
String fontPref = sharedPrefs.getString(SharedPreferanceConstants.PREF_TYPEFACE, "");
Log.d("STYLEID", String.valueOf(stylePref));
Log.d("FONTTYPE", fontPref);
onStyleChange(null , stylePref);
onFontChange(null, fontPref);
}
and for some reason it works fine now. If anyone could explain why that would be great
Try use 'apply' instead of 'commit' , the justification is here -> What's the difference between commit() and apply() in Shared Preference by Lukas Knuth.
But basically, apply() will commits its changes to the in-memory SharedPreferences immediately.
It's strong recommended use a class to manager your preferences, like 'MyPreferencesManager'.

Android JAVA - Service return OutOfMemoryError exception after some while

I am developing my first android app. I have been created a Service class which role is to check if any new information on an external webpage. The HTTP request and service work as i should, but after a while I get these OutOfMemoryError.
Are someone able to see where the Service gather all that memory?
Error message 1.
java.lang.OutOfMemoryError: pthread_create (stack size 16384 bytes) failed: Try again
at java.lang.VMThread.create(Native Method)
at java.lang.Thread.start(Thread.java:1029)
at org.apache.http.impl.conn.tsccm.AbstractConnPool.enableConnectionGC(AbstractConnPool.java:140)
at org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager.createConnectionPool(ThreadSafeClientConnManager.java:120)
at org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager.(ThreadSafeClientConnManager.java:98)
at com.loopj.android.http.AsyncHttpClient.(AsyncHttpClient.java:210)
at com.loopj.android.http.AsyncHttpClient.(AsyncHttpClient.java:149)
at com.loopj.android.http.AsyncHttpClient.(AsyncHttpClient.java:119)
at com.quickit.app.MyService.checkUpdates(MyService.java:89)
at com.quickit.app.MyService.access$1(MyService.java:75)
at com.quickit.app.MyService$TimeDisplayTimerTask$1.run(MyService.java:68)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5105)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:792)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:608)
at dalvik.system.NativeStart.main(Native Method)
Error message 2.
java.lang.OutOfMemoryError: thread creation failed
at java.lang.VMThread.create(Native Method)
at java.lang.Thread.start(Thread.java:1050)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:913)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1295)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:81)
at com.loopj.android.http.AsyncHttpClient.sendRequest(AsyncHttpClient.java:893)
at com.loopj.android.http.AsyncHttpClient.post(AsyncHttpClient.java:688)
at com.loopj.android.http.AsyncHttpClient.post(AsyncHttpClient.java:671)
at com.loopj.android.http.AsyncHttpClient.post(AsyncHttpClient.java:658)
at com.quickit.app.MyService.checkUpdates(MyService.java:90)
at com.quickit.app.MyService.access$1(MyService.java:75)
at com.quickit.app.MyService$TimeDisplayTimerTask$1.run(MyService.java:68)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:213)
at android.app.ActivityThread.main(ActivityThread.java:5092)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:797)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:564)
at dalvik.system.NativeStart.main(Native Method)
My service class.
public class MyService extends Service {
boolean login = false;
// constant
public static final long NOTIFY_INTERVAL = 10 * 1000; // 10 seconds
String address = Utilities.getAPIUrl();
// run on another Thread to avoid crash
private Handler mHandler = new Handler();
// timer handling
private Timer mTimer = null;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
SharedPreferences prefs = getSharedPreferences("com.quickit.app", Context.MODE_PRIVATE);
login = prefs.getBoolean("login", false);
// cancel if already existed
if(mTimer != null) {
mTimer.cancel();
} else {
// recreate new
mTimer = new Timer();
}
// schedule task
mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 0, NOTIFY_INTERVAL);
}
public class TimeDisplayTimerTask extends TimerTask {
#Override
public void run() {
// run on another thread
mHandler.post(new Runnable() {
#Override
public void run() {
if(login) {
checkUpdates();
}
}
});
}
}
private void checkUpdates() {
final SharedPreferences prefs = getSharedPreferences("com.quickit.app", Context.MODE_PRIVATE);
final String from_id = prefs.getInt("user", 0)+"";
final String lastCheck = prefs.getString("last_check", "0");
RequestParams params = new RequestParams();
params.put("type", "get_ask_questions");
params.put("fromid", from_id);
params.put("last_check", lastCheck);
AsyncHttpClient client = new AsyncHttpClient();
client.post(address, params, new AsyncHttpResponseHandler() {
#Override
public void onSuccess(String response) {
try {
notification(response);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
Your code is constantly creating a new AsyncHttpClient object every time that timer expires! If the object never finishes its work, at some point, you will run out of memory.
Since you are just periodically checking for updates, you should make the AsyncHttpClient object static and just reuse it.
Look at http://loopj.com/android-async-http/ specifically, the Recommended Usage section.

Categories

Resources