iOS程序员必须知道的Android要点

iOS程序员必须知道的Android要点

2014/05/06 | 分类: ANDROID开发 | 0
条评论
 | 标签: ANDROID

本文由 伯乐在线 - chris 翻译自 objc。欢迎加入Android小组。转载请参见文章末尾处的要求。

本博客英文原文副本 http://blog.csdn.net/opengl_es/article/details/25243257

在移动应用飞速发展的今天,APP只针对IOS平台进行开发已经不够了,如今Android在移动设备占有近80%的市场,如此大量的潜在用户怎么能被忽略掉呢。

在这篇文章中,本人会介绍在IOS开发中,怎么学习一些Android开发的理念,Android和IOS功能上本身有一定的相似之处,但是具体实现的方式各异,所以这篇文章会使用一个项目例子进行对比,说明怎么在这两个平台上分别去实现这个任务。

除了了解IOS的开发知识,本文还需要对Java有一定的了解,并能够安装和使用ADT(Android Development Tools)。此外,如果你是一个Android新手,那么请试试去看看Android的官方教程—— building
your first app
,非常有用。

UI设计简要说明

本文不会深入研究关于IOS和Android两个平台之间的用户体验或者设计模式之间的差异,不过如果能够理解Android上的一些优秀的UI范例也很有帮助:ActionBar、Overflow menu、back button share action等等。假如你很想尝试Android开发,那么强烈推荐你去Google Play Store上购置一台Nexus5,然后把它作为你日常使用的设备使用一周,然后尝试仔细了解这个操作系统的各种功能和扩展特性,如果开发者连操作系统的各种使用规则都不了解,那么做出来的产品一定有问题。

编程语言的应用框架

Objective-C和Java之间有很多不同之处,如果把Objective-C的编程风格带到Java里面的话,很多代码也许会和底层的应用框架冲突。简单地说,就是需要注意一些区别:

  • 去掉Objective-C里面的类前缀,因为Java里有显式的命名空间和包结构,所以就没必要用类前缀了。
  • 实例变量的前缀用“m”,不用“_”,在写代码的过程中要多利用JavaDoc文档。这样能使代码更清晰,更适合团队合作。
  • 注意检查NULL值,Objective-C对空值检查做的很好,不过Java没有。
  • 不直接使用属性,如果需要settergetter,需要创建一个getVariableName()方法,然后显式调用它。如果直接使用“this.object”不会调用自定义的getter方法,你必须使用this.getObject这样的方法。
  • 同样的,方法命名时带有getset前缀来标示它是gettersetter方法,Java的方法很喜欢写成actions或者queries等,比如Java会使用getCell(),而不用cellForRowAtIndexPath

项目结构

Android应用程序主要分为两部分。第一部分是Java源代码,以Java包结构排布,也可以根据自己的喜好进行结构排布。最基本的结构就是分为这几个顶层目录:activities、fragments、views、adapters和data(models和managers)。

第二部分是res文件夹,就是“resource”的简称,res目录存放的是图片、xml布局文件,还有其它xml值文件,是非代码资源的一部分。在IOS上,图片只需要匹配两个尺寸,而在Android上有很多种屏幕尺寸需要考虑,Android上用文件夹来管理管理图片、字符串,还有其它的屏幕配置数值等。res文件夹里也含有类似IOS中xib文件的xml文件,还有存储字符串资源、整数值,以及样式的xml文件。

最后,在项目结构上还有一点相似的地方,就是AndroidManifest.xml文件。这个文件相当于IOS的Project-Info.plist文件,它存储了activities、application还有Intent的信息,要了解更多关于Intent的资料,可以继续阅读这篇文章

Activities

Activities是Android APP最基本的可视单元,就像UIViewControllers是IOS最基本的显示组件一样。Android系统使用一个Activity栈来管理Activity,而IOS使用UINavigationController进行管理。当APP启动的时候,Android系统会把Main
Activity
压栈,值得注意的是这是还可以再运行别的APP Activity,然后把它放到Activity栈中。返回键默认会从Activity栈进行pop操作,所以如果用户按下返回键,就可以切换运行已运行的App了。

Activities还可以用Intent组件初始化别的Activity,初始化时可携带数据。启动一个新的Activity类似于IOS上创建一个UIViewController。最基本的启动一个新的Activity的方式就是创建一个带有data的Intent组件。Android上实现自定义Intent初始化器的最好方法就是写一个静态getter方法。在Activity结束的时候也可以返回数据,在Activity结束的时候可以往Intent里面放置额外的数据。

