1

我正在嘗試使用SupportFragment Google地圖與導航抽屜,每隔兩秒使用從服務器拉出的新疊加層初始化地圖。我還應該提到google maps supportfragment是默認的supportfragment,所以它應該在應用程序啓動時打開。問題是,當我運行它時,該應用程序非常緩慢且無響應,提示我的手機打開一個對話框,要求關閉應用程序,因爲它沒有響應。我假設它可能是getMapAsync(),但這只是一個非常廣泛的猜測。我試圖解決這個問題,使得應用程序只是一個普通的片段而沒有導航畫圖時的響應。以下是與問題相關的一些文件的代碼。如果您需要任何其他信息或任何其他文件,請隨時詢問。SupportFragment Map in Navigation抽屜活動非常緩慢且無響應

MainActivity.java:

package com.main.main; 

import android.app.FragmentManager; 
import android.graphics.Color; 
import android.os.AsyncTask; 
import android.os.Bundle; 
import android.support.design.widget.FloatingActionButton; 
import android.support.design.widget.Snackbar; 
import android.view.View; 
import android.support.design.widget.NavigationView; 
import android.support.v4.view.GravityCompat; 
import android.support.v4.widget.DrawerLayout; 
import android.support.v7.app.ActionBarDrawerToggle; 
import android.support.v7.app.AppCompatActivity; 
import android.support.v7.widget.Toolbar; 
import android.view.Menu; 
import android.view.MenuItem; 
import android.widget.Toast; 

import com.google.android.gms.maps.CameraUpdateFactory; 
import com.google.android.gms.maps.GoogleMap; 
import com.google.android.gms.maps.OnMapReadyCallback; 
import com.google.android.gms.maps.SupportMapFragment; 
import com.google.android.gms.maps.model.CameraPosition; 
import com.google.android.gms.maps.model.LatLng; 
import com.google.android.gms.maps.model.LatLngBounds; 
import com.google.android.gms.maps.model.Polygon; 
import com.google.android.gms.maps.model.PolygonOptions; 

import java.sql.Connection; 
import java.sql.DriverManager; 
import java.sql.PreparedStatement; 
import java.sql.ResultSet; 
import java.sql.SQLException; 
import java.util.ArrayList; 
import java.util.List; 
import java.util.Timer; 
import java.util.TimerTask; 
import java.util.concurrent.ExecutionException; 

public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener, OnMapReadyCallback { 
private SupportMapFragment sMapFragment; 
private android.support.v4.app.FragmentManager sFm; 

private GoogleMap mMap; 
List<ParkingSpot> list; //list of parking spots 
private static final LatLng DEFAULT_ZOOM_IN = new LatLng(40, -120); //default zoom-in of map 
public static final String IP_ADDRESS = "some ip"; 
public static final String DATABASE = "some db"; 
public static final String USER_USERNAME = "something"; 
public static final String USER_PASSWORD = "something"; 
public LatLngBounds bounds; 

@Override 
protected void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    sMapFragment = SupportMapFragment.newInstance(); 

    setContentView(R.layout.activity_main); 
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
    setSupportActionBar(toolbar); 

    FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); 
    fab.setOnClickListener(new View.OnClickListener() { 
     @Override 
     public void onClick(View view) { 
      Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) 
        .setAction("Action", null).show(); 
     } 
    }); 

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
      this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close); 
    drawer.setDrawerListener(toggle); 
    toggle.syncState(); 

    NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 
    navigationView.setNavigationItemSelectedListener(this); 
    if(bounds == null) { 
     sFm = getSupportFragmentManager(); 
     sFm.beginTransaction().remove(sMapFragment).commit(); 
     sFm.beginTransaction().add(R.id.map, sMapFragment).commit(); 
    } 
    try { 
     Object result = new LongOperation().execute().get(); 
    } catch (InterruptedException e) { 
     throw new NullPointerException("INTERRUPTED EXCEPTION"); 
    } catch (ExecutionException e) { 
     throw new NullPointerException("EXECUTION EXCEPTION"); 
    } 
} 

