Today I was tasked to Lint an imported module. Things went all for the most part, typical Linting affair: resolving unreferenced variables, uninitialized variables, potentially unbounded string copy, etc. Then I came across this gem:
Info 818: Pointer parameter 'var' (line 123) could be declared as pointing to const
Looking at the log, I figured all I was missing was a const qualifier. Not that bad.
Looking at the code, the offending line looked like this:
const char **values
which looks..... correct.
I proceed onto consulting Google for the next 2 hours while scratching my head. Eventually I reviewed the C const usage:
const char *ptr; // ptr is volatile, content of ptr is const
char * const ptr; // ptr is const, content of ptr volatile
const char * const ptr; // ptr is const, content of ptr is also const
Looking at the code closely, **values represents a table of strings, thus:
*table = table[0]; // pointer to the first string in the table
**table = table[0][0]; // points to the first byte of the first string in the table
The function in question performs string comparison by iterating through all strings in the table via:
tables++; // go to the next string
But since pointer to the strings are not modified (ie. *table or table[i]), Lint was effectively complaining that the API could be further qualified as:
const char * const * values // because *table is never modified
I then went a bit further and tried:
const char * const * const values // let's also make table itself const
and confirmed that the following line now generates an error:
table++; // this is now illegal
But since the function was defined as a callback to hook into another 3rd party library, I was unable to make the change as Lint suggested and had to purposely violate the const requirement to silence Lint:
*table = *table; // Lint now thinks *table has been updated.
But hey, I learnt something interesting today!
This is Ricky's tech blog, where he shares his thoughts on new technology and things. You can also checkout his youtube channel here: http://www.youtube.com/user/CodeMonkeyRicky
Thursday, September 27, 2012
Sunday, June 3, 2012
Android: Converting file to byte array
This came up as a requirement because I had to attach profile pictures to vCard on my eJabberd server. My first approach was to:
This then raise the requirement to convert the .jpg directly to a byte array. I was unable to use some of the existing Java libraries because Android does not contain them. Googling and Stackoverflowing for a bit eventually I found the solution:
InputStream is = new BufferedInputStream(new FileInputStream(
"/mnt/file_path_here"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (is.available() > 0) {
bos.write(is.read());
}
return bos.toByteArray();
- Decode the jpg as bitmap.
- Convert the bitmap into a byte array.
- Attach the byte array to the vCard, and perform the save.
This approach, however, was inadvertently saving the .bmp version of the file instead of the jpg copy. The .bmp version of the same file was too big and causing the server to timeout on transaction. I did not realize this silly mistake until I examine the server log.
This then raise the requirement to convert the .jpg directly to a byte array. I was unable to use some of the existing Java libraries because Android does not contain them. Googling and Stackoverflowing for a bit eventually I found the solution:
InputStream is = new BufferedInputStream(new FileInputStream(
"/mnt/file_path_here"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
while (is.available() > 0) {
bos.write(is.read());
}
return bos.toByteArray();
Monday, May 28, 2012
Android: Running Background Tasks
In my app I'm using the asmack library as the back bone for packet sending and processing. Doing these in the context of the UI thread is not ideal as it may freeze the UI and leads to perception of app freezing.
The better way to solve this problem is to run these tasks in the background. In general, this can be solved by having the UI thread queueing a Runnable onto an executor, and having the Runnable send a Broadcast when it finishes.
In practice, you'll need to instantiate an executor first:
/**
* Restart the executor pool.
*
*/
private void initExecutor() {
m_executor = Executors.newFixedThreadPool(1);
}
Then implement your Runnable:
/**
* Logs into the server.
*
*/
private class loginRunnable implements Runnable {
String user;
String pass;
public loginRunnable(String user, String pass) {
m_loginUser = user;
this.user = user;
this.pass = pass;
}
public void run() {
// TODO:
// Implement your login routine.
// Notify login success.
m_app.sendBroadcast(new Intent(LOGIN_SUCCESS));
}
}
To queue the login request, simply do:
m_executor.execute(new loginRunnable(user, pass));
After your Activity queues the Runnable onto the Executor, you should consider populating a loading Dialog indicate the request is being processed. The Dialog should be cancelled when the complete event is received in the Broadcast Receiver.
The better way to solve this problem is to run these tasks in the background. In general, this can be solved by having the UI thread queueing a Runnable onto an executor, and having the Runnable send a Broadcast when it finishes.
In practice, you'll need to instantiate an executor first:
/**
* Restart the executor pool.
*
*/
private void initExecutor() {
m_executor = Executors.newFixedThreadPool(1);
}
Then implement your Runnable:
/**
* Logs into the server.
*
*/
private class loginRunnable implements Runnable {
String user;
String pass;
public loginRunnable(String user, String pass) {
m_loginUser = user;
this.user = user;
this.pass = pass;
}
public void run() {
// TODO:
// Implement your login routine.
// Notify login success.
m_app.sendBroadcast(new Intent(LOGIN_SUCCESS));
}
}
To queue the login request, simply do:
m_executor.execute(new loginRunnable(user, pass));
After your Activity queues the Runnable onto the Executor, you should consider populating a loading Dialog indicate the request is being processed. The Dialog should be cancelled when the complete event is received in the Broadcast Receiver.
Sunday, May 27, 2012
Android: Using Preferences
Sometimes I find myself needing to cache certain little system status, such as if the user is currently logged in, what the logged in username/password are, etc. Using SQLiteHelper in this scenario is probably an overkill. This is when SharedPreferences comes in handy. In my app I implemented a simple Preferences class with the following definition:
public final class Preferences {
/**
* Configurable attributes.
*
*/
public static final String LOGIN_USERNAME = "username";
public static final String LOGIN_PASSWORD = "password";
public static final String LOGIN_STATE = "loginState";
private static SharedPreferences pref;
private Preferences() {
}
static public void init(Context context) {
pref = context
.getSharedPreferences("PREF", Context.MODE_PRIVATE);
}
static public void setBoolean(String key, boolean value) {
pref.edit().putBoolean(key, value).commit();
}
static public boolean getBoolean(String key) {
return pref.getBoolean(key, false);
}
static public void setString(String key, String value) {
pref.edit().putString(key, value).commit();
}
static public String getString(String key) {
return pref.getString(key, null);
}
}
Since the function is implemented as a static class, one can simply call
Preferences.setBoolean(Preferences.LOGIN_USERNAME, user);
to set the variable anywhere else in the app.
public final class Preferences {
/**
* Configurable attributes.
*
*/
public static final String LOGIN_USERNAME = "username";
public static final String LOGIN_PASSWORD = "password";
public static final String LOGIN_STATE = "loginState";
private static SharedPreferences pref;
private Preferences() {
}
static public void init(Context context) {
pref = context
.getSharedPreferences("PREF", Context.MODE_PRIVATE);
}
static public void setBoolean(String key, boolean value) {
pref.edit().putBoolean(key, value).commit();
}
static public boolean getBoolean(String key) {
return pref.getBoolean(key, false);
}
static public void setString(String key, String value) {
pref.edit().putString(key, value).commit();
}
static public String getString(String key) {
return pref.getString(key, null);
}
}
Since the function is implemented as a static class, one can simply call
Preferences.setBoolean(Preferences.LOGIN_USERNAME, user);
to set the variable anywhere else in the app.
Tuesday, May 22, 2012
Setting up Ejabberd
For the Android app I've been developing, I've been using OpenFire as the back end for development purposes. Openfire was chosen mostly for convenience, but I didn't plan on using it for production as it costs when number of user scales up.
I did a bit of research and settled on ejabberd for my production server. Installing the server was as painless as it can get:
sudo apt-get install ejabberd
Note that this will install the ejabberd from the repository, which may not be the latest. I did grab the latest copy, but had problem install it.
After installation completes, I had to do some custom configuration.
Stop ejabberd first:
sudo /etc/init.d/ejabberd stop
Open the cfg file:
sudo vi /etc/ejabberd/ejabberd.cfg
For debugging purposes, I enabled debug log:
{loglevel, 4}
Set the admin user:
{acl, admin, {user, "ricky", "password"}},
Set the host. Since I repurposed another PC at home, I just set the host to its IP address:
{hosts, ["192.168.1.101"]},
For my application, I also needed to enable user directory search:
{mod_vcard, [{host, "search.192.168.1.101"}]},
That's it. To restart the server:
sudo /etc/init.d/ejabberd start
To view the log, you can:
tail -f /var/log/ejabberd/ejabberd.log
Another useful feature is to dump the current database:
sudo ejabberdctl dump /tmp/ejabberd_dump
You can Google ejabberdctl command to see the exhaustive command set.
I'm also using asmack for the client. I'll put up some other posts in the future regarding asmack.
I did a bit of research and settled on ejabberd for my production server. Installing the server was as painless as it can get:
sudo apt-get install ejabberd
Note that this will install the ejabberd from the repository, which may not be the latest. I did grab the latest copy, but had problem install it.
After installation completes, I had to do some custom configuration.
Stop ejabberd first:
sudo /etc/init.d/ejabberd stop
Open the cfg file:
sudo vi /etc/ejabberd/ejabberd.cfg
For debugging purposes, I enabled debug log:
{loglevel, 4}
Set the admin user:
{acl, admin, {user, "ricky", "password"}},
Set the host. Since I repurposed another PC at home, I just set the host to its IP address:
{hosts, ["192.168.1.101"]},
For my application, I also needed to enable user directory search:
{mod_vcard, [{host, "search.192.168.1.101"}]},
That's it. To restart the server:
sudo /etc/init.d/ejabberd start
To view the log, you can:
tail -f /var/log/ejabberd/ejabberd.log
Another useful feature is to dump the current database:
sudo ejabberdctl dump /tmp/ejabberd_dump
You can Google ejabberdctl command to see the exhaustive command set.
I'm also using asmack for the client. I'll put up some other posts in the future regarding asmack.
Subscribe to:
Posts (Atom)