IOS和Android的一个大的区别是,任何一个在AndroidManifest文件中注册的Activity都可以作为程序的入口,为Activity设置一个intent
filter
属性比如“media intent”,就可以处理系统的媒体文件了。最好的例子就是编辑照片Activity。它可以打开一张照片,然后进行修改,最后在Activity结束时返回修改后的照片。

附加提醒:要想在Activity和Fragment之间传递对象,必须要实现Parcelable接口,就像在IOS里需要遵循协议一样。还有,Parcelable对象可以存在于Activity或者Fragment的savedInstanceState里,这样在它们被销毁后可以更容易重建它们的状态。

下面就来看看怎么在一个Activity中启动另一个Activity,然后在第二个Activity结束时进行返回。

启动其它Activity并返回结果


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

//
A request code is a unique value for returning activities

private

static

final

int

REQUEST_CODE_NEXT_ACTIVITY =
1234;

 

protected

void

startNextActivity() {

    //
Intents need a context, so give this current activity as the context

    Intent
nextActivityIntent =
new

Intent(
this,
NextActivity.
class);

       startActivityForResult(nextActivityResult,
REQUEST_CODE_NEXT_ACTIVITY);

}

 

@Override

protected

void

onActivityResult(
int

requestCode,
int

resultCode, Intent data) {

    switch

(requestCode) {

    case

REQUEST_CODE_NEXT_ACTIVITY:

        if

(resultCode == RESULT_OK) {

            //
This means our Activity returned successfully. For now, Toast this text. 

            //
This just creates a simple pop-up message on the screen.

                Toast.makeText(this,
"Result
OK!"
,
Toast.LENGTH_SHORT).show();

            }

            return;

        }   

        super.onActivityResult(requestCode,
resultCode, data);

}

Activity结束时返回数据


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public

static

final

String activityResultString =
"activityResultString";

 

/*

 *
On completion, place the object ID in the intent and finish with OK.

 *
@param returnObject that was processed

 */

private

void

onActivityResult(Object returnObject) {

        Intent
data =
new

Intent();

        if

(returnObject !=
null)
{

            data.putExtra(activityResultString,
returnObject.uniqueId);

        }

     

        setResult(RESULT_OK,
data);

        finish();       

}

Fragments

Fragment的概念在Android上比较独特,从Android3.0开始引入。Fragment是一个迷你版的控制器,可以显示在Activity上。它有自己的状态和逻辑,同时在一个屏幕上支持多个Fragment同时显示。Activity充当Fragment的控制器,Fragment没有自己的上下文环境,只能依赖Activity存在。

使用Fragment最好的例子就是在平板上的应用。可以在屏幕左边放一个fragment列表,然后在屏幕的右边放fragment的详细信息。Fragment可以把屏幕分成可重复利用的小块,分别控制管理。不过要注意Fragment的生命周期,会有些细微的差别。

Fragment是实现Android结构化的一种新的方式,就像IOS中的不用UITableview而用UICollectionView实现列表数据结构化。因为只使用Activity而不用Fragment的话,会简单一些。不过,之后你会遇到麻烦。如果不使用Fragment代替全盘使用Activity的话,在后面需要利用intent和进行多屏幕支持的时候就会遇到困难。

下面看一个UITableViewController的例子和一个ListFragment的地铁时刻表示例。

表格实现


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

@interface
MBTASubwayTripTableTableViewController ()

 

@property
(assign, nonatomic) MBTATrip *trip;

 

@end

 

@implementation
MBTASubwayTripTableTableViewController

 

-(instancetype)initWithTrip:(MBTATrip
*)trip

{

    self
= [super initWithStyle:UITableViewStylePlain];

    if
(self) {

        _trip
= trip;

        [self
setTitle:trip.destination];

    }

    return
self;

}

 

-(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]];

}

 

#pragma
mark - UITableViewDataSource

 

-(NSInteger)numberOfSectionsInTableView:(UITableView
*)tableView

{

    return
1;

}

 

-(NSInteger)tableView:(UITableView
*)tableView numberOfRowsInSection:(NSInteger)section

{

    return
[self.trip.predictions count];

}

 

#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;

}

 

-(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;

}

 

-(BOOL)tableView:(UITableView
*)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath

{

    return
NO;

}

 