@Override 
public void onBackPressed() { 
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 
    if (drawer.isDrawerOpen(GravityCompat.START)) { 
     drawer.closeDrawer(GravityCompat.START); 
    } else { 
     super.onBackPressed(); 
    } 
} 

@Override 
public boolean onCreateOptionsMenu(Menu menu) { 
    // Inflate the menu; this adds items to the action bar if it is present. 
    getMenuInflater().inflate(R.menu.main, menu); 
    return true; 
} 

@Override 
public boolean onOptionsItemSelected(MenuItem item) { 
    // Handle action bar item clicks here. The action bar will 
    // automatically handle clicks on the Home/Up button, so long 
    // as you specify a parent activity in AndroidManifest.xml. 
    int id = item.getItemId(); 

    //noinspection SimplifiableIfStatement 
    if (id == R.id.action_settings) { 
     return true; 
    } 

    return super.onOptionsItemSelected(item); 
} 

@SuppressWarnings("StatementWithEmptyBody") 
@Override 
public boolean onNavigationItemSelected(MenuItem item) { 
    // Handle navigation view item clicks here. 
    FragmentManager fm = getFragmentManager(); 

    int id = item.getItemId(); 

    sFm.beginTransaction().hide(sMapFragment).commitAllowingStateLoss(); 
    if (id == R.id.nav_map) { 
     Toast.makeText(MainActivity.this, "MAP CLICKED!", Toast.LENGTH_SHORT); 
     sFm.beginTransaction().show(sMapFragment).commitAllowingStateLoss(); 
    } else if (id == R.id.nav_history) { 
     Toast.makeText(MainActivity.this, "HISTORY CLICKED!", Toast.LENGTH_SHORT); 
    } else if (id == R.id.nav_notifications) { 
     Toast.makeText(MainActivity.this, "NOTIFICATIONS CLICKED!", Toast.LENGTH_SHORT); 
    } else if (id == R.id.nav_help) { 
     Toast.makeText(MainActivity.this, "HELP CLICKED!", Toast.LENGTH_SHORT); 
    } else if (id == R.id.nav_settings) { 
     Toast.makeText(MainActivity.this, "SETTINGS CLICKED!", Toast.LENGTH_SHORT); 
    } 
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); 
    drawer.closeDrawer(GravityCompat.START); 
    return true; 
} 

@Override 
public void onMapReady(GoogleMap googleMap) { 
    try { 
     Object result = new LongOperation().execute().get(); 
     mMap = googleMap; 
     mMap.clear(); 
     if (bounds == null) { //only completes first time when bounds == null 
      CameraPosition cameraPosition = new CameraPosition.Builder() //zooms in camera on default location 
        .target(DEFAULT_ZOOM_IN) //location (LatLng) 
        .zoom(18) //zoom integer 
        .build(); //builds camera 
      mMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); //animates camera to spot 
      mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); //sets map to hybrid type 
     } 
     bounds = mMap.getProjection().getVisibleRegion().latLngBounds; //gets latlng for next query based on camera spot 
     PolygonOptions rectOptions; 
     for (ParkingSpot spot : list) { //loops through ArrayList of Parking Spots, adding them to map 
      rectOptions = new PolygonOptions() 
        .addAll(spot.getCoordinates()) 
        .clickable(true); //adds all coordinates to map 
      if (spot.getStatus() == 'F') { //see ParkingSpot.java for char definitions/explanations 
       rectOptions.fillColor(Color.RED); 
      } else if (spot.getStatus() == 'T') { 
       rectOptions.fillColor(Color.GREEN); 
      } else if (spot.getStatus() == 'H') { 
       rectOptions.fillColor(Color.YELLOW); 
      } 
      Polygon polygon = mMap.addPolygon(rectOptions); //adds rectangle to map 
      spot.setPolygonId(polygon.getId()); 
     } 
     mMap.setOnPolygonClickListener(new GoogleMap.OnPolygonClickListener() { 
      @Override 
      public void onPolygonClick(Polygon polygon) { 
       Toast.makeText(MainActivity.this, "You clicked on polygon " + getInfoById(polygon.getId()), Toast.LENGTH_SHORT).show(); 
      } 
     }); 
     mMap.setMyLocationEnabled(true); 
     mMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() { 
      @Override 
      public boolean onMyLocationButtonClick() { 
       return false; 
      } 
     }); 
    } catch (SecurityException e) { 
     throw new NullPointerException("SECURITY EXCEPTION"); 
    } catch (InterruptedException e) { 
     throw new NullPointerException("INTERRUPTED EXCEPTION"); 
    } catch (ExecutionException e) { 
     throw new NullPointerException("EXECUTION EXCEPTION"); 
    } 

} 
public String getInfoById(String id) { 
    for (ParkingSpot spot : list) { 
     if (spot.getPolygonId().equals(id)) { 
      return spot.toString(); 
     } 
    } 
    return "-1"; 
} 

