I am a newbie android developer. I am trying to build a app engine based application. I could make it work on for now but I realised I need to use memcache to optimize database accesses. But I have a bad time understanding some basic concepts. So my questions are as follows:
Is memcache programming only about app engine? Is there nothing to do with android side? (I mean if any coding needed at android application?)
I used JPA to program my app engine app. Can I use low level API for memcache?
I got this example on a book but it usses many HTTP references. Is this type of example usable for android app also or is it only for websites' usage?.
public class ETagCacheServlet extends HttpServlet {
private static final long serialVersionUID = 4308584640538822293L;
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
MemcacheService cache = MemcacheServiceFactory
.getMemcacheService();
String cacheKey = request.getRequestURI() + "." + "etag";
String result;
if (!cache.contains(cacheKey) ||
!cache.get(cacheKey).equals(request
.getHeader("If-None-Match"))) {
String etag = Long.toString(System.currentTimeMillis());
response.setHeader("ETag", etag);
cache.put(cacheKey, etag);
result = "Loaded into cache at " + (new Date());
response.getWriter().write(result);
} else {
response.setStatus(304);
}
}
}
Do you know any source that has a working sample app or something?
Maybe you laugh reading these questions but I really cannot figure these things out. Thanks in advance.
Edit: I tried to add memcache to my code unsuccessfully, can you take a look at the code please?
#SuppressWarnings({ "unchecked", "unused" })
#ApiMethod(name = "queryDesire")
public CollectionResponse<Desire> queryDesire(
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit,
#Nullable #Named("first") Integer first,
#Nullable #Named("name") String name){
EntityManager mgr = null;
Cursor cursor = null;
List<Desire> execute = null;
try {
String keyDesire = "mem_" + name;
List<Desire> memDesire = (List<Desire>) memcache.get(keyDesire);
if (memDesire == null) {
mgr = getEntityManager();
Query query2 = mgr.createQuery("select i from Desire i where i.ctgry = :name ");
if (cursorString != null && cursorString != "") {
cursor = Cursor.fromWebSafeString(cursorString);
query2.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query2.setFirstResult(first);
query2.setMaxResults(limit);
}
execute = (List<Desire>) query2.setParameter("name", name).getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null)
cursorString = cursor.toWebSafeString();
for (Desire obj : execute)
;
CollectionResponse.<Desire> builder().setItems(execute)
.setNextPageToken(cursorString).build();
memcache.put("mem_cache", queryDesire);
}
return CollectionResponse.<Desire> builder().setItems(execute)
.setNextPageToken(cursorString).build();
}
finally {
mgr.close();
}
}
Memcache is used on the server-side, i.e. App Engine. It is often used to speed up the responses from the server to the client, but it is not related to the client code. In other words, no changes are necessary on the client side if you use Memcache on App Engine side.
Yes, you can use low-level API for Memcache.
See response to question 1. Memcache can be used regardless of how an app communicates with the server.
Memcache is used in many different ways, so you may need to post a specific question, and we may be able to help. Meanwhile, here is an example from my code. Time zones are frequently required for my app, and they never change. So it makes sense to use Memcache to speed up the response.
private static final MemcacheService memcache = MemcacheServiceFactory.getMemcacheService();
public static ArrayList<String> getTimeZones() {
ArrayList<String> timeZones = (ArrayList<String>) memcache.get("time_zones");
if (timeZones == null) {
// This method reads time zones from a properties file
timeZones = prepareTimeZones();
memcache.put("time_zones", timeZones);
}
return timeZones;
}
Related
I am making Android app for practicing driving licence theory tests. I will have about 3000 questions. Question object would have several atributes (text, category, subcategory, answers, group). I will create them and put in app, so data won't ever change. When user chooses category, app would go througt data, look which question meets requirements (that user selected) and put it in list for displaying. What should I use to store data/questions, XML or SQLite? Thanks in advance.
Edit:
I forgot to mentiont that app won't use internet connection. Also, I planned to make simple java app for entering data. I would copy text from government's website (I don't have access to their database and I have to create mine), so I thought to just put question's image url to java program and it would download it and name it automaticaly. Also, when entering new question's text it would tell me if that question already exist before I enter other data. That would save me time, I wouldn't have to save every picture and name it my self. That is what I thought if using XML. Can I do this for JSON or SQLite?
If you do not have to perform complex queries, I would recommend to store your datas in json since very well integrated in android apps using a lib such as GSON or Jackson.
If you don't want to rebuild your app / redeploy on every question changes. You can imagine to have a small webserver (apache, nginx, tomcat) that serves the json file that you will request on loading of the app. So that you will download the questions when your app is online or use the cached one.
XML is a verbose format for such an usage, and does not bring much functions....
To respond to your last question, you can organise your code like that :
/**
* SOF POST http://stackoverflow.com/posts/37078005
* #author Jean-Emmanuel
* #company RIZZE
*/
public class SOF_37078005 {
#Test
public void test() {
QuestionsBean questions = new QuestionsBean();
//fill you questions
QuestionBean b=buildQuestionExemple();
questions.add(b); // success
questions.add(b); //skipped
System.out.println(questions.toJson()); //toJson
}
private QuestionBean buildQuestionExemple() {
QuestionBean b= new QuestionBean();
b.title="What is the size of your boat?";
b.pictures.add("/res/images/boatSize.jpg");
b.order= 1;
return b;
}
public class QuestionsBean{
private List<QuestionBean> list = new ArrayList<QuestionBean>();
public QuestionsBean add(QuestionBean b ){
if(b!=null && b.title!=null){
for(QuestionBean i : list){
if(i.title.compareToIgnoreCase(b.title)==0){
System.out.println("Question "+b.title+" already exists - skipped & not added");
return this;
}
}
System.out.println("Question "+b.title+" added");
list.add(b);
}
else{
System.out.println("Question was null / not added");
}
return this;
}
public String toJson() {
ObjectMapper m = new ObjectMapper();
m.configure(Feature.ALLOW_SINGLE_QUOTES, true);
String j = null;
try {
j= m.writeValueAsString(list);
} catch (JsonProcessingException e) {
e.printStackTrace();
System.out.println("JSON Format error:"+ e.getMessage());
}
return j;
}
}
public class QuestionBean{
private int order;
private String title;
private List<String> pictures= new ArrayList<String>(); //path to picture
private List<String> responseChoice = new ArrayList<String>(); //list of possible choices
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getPictures() {
return pictures;
}
public void setPictures(List<String> pictures) {
this.pictures = pictures;
}
public List<String> getResponseChoice() {
return responseChoice;
}
public void setResponseChoice(List<String> responseChoice) {
this.responseChoice = responseChoice;
}
}
}
CONSOLE OUTPUT
Question What is the size of your boat? added
Question What is the size of your boat? already exists - skipped & not added
[{"order":1,"title":"What is the size of your boat?","pictures":["/res/images/boatSize.jpg"],"responseChoice":[]}]
GIST :
provides you the complete working code I've made for you
https://gist.github.com/jeorfevre/5d8cbf352784042c7a7b4975fc321466
To conclude, what is a good practice to work with JSON is :
1) create a bean in order to build your json (see my example here)
2) build your json and store it in a file for example
3) Using android load your json from the file to the bean (you have it in andrdoid)
4) use the bean to build your form...etc (and not the json text file) :D
I would recommend a database (SQLite) as it provides superior filtering functionality over xml.
Create the db using DB Browser for SQLite
And then use the library SQLiteAssetHelper in the link-
https://github.com/jgilfelt/android-sqlite-asset-helper
Tutorial on how to use -
http://www.javahelps.com/2015/04/import-and-use-external-database-in.html
You can use Paper https://github.com/pilgr/Paper its a fast NoSQL data storage for Android.
SQLite is the best for your system. because you will have to maintain (text, category, subcategory, answers, group) etc. So if you create db and create table for them. That will be easy to manage and you can relationship with each other which is not possible to XML.
Im using the ActiveAndroid library and I have read the entire information (very minimalist and insufficient unfortunately)
There is no mention whether the .save() operation is executed syncrhonously.
If it is asynchronous, how do I "listen" for it to end before proceeding?
http://www.activeandroid.com/ - this is the documentation I read
If you have a look at the source code of the Model class, you'll see that the save method does not do any thread handling:
public final Long save() {
final SQLiteDatabase db = Cache.openDatabase();
final ContentValues values = new ContentValues();
for (Field field : mTableInfo.getFields()) {
/* ... */
}
if (mId == null) {
mId = db.insert(mTableInfo.getTableName(), null, values);
}
else {
db.update(mTableInfo.getTableName(), values, idName+"=" + mId, null);
}
Cache.getContext().getContentResolver()
.notifyChange(ContentProvider.createUri(mTableInfo.getType(), mId), null);
return mId;
}
Saving thus occurs synchronously.
I just started learning about Cloud Endpoints from this tutorial by Google. The sample app provided in this tutorial defines Place as a JPA entity that has a GeoPt field for storing its location in the the App-Engine backend app which can be accessed by an android client via Google Cloud Endpoints.
I want to modify the PlaceEndPoint class in this sample app to return the list of only those places stored in the datastore that are nearby the user of the Android app instead of listing all the places in the datastore. By nearby, I mean a list of those places that lie in around 30 km radius sorted by the distance from the user. I would later need to apply more filters to these results by the type of places the user wants to see.
To do this I would probably have to:
1. Pass the user's location from the android client to the App-Engine backend
2. Tweak the function listPlace() in the PlaceEndpoint class of the backend app to return places near the user
But I'm not familiar enough with App Engine and Endpoints to figure out how to go about it. Please give me any hint or direction that would be helpful. Thanks so much in anticipation!!
Here is the code for the listPlace method in the PlaceEndpoint class as contained in the Google's sample app:
#Api(name = "placeendpoint", namespace = #ApiNamespace(ownerDomain = "google.com", ownerName = "google.com", packagePath = "samplesolutions.mobileassistant"))
public class PlaceEndpoint {
/**
* This method lists all the entities inserted in datastore.
* It uses HTTP GET method and paging support.
*
* #return A CollectionResponse class containing the list of all entities
* persisted and a cursor to the next page.
*/
#SuppressWarnings({ "unchecked", "unused" })
#ApiMethod(name = "listPlace")
public CollectionResponse<Place> listPlace(
#Nullable #Named("cursor") String cursorString,
#Nullable #Named("limit") Integer limit) {
EntityManager mgr = null;
Cursor cursor = null;
List<Place> execute = null;
try {
mgr = getEntityManager();
Query query = mgr.createQuery("select from Place as Place");
if (cursorString != null && cursorString != "") {
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null) {
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<Place>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null)
cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (Place obj : execute)
;
} finally {
mgr.close();
}
return CollectionResponse.<Place> builder().setItems(execute)
.setNextPageToken(cursorString).build();
}
I have two tables CustomerBalance and Customer which are bound with CustomerRefId field.
I want the CustomerBalance records that are lets say greater than 100 for a field balance of this tables. I also want to include into my results the name of the particular customer that fulfills that criteria. I created the following method that works!
public List<CustomerBalance> getCustomerBalanceFilter(String filterVal) {
try {
PreparedQuery<CustomerBalance> preparedQuery = mDbHelper.getCustomerBalanceDao().queryBuilder()
.where().gt(CustomerBalance.DB_COL_CUSTOMER_BALANCE, filterVal)
.prepare();
List<CustomerBalance> result = mDbHelper.getCustomerBalanceDao().query(preparedQuery);
for(CustomerBalance alert : result) {
PreparedQuery<Customer> getCustQuery = mDbHelper.getCustomerDao().queryBuilder()
.where().eq(Customer.DB_COL_CUSTOMER_REF_ID, alert.getCustomerID())
.prepare();
List<Customer> customer = mDbHelper.getCustomerDao().query(getCustQuery);
alert.setCustomer(customer.size() == 1 ? customer.get(0) : null);
}
return result;
} catch(Exception ex) {
return null;
}
}
This methods is working, is this the best way to write such a query? or is there a more appropriate approach?
One improvement to your query is to use ORMLite's SelectArg to pass in the customer-id instead of a new query each time. Something like:
...
List<CustomerBalance> result = mDbHelper.getCustomerBalanceDao()
.query(preparedQuery);
SelectArg custIdArg = new SelectArg();
PreparedQuery<Customer> getCustQuery = mDbHelper.getCustomerDao().queryBuilder()
.where().eq(Customer.DB_COL_CUSTOMER_REF_ID, custIdArg)
.prepare();
for (CustomerBalance alert : result) {
custIdArg.setValue(alert.getCustomerID());
List<Customer> customer = mDbHelper.getCustomerDao().query(getCustQuery);
alert.setCustomer(customer.size() == 1 ? customer.get(0) : null);
}
Here are the docs for SelectArg:
http://ormlite.com/docs/select-arg
FYI, there also is an UpdateBuilder, but I don't see an easy way to turn your code above into a single UPDATE statement.
Is it possible to get the calendar's entries from the phone offline? It seem the only way is to use gdata-java-client.
Josef and Isaac's solutions for accessing the calendar only work in Android 2.1 and earlier. Google have changed the base content URI in 2.2 from "content://calendar" to "content://com.android.calendar". This change means the best approach is to attempt to obtain a cursor using the old base URI, and if the returned cursor is null, then try the new base URI.
Please note that I got this approach from the open source test code that Shane Conder and Lauren Darcey provide with their Working With The Android Calendar article.
private final static String BASE_CALENDAR_URI_PRE_2_2 = "content://calendar";
private final static String BASE_CALENDAR_URI_2_2 = "content://com.android.calendar";
/*
* Determines if we need to use a pre 2.2 calendar Uri, or a 2.2 calendar Uri, and returns the base Uri
*/
private String getCalendarUriBase() {
Uri calendars = Uri.parse(BASE_CALENDAR_URI_PRE_2_2 + "/calendars");
try {
Cursor managedCursor = managedQuery(calendars, null, null, null, null);
if (managedCursor != null) {
return BASE_CALENDAR_URI_PRE_2_2;
}
else {
calendars = Uri.parse(BASE_CALENDAR_URI_2_2 + "/calendars");
managedCursor = managedQuery(calendars, null, null, null, null);
if (managedCursor != null) {
return BASE_CALENDAR_URI_2_2;
}
}
} catch (Exception e) { /* eat any exceptions */ }
return null; // No working calendar URI found
}
These answers are good, but they all involve hard-coding the Calendar URI (which I've seen in three different incarnations across different Android devices).
A better way to get that URI (which hard-codes the name of a class and a field instead) would be something like this:
Class<?> calendarProviderClass = Class.forName("android.provider.Calendar");
Field uriField = calendarProviderClass.getField("CONTENT_URI");
Uri calendarUri = (Uri) uriField.get(null);
This isn't perfect (it will break if they ever remove the android.provider.Calendar class or the CONTENT_URI field) but it works on more platforms than any single URI hard-code.
Note that these reflection methods will throw exceptions which will need to be caught or re-thrown by the calling method.
Currently, this is not possible without using private APIs (see Josef's post.) There is a Calendar provider, but it is not public yet. It could change anytime and break your app.
Though, it probably will not change (I don't think they will change it from "calendar"), so you might be able to use it. But my recommendation is to use a separate class like this:
public class CalendarProvider {
public static final Uri CONTENT_URI = Uri.parse("content://calendar");
public static final String TITLE = "title";
public static final String ....
And use those instead of the strings directly. This will let you change it very easily if/when the API changes or it is made public.
You can use the calendar content provider (com.android.providers.calendar.CalendarProvider). Example:
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://calendar/events"), null, null, null, null);
while(cursor.moveToNext()) {
String eventTitle = cursor.getString(cursor.getColumnIndex("title"));
Date eventStart = new Date(cursor.getLong(cursor.getColumnIndex("dtstart")));
// etc.
}
edit: you might want to put this in a wrapper (see Isaac's post) as it's currently a private API.
You can use the CalendarContract from here: https://github.com/dschuermann/android-calendar-compatibility
It is the same API class as available on Android 4, but made to work with Android >= 2.2.
About the API that can change... The whole ContentProvider approach won't change that quickly so can already overcome a lot of problems by only updating the strings. Therefor create constants you reuse over the whole project.
public static final String URI_CONTENT_CALENDAR_EVENTS = "content://calendar/events";
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(URI_CONTENT_CALENDAR_EVENTS), null, null, null, null);
//etc
If you want a proper private API you'll have to create a pojo and some services like this:
public class CalendarEvent {
private long id;
private long date;
//etc...
}
public interface CalendarService {
public Set<CalendarEvent> getAllCalendarEvents();
public CalendarEvent findCalendarEventById(long id);
public CalendarEvent findCalendarEventByDate(long date);
}
and so on. This way you'll only have to update the CalendarEvent object and this service in case the API changes.
Nick's solution involves managedQuery, which is not defined in the Context class. Many times when you are running things in the background you would want to use a context object. Here's a modified version:
public String getCalendarUriBase() {
return (android.os.Build.VERSION.SDK_INT>=8)?
"content://com.android.calendar":
"content://calendar";
}
The catch for null should not be carried out here since there might be more exceptions even if the managedQuery succeeded earlier.