-
(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath

{

    [tableView
deselectRowAtIndexPath:indexPath animated:YES];

}

 

@end

List Fragment实现


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

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;

 

    /**

     *
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() { }

 

    @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);

    }

 

    @Override

    public

void

onViewCreated(View view, Bundle savedInstanceState) {

        super.onViewCreated(view,
savedInstanceState);

        TripDetailsView
headerView =
new

TripDetailsView(getActivity());

        headerView.updateFromTripObject(mTrip);

        getListView().addHeaderView(headerView);

    }

}

下面,我们来分析Android上特有的一些组件。

Android通用组件

ListView和Adapter

ListView和IOS的UITableView最像,也是使用最频繁的组件之一。类似于UITableView的UITableViewControllerListView也有一个ListActivity,还有ListFragment。这些组件会更好地处理一些布局问题,也为操作数据适配器提供了便利,这个接下来会说到。下面这个例子就是使用ListFragment来展示数据,类似TableViewdatasource

关于datasource,Android上没有datasource和delegate,只有Adapter。Adapter有很多种形式,主要功能其实就是为了把datasource和delegate合在一起。Adapter拿到数据然后填充到Listview中,在ListView中初始化响应的组件并显示出来,下面是arrayAdapter的使用:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

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);

    }

 

    @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);

        }

 

        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;

    }

}

可以看到,adapter里面有一个很重要的方法叫getView,和IOS的cellForRowAtIndexPath方法一样。还有一个相似之处就是循环利用的策略,和IOS6上的实现很相似。在Android和IOS上循环利用View都很重要,事实上它对列表的实现有很大帮助。这个adapter很简单,使用了一个内建的类ArrayAdapter来存放数据,也解释了怎么把数据填入ListView中。

AsyncTask

对于IOS上的Grand Central Dispatch,Android上也有AsyncTask。它是异步操作工具的又一选择,用一种很友好的方式实现异步任务。不过AsyncTask有点超出了本文的范围,所以本人还是推荐你看看这里

Activity的生命周期

IOS开发者在写Android的过程中还要注意的就是Android的生命周期。可以先从Activity的生命周期文档开始:

本质上Activity的生命周期很像UIViewController的生命周期,主要区别在于Android上可以任意销毁Activity,所以保证Activity的数据和状态很重要,如果在onCreate()中保存了的话,可以在saved state中恢复Activity的状态。最好的方法就是使用saveInstanceState来存储bundled数据,例如下面的TripListActivity是示例工程的一部分,用来保存当前显示的数据:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

public

static

Intent getTripListActivityIntent(Context context, TripList.LineType lineType) {

    Intent
intent =
new

Intent(context, TripListActivity.
class);

    intent.putExtra(TripListActivityState.KEY_ACTIVITY_TRIP_LIST_LINE_TYPE,
lineType.getLineName());

    return

intent;

}

 

public

static

final

class

TripListActivityState {

    public

static

final

String KEY_ACTIVITY_TRIP_LIST_LINE_TYPE =
"KEY_ACTIVITY_TRIP_LIST_LINE_TYPE";

}

     

TripList.LineType
mLineType;   

     

@Override

protected

void

onCreate(Bundle savedInstanceState) {

   super.onCreate(savedInstanceState);

   mLineType
= TripList.LineType.getLineType(getIntent().getStringExtra(TripListActivityState.KEY_ACTIVITY_TRIP_LIST_LINE_TYPE));

}   

还有一个要注意的地方就是屏幕旋转:如果屏幕发生旋转,会改变Activity的生命周期。也就是说,Activity会先被销毁,然后再重建。如果已经保存了数据和状态,Activity可以重建原来的状态,实现无缝重建。很多APP开发者在遇到APP旋转时会出现问题,因为Activity没有处理旋转的改变。注意不要用锁定屏幕的方向来解决这个问题,因为这样会存在一个隐含的生命周期的bug,在某些情况下还是可能发生的。

Fragment生命周期

Fragment的生命周期和Activity的很像,但是有一些区别:

还有一个问题就是Fragment和Activity通信的问题。需要注意的是onAttach()方法在onActivityCreated()方法之前被调用,这就意味着在fragment创建完成后Activity还不能保证已经存在。如果需要为父Activity设置接口或者代理,则需要在onActivityCreated()方法调用之后。

Fragment也有可能会在系统需要的时候被创建和销毁。如果要保存它的状态,那么也要像Activity一样进行处理。下面这个是示例项目中的一个小例子,trip列表Fragment会记录相应的数据,和上面的地铁时间示例一样:


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

/**

 *
The configuration flags for the Trip List Fragment.

 */

public

static

final

class

TripListFragmentState {

    public

static

final

String KEY_FRAGMENT_TRIP_LIST_LINE_TYPE =
"KEY_FRAGMENT_TRIP_LIST_LINE_TYPE";

    public

static

final

String KEY_FRAGMENT_TRIP_LIST_DATA =
"KEY_FRAGMENT_TRIP_LIST_DATA";

}

 

/**

 *
Use this factory method to create a new instance of

 *
this fragment using the provided parameters.

 *

 *
@param lineType the subway line to show trips for.

 *
@return A new instance of fragment TripListFragment.

 */

public

static

TripListFragment newInstance(TripList.LineType lineType) {

    TripListFragment
fragment =
new

TripListFragment();

    Bundle
args =
new

Bundle();

    args.putString(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_LINE_TYPE,
lineType.getLineName());

    fragment.setArguments(args);

    return

fragment;

}

 

protected

TripList mTripList;

protected

void

setTripList(TripList tripList) {

    Bundle
arguments =
this.getArguments();

    arguments.putParcelable(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_DATA,
tripList);

    mTripList
= tripList;

    if

(mTripArrayAdapter !=
null)
{

        mTripArrayAdapter.clear();

        mTripArrayAdapter.addAll(mTripList.trips);

    }

}

 

@Override

public

void

onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    if

(getArguments() !=
null)
{

        mLineType
= TripList.LineType.getLineType(getArguments().getString(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_LINE_TYPE));

        mTripList
= getArguments().getParcelable(TripListFragmentState.KEY_FRAGMENT_TRIP_LIST_DATA);

    }

}   