private class LongOperation extends AsyncTask<String, Void, List> { //main network operation to initialize list 
    @Override 
    protected List<ParkingSpot> doInBackground(String... params) { 
     list = new ArrayList<ParkingSpot>(); //initializes list to ArrayList 
     String url = "jdbc:mysql://" + IP_ADDRESS + "/" + DATABASE; //creates URL for MySQL query 
     try { 
      Class.forName("com.mysql.jdbc.Driver"); //Driver for MySQL query 
      Connection con = DriverManager.getConnection(url, USER_USERNAME, USER_PASSWORD); //Starts connection to server 
      PreparedStatement pst; //initializes PreparedStatement outside to allow scope 
      if (bounds == null) { //if requesting first time 
       pst = con.prepareStatement("select * from sample_parking_spots"); 
      } else { //if requesting later than first time; 
       String statement = "select * from sample_parking_spots where coord1lat>=" + bounds.southwest.latitude + " and coord3lat<=" + bounds.northeast.latitude + " and coord3lon>=" + bounds.southwest.longitude + " and coord1lon<=" + bounds.northeast.longitude; 
       pst = con.prepareStatement(statement); 
      } 
      ResultSet rs = pst.executeQuery(); //gets information from server 
      while (rs.next()) { 
       String uid = "U" + rs.getString("id"); //gets UID 
       String coordinate1Lat = rs.getString("coord1lat"); //gets top left latitude (x) 
       String coordinate1Lon = rs.getString("coord1lon"); //gets top left longitude (y) 
       String coordinate3Lat = rs.getString("coord3lat"); //gets bottom right latitude (x) 
       String coordinate3Lon = rs.getString("coord3lon"); //gets bottom right longitude (y) 
       char status = rs.getString("status").charAt(0); //get status as char 
       list.add(new ParkingSpot(uid, status, coordinate1Lat, coordinate1Lon, coordinate3Lat, coordinate3Lon)); //adds new ParkingSpot object to ArrayList 
      } 
      return list; 
     } catch (RuntimeException e) { 
      throw new NullPointerException("SOME OTHER RUNTIME EXCEPTION"); 
      //Json parser can't find some variable or UID or NullPointerException 
     } catch (SQLException e) { 
      throw new NullPointerException("SQL EXCEPTION"); 
      //if credentials for SQL are bad or something is wrong with the server 
     } catch (ClassNotFoundException e) { 
      throw new NullPointerException("CLASS NOT FOUND EXCEPTION"); 
      //if cannot find SQL driver 
     } 
    } 

    @Override 
    protected void onPostExecute(List result) { //called after doInBackground completes 
     super.onPostExecute(list); 
     list = result; //sets public list to List initialized from database 
     Timer timer = new Timer("updateMapAvailTask"); 
     TimerTask myTask = new TimerTask() { //creates new task to execute again after timer 
      @Override 
      public void run() { 
       try { 
        Object result = new LongOperation().execute().get(); //second network thread to download data and initialize map 
        //gets object so ENTIRE LongOperation thread completes 
       } catch (InterruptedException e) { 
        throw new NullPointerException("INTERRUPTED EXCEPTION"); 
       } catch (ExecutionException e) { 
        throw new NullPointerException("EXECUTION EXCEPTION"); 
       } 
      } 
     }; 
     sMapFragment.getMapAsync(MainActivity.this); 
     timer.schedule(myTask, 2000); //schedules for server pull to happen every couple of seconds 
    } 
} 

}

