 this:
"The author maintains a copy of the example code at the following site, as well:"
Where says: "You may notice that there are not a lot of users of Android 1.5 and 1.6." should be "You may notice that there are a lot of users of Android 1.5 and 1.6." to be in according with the graph (more than 60% uses Android 1.5 or 1.6)
Though it said it used the January 2011 Graph. I know from previously looking at it on Google, that is wrong picture. It is also reflected in paragraph 3.
Note from the Author or Editor:I did update the picture closer to book coming out and I may not have updated all there references to it. The actual numbers are not as important as is the fact that we have mixed bag of versions out there. But should be update to just be sample of distribution at some point in time.
On Android runtime box there are two boxes: Core libs and Delvik VM.
Delvik is a typo, it should be Dalvik.
GNU libc is *not* licensed under the GPL license as stated in the text.
It is licensed under the *LGPL* (Lesser General Public License), which is a _weak-copyleft_ license, i.e. a _compromise_ between strong-copyleft licenses such as the GPL and permissive licenses such as Apache/MIT or BSD.
OpenSSL is described as Secure Locket Layer rather than Secure Socket Layer.
Note from the Author or Editor:Typo
"An APK file roughly has three main components. An API consists of the following major components"
The term API is incorrectly used (typo for APK?), and the two sentences repeat the same meaning.
Note from the Author or Editor:Yes, API should be APK. Typo
"everything should be pretty much for different operating systems" should be "everything should be pretty much *the same* for different operating systems"
Says:
~/android-skd/platform-tools/
Must say;
~/android-sdk/platform-tools/
Note from the Author or Editor:Correct!
I wouldn't call this a "technical error", but more of a content issue.
The "Quick Start" chapter has you enter the entire HelloWorld application. Then it tells you how to start the emulator. Then the chapter ends. Nowhere does it even suggest RUNNING the HelloWorld application and seeing it in the emulator. That's a pretty wimpy "Quick Start" if that's really what you intended.
Note from the Author or Editor:There should be a sentence that says: To run this application on the emulator, select the HelloWorld project in the Eclipse's Package Explorer window, right click on it, and choose Run As > Android Application.
There are two "Stopped" states in the diagram instead of "Stopped" and "Paused".
Note from the Author or Editor:Correct! The "Stopped" on right side should be "Paused".
The paragraph ends with:
Finally, we'll get it right by introducing Lists and Adapters to the mix. Finally, we'll get it right by introducing Lists and Adapters to the mix and use them to tie the data to our user interface.
It seems to be a typo.
Note from the Author or Editor:Should be just: Finally, we'll get it right by introducing Lists and Adapters to the mix to use them to tie the data to our user interface.
Change: "In our case, the app will be able to work on API level 4 (Android 1.6), so enter 4 here." to
"In our case, the app will be able to work on API level 5 (Android 2.0), so enter 5 here."
Change: "In our case, the app will be able to work on API level 4 (Android 1.6), so enter 4 here." to
"In our case, the app will be able to work on API level 5 (Android 2.0), so enter 5 here."
Also update the Fig 6-2 to say 5 instead of 4 for minimum SDK version.
On page 51 in the chosen package name is "com.marakana.yamba" ("I'm going to use "com.marakana.yamba" ") but in page 56 (and in the source code presented later) the package name changes to "com.marakana.yamba1"
Note from the Author or Editor:For code manageability reasons, I started numbering packages in the actual code, so there's no collision. In the book, I might have missed it in this place.
You pass this as an argument to setOnClickListener [...]
In this sentence, "this" is a Java reserved word, and should be formatted as code. The sentence is hard to understand as it is.
The Twitter constructor as provided in the text is deprecated in the Winterwell jar. The note mentions "the jtwitter.jar library provided with this code has been slightly modified from the official Winterwell version to make it work" but at least in Safari Books Online, I see no provision to download a special version of this jar.
Note from the Author or Editor:The code for the modified version of JTwitter library is available here:
In the example code,
editText = (EditText) findViewById(R.id.editText);
should read:
editText = (EditText) findViewById(R.id.editStatus);
Note from the Author or Editor:
Not sure what page this is on - it's not on page 60 in Safari (Figure 6.3 isn't on page 60 and doesn't show that code.) The code "editText = (EditText) findViewById(R.id.editText);" is used in a couple of places. Need more explicit information on exactly where the erratum is.
at the bottom that says AnddroidManifest.xml
One `d' in Addroid should be removed.
In the previous paragraph he filename is correctly spelled, but its formatting is wrong.
Ctrl-O is listed as the shortcut for "Organize Imports." The actual shortcut is Ctrl-Shift-O. This is in the Eclipse IDE for Java Developers.
Note from the Author or Editor:It should be Ctrl-Shift-O.
In Figure 6.11, the contents of the EditText and the title of the app doesn't match the code (ie, `140-characeter tweet' should be `140-character status').
implementation.Figure 6-12 shows [...]
There should be a space after the period.
"PNG is preferred to the GIF standard because PNG is lossless and doesn't require any patent licenses."
While GIF did require patent licenses in the past, Unisys patent expired on 2003/2004 (depending for which country), and IBM patent expired on 2006.
Since 5 years ago, employment of the GIF format doesn't require any patent license.
On the other hand, PNG provides true-color, alpha transparency and better compression.
Note from the Author or Editor:
"You might want multiple versions of same resource" should be " You might want multiple versions of *the* same resource"
"if the user in France" should be "if the user *is* in France"
"it is worth keeping in mind few optimization points" should be "it is worth keeping in mind **a** few optimization points"
From the text : "You may recall from <<Activities> that every
screen in an Android app is an activity."
Perhaps "<<Activities>" should be a link to another section. As it is, it is unclear what is meant.
Note from the Author or Editor:This is a bug in Asciidoc. <<Activities> should have been <<Activities>> and would have become a link.
The text reads that the onClick method requires updating and shows the updated onClick method. However, the onClick method doesn't set the status. It does this:
new PostToTwitter().execute(status);
The method that should be updated instead is PostToTwitter.doInBackground()
The downloadable sample code shows this.
Note from the Author or Editor:That is correct - we did add AsyncTask in Chapter 6. So, we should say:
... So, doInBackground() in PostToTwitter task becomes:
protected String doInBackground(String... statuses) {
try {
Twitter.Status status = getTwitter().updateStatus(statuses[0]);
return status.text;
} catch (TwitterException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return "Failed to post";
}
}
the following sentence makes no sense in context:
"Note that although we moved the code where we initialize our connection to the cloud, we still need the AsyncTask to deal with the fact that this call is still blocking and may take a while to complete, as it's subject to network availability and latency."
we didn't move the initialization to the cloud; we simply moved it to a different function (a function that isn't in the cloud). also, the word "still" is repeated unnecessarily.
Note from the Author or Editor:Should be:
Note that we need AsyncTask to deal with the fact that this call is blocking and may take a while to complete, as it's subject to network availability and latency.
Saying "to create our own instance of this object" meaning "to subclass" is confusing, at best.
An object is an instance of a class, but a class is not an instance of an object.
Note from the Author or Editor:"We are going to create our own instance of this object and call it YambaApplication." should say: "We are going to create our own subclass of Application class and call it YambaApplication."
`it also provides the onTerimante() callback'
It should be `onTerminate()'.
The value START_STICKY is returned from the onStartCommand() method. This value occurs 4 times in the book, but is never explained.
Note from the Author or Editor:START_STICKY is used as a flag to indicate this service is started and stopped explicitly, which is our case.
"stated" should be "started"
The text says to look at figure 8-2 for an example of what the Running services activity looks like on the android, but instead it shows essentially the same diagram as is shown in figure 8-1
Note from the Author or Editor:What is Figure 8-3 should be in place of Figure 8-2. Figure 8-3 should be replaced with what was originally yamba-3.png before designer re-did them.
"an then go back to "sleep" for some time"
should be
"and then go back to "sleep" for some time"
Putting Thread#start() in Service#onStartCommand(Intent, int, int) which may be invoked few times will result in IllegalThreadStateException.
Note from the Author or Editor:That is correct. I ended up moving all the code from onStartCommand() on onCreate() and getting rid of onStartCommand() completely. Alternatively, one could catch IllegalThreadStateException.
You declare a flag, runFlag, to check if service is running. However, you don't used it later in any method.
In next version of class UpdaterService (Example 9-2 / p.124), it appears in method onStartCommand. The same code should be in the Example 8-6 (p. 114)
Note from the Author or Editor:We use this flag later on...
Figure 8-3 does not display what it says it displays
Note from the Author or Editor:Yes, this is a wrong picture. It should be image called yamba-3.png, but for some reason it got replaced somewhere.
"it's" should be "its"
The create table SQL statement does not include the "source" column (which is referenced later in the chapter)
Note from the Author or Editor:Change this line to look like this:
String sql = "create table " + TABLE + " (" + C_ID + " int primary key, " + C_CREATED_AT + " int, " + C_SOURCE + " text, " + C_USER + " text, " + C_TEXT + " text)"; //
"...define these as constants to that we can refer to them..."
should be
"...define these as constants so that we can refer to them..."
values.put(DbHelper.C_SOURCE, status.source);
This throws an SQLiteException, trying to use the column because it isn't in the "create table" statement on page 123. Either remove from this loop, or add to the create statement.
This is more an omission than mistake.
Last sentence: We can also verify that the data is indeed in the database by using sqlite3.
This can be done with the dump command at the sqlite3 prompt:
sqlite> .dump
Hi ,
the method was wrote like this :
public StatusData getStatusData() { //
return statusData;
}
this will lead to NullPointerException when you run the application
it suppose to be like this :
public StatusData getStatusData()
{ statusData = new StatusData(this);
return statusData;
}
Note from the Author or Editor:
I would actually improve it like this instead:
public StatusData getStatusData() {
if (statusData==null) {
statusData = new StatusData(this);
}
return statusData;
}
Missing the following line:
import android.widget.SimpleCursorAdapter.ViewBinder;
There are several ViewBinder classes, and if the wrong one is chosen when Eclipse offers to import one for you it won't compile.
Note from the Author or Editor:Correct. In general, I tried to leave imports out of the book as they are more/less automatic in Eclipse, however in this case there are a view choices making it a potential issue.
"In our final iteration of TimelineAdapter..."
The caption for Example 10-8 says "TimelineActivity.java."
These can't both be correct.
Note from the Author or Editor:Yes, it should say just "adapter" not TimelineAdapter.
Wouldn't moving this section prior to the Adapter discussion make more sense? There are several iterations that are not testable until the manifest has been updated (e.g., the entire TimelineAdapter class is built, then refactored out before it can be used).
Note from the Author or Editor:Sure, that would make sense. Whenever there's refactoring, there's a lot of moving code around, which is easier to do, than explain to others. And while this is happening, code is generally broken. That's why refactoring is best done in small steps.
No indication is given as to which onCreate() method is being modified.
Note from the Author or Editor:I would add a title to that block of code that says:
onCreate() in TimelineActivity
This block needs a little more setup. The variable 'yamba' has not yet been defined in TimelineActivity.java. There is also no previous definition of the getPrefs() function.
((YambaApplication) getApplication()).getStatusData().delete();
The delete() method was not described in the StatusData class on pp. 130-133 or added in the intervening pages.
In fact, the capability of Yamba to delete status data is not even mentioned in the text.
Note from the Author or Editor:It is added in the code but not discussed in the book, correct.
getPrefs() method is not defined for yamba object in code line:
// Check if preferences have been set
if (yamba.getPrefs().getString("username", null) == null) {
I added following code in YambaApplication.java, which seems to work
public SharedPreferences getPrefs() {
return prefs;
}
(Note: the code snippet containing yamba.getPrefs() first appeared on page 152.)
Note from the Author or Editor:Yes, that is a possible fix. Another one is to make prefs be package-visible, i.e. remove "private" declaration.
Omits to say that StatusActivity.java must be refactored to extend BasicActivity instead of Activity; otherwise, Options Menu cannot be called from the Status Update screen. The following line seems to work ok for me.
public class StatusActivity extends BaseActivity implements OnClickListener, TextWatcher {
...
}
Note from the Author or Editor:Correct. This is true for StatusActivity same way it is true for TimelineActivity.
Android reference for requery() in Cursor class says:
"This method is deprecated. Don't use this. Just request a new cursor, so you can do this asynchronously and update your list view once the new cursor comes back."
So instead of
@Override
public void onReceive(Context context, Intent intent) { // 2
cursor.requery(); // 3
adapter.notifyDataSetChanged(); // 4
Log.d("TimelineReceiver", "onReceived");
I tried following, which seems to work ok, and I don't see "ERROR/Cursor(145): Invalid statement in fillWindow()" in LogCat
@Override
public void onReceive(Context context, Intent intent) { // 2
cursor = yamba.getStatusData().getStatusUpdates();
adapter.changeCursor(cursor);
adapter.notifyDataSetChanged(); // 4
Log.d("TimelineReceiver", "onReceived");
Adding a network receiver as in Example 11-8 requires a code change in the UpdaterService.java class. The downloadable example code reflects this change needed, but there is no mention of that in the book.
The code change is required because the onReceive() callback in NetworkReceiver also starts the UpdaterService as seen in the following code snippet.
...
} else {
Log. d(TAG, "onReceive: connected, starting UpdaterService");
context. startService(new Intent(context, UpdaterService.class));
}
Trying to start the inner class Updater object (is-a Thread) a second time results in an Exception and a force close of the program. So the following change is required in the UpdaterService.java class, as shown below, to check whether the service is already running and if so, not to start updater again.
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (!runFlag) { // swCL: otherwise Exception thrown when NetworkReceiver calls startService(); in downloaded code, not in book
this.runFlag = true;
this.updater.start();
this.yamba.setServiceRunning(true);
Log.d(TAG, "onStarted");
}
return Service.START_STICKY;
}
The word "recorder" should have been "receiver" when describing Boot and Network receivers.
Part B, com.marakana.yamba.provider
should match the URI given.
StatusData statusData;
No initialization is provided in the book. The reader is left to examine the downloaded example code for:
@Override
public boolean onCreate() {
statusData = new StatusData(getContext());
return false;
}
The issue is actually in the StatusData.java class, but surfaces here in the code to override the insert() method
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = statusData.dbHelper.getWritableDatabase();
...
In StatusData.java class, the access level modifier for the member field dbHelper was 'private':
private final DbHelper dbHelper;
but that should be changed to 'no modifier' or package-private:
final DbHelper dbHelper;
in order for statusData.dbHelper to be visible in other classes such as StatusProvider here.
The change is made in the downloadable code, but not mentioned in the book.
Note from the Author or Editor:Correct.
The code pointed to by Note #3 of example 12-2:
Cursor c =
context.getContentResolver().query(StatusProvider.CONTENT_URI,
null, null, null, null);
does not sort the dataset returned. Changing the code to the following worked for me:
Cursor c =
context.getContentResolver().query(StatusProvider.CONTENT_URI,
null, null, null, StatusData.C_CREATED_AT+" DESC");
This is so that the next couple of lines of code will work
....
try {
if (c.moveToFirst())
...
since the dataset would then be sorted in descending order of time created, and the first record would be the last 'post.'
Note from the Author or Editor:That is correct - to sort the cursor:
Cursor c = context.getContentResolver().query(StatusProvider.CONTENT_URI, null, null, null, null);
should be:
Cursor c =
context.getContentResolver().query(StatusProvider.CONTENT_URI,
null, null, null, StatusData.C_CREATED_AT+" DESC");
@color/edit_text_background is undefined.
Note from the Author or Editor:It is defined in res/values/colors.xml, but not explained in the book.
In Chapter 8, when testing the UpdaterService on the emulator, the app crashes when you try to start the service using the menu. It appears that onStartCommand is not available in SDKVersion 4 so in order to fix this you need to change your AndroidManifest.xml file to:
<uses-sdk android:
Note from the Author or Editor:Yes, that's correct, the minimumSdkVersion should be 5 so that onStartCommand() is available. Version 4 didn't have it.
if (!YambaApplication.LOCATION_PROVIDER_NONE.equals(provider)) { // 4
locationManager = (LocationManager) getSystemService(LOCATION_SERVICE); // 5
}
To prevent problems when location preference is on, then turned off, should clear location member variables as follows:
else {
locationManager = null;
location = null;
}
yamba.getTwitter().setMyLocation(latlong);
The location is never cleared in situations where location preference is on, and then turned off.
Maybe call setMyLocation(null) in an else?
Note from the Author or Editor:That should work.
App will crash if reader forgets to add uses-permission elements in the AndroidManifest.xml file for Yamba (just as for the standalone WhereAmI app discussed in the previous section).
<manifest ...>
...
<uses-permission android:
<uses-permission android:
</manifest>
Note from the Author or Editor:That's correct. I thought this is explained in the book prior to this example already.
Useful information when simulating location updates/provider in an emulator (my target virtual device was for API Level 8, Android 2.2):
1. On phone's Home page > Menu > Settings >
a. Location & security: Set at least Use GPS satellites (maybe also set Use wireless networks, although using network didn't work for me; see below)
b. Application > Development > set Allow mock locations
2. In Eclipse with the Android plugins (ADT and DDMS) > Window > Show View > Other... > Android folder > Emulator Control > scroll to Location Controls to enter Longitude and Latitude > Send
If reader is having trouble getting the emulator's location provider to work, try the following.
a. For preference, I could only make the location provider accept the lat and lon values entered in the emulator location controls when I selected "GPS via satellites!", and not when I selected "Mobile Network will do."
b. For debugging, I also changed the LOCATION_MIN_TIME to something much shorter, say 10000 (10 seconds).
Note from the Author or Editor:Correct. Useful clarification.
Similar to the omission in Note 1, does not remind reader here that the static final field INTERVAL_NEVER has to be defined in YambaApplication.java (snippet below is from the downloadable example code).
public class YambaApplication extends Application implements
OnSharedPreferenceChangeListener {
...
public static final long INTERVAL_NEVER = 0;
...
}
Above code does not appear to work; no alarm call is triggered.
The problem appears to be a mismatch of the first two parameters. Replace this line of code
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, System
.currentTimeMillis(), interval, pendingIntent);
with this line
alarmManager.setInexactRepeating(AlarmManager.RTC, System
.currentTimeMillis(), interval, pendingIntent);
If the second parameter is to be System.currentTimeMillis(), then the first parameter has to be AlarmManager.RTC, which is described in the Android Reference as: "Alarm time in System.currentTimeMillis() (wall clock time in UTC). This alarm does not wake the device up; if it goes off while the device is asleep, it will not be delivered until the next time the device wakes up."
Note from the Author or Editor:True.
Change ELAPSED_REALTIME () to AlarmManager.RTC.
Might wish to remind reader explicitly to create a new Android Project in Eclipse, with a new package name com.marakana.logservice
Note from the Author or Editor:Correct, but assumed they'd know that by this point in the book.
"The new Java file is located in the gen folder under /gen/com/marakana/logservice/LogService.java." should say:
"The new Java file is located in the gen folder under /gen/com/marakana/logservice/ILogService.java."
(just rename LogService.java to ILogService.java)
Note (3) This instance of IBinder is represented by ILogService.Stub(), a helper method that is generated for us in the Java stub file...
should read
Note (3) This instance of IBinder is represented by ILogService.Stub, a helper inner class that is generated for us in the Java stub file...
typo in name of generated class file
... This code is part of /gen/com/marakana/logservice/LogService.java.
should read
... This code is part of /gen/com/marakana/logservice/ILogService.java.
Note from the Author or Editor:Correct
The following can also be inferred from looking into the LogClient folder in the Download Example Code, but was not obvious to me and caused a NoClassDefFoundError at run time before I figured out what to do.
The second paragraph says:
... However, this time around we are also going to make this project depend on the LogService project. This is important because LogClient has to find the AIDL files we created as part of LogService in order to know what that remote interface looks like...
However, I found that I needed to save the (two) .aidl files and the Message.java file in the LogClient project anyway -- as described more below. So, I believe making the LogClient project depend on the LogService project is just so that Android will know to install the LogService.apk when it is installing the LogClient.apk (to the emulator in my case.)
The Android Dev Guide on "Android Interface Definition Language (AIDL)"
says in the Defining an AIDL Interface section: "You must define your AIDL interface in an .aidl file using the Java programming language syntax, then save it in the source code (in the src/ directory) of both the application hosting the service and any other application that binds to the service."
Here's how I recall I did it:
1. In Eclipse, right click on the LogClient project > New > Package > enter Name for new package: com.marakana.logservice
2. Right click on the new package > Import... > File System | Next > Browse... > navigate to your Eclipse workspace folder | locate the folder for LogService project > src/com/marakana/logservice/ > select the files: ILogService.aidl, Message.aidl, Message.java > Finish
Note from the Author or Editor:That is correct - you need to copy both AIDL files as well as Message.java form Server to Client preserving original package names. This is so that both projects can use the same interfaces. The reason for this copying is because one app cannot load another app's code.
The reason for making one project dependent on another in Eclipse is just to help with Eclipse installing both projects when they chance.
© 2016, O’Reilly Media, Inc.
(707) 827-7019
(800) 889-8969
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.