还要注意的是,Fragment经常会在onCreate方法中利用bundled参数重建自己的状态。而自定义的Trip列表模型类相关的setter方法也会把对象添加到bundled参数中。这样就可以保证在Fragment被销毁或者重建时,比如屏幕旋转后,可以利用最新的数据去重建状态。

关于布局

和Android上其它部分的开发工作一样,指定布局文件也有自己的优缺点。Android上的布局文件都存放在res/layouts文件夹中,以易读的xml形式存储。

地铁列表布局


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<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>

下面这个是IOS上用UITableView和UIButton来制作的类似效果:

可以发现,Android的布局文件更容易阅读和理解,而且提供了多种布局方式,我们只介绍了其中的一小部分。

通常来说,我们接触的最基本的UI结构就是ViewGroup的子类,RelativeLayout、LinearLayout、FrameLayout是最常用的。这些ViewGroup的子类可以容纳别的View,并包含了一些排布控件的属性。

一个很好的例子就是上面用到的RelativeLayout,在里面可以使用android:layout_alignParentBottom="true"来把按钮定位到布局底部。

最后,如果要在Fragment或者Activity中使用这些控件的话,可以在onCreateView()方法中使用布局的资源ID:


1

2

3

4

@Override

public

View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

    return

inflater.inflate(R.layout.fragment_subway_listview, container,
false);

}

布局小贴士

  • 请使用dp(density-independent pixels),不直接使用dx(pixels);
  • 不要在可视化编辑器中移动布局组件——通常来说可视化编辑器在你调好高和宽后,会为组件添加一些多余的像素,所以最好就是直接操作xml文件;
  • 如果在布局的heightwidth看到有用fill_parent这个属性的话,你会发现在API
    8的时候这个属性就已经被限制了,改用match_parent替换。

如果要了解更多关于这方面的内容可以看看这篇文章——responsive android applications

数据

Android上的数据存储也和IOS上差不多:

  • SharedPreferences、NSUserDefaults;
  • 内存存储对象;
  • internal、external文件读写document directory文件读写;
  • SQLite数据库存储Core Data形式数据库存储。

其中最基本的区别就是Core Data。在Android上可以直接访问sqlite数据库并可以返回cursor对象得到结果。更详细的介绍请看这篇文章—— using
SQLite on Android

Android课后作业

之前已经讨论的东西只是描述了Android的大概 ,要想好好利用Android上的更多的特性,本人建议你看看下面的这些概念:

  • ActionBar,Overflow Menu,还有Menu Button;
  • 跨应用间数据共享;
  • 响应系统actions;
  • 好好学习Java的特性:泛型、抽象方法和抽象类等等;
  • 看看Google的低版本兼容库;
  • 关于Android上的模拟器:可以安装x86
    HAXM plugin
    来使模拟器更流畅。

最后的工作

以上所有涉及的知识点都在MBTA中实现了(托管在Github上)。这个项目仅仅是为了解释两个不同平台之间的一些基本的概念,比如应用架构、数据处理、界面开发等。

我们可以学到更多的解决问题的技巧和方式。因为两平台的实现细节各不相同,也许了解Android的工作原理可以对IOS的下一个版本的开发工作有所帮助。系统之间有很多相似的地方,谁知道下个版本的IOS会出现什么呢?



1.源代码 Source

2.关于多屏幕支持的google官方文档:http://developer.android.com/guide/practices/screens_support.html

3.关于 Intents 的文档:here