content_main.xml:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
xmlns:tools="http://schemas.android.com/tools" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:paddingBottom="@dimen/activity_vertical_margin" 
android:paddingLeft="@dimen/activity_horizontal_margin" 
android:paddingRight="@dimen/activity_horizontal_margin" 
android:paddingTop="@dimen/activity_vertical_margin" 
app:layout_behavior="@string/appbar_scrolling_view_behavior" 
tools:context="com.main.main.MainActivity" 
tools:showIn="@layout/app_bar_main"> 
<FrameLayout 
    android:id="@+id/content_frame" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent"></FrameLayout> 

<FrameLayout 
    android:id="@+id/map" 
    android:name="com.google.android.gms.maps.SupportMapFragment" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

activity_main.xml中:

<?xml version="1.0" encoding="utf-8"?> 
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:app="http://schemas.android.com/apk/res-auto" 
xmlns:tools="http://schemas.android.com/tools" 
android:id="@+id/drawer_layout" 
android:layout_width="match_parent" 
android:layout_height="match_parent" 
android:fitsSystemWindows="true" 
tools:openDrawer="start"> 

<include 
    layout="@layout/app_bar_main" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" /> 

<android.support.design.widget.NavigationView 
    android:id="@+id/nav_view" 
    android:layout_width="wrap_content" 
    android:layout_height="match_parent" 
    android:layout_gravity="start" 
    android:fitsSystemWindows="true" 
    app:headerLayout="@layout/nav_header_main" 
    app:menu="@menu/activity_main_drawer" /> 

activity_main_drawer.xml:

<?xml version="1.0" encoding="utf-8"?> 
<menu xmlns:android="http://schemas.android.com/apk/res/android"> 

<group android:checkableBehavior="single"> 
    <item 
     android:id="@+id/nav_map" 
     android:icon="@drawable/ic_map" 
     android:title="Map" /> 
    <item 
     android:id="@+id/nav_history" 
     android:icon="@drawable/ic_search_history" 
     android:title="History" /> 
    <item 
     android:id="@+id/nav_notifications" 
     android:icon="@drawable/ic_error" 
     android:title="Notifications" /> 
</group> 

<item android:title="User"> 
    <menu> 
     <item 
      android:id="@+id/nav_help" 
      android:icon="@drawable/ic_help" 
      android:title="Help" /> 
     <item 
      android:id="@+id/nav_settings" 
      android:icon="@drawable/ic_settings" 
      android:title="Settings" /> 

    </menu> 
</item> 

的AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
package="com.main.main"> 

<!-- 
    The ACCESS_COARSE/FINE_LOCATION permissions are not required to use 
    Google Maps Android API v2, but you must specify either coarse or fine 
    location permissions for the 'MyLocation' functionality. 
--> 
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 
<uses-permission android:name="android.permission.INTERNET" /> 
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 

<application 
    android:allowBackup="true" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name" 
    android:supportsRtl="true" 
    android:theme="@style/AppTheme"> 
    android:name="android.support.multidex.MultiDexApplication"> 
    <!-- 
     The API key for Google Maps-based APIs is defined as a string resource. 
     (See the file "res/values/google_maps_api.xml"). 
     Note that the API key is linked to the encryption key used to sign the APK. 
     You need a different API key for each encryption key, including the release key that is used to 
     sign the APK for publishing. 
     You can define the keys for the debug and release targets in src/debug/ and src/release/. 
    --> 
    <meta-data 
     android:name="com.google.android.geo.API_KEY" 
     android:value="@string/google_maps_key" /> 

    <activity 
     android:name=".MainActivity" 
     android:label="@string/title_activity_main" 
     android:theme="@style/AppTheme.NoActionBar"> 
     <intent-filter> 
      <action android:name="android.intent.action.MAIN" /> 

      <category android:name="android.intent.category.LAUNCHER" /> 
     </intent-filter> 
    </activity> 
</application> 

回答

1

AsyncTask實際阻塞UI線程。

AsyncTask.get()方法是一個阻塞呼叫,你應該只是做AsyncTask.execute()。在你的情況下,用new LongOperation().execute()代替new LongOperation().execute().get()