It's my first project in Android Studio, basically I'm trying to develop a map with multiple markers using Mapbox. So, my problem is when loading the markers on the map it takes a lot of time to load ~3-5 seconds and the app freezes until i get the json from my API call.
Here is my retrofit2 call to API:
private void getNearbyStations() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("***")//my API, not relevant
.addConverterFactory(GsonConverterFactory.create())
.build();
jsonPlaceHolderApi = retrofit.create(JsonPlaceHolderApi.class);
Utilizator utilizator = Utilizator.getUtilizatorInstance();
Call<ResponseNearbyStations> call = jsonPlaceHolderApi.getNearbyStations(utilizator.getAuthentificationKey(), 47.1744354, 27.5746688);//Static Lat and Long for test, in future will use current location
try {
ResponseNearbyStations body = call.execute().body();
JsonObject jsonObject = body.getData();
JsonArray ja_data = jsonObject.getAsJsonArray("stationAround");
Station[] statiiPrimite = gson.fromJson(ja_data, Station[].class);
stationList = new ArrayList<>(Arrays.asList(statiiPrimite));
} catch (IOException e) {
e.printStackTrace();
}
}
I'm saving all my Stations in an ArrayList called stationList. In Station class i have the lat and long coordinates besides other information.
Here is my addMarkers function:
private void addMarkers(#NonNull Style loadedMapStyle) {
List<Feature> features = new ArrayList<>();
for(Station statie:stationList){
features.add(Feature.fromGeometry(Point.fromLngLat(Double.valueOf(statie.getCoordinates().getLongitude()),
Double.valueOf(statie.getCoordinates().getLatitude()))));
}
loadedMapStyle.addSource(new GeoJsonSource(MARKER_SOURCE, FeatureCollection.fromFeatures(features)));
loadedMapStyle.addLayer(new SymbolLayer(MARKER_STYLE_LAYER, MARKER_SOURCE)
.withProperties(
PropertyFactory.iconAllowOverlap(true),
PropertyFactory.iconIgnorePlacement(true),
PropertyFactory.iconImage(MARKER_IMAGE),
PropertyFactory.iconOffset(new Float[]{0f, -52f})
));
}
So after several searching i find out that the "problem" here is that i'm using call.execute() in getNearbyStations() which is not async so the main thread is waiting for the Stations to load. I tried to use call.enqueue but after that i got another problem, in my function addMarkers i get NullPointerException because stationList doesn't have enough time to load in
for(Station statie:stationList){
features.add(Feature.fromGeometry(Point.fromLngLat(Double.valueOf(statie.getCoordinates().getLongitude()),
Double.valueOf(statie.getCoordinates().getLatitude()))));
}
I'm guessing that i have to use some sort of Threading to solve this problem, but i'm a beginner in using Threads for Android Studio, and i couldn't figure it out.
I think the solution would be:
1.Display the map empty
2.Add markers after they load.
In this way the user doesn't experience any freezing. Any idea how to solve this problem is welcome.
Since the problem is that you want the application not to wait of the synchronous function, I would recommend to use an asynchronous task for that. You can then execute the addMarkers function once the onPostExecute callback of the async task is invoked. But make sure to run the addMarkers only after the style has been set in your onMapReady
Please see this documentation on how to use an asynchronous task: https://developer.android.com/reference/android/os/AsyncTask
The nice side effect you get by using an aysnchronous task, is that Android will execute it in a different thread and thereby will take load off the main thread.
Related
I have a trouble, i need to get event/callback when i try to write to database.
I added greenDao lib to project, and i able to write/delete in db.
But no idea how to get callback after some operation under db.
In introduction to lib i read "AsyncOperationListener for asynchronous callback when operations complete".
Used this tutorial:
http://blog.surecase.eu/using-greendao-with-android-studio-ide/
Can anybody help me with this trouble?
UPD:
ok here we added some list in storage
getMyObjectDao().getSession().startAsyncSession().insertOrReplaceInTx(MyObject.class, list);
error here
List<MyObject> items = getBoxDao(c).getSession().startAsyncSession().loadAll(MyObject.class);
How can we asynchronously load data from db?
Is this correct solution?
#Override
public void onAsyncOperationCompleted(AsyncOperation operation) {
String operationIs = null;
switch (operation.getType()) {
case LoadAll:
itemsList = BoxRepository.getAllBoxes(getApplicationContext());
By default all the operations are performed synchronously, eliminating the need to get any callback. But the recent version of GreenDAO introduces AsyncSession, which can be used to perform operations asynchronously and also provides a way set listener on it. See the example below:
AsyncSession asyncSession = App.getInstance().daoSession.startAsyncSession();
asyncSession.setListener( new AsyncOperationListener() {
#Override
public void onAsyncOperationCompleted(AsyncOperation operation) {
// do whats needed
}
});
asyncSession.insert(MyObject);
Simple ask if anything unclear!
Been a lurker on this site to help find answers to some of my problems before, but I am currently stuck on this and could not find a recent solution. The closest answers I found to my problem were Yelp API Android Integration and Yelp Integration in Android
I tried following the steps in the 2nd link but they are a bit outdated. I have registered for an API, downloaded the jar files from the github and synced them, and made the YelpAPI.java and TwoStepOAuth.java files and removed the main method from YelpAPI. I am stuck on step 4 on the search part. I tried to call the queryAPI method from inside an onClick method I made for a button
public void getRandom(View view) {
Intent intent = new Intent(this, SecondActivity.class);
startActivity(intent);
YelpAPI.YelpAPICLI yelpApiCli = new YelpAPI.YelpAPICLI();
new JCommander(yelpApiCli);
YelpAPI yelpApi = new YelpAPI(CONSUMER_KEY, CONSUMER_SECRET, TOKEN, TOKEN_SECRET);
try {
YelpAPI.queryAPI(yelpApi, yelpApiCli);
} catch (JSONException e) {
e.printStackTrace();
}
}
Basically what I want this to do is, when the button is pressed, I want it to go to a second screen that will display what I query Yelp for. I haven't worked on that part yet, right now I just want to get a result back from Yelp. Keep in mind I am a complete noob at Android Studio and at most intermediate at Java.
Any help is greatly appreciated, it seems like its a really simple problem but its taking me forever to figure out on my own.
You can't do blocking task like downloading or image loading in android's Main thread (UI Thread). You can't block UI for more than 5 seconds. If you try it to block for more than 5 seconds than your app will stop working and display "Unfornutaley your app has stopped working" because of error too much work on main thread. So you need to make use of async task.
I have a service which I need to access from an android app. The goal is simply to send a number value from a textbox and get a result string back.
I know it is possible to do so in VB using a Service Reference, which I have done. Here is the sample code :
Private Async Sub Button_Click(sender As Object, e As RoutedEventArgs)
Dim service As New ServiceReference2.Service1Client(ServiceReference2.Service1Client.EndpointConfiguration.BasicHttpBinding_IService1)
Try
lblReturn.Text = Await service.GetDataAsync(CInt(txtValueSent.Text))
Catch ex As Exception
lblReturn.Text = ex.Message
If Not ex.InnerException.Message Is Nothing Then
lblReturn.Text = lblReturn.Text + ex.InnerException.Message
End If
End Try
End Sub
After research I can't seem to find any way to have a quick and simple result like it is possible to with Visual Studio using Android Studio
Are there any tools available which work in a similar way ?
If not which process would be recommanded to achieve the same result ?
Are there any usefull links which could help enlighten me?
I have developed an application in Android that downloads a lot of data through XML query REST.
The problem is that every time you start the app takes a long time to download the data.
My question is:
How can I serialize these data, and update perhaps after a certain period of time?
I want some advice or idea to implement, or even better an example.
thanks
Use that one to serialize:
http://simple.sourceforge.net/
You can schedule an async task or a thread to update it.
Example for a thread that serializes data incl. a lock (only parts of the code)
static final Object sDataLock = new Object();
Serializer mSerializer;
class AsyncSave implements Runnable
{
Object mSerialize;
File mStorage;
public AsyncSave(Object serialize, File storage)
{
mSerialize = serialize;
mStorage = storage;
}
#Override
public void run()
{
try {
synchronized (sDataLock) {
// write
mSerializer.write(mSerialize, mStorage);
}
} catch (Exception e) {
Log.e(TAG, e.toString());
}
}
}
How can I serialize these data, and update perhaps after a certain period of time?
You can use thread/async task if you want certain data to be downloaded in certain activity. The async task/thread will be destroyed if the activity is destroyed.
If you want to download the data in certain time, use a Service instead. With a service, your data will be downloaded even if the apps is closed. For example, you can set your apps to download certain data once a day.
I'm still learning how to extract data from a website and I really hope I'll get some nice answers adequate for a starter. Anyways, my goal here is to extract the data in the background of my app(without openning and showing it in my app). The idea is that data then would be stored for later use.
The API I'm using has 2 GetMethods:
GetProductJSON(which has JSON Response) and GetProduct(with a Comma Seperated Values(CSV) Response)
Here is an example of the JSON Response website:
{"0":{"productname":"Neutrogena Lips Stick 4.8g","imageurl":"http://ecx.images- amazon.com/images/I/31E1ct854gL._SL160_.jpg","producturl":"","price":"5.65","currency":"USD","saleprice":"","storename":"N/A"}}
The Comma Seperated Values Response looks like this:
"productname","imageurl","producturl","price","currency","saleprice","storename"
"Neutrogena Lips Stick 4.8g","http://ecx.images-amazon.com/images/I/31E1ct854gL._SL160_.jpg","","5.65","USD","","N/A"
Here is how I call the website:
url = url.replace("{CODE}", codeValue);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
cardPresenter.setPendingIntent(createPendingIntent(getContext(), intent));
Any suggestions on how to make this a background task and how to actually get the data in java so that I can use them on a Livecard.
First, you need to have access to the Internet. Include the following permission into your AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
Using GDK and AsyncTask:
import android.os.AsyncTask;
public class RetrieveData extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... resource) {
String data;
try {
URL url = new URL(resource[0]);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
InputStream in = new BufferedInputStream(connection.getInputStream());
data = convertStreamToString(in);
in.close();
} catch (Exception e) {
e.printStackTrace();
}
return data;
}
private String convertStreamToString(InputStream in) {
Scanner s = new Scanner(in);
return s.useDelimiter("\\A").hasNext() ? s.next() : "";
}
}
The method convertStreamToString() is described there.
In your Service or Activity:
String retrievedData;
try {
retrievedData = new RetrieveData().execute("http://www.example.com/GetProductJSON").get();
} catch (Exception e) {
e.printStackTrace();
}
// Process data
Hope that helps.
You have at least these choices using GDK: 1) create an asynch task; or 2) create a private service that you assign tasks to periodically.
The first thing you will need to do in your Android Application is to add permission to access the Internet in your AndroidManifest.xml file. Add this tag as a sibling of <application>.
<uses-permission android:name="android.permission.INTERNET"/>
You will then need to consider in what manner you will make the HTTP requests. In an ideal application, you need to use the AsyncTask class to make these requests to avoid blocking the UI thread.
If you are just looking for a quick proof of concept, you can permit these requests on the UI thread by modifying your policy. Add this code to your onCreate() method in the MainActivity
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
This is not an advisable long term solution since it blocks the UI thread, but there are StackOverflow topics on it. How to fix android.os.NetworkOnMainThreadException?.
Here is another article on the concept of an AsyncTask http://www.androiddesignpatterns.com/2012/06/app-force-close-honeycomb-ics.html.
Although you seem to be indicating you're using the GDK, you may want to consider a server-push via the Mirror API instead. Since you need to fetch the information via the network anyway, you're losing out on many of the advantages the GDK offers.
With the Mirror API, you would create a new timeline item with timeline.insert and save the id of the card that was created. You would probably want to give your user the option to pin the card so it is placed in the "now" area of the timeline.
When updating, you can call timeline.update with the new information.
Keep in mind that you do need to update the card periodically or it may fall off the timeline or out of the pinned area after seven days of inactivity.