4.关于平板分块处理的文档资料: multi-pane tablet view

5.感谢 NSHipster

原文链接: objc   翻译: 伯乐在线 chris
译文链接: http://blog.jobbole.com/67098/
[ 转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]

时间: 2024-09-04 23:10:24

iOS程序员必须知道的Android要点的相关文章

作为程序员必须知道的10大网站

问题描述 1.CSDN(http://www.csdn.net/)这个地球人都知道2.MSDN(http://www.msdn.com)做.net的不去就算做.net的3.博客园(http://www.cnblogs.com/)这个水还是比较深的4.编程中国社区(http://www.hello-code.com)还不错,内容覆盖比较广5.iteye(http://www.iteye.com/)貌似被CSDN上了6.codeplex(http://www.codeplex.com/)微软的一个开

Java程序员必须知道的10个调试技巧

调试可以帮助识别和解决应用程序缺陷,在本文中,作者将使用大家常用的的开发工具Eclipse来调试Java应用程序.但这里介绍的调试方法基本都是通用的,也适用于NetBeans IDE,我们会把重点放在运行时上面. 在本文中使用的是Eclipse Juno版(Eclipse 4.2),在开始前给大家提3点建议! 不要使用System.out.println作为调试工具 把所有涉及到的组件日志级别激活并使用 使用日志分析器来读取日志 1.条件断点 如果你不知道如何添加断点,只需点击左边面板(行号前面

优秀程序员必须知道的20个位运算技巧_C 语言

一提起位运算,人们往往想到它的高效性,无论是嵌入式编程还是优化系统的核心代码,适当的运用位运算总是一种迷人的手段,或者当您求职的时候,在代码中写入适当的位运算也会让您的程序增加一丝亮点,最初当我读<编程之美>求"1的数目"时,我才开始觉得位运算是如此之美,后来读到 <Hacker's Delight>,感慨到Henry S.Warren把位运算运用的如此神出鬼没,很多程序都十分精妙,我觉得在一个普通的程序中大量运用这样的代码的人简直是疯了!但掌握简单的位运算技巧

Java程序员必须知道的5个JVM命令行标志_java

本文是Neward & Associates的总裁Ted Neward为developerworks独家撰稿"你不知道5个--"系列中的一篇,JVM是多数开发人员视为理所当然的Java功能和性能背后的重负荷机器.然而,我们很少有人能理解JVM是如何进行工作的-像任务分配和垃圾收集.转动线程.打开和关闭文件.中断和/或JIT编译Java字节码,等等. 不熟悉JVM将不仅会影响应用程序性能,而且当JVM出问题时,尝试修复也会很困难. 本文将介绍一些命令行标志,您可以使用它们来诊断和

每个Java程序员必须知道的5个JVM命令行标志

不熟悉JVM将不仅会影响应用程序性能,而且当JVM出问题时,尝试修复也会很困难. 本文将介绍一些命令行标志,您可以使用它们来诊断和调优您的Java虚拟机性能. 1.DisableExplicitGC 我已记不清有多少次用户要求我就应用程序性能问题提供咨询了,其实只要跨代码快速运行grep,就会发现清单1所示的问题-原始Java性能反模式: 清单 1. System.gc; // We just released a bunch of objects, so tell the stupid //

程序员必须知道的10大基础实用算法及其讲解

算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来. 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists). 算法步骤: 1 从数列中挑出一个元素,称为 "

程序员必须知道的10大基础实用算法及其讲解(转)

  算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序n个项目要Ο(nlogn)次比较.在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(nlogn)算法更快,因为它的内部循环(innerloop)可以在大部分的架构上很有效率地被实现出来. 快速排序使用分治法(Divideandconquer)策略来把一个串行(list)分为两个子串行(sub-lists). 算法步骤: 1从数列中挑出一个元素,称为"基准"(p

程序员必须知道的10大基础实用算法及其讲解 - WEB开发者

来源:http://www.admin10000.com/document/4574.html#jtss-hi

神经网络API、Kotlin支持,那些你必须知道的Android 8.1预览版和Android Studio 3.0新特性

谷歌2017发布会更新了挺多内容的,而且也发布了AndroidStudio3.0预览版,一些功能先睹为快. 过去的五个月里, Kotlin一直是我们反复谈论的重点.现在要告诉大家的是,Android Studio 3.0可以将Kotlin添加到您的项目中了.最新版本的Android Studio在支持Java 8语言功能上得到了改进,另外一个亮点是,有了用于Gradle 3.0.0的Android插件. 好,下面步入正文. 曾仅用 55 秒发布会的 Android 8.0 Oreo 在时隔两个月