Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Tales from the Darkside, Android Development for iOS Developers

Tales from the Darkside, Android Development for iOS Developers

Love your iPhone? Think Android is a cheap knock off? I did too. As a longtime Apple user and iOS developer I recently embarked on a journey to learn Android development.

In this technical talk I will go over the pros and cons of each platform. We'll look at a handful of code samples demonstrating the similarities and differences between Android's Java APIs and iOS's Objective-C APIs.

Denver Startup Week 2014
Android has come a long way very quickly. Understanding it's user interaction paradigms and coding practices will make us better iOS users and developers.

Sean Dougherty

September 16, 2014
Tweet

Other Decks in Programming

Transcript

  1. "My phone is eligible for an upgrade on Sunday. Eff

    yeah!" -Me "It’s as if you buy the new iPhone every year right after it is announced." -Gabe Varela
  2. Background —Started out as a designer —Web Developer 1997-2009 —iOS

    Developer 2009-present —Android Developer, a couple months
  3. iOS

  4. Challenges —11,000+ unique devices —dozens of screen sizes —low upgrade

    numbers Thanks to Jason Pierce ([email protected]) for the stats http://opensignal.com/reports/fragmentation-2013/ (as of July 2013)
  5. Up vs. Back The Up button is used to navigate

    within an app based on the hierarchical relationships between screens. The system Back button is used to navigate, in reverse chronological order, through the history of screens the user has recently worked with.
  6. Back Button —Dismisses floating windows (dialogs, popups) —Dismisses contextual action

    bars, and removes the highlight from the selected items —Hides the onscreen keyboard (IME) —Crosses the app boundry
  7. Navigation Drawer A panel that transitions in from the left

    edge of the screen and displays the app’s main navigation options.
  8. Action Bar A dedicated piece of real estate at the

    top of each screen that is generally persistent throughout the app.
  9. the good —back button, back button!, BACK BUTTON!!! —launching apps

    from any screen, to any screen —large screens —widgets —haptic feedback —notifications —vanilla Android (as provided by google)
  10. the bad —fragmentation —lack of security —carrier and device customizations

    —lack of upgrades —lack of rubber banding animation
  11. Languages —iOS is written in Objective-C, C and now Swift!

    —Android is written in Java and sometimes Scala
  12. iOS Architecture —main.m launches app —AppDelegate entry point for custom

    code application:didFinishLaunchingWithOption s: —Custom UIViewControllers are created as needed —UIViews are added to the UIWindow view hierarchy
  13. iOS Architecture int main(int argc, char *argv[]) { @autoreleasepool {

    return UIApplicationMain( argc, argv, nil, NSStringFromClass([AppDelegate class])); } }
  14. iOS Architecture - (BOOL)application:(UIApplication *)app didFinishLaunchingWithOptions:(NSDictionary *)options { // custom

    launch code // create UIViewControllers as needed via code // or using UIStoryboard (preferred method) return YES; }
  15. Android Architecture —AndroidManifest.xml defines app config similar to Info.plist in

    iOS —Activities represent a visual component similar to UIViewControllers in iOS —"android.intent.action.MAIN" defines the default app Activity —Intents allow app to launch from any screen
  16. Android Architecture AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.androidforios.app" >

    <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".activities.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
  17. Android Architecture MainActivity.java package com.example.appName.app.activities; //...imports public class MainActivity extends

    FragmentActivity { @Override protected void onCreate(Bundle savedInstanceState) { // ... setup activity } }
  18. Android Architecture Intents An Intent is a messaging object you

    can use to request an action from another app component. —start an activity —start a service —deliver a broadcast
  19. Android Architecture Activity Intents An Activity represents a single screen

    in an app. Pass a new Intent to startActivity(). Call startActivityForResult() to receive data when the launched activity finishes. Your activity receives the result as a separate Intent object in your activity's onActivityResult() callback.
  20. Android | iOS —Activity | UIViewController —ListActivity | UITableViewController —View

    | UIView —Fragments | Child ViewControllers —SharedPreferences | NSUserDefaults
  21. UIViewController @interface UIViewController : UIResponder <NSCoding, UIAppearanceContainer> - (void)viewDidLoad; -

    (void)viewWillAppear:(BOOL)animated; - (void)viewDidAppear:(BOOL)animated; - (void)viewWillDisappear:(BOOL)animated; - (void)viewDidDisappear:(BOOL)animated; - (void)viewWillLayoutSubviews; - (void)viewDidLayoutSubviews; @end
  22. Activity public class Activity extends ApplicationContext { protected void onCreate(Bundle

    savedInstanceState); protected void onStart(); protected void onRestart(); protected void onResume(); protected void onPause(); protected void onStop(); protected void onDestroy(); }
  23. Objc.io Android 101 for iOS Developers Issue #11 Android, April

    2014 By Stephen Barnes http://www.objc.io/issue-11/android101foriosdevelopers.html
  24. MBTASubwayTripTableViewController (continued) @implementation MBTASubwayTripTableViewController - (instancetype)initWithTrip:(MBTATrip *)trip { self =

    [super initWithStyle:UITableViewStylePlain]; if (self) { _trip = trip; [self setTitle:trip.destination]; } return self; } ...
  25. MBTASubwayTripTableViewController (continued) ... - (void)viewDidLoad { [super viewDidLoad]; [self.tableView registerClass:[MBTAPredictionCell

    class] forCellReuseIdentifier:[MBTAPredictionCell reuseId]]; [self.tableView registerNib: [UINib nibWithNibName:NSStringFromClass([MBTATripHeaderView class]) bundle:nil] forHeaderFooterViewReuseIdentifier:[MBTATripHeaderView reuseId]]; } ...
  26. MBTASubwayTripTableViewController (continued) ... #pragma mark - UITableViewDataSource - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView

    { return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return [self.trip.predictions count]; } ...
  27. MBTASubwayTripTableViewController (continued) ... #pragma mark - UITableViewDelegate - (CGFloat)tableView:(UITableView *)tableView

    heightForHeaderInSection:(NSInteger)section { return [MBTATripHeaderView heightWithTrip:self.trip]; } - (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section { MBTATripHeaderView *headerView = [self.tableView dequeueReusableHeaderFooterViewWithIdentifier: [MBTATripHeaderView reuseId]]; [headerView setFromTrip:self.trip]; return headerView; } ...
  28. MBTASubwayTripTableViewController (continued) ... - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MBTAPredictionCell reuseId] forIndexPath:indexPath]; MBTAPrediction *prediction = [self.trip.predictions objectAtIndex:indexPath.row]; [(MBTAPredictionCell *)cell setFromPrediction:prediction]; return cell; } @end
  29. TripDetailFragment public class TripDetailFragment extends ListFragment { /** * The

    configuration flags for the Trip Detail Fragment. */ public static final class TripDetailFragmentState { public static final String KEY_FRAGMENT_TRIP_DETAIL = "KEY_FRAGMENT_TRIP_DETAIL"; } protected Trip mTrip; ...
  30. TripDetailFragment (continued) ... /** * Use this factory method to

    create a new instance of * this fragment using the provided parameters. * * @param trip the trip to show details * @return A new instance of fragment TripDetailFragment. */ public static TripDetailFragment newInstance(Trip trip) { TripDetailFragment fragment = new TripDetailFragment(); Bundle args = new Bundle(); args.putParcelable(TripDetailFragmentState.KEY_FRAGMENT_TRIP_DETAIL, trip); fragment.setArguments(args); return fragment; } public TripDetailFragment() { } ...
  31. TripDetailFragment (continued) ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);

    if (getArguments() != null) { mTrip = getArguments().getParcelable(TripDetailFragmentState.KEY_FRAGMENT_TRIP_DETAIL); } } ...
  32. TripDetailFragment (continued) ... @Override public View onCreateView( LayoutInflater inflater, ViewGroup

    container, Bundle savedInstanceState) { Prediction[] predictions = mTrip.predictions.toArray(new Prediction[mTrip.predictions.size()]); PredictionArrayAdapter predictionArrayAdapter = new PredictionArrayAdapter(getActivity(), predictions); setListAdapter(predictionArrayAdapter); return super.onCreateView(inflater, container, savedInstanceState); } ...
  33. TripDetailFragment (continued) ... @Override public void onViewCreated(View view, Bundle savedInstanceState)

    { super.onViewCreated(view, savedInstanceState); TripDetailsView headerView = new TripDetailsView(getActivity()); headerView.updateFromTripObject(mTrip); getListView().addHeaderView(headerView); }
  34. PredictionArrayAdapter public class PredictionArrayAdapter extends ArrayAdapter<Prediction> { int LAYOUT_RESOURCE_ID =

    R.layout.view_three_item_list_view; public PredictionArrayAdapter(Context context) { super(context, R.layout.view_three_item_list_view); } public PredictionArrayAdapter(Context context, Prediction[] objects) { super(context, R.layout.view_three_item_list_view, objects); } ...
  35. PredictionArrayAdapter (continued) ... @Override public View getView(int position, View convertView,

    ViewGroup parent) { Prediction prediction = this.getItem(position); View inflatedView = convertView; if(convertView==null) { LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflatedView = inflater.inflate(LAYOUT_RESOURCE_ID, parent, false); } ...
  36. PredictionArrayAdapter (continued) ... TextView stopNameTextView = (TextView)inflatedView.findViewById(R.id.view_three_item_list_view_left_text_view); TextView middleTextView =

    (TextView)inflatedView.findViewById(R.id.view_three_item_list_view_middle_text_view); TextView stopSecondsTextView = (TextView)inflatedView.findViewById(R.id.view_three_item_list_view_right_text_view); stopNameTextView.setText(prediction.stopName); middleTextView.setText(""); stopSecondsTextView.setText(prediction.stopSeconds.toString()); return inflatedView; } }
  37. User Interaction - Buttons final Button button = (Button)findViewById(R.id.button_id); button.setOnClickListener(new

    View.OnClickListener() { public void onClick(View v) { // Perform action on click } });
  38. Layouts —XML based layout* —stored in res/layouts —can be instaniated

    programmatically *Storyboards are MUCH better IMO
  39. Layouts —Views are nested in Layouts —can also be nested

    in other views or view groups —referenced by android:id
  40. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.androidforios.app.activities.MainActivity$PlaceholderFragment"> <ListView android:id="@+id/fragment_subway_list_listview" android:layout_width="match_parent" android:layout_height="match_parent"

    android:paddingBottom="@dimen/Button.Default.Height"/> <Button android:id="@+id/fragment_subway_list_Button" android:layout_width="match_parent" android:layout_height="@dimen/Button.Default.Height" android:minHeight="@dimen/Button.Default.Height" android:background="@drawable/button_red_selector" android:text="@string/hello_world" android:textColor="@color/Button.Text" android:layout_alignParentBottom="true" android:gravity="center"/> </RelativeLayout>
  41. Loading Resources —I love how Android handles strings, layouts and

    images —iOS needs to get on this, no stringly typed references —R.drawable.drawable_id —R.layout.layout_id —R.string.string_name
  42. Data Storage —SharedPreferences for any primitive data: booleans, floats, ints,

    longs, and strings —SQLite for relational data —Internal Storage openFileOutput() returns a FileOutputStream for saving files to disk —External Storage for saving to mounted media
  43. Shipping an Android App Unlike in iOS, it is dead

    simple to ship an app to the Google Play store. Just build and upload. —No review. —No finger crossing. —No waiting. https://play.google.com/apps/publish
  44. Summary —Android is mature and very high quality* —An overwhelming

    majority of smart phone users have Android —Almost every app I use is awesome on Android *vanilla Android provided by Google
  45. Summary (continued) —Andriod has a ton of momentum —Engineers are

    building great apps —Google has done outstanding work —Android is pushing Apple to try harder
  46. I'm glad we have both iOS and Android They're both

    better because the other exists
  47. —4 months ago I was worried about iOS —iDevice screens

    were too small —apps were too siloed —notifications were annoying and unspecific —the keyboard sucked
  48. iOS 8 and iPhone 6/6+ —I am not worried about

    iOS anymore —iDevice screens are too big —apps are (a bit) less siloed with extensions —notifications are more useful —custom keyboards (finally)