my application started crashing after the implementation of google maps into a separate tab in my android application. i have looked through all other classes and logcat and deemed those classes to lightweight to be causing this issue.
here is my maps class
public class FragmentShouts_Maps extends Fragment implements OnMapReadyCallback {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Get the view from fragment shouts.xml
View view = inflater.inflate(R.layout.fragmentshouts_maps, container, false);
SupportMapFragment fragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.maps);
if (fragment!= null) {
fragment.getMapAsync(this);
}
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
#Override
public void onMapReady (GoogleMap googleMap) {
}
}
i've noticed when i use an emulator with a lower dpi and without google play my application will make it to this tab but will be stopped and ended. on newer api(23) with google play services it wont even make it that far, once i swipe away from the home tab, it becomes unresponsive and ends.
i have no error codes so should work once i implement the doinbackground method i believe i just dont understand how to implement this?
Don't know whats wrong with your code but I implemented google maps in my app and it works just fine. See if this is the kind of doInBackground implementation you are looking for. Hope this helps.
public class MapActivity extends Activity implements OnMapReadyCallback{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map);
MapFragment mapFragment = (MapFragment)getFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(final GoogleMap map){
// Your Code
AsyncTask<String,Void,String> asyncTask = new AsyncTask<String,Void,String>(){
#Override
protected String doInBackground(String... params) {
// Do Something
}
#Override
protected void onPostExecute(String json) {
// Do Something
}
};
asyncTask.execute(url);
}
}
Related
I'm creating a simple application using Mapbox (using mapbox-android-sdk:7.1.0 ) in a fragment.
I have an activity which is composed of a FrameLayout (containing the Fragments) and a button.
At the beginning, the Fragment1 (containing the map) is displayed in the FrameLayout. When the user clicks on the button, the Fragment1 is replaced by the Fragment2 (containing a TextView).
During the transition there is a short black screen.
When I use Mapbox in an activity I don't have any problem, It seems to happen when the method onDestroyView() is called.
MainActivity.java :
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Load Fragment1 containing the map
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new Fragment1()).commit();
AppCompatButton button = findViewById(R.id.btn_changeFragment);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// Load Fragment2 containing a TextView
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new Fragment2()).addToBackStack(null).commit();
}
});
}
}
Fragment1.java :
public class Fragment1 extends SupportMapFragment {
private MapView mapView;
public Fragment1(){}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Mapbox.getInstance(getContext(), getString(R.string.mapbox_token));
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.fragment_1, container, false);
mapView = view.findViewById(R.id.mapView);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(#NonNull final MapboxMap mapboxMap) {
mapboxMap.setStyle(Style.LIGHT, new Style.OnStyleLoaded() {
#Override
public void onStyleLoaded(#NonNull Style style) {
// Configure the map
}
});
}
});
return view;
}
#Override
public void onStart() {
super.onStart();
mapView.onStart();
}
#Override
public void onResume() {
super.onResume();
mapView.onResume();
}
#Override
public void onPause() {
super.onPause();
mapView.onPause();
}
#Override
public void onStop() {
super.onStop();
mapView.onStop();
}
#Override
public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override
public void onDestroyView() {
super.onDestroyView();
mapView.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
}
Using this Attribute app:mapbox_renderTextureMode="true" while loading mapview inside the fragment did the trick for me.
This is a known issue with how MapFragment's behave: https://github.com/mapbox/mapbox-gl-native/issues/9570. Per the ticket, there are currently two possible ways to resolve this issue in your app.
Either:
Use a bitmap of the map in an ImageView while transitioning instead of a MapView. You can then make the MapView visible as part of the OnMapReady callback in your activity.
Or:
Use a TextureView implementation instead. This can be enabled via MapboxMapOptions or .xml attributes. You should also keep in mind that this solution may lead to performance issues down the road.
I tried the first solution proposed by #riastrad but I didnĀ“t find how to make a bitmap of the mapView ( I always had a white screen with the mapbox logo at the bottom).
Anyway, I tried to use setAlpha to change the opacity of the mapView, and it works. I don't realy know why it doesn't work with setVisibility(View.INVISIBLE).
So before to load the Fragment2, I call a method from Fragment1 to set the opacity of the mapView to 0.
MainActivity.java
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if(currentFragment instanceof Fragment1){
// Set the opacity of the mapView to 0
((Fragment1) currentFragment).transition();
// Load Fragment2 containing a TextView
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, new Fragment2()).addToBackStack(null).commit();
}else {
getSupportFragmentManager().popBackStack();
}
}
});
Fragment1.java
public void transition(){
mapView.setAlpha(0f);
}
I have a fragment (ApartmentFragment) which is a page that show details of an apartment. I want to add a little map to show where the apartment is.
just for clarity, I'm using the MaterialNavigationDrawer that is on github, my app is composed by a single Activity (the navigation drawer) and many fragments.
Every stackoverflow post I have read didn't helped me. Many of them were a bit confusing. I don't want only the map inside ApartmentFragment, i want to show other things plus the map.
Which is the best way to do this?
Is possible to put a mapfragment inside a fragment? or I need to transform ApartmentFragment in activity?
most of what u read it's correct. You'll just use MapView instead of MapFragment
https://developer.android.com/reference/com/google/android/gms/maps/MapView.html
then on your ApartmenFragment you have to make all callbacks to mapview
onCreate(Bundle)
onResume()
onPause()
onDestroy()
onSaveInstanceState()
onLowMemory()
like this:
private MapView mapView;
#Nullable #Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// inflate your view `root` and then call ...
mapView = (MapView) root.indViewById(R.id.map_view);
mapView.onCreate(savedInstanceState);
mapView.getMapAsync(this);
return root;
}
#Override public void onResume() {
super.onResume();
mapView.onResume();
}
#Override public void onPause() {
super.onPause();
mapView.onPause();
}
#Override public void onDestroyView() {
super.onDestroyView();
mapView.onDestroy();
}
#Override public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
mapView.onSaveInstanceState(outState);
}
#Override public void onLowMemory() {
super.onLowMemory();
mapView.onLowMemory();
}
#Override public void onMapReady (GoogleMap googleMap) {
// from https://developer.android.com/reference/com/google/android/gms/maps/OnMapReadyCallback.html
// and here you can do your map stuff with `googleMap`
}
I've got an app which is quite intensive in terms of memory and i'm trying to get all I can. I've noticed something which I can't work out, Google maps is keeping its allocation of memory even after (I think) i'm getting rid of it.
Before Google Maps is called:
After Google Maps is called:
Returned from Google Maps with back button:
Returned from Google Maps with on info window press button:
(the slightly higher number is just due to the fact I included a couple of methods more to include a marker to return)
As you can see, it's retaining a lot of the memory. So i'll show you how this is working:
Google Maps is not actually called, but a container for Google Maps is, and it's below:
public class GoogleMapsAndBookmarksContainer extends ActionBarActivity {
private FragmentTabHost mTabHost;
public ArrayList frameListContainer;
public HashMap<String, HashMap> bookmarkInfo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_google_maps_and_bookmarks_container);
frameListContainer = (ArrayList) getIntent().getSerializableExtra("arrayListWithFrameAttributes");
bookmarkInfo = (HashMap) getIntent().getSerializableExtra("hashmapWithBookmarks");
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), android.R.id.tabcontent);
// The first tab is the google maps fragment which i'll include below
mTabHost.addTab(
mTabHost.newTabSpec("tab1").setIndicator("Google Maps", null),
GoogleMapsFragment.class, null);
mTabHost.addTab(
mTabHost.newTabSpec("tab2").setIndicator("Bookmarks", null),
BookmarksFragment.class, null);
}
#Override
protected void onDestroy() {
super.onDestroy();
/// **** THIS WAS JUST ADDED A LOSS FOR WHAT IT COULD BE RETAINING ****
mTabHost = null;
frameListContainer = null;
bookmarkInfo = null;
}
// access for fragments
public HashMap getBookmarkInfo(){
return bookmarkInfo;
}
public ArrayList getFrameAttributesArrayList(){
return frameListContainer;
}
}
I managed to remove everything from my class but this and still the problem remain:
public class GoogleMapsFragment extends Fragment {
private GoogleMap map;
private SupportMapFragment mapFragment;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_google_maps, container, false);
FragmentManager fm = getChildFragmentManager();
mapFragment = (SupportMapFragment) fm.findFragmentById(R.id.map);
mapFragment.getMapAsync(new OnMapReadyCallback() {
public void onMapReady(GoogleMap googleMap) {
map = googleMap;
initMap();
}
});
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// frameListContainer = ((GoogleMapsAndBookmarksContainer) this.getActivity()).getFrameAttributesArrayList();
// bookmarkInfo = ((GoogleMapsAndBookmarksContainer) this.getActivity()).getBookmarkInfo();
// markerToFrameAndRoute = new HashMap<Marker, String[]>();
}
#Override
public void onDestroy() {
super.onDestroy();
mapFragment = null;
System.gc();
map = null; // I did add this in an edit, but it was mistakenly taken away when I was cutting it up, sorry
}
}
So, is this solvable? I can't work out what it is that it's retaining. I did notice if I keep going back on Google Maps and off it, it is garbage collecting it, so it's not a memory leak.
edit: have tried map.clear(); still keeps the memory
Just clear map
#Override
public void onDestroy() {
super.onDestroy();
map.clear();
}
One probable solution to this problem may be to use a retained fragment using setRetainInstance(boolean retain). In this process the map fragment wont destroy and recreate itself dumping a lot of memory, instead it will just retain the memory when it is needed, when you switch map fragment back and forth (by pressing back button).
public class RetainMapActivity extends FragmentActivity {
private GoogleMap mMap;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.basic_demo);
SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.map);
if (savedInstanceState == null) {
// First incarnation of this activity.
mapFragment.setRetainInstance(true);
} else {
// Reincarnated activity. The obtained map is the same map instance in the previous
// activity life cycle. There is no need to reinitialize it.
mMap = mapFragment.getMap();
}
setUpMapIfNeeded();
}
#Override
protected void onResume() {
super.onResume();
setUpMapIfNeeded();
}
private void setUpMapIfNeeded() {
if (mMap == null) {
mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
if (mMap != null) {
setUpMap();
}
}
}
private void setUpMap() {
mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0)).title("Marker"));
}
Another solution to this may be to use a Otto event bus which is designed to decouple different parts of your application while still allowing them to communicate efficiently.
Hope this Helps!!
I am trying to get google maps v2 working in my app. I have seen several examples showing how you can open up SupportMapFragment inside an activity. The idea being that your activity will call setContentView(R.layout.map_layout); where map_layout.xml links to the fragment with the lines:
android:name="com.google.android.gms.maps.SupportMapFragment"
xmlns:map="http://schemas.android.com/apk/res-auto"
The "name=" line effectively says that "this layout is to be controlled by a fragment of type 'SupportMapFragment'".
My complication is that I am attempting to get the map to appear in an activity with tabs (implemented with actionbarsherlock). This means that whatever fragment corresponds to a tab selection must implement a TabListener. But SupportMapFragment doesn't. So now presumably I need to create a new fragment like so:
public class MyMapFragmentWithTabListener extends SupportMapFragment implements TabListener
{
But now I have got all confused about how to write the contents of MapFragmentWithTabListener in particular onCreateView... should I be inflating some layout? Surely I can't be inflating exactly the same map_layout.xml from the examples because that already declares that it is controlled by SupportMapFragment, whereas in this implementation it should be controlled by MyMapFragmentWithTabListener - do I need a slightly different xml file to inflate (if so, what should it look like?) - or should I be creating my view programatically?
I've done this in quite a few applications now. Instead of extending SupportMapFragment, you just create your own MapFragment. You can have your own layout, with a MapView view inside of it. The key is to route the lifecycle events of the Fragment to the MapView, and bobs your uncle.
Heres some example code:
MapFragment
package com.example.testapplication;
import java.util.concurrent.TimeUnit;
import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
public class TestMapFragment extends Fragment {
private MapView mMapView;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.home_fragment, container, false);
mMapView = (MapView) view.findViewById(R.id.mapview);
// inflat and return the layout
mMapView.onCreate(savedInstanceState);
mMapView.onResume();// needed to get the map to display immediately
try {
MapsInitializer.initialize(getActivity());
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
}
GoogleMap googleMap = mMapView.getMap();
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
/*
* Using a mapview in a fragment requires you to 'route'
* the lifecycle events of the fragment to the mapview
*/
#Override
public void onResume() {
super.onResume();
if (null != mMapView)
mMapView.onResume();
}
#Override
public void onPause() {
super.onPause();
if (null != mMapView)
mMapView.onPause();
}
#Override
public void onDestroy() {
super.onDestroy();
if (null != mMapView)
mMapView.onDestroy();
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (null != mMapView)
mMapView.onSaveInstanceState(outState);
}
#Override
public void onLowMemory() {
super.onLowMemory();
if (null != mMapView)
mMapView.onLowMemory();
}
}
And the layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:map="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<com.google.android.gms.maps.MapView
android:id="#+id/mapview"
android:layout_width="match_parent"
android:layout_height="match_parent"
map:uiZoomControls="false" />
</RelativeLayout>
You can used this way.
public class NewActivity extends FragmentActivity {
private GoogleMap mMap;
SupportMapFragment mapFragment;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activit);
mapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
if(isGooglePlayServicesIsInstalled(mContext)){
mMap = mapFragment.getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
mMap.getUiSettings().setCompassEnabled(true);
mMap.getUiSettings().setZoomControlsEnabled(true);
}else{
//display any toast message
//Global.Toast("Please First install Google Maps");
}
public static boolean isGooglePlayServicesIsInstalled(Context context){
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(context);
if (resultCode == ConnectionResult.SUCCESS)
return true;
else
return false;
}
Please check it out all the permission,api key and require all the thing.if you getting any error log then put as a comment.
My problem is the same as the one in the link below, but I couldn't figure out the solution. And could not work out how to ask the author what his solution was.
Fragment with map crashes when I change activity and come back to the map
My app works fine until the orientation changes, Touch works beautiful up until that point. On orientation change it redraws the MapView as it should, but as soon as the user does any onTouch action on the map it crashes the app with:
android.view.WindowManager$BadTokenException error Unable to add window --token android.view.ViewRootImpl$blahblahblah is not valid.
I'm using ActionBarSherlock with the googlemaps plugin as suggested in the same tutorial the other question used. I just don't get how I'm supposed to create a new listener for the onTouch event after the activity restarts on orientation change. I have tried just creating a new OnTouchListener to the onTouch event but as I'm unsure, and can't find any examples, I haven't been able to guess my way to get a working Touch interface on the MapView again.
I'm sure it's something simple I'm missing.
The code I have:
public MainActivity extends SherlockFragmentActivity {
private MapView mapView;
private MapFragment mMapFragment;
public void onCreate(Bundle savedInstanceState){
View view = getLayoutInflater().inflate(R.layout.mapLayout, null);
mapView = (MapView)view.findViewById(R.id.map);
mapView.setBuiltInZoomControls(true);
setContentView(R.layout.activity_main);
}
public void onResume(){
setupFragments();
}
private void setupFragments(){
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
mMapFragment = (MapFragment) getSupportFragmentManager().findFragmentByTag(MapFragment.TAG);
if (mMapFragment == null){
mMapFragment = new MapFragment(mapView);
ft.add(R.id.fragment_container, mMapFragment, MapFragment.TAG);
}
ft.show(mMapFragment);
ft.commit();
}
}
public class MapFragment extends SherlockFragment{
public static final String TAG = "MapFragment";
private MapView mapView;
private MapOverlay itemizedOverlay;
public MapFragment(MapView mapView){
this.mapView=mapView;
}
public void onResume(){
super.onResume();
List<Overlay> mapOverlays = mapView.getOverlays();
itemizedOverlay(defaultActiveRoad, mapView.getContext());
mapOverlays.add(itemizedOverlay);
}
public View onCreateView(LayoutInflater inflater, ViewGroup vg, Bundle savedInstanceBundle){
super.onCreateView(inflater,vg,savedInstanceBundle);
return mapView;
}
public void onViewCreated(View view, Bundle savedInstanceState){
super.onViewCreated(view, savedInstanceState);
}
public void onDestroyView(){
super.onDestroyView();
((ViewGroup)mapView.getParent()).removeView(mapView);
}
public void setMapView (MapView mapView){
this.mapView = mapView;
}
public MapView getMapView(){
return mapView;
}
}
The error log contains
android.view.WindowManager$BadTokenException: Unable to add window -- token android.view.ViewRootImpl$W#40fab3b0 is not valid; is your activity running?
at android.view.ViewRootImpl.setView(ViewRootImpl.java:585)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:326)
at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:224)
at android.view.WindowManagerImpl$CompatModeWrapper.addView(WindowManagerImpl.java:149)
at android.view.Window$LocalWindowManager.addView(Window.java:547)
at android.widget.ZoomButtonsController.setVisible(ZoomButtonsController.java:371)
at com.google.android.maps.MapView.displayZoomControls(MapView.java:1055)
at com.google.android.maps.MapView$1.onDown(MapView.java:341)
at com.google.android.maps.GestureDetector.onTouchEvent(GestureDetector.java:517)
at com.google.android.maps.MapView.onTouchEvent(MapView.java:685)
After hitting my head against a wall for a few days, I decided to upgrade the map system to Google Maps Android API v2. This has solved the problem I was having with the zoom controls.
After following the instructions on: Using ActionBarSherlock With the New SupportMapFragment
I created a SherlockMapFragment as instructed in the instructions above, but in my app package. My MapFragment now extends the new SherlockMapFragment just created.
public class MapFragment extends SherlockMapFragment {
public static final String TAG = "MapFragment";
private GoogleMap mapView;
private Application global;
public MapFragment(){
}
public void onCreate(Bundle savedInstanceBundle){
super.onCreate(savedInstanceBundle);
setRetainInstance(true);
}
public void onResume(){
super.onResume();
global = (Application)getSherlockActivity().getApplication();
}
public View onCreateView(LayoutInflater inflater, ViewGroup vg, Bundle savedInstanceBundle){
View view=super.onCreateView(inflater, vg, savedInstanceBundle);
mapView = getMap();
return view;
}
}