Android Studio中Make Project,Clean Project,Rebuild Project区别

其实好几个版本的这几个选项都不一样,里面的机理也不同,这里以Android Studio2.1.1为例,亲自验证过,见下图:

屏幕快照 2016-05-24 下午1.44.58         屏幕快照 2016-05-24 下午2.11.13

  1. Make Project:编译Project下所有Module,一般是自上次编译后Project下有更新的文件,不生成apk。
  2. Make Selected Modules:编译指定的Module,一般是自上次编译后Module下有更新的文件,不生成apk。
  3. Clean Project:删除之前编译后的编译文件,并重新编译整个Project,比较花费时间,不生成apk。
  4. Rebuild Project:先执行Clean操作,删除之前编译的编译文件和可执行文件,然后重新编译新的编译文件,不生成apk,这里效果其实跟Clean Project是一致的,这个不知道Google搞什么鬼~~
  5. Build APK:前面4个选项都是编译,没有生成apk文件,如果想生成apk,需要点击Build APK。
  6. Generate Signed APK:生成有签名的apk。

注意:

  1. 对于Clean和Rebuild看到最后的效果是一样的。
  2. 平时小的改动直接用Make Project就可以,可以看到只有它有快捷方式,表明这个功能要经常用。对于一些大的改动比如更新lib,大功能修改等,用Clean或Rebuild,毕竟这两个编译起来要费时间。
  3. 如果有的时候死活编译不过,多试试Clean吧,会有意想不到的效果!

一些感悟

这里记录下平时工作和生活上的一些想法,不定时更新:

  1. 有的时候工作上开不开心取决去你在工作中的地位,以及上头boss是否认可你、是否在乎你,特别是比较敏感的人。
  2. 发现不同的公司,公司文化和环境确实不一样,就拿一点来说,对于老板的问话,以前公司是必须及时回复,回复的技巧也要高级,现在就不一样了。。。

This one simple change for better class naming will blow your mind

英文链接:https://www.novoda.com/blog/better-class-naming/

Single Responsibility1, Beware the Share2, Boyscout Rule3. These are some conventions that guide good practice in software development and I believe naming can benefit from these ideas and other practices. This blog post will discuss class naming using Model View Presenter as the example and show you one change you can do to make your code cleaner and more readable.

MVP4 is being used as one example of a design pattern that requires thoughtful naming, this article doesn’t give you direction for what your Model, View or Presenter should do as behaviour, but we can all agree that you usually have an interface for each of these ideas. Infact the main benefits of this article can be taken to naming any grouping of classes and you can reinterpret these ideas for other class naming.

Many people try to come up with a great name all at once. This is hard and rarely works well. The problem is that naming is design: it is picking the correct place for each thing and creating the right abstraction.

This quote comes from an amazing eight part blog post5 that talks about how naming can go through seven stages. As a TLDR; The stages include Missing name, Nonsense naming, Honest naming, Honest and Complete, Does the Right Thing, Intent, Domain Abstracted naming. I believe many people change variable names and go through the stages as discussed maybe not hitting every stage and maybe not getting to the end, but still, variables are renamed often. If these names move towards the right side of this scale, this is movement towards more expressive and readable code.

I want to skip variable naming and move straight on to class naming. Variables get renamed an order of magnitude more often than Classes get renamed. There seems to be a bigger barrier to class renaming, in that it will likely touch more code, affect more people, ruffle more feathers, and so it is done less often. Let’s drop this as an excuse not to strive for clean code and accept class naming as fair game.

Consider a class that implements the interface TweetsView, one bad way of naming this implementer is like this TweetsViewImpl implements TweetsView. Impl?? Impl!! Naming an implementation is an important opportunity to convey some meaning about what that specific implementation does. It’s lazy to just go with Impl and not think about what your class does.

My interface name describes the behaviour of my class. I can’t think of anything more to add to that, I’ll just use Impl.

Next logical step people usually take is, they know Impl is bad naming, but still can’t think of a name because the interface TweetsView seems exactly the right name; its a view of the tweets. They avoid the Impl code smell with something just as bad: DefaultTweetsView, SimpleTweetsView or flip it round TweetsView implements ITweetsView (this just moves the problem rather than resolves it, but a good hint of what is to come 🙂 ).

You want your classes to be named after the behaviour they exhibit, following the Single Responsibility Principle you should know what this responsibility/behaviour is.

There are two solutions here:

  • Only 1 implementer of the interface? Get rid of the interface! Over abstraction is unnecessary and confusing.
  • Understand the problem space and name explicitly according to behaviour TrendingTweetsView implements TweetsView

Now thinking about class naming and interfaces from another perspective. If we are using MVP and have an interface for TweetsModel, TweetsView, TweetsPresenter. Then these three interfaces are actually intertwined and should be considering as a system. For example, someone new to the code base, seeing a class implementing TweetsView could think this is just some type of custom view (not MVP), they could then see the other code implementing TweetsModel and still consider this as some other pattern, it’s not until they also notice implements TweetsPresenter that they’ll put the three together. It would be nice if we could make this grouping explicit in the code, lets see an example.

public interface TweetsMvp {

    interface Model {
      ...    
    }

    interface View {
      ...    
    }

    interface Presenter {
      ...    
    }
}

Awesome, we have declared our triad of interfaces under one common name. This fixes the problem of understanding the system. Any legacy developer reading this code or reading one of the implementations can see this interface comes from this triad and that it’s clear intention is to be used in the MVP pattern.

public class TweetsModel implements TweetsMvp.Model {  
  ...
}

public class TweetsView implements TweetsMvp.View {  
  ...    
}

public class TweetsPresenter implements TweetsMvp.Presenter {  
  ...    
}

When we implement the triad of interfaces it further backs up the knowledge that this class is being used in the MVP pattern. Further we have resolved our Impl naming issue. We can have our view of the tweets called TweetsView and this implements an MVP View of Tweets; implements TweetsMvp.View.

This wouldn’t be possible if you just called the individual interfaces Model, View and Presenter. Firstly you usually have multiple MVP interfaces in a codebase and so such generic naming would get very confusing very quickly. Secondly on Android View is already used for the UI system and so one of the two View classes/interfaces would have to be fully qualified which adds noise to the code.

I hope you find the grouping concept useful even if my poor textual explanation doesn’t get you, then the code example should make it obvious.

Always remember to consider your systems as a whole, what parts of the system are interconnected and should these relationships be made explicit. Naming plays an important role in your codebase and getting it right is hard. If ever stuck, think where you are up to on the seven stages of naming; missing name, nonsense naming, honest naming, honest and complete, does the right thing, intent, domain abstracted and what other entities changing this name might effect.

Understanding Android’s LayoutInflater.inflate()

英文链接:https://www.bignerdranch.com/blog/understanding-androids-layoutinflater-inflate/

It’s easy to get comfortable with boilerplate setup code, so much so that we gloss over the finer details. I’ve experienced this with LayoutInflater (which coverts an XML layout file into corresponding ViewGroups and Widgets) and the way it inflates Views inside Fragment’s onCreateView() method. Upon looking for clarification in Google documentation and discussion on the rest of the web, I noticed that many others were not only unsure of the specifics of LayoutInflater’s inflate() method, but were completely misusing it.

Much of the confusion may come from Google’s vague documentation in regards to attachToRoot, the optional third parameter of the inflate() method.

Whether the inflated hierarchy should be attached to the root parameter? If false, root is only used to create the correct subclass of LayoutParams for the root view of the XML.

Maybe the confusion comes from a statement that ends in a question mark?

Confusion

The general gist is this: If attachToRoot is set to true, then the layout file specified in the first parameter is inflated and attached to the ViewGroup specified in the second parameter.

Then the method returns this combined View, with the ViewGroup as the root. When attachToRoot is false, the layout file from the first parameter is inflated and returned as a View. The root of the returned View would simply be the root specified in the layout file. In either case, the ViewGroup’s LayoutParams are needed to correctly size and position the View created from the layout file.

Passing in true for attachToRoot results in a layout file’s inflated View being added to the ViewGroup right on the spot. Passing in false for attachToRoot means that the View created from the layout file will get added to the ViewGroup in some other way.

Let’s break down both scenarios with plenty of examples so we can better understand.

attachToRoot Set to True

Imagine we specified a button in an XML layout file with its layout width and layout height set to match_parent.

<Button xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/custom_button">
</Button>

We now want to programmatically add this Button to a LinearLayout inside of a Fragment or Activity. If our LinearLayout is already a member variable, mLinearLayout, we can simply add the button with the following:

inflater.inflate(R.layout.custom_button, mLinearLayout, true);

We specified that we want to inflate the Button from its layout resource file; we then tell the LayoutInflater that we want to attach it to mLinearLayout. Our layout parameters are honored because we know the Button gets added to a LinearLayout. The Button’s layout params type should be LinearLayout.LayoutParams.

The following would also be equivalent. LayoutInflater’s two parameter inflate() method automatically sets attachToRoot to true for us.

inflater.inflate(R.layout.custom_button, mLinearLayout);

Another appropriate use of passing true for attachToRoot is a custom View. Let’s look at an example where a layout file uses a <merge> tag for its root. Using a <merge> tag signifies that the layout file allows for flexibility in terms of the type of root ViewGroup it may have.

public class MyCustomView extends LinearLayout {
    ...
    private void init() {
        LayoutInflater inflater = LayoutInflater.from(getContext());
        inflater.inflate(R.layout.view_with_merge_tag, this);
    }
}

This is a perfect use for a true attachToRoot parameter. The layout file does not have a root ViewGroup in this example, so we specify our custom LinearLayout to be its root. If our layout file had a FrameLayout as its root instead of <merge>, the FrameLayout and its children would inflate as normal. Then the FrameLayout and children would get added to the LinearLayout, leaving the LinearLayout as the root ViewGroup containing the FrameLayout and children.

attachToRoot Set to False

Let’s take a look at when you would want to set attachToRoot to false. In this scenario, the View specified in the first parameter of inflate() is not attached to the ViewGroup in the second parameter at this point in time.

Recall our Button example from earlier, where we want to attach a custom Button from a layout file to mLinearLayout. We can still attach our Button to mLinearLayout by passing in false for attachToRoot—we just manually add it ourselves afterward.

Button button = (Button) inflater.inflate(R.layout.custom_button, mLinearLayout, false);
mLinearLayout.addView(button);

These two lines of code are equivalent to what we wrote earlier in one line of code when we passed in true for attachToRoot. By passing in false, we say that we do not want to attach our View to the root ViewGroup just yet. We are saying that it will happen at some other point in time. In this example, the other point in time is simply the addView() method used immediately below inflation.

The false attachToRoot example requires a bit more work when we manually add the View to a ViewGroup. Adding our Button to our LinearLayout was more convenient with one line of code when attachToRoot was true. Let’s look at some scenarios that absolutely require attachToRoot to be false.

A RecyclerView’s children should be inflated with attachToRoot passed in as false. The child views are inflated in onCreateViewHolder().

public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(getActivity());
    View view = inflater.inflate(android.R.layout.list_item_recyclerView, parent, false);
    return new ViewHolder(view);
}

RecyclerViews, not us, are responsible for determining when to inflate and present its child Views. The attachToRoot parameter should be false anytime we are not responsible for adding a View to a ViewGroup.

When inflating and returning a Fragment’s View in onCreateView(), be sure to pass in false for attachToRoot. If you pass in true, you will get an IllegalStateException because the specified child already has a parent. You should have specified where your Fragment’s view will be placed back in your Activity. It is the FragmentManager’s job to add, remove and replace Fragments.

FragmentManager fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentById(R.id.root_viewGroup);

if (fragment == null) {
    fragment = new MainFragment();
    fragmentManager.beginTransaction()
        .add(R.id.root_viewGroup, fragment)
        .commit();
}

The root_viewGroup container that will hold your Fragment in your Activity is the ViewGroup parameter given to you in onCreateView() in your Fragment. It’s also the ViewGroup you pass into LayoutInflater.inflate(). The FragmentManager will handle attaching your Fragment’s View to this ViewGroup, however. You do not want to attach it twice. Set attachToRoot to false.

public View onCreateView(LayoutInflater inflater, ViewGroup parentViewGroup, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_layout, parentViewGroup, false);
    …
    return view;
}

Why are we given our Fragment’s parent ViewGroup in the first place if we don’t want to attach it in onCreateView()? Why does the inflate() method request a root ViewGroup?

It turns out that even when we are not immediately adding our newly inflated View to its parent ViewGroup, we should still use the parent’s LayoutParams in order for the new View to determine its size and position whenever it is eventually attached.

You are bound to run into some poor advice about LayoutInflater on the web. Some people will advise you to pass in null for the root ViewGroup if you are going to pass in false for attachToRoot. However, if the parent is available, you should pass it in.

FrameLayout Root

Lint will now warn you not to pass in null for root. Your app won’t crash in this scenario, but it can misbehave. When your child View doesn’t know the correct LayoutParams for its root ViewGroup, it will try to determine them on its own using generateDefaultLayoutParams.

These default LayoutParams might not be what you desired. The LayoutParams that you specified in XML will get ignored. We might have specified that our child View should match the width of our parent View, but ended up with our parent View wrapping its own content and ending up much smaller than we expected.

There are a few scenarios in which you will not have a root ViewGroup to pass into inflate(). When creating a custom View for an AlertDialog, you do not yet have access to its parent.

AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(mContext);
View customView = inflater.inflate(R.layout.custom_alert_dialog, null);
...
dialogBuilder.setView(customView);
dialogBuilder.show();

In this case, it is okay to pass in null for the root ViewGroup. It turns out that the AlertDialog would override any LayoutParams to match_parent anyway. However, the general rule of thumb is to pass in the parent if you’re able to do so.

Avoiding Crashes, Misbehaviors and Misunderstandings

Hopefully this post helps you avoid crashes, misbehaviors and misunderstandings when using LayoutInflater. Here are some key takeaways for different uses in certain circumstances:

  • If you have a parent to pass into the root ViewGroup parameter, do so.
  • Try to avoid passing in null for the root ViewGroup.
  • Pass in false for the attachToRoot parameter when we are not the ones responsible for attaching our layout file’s View to its root ViewGroup.
  • Do not pass in true for a View that has already been attached to a ViewGroup.
  • Custom Views are a good use case to pass in true for attachToRoot.

Android开发的前景到底怎么样?

转载:http://geek.csdn.net/news/detail/74896

本文原创发布于微信公众号AndroidDeveloper「googdev」,转载请务必注明出处。

最近公众号有不少人问我这样一个问题:「张哥,我刚接触编程,准备学习下Android开发,但是担心现在市场饱和了,Android开发的前景怎么样?」

想着可能有很多人都有这样的担心,于是就赶紧写篇文章,来跟你们谈下Android开发的前景到底怎么样?

1. 编程语言

众 所周知,Android 开发是基于 Java 编程语言的,而 Java 作为老牌成熟的编程语言,虽然经常被人诟病,但是毫无疑问,Java是目前市场上最成熟、应用最广泛的编程语言,很多成熟的业务系统 Java 都是第一选择,每月的 TIOBE 编程语言排行榜Java一直稳坐榜首位置。所以从编程语言这个点来说,学习 Android 开发你至少掌握了Java编程语言,而目前来看不管是语言的使用范围还是市场需求来说,Java 都看不到被淘汰的影子。

2. 市场需求

这个是很多人关心的,说最近感觉 Android 开发饱和了,找工作很难,所以纠结要不要学习 Android 。

不得不说,前两年的移动开发确实很火,为什么?因为一个行业刚兴起,智能手机发展之快速让人震惊,加上创业的越来越多,基本上创业都需要做一个 App,以上种种原因综合下来导致移动开发人才特别紧缺,可以说随便能鼓捣出来点东西都很容易找到工作。

而现在明显降温了,对人才的要求也越来越苛刻,现在搞一个公众号就可以创业融资了,App 不再是刚需了,所以跟前两年比需求确实少了很多。

但 是跟同类其他编程行业比移动开发依然需求蛮大的,这种现象只能说明以前是「疯狂」,而现在才是「正常」,所以那些说 Android 开发饱和了,我并不认同,因为我看到各大招聘网站 Android 跟 iOS 开发的职位跟其他岗位的招聘比并没有少,所以所谓的「饱和」只是一种从「疯狂」到「正常」的错觉而已。

3. 薪资水平

说到开发前景,工资就不得不谈,这也是很多人关注的一个话题。我今天随意看了下一些招聘网站给各个编程岗位开的价格,姑且以一线城市1-3年工作经验来看:

Android & iOS开发月薪范围大概在10-20k
Java、Php、.NET等月薪范围大概在8-15k
一些小众语言如Ruby、Python等月薪范围大概在10-20k

以上根据个人能力会有差别,而且不同公司也会有差距,上面的数据并不是那么准确,只是我粗略的一个观察,所以别纠结数据层面,但是我感觉这个范围不会差别太大,所以得出结论大概是移动开发依然是目前薪资相对较高的行业。

4. 为什么感觉不好找工作

这个也是很多人的疑问,说投了很多简历,都没有回应,总感觉今年工作特别难找。这个在这里解释下原因:

  1. 不知道我的读者们关注经济不,我老板是做投资出身的,所以跟着他我也了解了不少知识。从15年下半年开始,中国经济特别差,企业亏损严重,股票大 跌,投资人手上也没多少现金了,所以看到去年下半年很多公司倒闭,老板跑路,我亲身经历的一好哥们公司倒闭了,还拖欠了两个月工资没发。这还只是上海这 边,相对影响已经算小了,如果你仔细观察,应该能发现小城市经济更差,我是亲身体验过的,去年回家,家里很多煤矿倒闭,房地产不经济,钢铁厂亏损严重,村 里很多人都没有工作,我是亲眼见证的。

我在去年底今年初的时候还特意在公号发过一篇文章说如果想换工作建议不要裸辞,先找到好的机会再辞职,最近经济很差,不像往年,工作很好找,相信很多那时候关注我的人还有印象。

一直到现在,虽然公布的中国经济数据慢慢有所恢复,但是受到的影响不会那么快消除,还需要点时间恢复,所以经济大环境是工作不好找的一个重要原因。

  1. 因为移动开发前两年的火爆,导致很多新兴的培训机构大举招聘移动开发,所以这两年你会发现培训 iOS、Android 开发的人特别多,我自己是能感受到的,收到的一些简历比以前培训的多多了。而且相对来说培训iOS的人要更多些,我自己也是培训出来的,我这里并不是看不 起培训的。只是人越来越多,质量也是越来越差,现在很多培训的都是伪造简历来求职,所以你会发现应届生、没工作经验的人找工作较难些,因为没工作经验的初 级太多了,而现在企业也优先选择有工作经验的,现在再也不像两年前招不到人需要自己培养了,现在可选的人一大把,我为什么要先招进来再培养你呢?

但是对于一些实习生以及初学者并不是没有需求,大部分企业还是想要招聘一些基础扎实,学习能力强,甚至有点工作经验的同学,而这些可能刚好是很多培训出来的人缺乏的,至于没工作怎么获取工作经验?可以看我这篇文章「自学Android到什么程度才能找到工作?」

5. 如何选择

所以综上,真的别杞人忧天,沉下心来学好基础,提升技能比什么都重要,如果你找不到工作,只能说你学的还不够好,或者能力还不达标,并不能说明没有这个岗位的招聘,以个人能力的不行来否定整个市场的需求是弱者的行为!

还 有很多人纠结到底是选择 Android、iOS、web前端还是后端?虽然我是做Android开发的,但是我必须客观的说,没有什么区别,谁也不知道以后 Android、iOS 谁把谁打败了,谁也不知道web前端还是后端以后更吃香,我能给到你的建议是看你的兴趣所在,别纠结太多,学好了哪一个都能找到一份好的工作,先跟着你内 心的感觉学好、学精一门,而且编程语言都是互通的,以后你都会有机会接触其他领域,目前第一重要的是全身心的投入你现在想要从事的职业上,吃饱饭比什么都 重要!

以上就是我能给到你们的建议,不一定是对的,但是从我自身角度来说都是非常中肯的建议,我能帮到你们的也就以我过来的人经验与见解,帮你们指明方向而已,至于怎么走,怎么选择,全凭自己!

Service要点全解析

转载:

1、Service概述

Service的主要作用是,让系统可以在后台干一些不与用户交互的操 作,这些操作可能会比较耗时,比如去下载一些网络资源等;也可能是一项长期运行的工作,比如说监听电话来电、播放音乐等。初听起来,Service与线程 Thread很像,但Service和Thread完全是两个不同的东西啊。

(1)Service不是运行在一个独立的进程中,它和我们的应用程序在同一个进程中;

(2)Service也不是一个线程,相反,Service是运行在主线程的,因此我们不能直接在Service中干上面那些耗时操作,因为它会很耗CPU,阻塞主线程,很容易出现ANR错误(Application Not Responding),合适的做法是,在Service中开启一个Thread,进行上面的耗时操作。

如果我们要在Service中开启线程进行工作,我们也可以使用Service的一个子类IntentService,IntentService类中已经开启了线程,我们只需要实现一个方法即可,后面具体介绍。

2、Service用法

和Activity类似,我们要使用Service,只需要通过Intent发出请求即可。当然,在使用Service前,记得在AndroidManifest.xml中进行声明。启动方式有两种:

我们先在Activity中加入两组四个按钮,一组为startService的启动按钮和相应的停止按钮;一组为bindService的启动按钮和相应的停止按钮。如图:

启动方式一:startService()

在Activity中,我们如下使用:

  1. public class MainActivity extends Activity implements OnClickListener{
  2.     private Button startServiceBtn,stopServiceBtn,bindServiceBtn,unBindServiceBtn;
  3.     @Override
  4.     protected void onCreate(Bundle savedInstanceState) {
  5.         super.onCreate(savedInstanceState);
  6.         setContentView(R.layout.activity_main);
  7.         startServiceBtn = (Button) findViewById(R.id.startService);
  8.         stopServiceBtn = (Button) findViewById(R.id.stopService);
  9.         bindServiceBtn = (Button) findViewById(R.id.bindService);
  10.         unBindServiceBtn = (Button) findViewById(R.id.unBindService);
  11.         startServiceBtn.setOnClickListener(this);
  12.         stopServiceBtn.setOnClickListener(this);
  13.         bindServiceBtn.setOnClickListener(this);
  14.         unBindServiceBtn.setOnClickListener(this);
  15.     }
  16.     @Override
  17.     public void onClick(View v) {
  18.         switch (v.getId()) {
  19.         case R.id.startService://通过startService()的方式启动Service
  20.             Intent intent = new Intent(this,ServiceTest.class);
  21.             startService(intent);
  22.             break;
  23.         case R.id.stopService:
  24.             stopService(new Intent(this,ServiceTest.class));
  25.             break;
  26.         case R.id.bindService://通过bindService()的方式启动Service
  27.             //TODO
  28.             break;
  29.         case R.id.unBindService:
  30.             //TODO
  31.             break;
  32.         }
  33.     }
  34. }
我们的Service如下:
  1. public class ServiceTest extends Service {
  2.     @Override
  3.     public void onCreate() {
  4.         super.onCreate();
  5.         Log.d(“TAG”“onCreate”);
  6.     }
  7.     @Override
  8.     public int onStartCommand(Intent intent, int flags, int startId) {
  9.         Log.d(“TAG”“onStartCommand”);
  10.         return super.onStartCommand(intent, flags, startId);
  11.     }
  12.     @Override
  13.     public IBinder onBind(Intent arg0) {
  14.         Log.d(“TAG”“onBind”);
  15.         return null;
  16.     }
  17.     @Override
  18.     public boolean onUnbind(Intent intent) {
  19.         Log.d(“TAG”“onUnbind”);
  20.         return super.onUnbind(intent);
  21.     }
  22.     @Override
  23.     public void onDestroy() {
  24.         Log.d(“TAG”“onDestroy”);
  25.         super.onDestroy();
  26.     }
  27. }

上面,点击“startService”按钮,我们就通过startService()将传进来的Intent中的Service启动了,启动时Log打印如下:

说明,我们第一次启动Service的时候,会执行onCreate()和onStartCommand()方法,如果我们这个时候,再次点击“startService”按钮,此时打印如下:

可以看到,如果一个Service已经运行了,再次启动这个Service,只会进入onStartCommand(),onCreate()方法只会在第一次启动的时候进行初始化。Service不像我们的Activity,Activity每次通过Intent启动时都会创建一个新的Activity(默认模式下),然后放入到栈中。而同一个Service,应用中只会存在一个实例,在第一次创建时通过onCreate初始化,运行后,再次启动只是进入onStartCommand。

点击“stopService”后,Service被销毁,进入onDestroy()方法。不管前面我们启动了多少次Service,只要在外部调用一次Context.stopService()或者在Service内部自己调用一次stopSelf(),Service就会被销毁。

上面这种启动方式,在启动完Service后,这个Service就开始在后台运行了,同时,也与启动它的Activity失去了联系,因为不能通过ServiceTest service = new ServiceTest()的方式启动Service,因而我们的Activity中不能获取到ServiceTest的实例,也就不能够控制启动后的Service干Activity想干的事,这个Service只能干它内部定义好的功能,没有与启动它的Activity交互能力。

为了解决与启动Service的组件的通信能力,有一种解决方案就是通过广播的形式, 我们在Activity中发出一些想用操作广播,在Service中注册该广播,Service接收到该广播信息后,完成相应的功能。但是这种方案不够优 雅,频繁发送广播比较消耗性能,同时,由于广播接受者中的onReceive()中,不能执行长时间的工作,时间超过后,可能就直接跳出了方法。因此,这 种方案不是首选。

启动方式二:bindService()

如上面所述,如果要求我们的Service能够和启动Service的组件进行通信,我们可以使用bindService的启动方式。

首先改造Service,Service和组件之间是通过管道IBinder接口进行通信的。

  1. public class ServiceTest extends Service {
  2.     private MyLocalBinder mBinder = new MyLocalBinder();
  3.     @Override
  4.     public void onCreate() {
  5.         super.onCreate();
  6.         Log.d(“TAG”“onCreate”);
  7.     }
  8.     @Override
  9.     public int onStartCommand(Intent intent, int flags, int startId) {
  10.         Log.d(“TAG”“onStartCommand”);
  11.         return super.onStartCommand(intent, flags, startId);
  12.     }
  13.     class MyLocalBinder extends Binder{
  14.         public ServiceTest getServiceInstance(){
  15.             return ServiceTest.this;
  16.         }
  17.         //…这里也可以继续写方法对外提供
  18.     }
  19.     @Override
  20.     public IBinder onBind(Intent arg0) {
  21.         Log.d(“TAG”“onBind”);
  22.         return mBinder;
  23.     }
  24.      //对外提供的访问方法
  25.     public void downLoad(){
  26.         System.out.println(“下载操作方法…”);
  27.     }
  28.     @Override
  29.     public boolean onUnbind(Intent intent) {
  30.         Log.d(“TAG”“onUnbind”);
  31.         return super.onUnbind(intent);
  32.     }
  33.     @Override
  34.     public void onDestroy() {
  35.         Log.d(“TAG”“onDestroy”);
  36.         super.onDestroy();
  37.     }
  38. }

继续改造我们的Activity,在ASctivity中获得连接通道,如下:

  1. private ServiceTest mService;
  2.     private boolean isConnected = false;//我们在使用bindService时,最好定义一个是否连接的标志,方便我们在组件中通信前的判断操作
  3.     ServiceConnection connection = new ServiceConnection() {
  4.         @Override
  5.         public void onServiceConnected(ComponentName className, IBinder binder) {
  6.             ServiceTest.MyLocalBinder localBinder = (MyLocalBinder)binder;    //先获得管道
  7.             mService = localBinder.getServiceInstance();    //通过管道,拿到Service的实例
  8.             isConnected = true;
  9.             mService.downLoad();//拿到Service实例后想干嘛干嘛
  10.         }
  11.         //注意:这个方法当Service意外运行失败时调用,如系统杀死这个Service或者运行Service时遇到崩溃,调用unBinderService并不会调用该方法
  12.         @Override
  13.         public void onServiceDisconnected(ComponentName arg0) {
  14.             isConnected = false;
  15.         }
  16.     };
  17.     @Override
  18.     public void onClick(View v) {
  19.         switch (v.getId()) {
  20.         case R.id.startService://通过startService()的方式启动Service
  21.             Intent intent = new Intent(this,ServiceTest.class);
  22.             startService(intent);
  23.             break;
  24.         case R.id.stopService:
  25.             stopService(new Intent(this,ServiceTest.class));
  26.             break;
  27.         case R.id.bindService://通过bindService()的方式启动Service
  28.             Intent intent2 = new Intent(this,ServiceTest.class);
  29.             bindService(intent2, connection, BIND_AUTO_CREATE);
  30.             break;
  31.         case R.id.unBindService:
  32.             unbindService(connection);
  33.             break;
  34.         }
  35.     }
  36.     @Override
  37.     protected void onDestroy() {
  38.         super.onDestroy();
  39.         if(isConnected){
  40.             unbindService(connection);
  41.             isConnected = false;
  42.         }
  43.     }

点击“bindService”按钮,我们就通过bindService()将传进来的Intent中的Service启动了,启动时Log打印如下:

我 们通过bindService()方法第一次启动后,会进入Service的onCreate()和onBind()方法。如果我们另一个组件(如 Activity),又对同一个Service发起了bindService()操作(也就是说在bindService()中传入了不同的 ServiceConnection),此时只会进入onBind()方法。也就是说onCreate也只是在Service第一次创建时执行。

我们点击“unBindService”时,打印如下:

此时走的是onUnbind和onDestroy方法。

可以看到,不管通过哪种方式启动Service,同一个Service在整个应用程序中只会有一个实例存在,如果通过bindService启动,获得的IBinder实例也都是同一个。四大组件中,只有在Activity、Service、Content Providers中通过bindService启动Service。

3、Service生命周期

从上面的打印日志可以看到,两种启动Service方式走的生命周期方法是不同的,官方的生命周期图很好地描述了两种启动方式下的回调:

两种方式都是只有在第一次启动没有运行的Service时,才会进入onCreate()方法。当Service启动后,后面多次继;启动该Service,只会进入onStartCommand()或者onBind()。

(1)当我们通过startService()方法启动Service时,不管前面我们启动了多少次Service,只要在外部调用一次stopService()或者在Service内部自己调用一次stopSelf(),Service就会被销毁;

(2)当我们通过bindService()启动Service时,前面我们多次启动Service后,当没有客户端连接后(即所有客户端发出了unBindService),这个Service将会被系统销毁;

(3)当这个 Service既被startService启动,又被bindService启动时,即使当所有连接的客户端断开连接后,Service也不会被销毁, 除非再调用一次stopService或内部使用stopSelf(),这个时候Service才会被销毁。或者说如果我们调用了stopService或内部使用stopSelf(),Service也不会被销毁,只有当所有的客户端断开连接后,Service才会被销毁。也就是说,在这种情况下,Service必须在既没有任何Activity关联又停止的情况下,Service才会被销毁。这种情况下的生命周期如下:

注意:为了让我们的Service不过多的浪费CPU资源、内存资源,我们需要在必要的时候进行解除绑定。

(1)当我们的Service只在Activity被用户可见的时候,才与Activity进行交互,那我们应该在Activity的onStart()中bindService,在onStop()中unBindService();

(2)如果我们希望Activity即使在后台时也能够与Service交互,那我们应该在onCreate()中bindService(),在onDestroy()中unBindService(),即Activity在整个生命周期中要与Service交互。

4、IntentService

正 如我们第一部分所谈到的,Service和Thread完全是两个不同的东西,Service主要功能是可以在后台执行一些操作,这些操作不需要与用户交 互。Service是直接运行在主线中中的,如果我们需要在Service中执行耗时操作,为了避免ANR错误,是需要在Service中开启线程来执行 的。对于使用Service有开启线程需求的开发者来说,Android提供了IntentService给用户,IntentService内部已经帮我们开启了线程,我们只需要实现它定义的onHandleIntent()方法,在里面实现我们的功能即可。注意,IntentService不能处理多个线程的请求,但是可以处理多个启动Service的请求。

IntentService提供的功能:

(1)内部创建了一个工作线程,来处理每个启动Service的请求传来的Intent;

(2)内部有一个工作队列,来分别处理多个启动Service的请求,因此避免了多线程的问题;

(3)在所有的请求处理完后,自动停止服务,因此我们不必在Service内部自己写stopSelf();

(4)提供了默认onBind()的实现,直接返回null,意味着IntentService只能通过startService()的方式启动;

(5)提供了默认onStartCommand()的实现,它将我们的Intent放入到了工作队列中,然后执行我们具体实现的onHandleIntent()方法。

一个简单实例如下:

  1. public class IntentServiceTest extends IntentService {
  2.     public IntentServiceTest(){//提供一个默认的构造方法,调用父类构造方法,传入工作线程的名字
  3.         super(“IntentServiceTest”);
  4.     }
  5.     @Override
  6.     protected void onHandleIntent(Intent arg0) {
  7.         long endTime = System.currentTimeMillis() + 5*1000;
  8.           while (System.currentTimeMillis() < endTime) {
  9.               synchronized (this) {
  10.                   try {
  11.                       wait(endTime – System.currentTimeMillis());
  12.                   } catch (Exception e) {
  13.                   }
  14.               }
  15.           }
  16.     }
  17. }

使用IntentService主要是注意两点,一是定义一个默认构造方法,里面调用父 类构造方法,并定义工作线程的名字;第二是实现onHandleIntent()方法,我们在这里面具体实现我们Service需要做的事情。可以看到, 如果我们要使用开启线程的Service,IntentService提供了一种非常好的方案,让用户只需要关注与onHandleIntent接口的具 体实现即可,同时用工作队列的形式支持多启动服务的访问。

5、让我们的Service到前台运行

我 们的Service默认都是在后台默默运行的,用户基本察觉不到有Service在运行。此时,Service的优先级是比较低的,当系统资源不足的时 候,很容易被销毁。因此,如果我们想让用户知道有Service在后台运行,如音乐播放器,或者想让Service一直保持运行状态,不容易被系统回收, 此时,就可以考虑使用前台Service。前台Service必须要设置有一个Notification到手机的状态栏,类似360一样,因此前台 Service能够与用户进行交互,它的优先级也就提高了,在内存不足的时候,不会优先去回收前台Service。

创建前台Service也很简单,就是设置一个Notification到状态栏,如下:

  1. @Override
  2.     public void onCreate() {
  3.         super.onCreate();
  4.         Log.d(“TAG”“onCreate”);
  5.         Notification notification  = new Notification(R.drawable.ic_launcher,“前台Service通知来了”,System.currentTimeMillis());
  6.         Intent notificationIntent = new Intent(this,MainActivity.class);
  7.         PendingIntent pendingIntent = PendingIntent.getActivity(this0, notificationIntent, 0);
  8.         notification.setLatestEventInfo(this“通知标题”“前台Service内容”, pendingIntent);
  9.         //设置到前台运行,第一个参数为通知notification的唯一ID
  10.         startForeground(1, notification);
  11.     }

运行效果如下:

如果,我们要移除掉这个前台Service,只需要调用stopService()即可。这个方法并不会停止Service,只是移除掉Notification。

6、如何保证Service不被杀死

(本部分内容来自:http://www.cnblogs.com/rossoneri/p/4530216.html

这个倒是有点流氓软件的意思,但有些特定情况还是需要服务能保持开启不被杀死,当然这样做我还是在程序里添加了关闭服务的按钮,也就是开启了就杀不死,除非在软件里关闭。

服务不被杀死分3种来讨论

1.系统根据资源分配情况杀死服务

2.用户通过 settings -> Apps -> Running -> Stop 方式杀死服务

3.用户通过 settings -> Apps -> Downloaded -> Force Stop 方式杀死服务

第一种情况:

用户不干预,完全靠系统来控制,办法有很多。比如 onStartCommand() 方法的返回值设为 START_STICKY ,服务就会在资源紧张的时候被杀掉,然后在资源足够的时候再恢复。当然也可设置为前台服务,使其有高的优先级,在资源紧张的时候也不会被杀掉。

第二种情况:

用户干预,主动杀掉运行中的服务。这个过程杀死服务会通过服务的生命周期,也就是会调用 onDestory() 方法,这时候一个方案就是在 onDestory() 中发送广播开启自己。这样杀死服务后会立即启动。如下:

@Overridepublicvoid onCreate() {
// TODO Auto-generated method stubsuper.onCreate();

mBR = new BroadcastReceiver() {
@Overridepublicvoid onReceive(Context context, Intent intent) {
// TODO Auto-generated method stubIntent a = new Intent(ServiceA.this, ServiceA.class);
startService(a);
}
};
mIF = new IntentFilter();
mIF.addAction("listener");
registerReceiver(mBR, mIF);
}

@Overridepublicvoid onDestroy() {
// TODO Auto-generated method stubsuper.onDestroy();

Intent intent = new Intent();
intent.setAction("listener");
sendBroadcast(intent);

unregisterReceiver(mBR);
}

当然,从理论上来讲这个方案是可行的,实验一下也可以。但有些情况下, 发送的广播在消息队列中排的靠后,就有可能服务还没接收到广播就销毁了(这是我对实验结果的猜想,具体执行步骤暂时还不了解)。所以为了能让这个机制完美 运行,可以开启两个服务,相互监听,相互启动。服务A监听B的广播来启动B,服务B监听A的广播来启动A。经过实验,这个方案可行,并且用360杀掉后几 秒后服务也还是能自启的。到这里再说一句,如果不是某些功能需要的服务,不建议这么做,会降低用户体验。

也就是如下操作,启动服务时,我们开启两个Service,A和B,在 A的onCreate中注册B的广播事件,在B的onCreate中注册A的广播事件,当A被销毁时,进入onDestroy()方法时,发送A的广 播,B接收到广播之后再启动A;同理,如果B被销毁,发送广播给A,让A启动B。

第三种情况:

强制关闭就没有办法。这个好像是从包的level去关的,并不走完整的生命周期。所以在服务里加代码是无法被调用的。处理这个情况的唯一方法是屏蔽掉 force stop和 uninstall 按钮,让其不可用。方法自己去找吧。当然有些手机自带的清理功能就是从这个地方清理的,比如华为的清理。所以第三种情况我也没有什么更好的办法了。

在一个创业型公司做研发总监应该干什么?

转自:http://www.cnblogs.com/Fredric-2013/p/5327777.html

从大公司出来在创业公司做研发总监一年半多,时间虽然不长,但也经历了不少问题和困难。想在本文中谈一谈个人对这个岗位的理解。

因为,我所在的公司是以承接项目为主,因此我也是站在这类岗位的角度来谈的。

1、项目前期(售前阶段):

1.1、需求澄清,引导并与客户一起梳理出一个利于双方的需求边界,识别出客户的关键需求;

备注:这个时候对研发总监的要求就是要和客户聊得开,展示自己的能力以给对方信任感;

1.2、架构设计,能在短期交付和长期演进的平衡下设计架构,划分子系统,在可用、可靠性等方面,前期可以简单,但要保证能扩展;

1.3、技术选型,结合团队自身和‘架构设计’完成语言、数据库、中间件等选型,此时点在于权衡交付压力和团队能力提升;

1.4、关键技术风险,对项目中可能出现风险的技术点进行识别,并在初步计划中预留额外工作量;

1.5、工作量评估及开发计划初步制定,支撑商务谈判和合同签订;

 

2、项目中期(售中阶段)

2.1、制定详细的开发计划,协调终端、前端、后端(大的项目也可能是各个子系统)之间的交付,避免彼此的功能存在依赖而阻塞,同时也要能够照顾到前期识别出的关键需求;

备注:尽可能先定接口;尽可能采用迭代;

2.2、对外控制客户的预期(这个时候项目合同已定,可以适当的和客户谈谈困难,给自己的交付留有余地),包括控制需求变更;

2.3、对内控制项目的进度和质量,包括组织例会、关键的质量活动如检视、测试、持续集成等;

2.4、项目框架搭建及核心代码编写;

3、项目后期(售后阶段)

3.1、妥善处理客户需求变更,保证不过多增加项目成本,而又不影响后续款项的收取;

3.2、版本管理,每次发布后的版本要能在配置库可回溯,出了问题可回滚;

10 things you didn’t know about Android’s Service component

英文链接:https://medium.com/@workingkills/10-things-didn-t-know-about-android-s-service-component-a2880b74b2b3#.k5ni2oe4g

I’m terribly sorry for the clickbait-y title, I just couldn’t resist.

Over the years I had more than a few conversations with other fellow Android developers, and one constant topic that kept popping up is how much the Service component is misunderstood, across both beginners and experts. This list is the result of those discussions: it has been wandering in the back of my mind for quite some time, and I finally decided to give it physical (digital?) shape here.

This is not meant to explain in detail how Service works, the official documentation (here, here and here) does a surprisingly good job at that; instead, it’s a collection of overlooked/misunderstood/forgotten concepts that are essential to grasp in order to master the subject.


1. A Service is not a “better AsyncTask”

It’s not made to perform generic asynchronous/background operations: its purpose is to execute logic even when no Activityis visible; think of it as an invisible Activity.

Keep in mind that each Service is a special component that has a cost, not just for your app, but for the entire system.

2. A Service by default runs on the main thread, in the same process of your application

You can optionally run it in a different process, but you should avoid itunless it’s absolutely necessary and you understand all the costs associated with doing so.

3. There’s no magic in IntentService

It works by creating a HandlerThread on which it queues the work to be done, a technique you can easily use outside of a Service.

It’s a simple class, long only 164 lines (74 without comments), go check it out!

4. There can only be one instance of a Service at a time

No matter how you create it, there can only ever be one instance at a time, even when external applications/processes interact with it.

5. A Service can be killed pretty easily

Don’t think that memory pressure is an “exceptional” condition: design your Service to gracefully handle restarts by the system, it’s a natural part of its lifecycle.

You can flag it as foreground to make it harder to kill, but do it only if absolutely necessary.

Note that when code is running inside onCreate(), onStartCommand(), oronDestroy(), it’s treated as if it was in foreground even if it’s not.

See here to understand how likely it is for your process to be killed.

6. A Service can be “started”, “bound”, or both at the same time

Stopping it explicitly won’t terminate it as long as there are bound components, and unbinding all components won’t terminate it until it’s explicitly stopped (if it was ever started). Also note that no matter how many times you call startService(), a single call to stopService() orstopSelf() will stop it.

See this useful chart:

Lifecycle of started and bound modes

7. START_FLAG_REDELIVERY avoids losing input data

If you pass data while starting a Service, returning the flagSTART_FLAG_REDELIVERY in onStartCommand() is useful to avoid losing it if the Service is killed while processing it.

8. Foreground notifications can be partially hidden

A foreground Service has to show a persistent notification, but you can give it a priority value of PRIORITY_MIN to hide it from the status bar (it will still be visible in the notification shade).

9. A Service can start an Activity

Like every Context that is not an Activity, you can start an Activity from aService if you add the FLAG_ACTIVITY_NEW_TASK flag.

You can also show Toast and Status Bar notifications.

10. You are allowed to use the Single Responsibility Principle

Incredibly enough, you should not bundle your business logic in the Serviceclass, but in a separate class. That way, among many other benefits, you are free to run it anywhere else, if needed. Amazing!


Now keep this list in mind, spread the word, and help stop Service abuse!

Android Networking I: OkHttp, Volley and Gson

英文链接:https://medium.com/android-news/android-networking-i-okhttp-volley-and-gson-72004efff196#.ogjc1kdf6

Prologue

This is the first article of a series:(后面跟进)

  • Android Networking II: OkHttp, Retrofit, Moshi and Picasso (coming article)
  • Android Networking III: Ion (coming article)
  • Android Networking IV: Android Async Http (coming article)

Assumptions

Motivation

There’s something that probably you can’t avoid in an Android project: Networking. Whether you are loading images, requesting data from an API server or getting a single byte from internet, you are doing networking.

Given how complex and omnipresent networking is on Android, one of the questions Android developers are facing nowadays is which solutions should I use? There are a lot of good libraries out there and you can stack one upon another in different ways.

The root of so many great networking libraries is that the offered options in the Android framework are not great and were a mess to deal with in the old days (Eclair, Froyo and Gingerbread). You had to write a lot of boilerplate code each time you were doing networking and probably you’ll be doing a sub-optimal (a.k.a. poor) job. This was the ideal scenario to solve once for all a really big and important problem and libraries started to appear and evolve.

In the old days networking in Android was a nightmare, nowadays the problem is to find out which solution best fits the project necessities.

In this article we’ll talk about a particular solution: OkHttp, Volley and Gson.

The aim of this article is just share my discoveries and experiences, learn from others and maybe help some people.

OkHttp

OkHttp is an modern, fast and efficient Http client which supports HTTP/2 and SPDY and does a lot of stuff for you. Reading how many things OkHttp does it’s a good way to understand how hard networking is: Connection pooling, gziping, caching, recovers from network problems, sync and async calls, redirects, retries … and so on.

OkHttp is a very capable networking tool out of the box, without the need of any REST library (Retrofit, Volley…) and probably is the library most developers would choose if they could only include one library in their projects.

OkHttp sits on top of Okio, a library that complements java.io and java.nio to make it much easier to access, store, and process your data. It provides fast I/O and resizable buffers.

OkHttp depends Okio, but Okio can be used by its own.

OkHttp is an modern, fast and efficient Http client which supports HTTP/2 and SPDY and sits on top of Okio.

Volley

Volley is a REST client that makes easy common networking tasks. Takes care of requesting, loading, caching, threading, synchronization and some more stuff. It’s ready to deal with JSON, images, caching, raw text and allow some customization.

Volley was design for RPC style network operations that populate the UI. Is good for short operations.

Volley by default uses as transport layer the Apache Http stack on Froyo and HttpURLConnection stack on Gingerbread and above. The reason is there are problems with those http stacks have on different Android versions.

Volley allow us to easily set up OkHttp as its transport layer.

Volley was developed by Google.

This is what Android networking looks like in Ficus Kirkpatrick (a Googler behind Volley) words. A lot of parallel async calls.

Volley takes care of requesting, loading, caching, threading, synchronization and more. It’s ready to deal with JSON, images, caching, raw text and allow some customization.

Gson

Gson is JSON serialization and deserialization library that uses reflection to populate your Java model objects from JSON objects. You can add your own serializers and deserializers as well to better control and customization.

Gson was developed by Google.

Setup

Gradle dependencies in Android Studio

You need to add the next lines to your app’s build.gradle file.

compile 'com.google.code.gson:gson:2.4.0'
compile 'com.squareup.okhttp:okhttp:2.5.0'
compile 'com.mcxiaoke.volley:library:1.0.19'

Note: The versions may be different as they are updated. Try to avoid + syntax on version numbers as is recommended.

It´s not necessary to explicitly include the Okio dependency because OkHttp already has it.

Update: As of SDK 23, apache http client was removed so it´s necessary to include in the next line in the android section of your build.gradle:

useLibrary 'org.apache.http.legacy'

All the dependencies above are official but Volley, that is not official but is trustworthy. There’s not official gradle dependency for Volley as far as I know as I’m writing this.

Volley

The way Volley works is creating requests and adding them to a queue. One queue is enough for the whole application, so each time you want to make a request you’ll get the (only) Volley queue to add the request to that queue.

I’m using a global application singleton instance of the queue with the next method:

/**
 * Returns a Volley request queue for creating network requests
 *
 * @return {@link com.android.volley.RequestQueue}
 */
public RequestQueue getVolleyRequestQueue()
{
   if (mRequestQueue == null)
   {
      mRequestQueue = Volley.newRequestQueue(this, new OkHttpStack(new OkHttpClient()));
   }
   return mRequestQueue;
}

The method we are using to create a new request queue has an HttpStack as a parameter. If you use the method that don’t provide an HttpStack Volley will create an stack depending on your API level. (based on theAndroidHttpClient for API level 9 and and HttpURLConnection stack for API level 10 and above)

As I mention before, we’d like to use OkHttp as our transport layer, that’s the reason we are using as a parameter an OkHttpStack. The OkHttpClient implementation I’m using is this one.

The next are methods to add the requests to the Volley requests queue:

/**
 * Adds a request to the Volley request queue with a given tag
 * 
 * @param request is the request to be added
 * @param tag is the tag identifying the request
 */
public static void addRequest(Request<?> request, String tag)
{
    request.setTag(tag);
    addRequest(request);
}
/**
 * Adds a request to the Volley request queue
 * 
 * @param request is the request to add to the Volley queue
 */
public static void addRequest(Request<?> request)
{
    getInstance().getVolleyRequestQueue().add(request);    
}

And this is the method to cancel requests that should normally used in theonStop lifecycle method.

/**
 * Cancels all the request in the Volley queue for a given tag
 *
 * @param tag associated with the Volley requests to be cancelled
 */
public static void cancelAllRequests(String tag)
{
    if (getInstance().getVolleyRequestQueue() != null)
    {
        getInstance().getVolleyRequestQueue().cancelAll(tag);
    }
}

So far we already have Volley and OkHttp ready. So we can start making either String, JsonObject or JsonArray request.

A JsonObject request would be like this:

JsonObjectRequest jsonObjectRequest =
        new JsonObjectRequest(Request.Method.GET, mUrl, new Response.Listener<JSONObject>()
        {
            @Override
            public void onResponse(JSONObject response)
            {
                // Deal with the JSONObject here
            }
        },
        new Response.ErrorListener()
        {
            @Override
            public void onErrorResponse(VolleyError error)
            {
                // Deal with the error here
            }
        });

App.addRequest(jsonObjectRequest, mTAG);

We still need to parse the JSON object to our Java model. The response we are receiving on every Volley request (either String, JsonObject or JsonArray) is not really useful as it is.

You are not alone in the Android networking world.

Gson

We can customize the request to get as responses Java objects that match our data model and we are more comfortable with. All it’s needed is aGsonRequest class extending the Volley Request like in this example.

In the next example we can how would be a GET call to retrieve and parse a Json object:

/**
 * Returns a dummy object parsed from a Json Object to the success  listener and a Volley error to the error listener
 *
 * @param listener is the listener for the success response
 * @param errorListener is the listener for the error response
 *
 * @return @return {@link com.sottocorp.sotti.okhttpvolleygsonsample.api.GsonGetRequest}
 */
public static GsonRequest<DummyObject> getDummyObject
(
        Response.Listener<DummyObject> listener,
        Response.ErrorListener errorListener
)
{
    final String url = "http://www.mocky.io/v2/55973508b0e9e4a71a02f05f";

    final Gson gson = new GsonBuilder()
            .registerTypeAdapter(DummyObject.class, new DummyObjectDeserializer())
            .create();

    return new GsonRequest<>
            (
                    url,
                    new TypeToken<DummyObject>() {}.getType(),
                    gson,
                    listener,
                    errorListener
            );
}

In the next example we can how would be a GET call to retrieve and parse a Json array:

/**
 * Returns a dummy object's array in the success listener and a Volley error in the error listener
 *
 * @param listener is the listener for the success response
 * @param errorListener is the listener for the error response
 *
 * @return @return {@link com.sottocorp.sotti.okhttpvolleygsonsample.api.GsonGetRequest}
 */
public static GsonRequest<ArrayList<DummyObject>> getDummyObjectArray
(
        Response.Listener<ArrayList<DummyObject>> listener,
        Response.ErrorListener errorListener
)
{
    final String url = "http://www.mocky.io/v2/5597d86a6344715505576725";

    final Gson gson = new GsonBuilder()
            .registerTypeAdapter(DummyObject.class, new DummyObjectDeserializer())
            .create();

    return new GsonRequest<>
            (
                    url,
                    new TypeToken<ArrayList<DummyObject>>() {}.getType(),
                    gson,
                    listener,
                    errorListener
            );
}

The Gson parsing with a GsonRequest happens on the background worker thread instead of the main thread.

I’m providing a deserializer in the examples above but note that is not mandatory to provide serializers/deserializers and Gson can handle this very well as far as the field names in the class match (including the case) with the names in the JSON file. I like to provide my serializer/deserializer for customization.

On both examples above we are making GET calls. In case the call is a POST one I’ve included an for a GsonPostRequest and how to use it.

OkHttp works as the transport layer for Volley, which on top of OkHttp is a handy way of making network requests that are parsed to Java objects by Gson just before delivering the response to the main thread

Loading images

Image loading in Android is common and complex. Threading, requesting, transformations, memory management, caches…Probably we could right an entire article about it. The good news is there are out there so many good libraries that most of us will never realize how hard it is.

ImageLoader and NetworkImageView

Volley has a custom view called NetworkImageView (subclassingImageView) that is very handy to load images. You can set an URL, a default view holder and an error image.

Example:

mNetworkImageView = (NetworkImageView) itemView.findViewById(R.id.networkImageView);
mNetworkImageView.setDefaultImageResId(R.drawable.ic_sun_smile);
mNetworkImageView.setErrorImageResId(R.drawable.ic_cloud_sad);
mNetworkImageView.setImageUrl(imageUrl, App.getInstance().getVolleyImageLoader());

The important bit in the code above is the setImageUrl method, which receives two parameters: the image address and an ImageLoader (Volleyhelper that handles loading and caching images from remote URLs)

Let’s take a look at the getVolleyImageLoader method and how we can get an ImageLoader.

/**
 * Returns an image loader instance to be used with Volley.
 *
 * @return {@link com.android.volley.toolbox.ImageLoader}
 */
public ImageLoader getVolleyImageLoader()
{
    if (mImageLoader == null)
    {
        mImageLoader = new ImageLoader
                (
                        getVolleyRequestQueue(),
                        App.getInstance().getVolleyImageCache()
                );
    }

    return mImageLoader;
}

/**
 * Returns a bitmap cache to use with volley.
 *
 * @return {@link LruBitmapCache}
 */
private LruBitmapCache getVolleyImageCache()
{
    if (mLruBitmapCache == null)
    {
        mLruBitmapCache = new LruBitmapCache(mInstance);
    }
    return mLruBitmapCache;
}

The only piece missing in this puzzle is LruBitmapCache. Volley does not provide us with an implementation but we can get one from here that looks appropriate and handles the cache size per device specs, which is cool.

ImageRequest

In some cases we might want not to use NetworkImageView. Image for example we want circular images and we are using CircleImageView. In that case we’ll have to use ImageRequest, which works like this:

final CircleImageView circleImageView =
            (CircleImageView) findViewById(R.id.circularImageView);

    // Retrieves an image specified by the URL, displays it in the UI.
    final com.android.volley.toolbox.ImageRequest imageRequest =
            new ImageRequest
            (
                    mImageUrl,
                    new Response.Listener<Bitmap>()
                    {
                        @Override
                        public void onResponse(Bitmap bitmap)
                        {
                            circleImageView.setImageBitmap(bitmap);
                        }
                    },
                    0,
                    0,
                    ImageView.ScaleType.CENTER_INSIDE,
                    null,
                    new Response.ErrorListener()
                    {
                        public void onErrorResponse(VolleyError error)
                        {          circleImageView.setImageResource(R.drawable.ic_cloud_sad);
                        }
                    }
            );
    // Access the RequestQueue through your singleton class.
    App.getInstance().getVolleyRequestQueue().add(imageRequest);
}

Interesting facts

  • All of the components we’ve talk about in this article (Okio, OkHttp, Volley and Gson) can be used as a standalone. They don’t need each other except OkHttp needing Okio.
  • One of the first articles I linked in the introduction (this one) was written by Jesse Wilson. Jesse Wilson is one of the guys behind Android’s HTTP, Gson, OkHttp and Okio. I thought that deserves a mention and my acknowledgement.
  • OkHttp engine is backing HttpURLConnection as of Android 4.4. Twitter, Facebook and Snapchat bundles it as well.
  • Volley was born as a solution to be used in the Google Play Store inside Google.
  • Both OkHttp and Okio are developed by the Square guys.

Final thoughts

The Volley/Gson solution is mature and was quite popular around 2013 and 2014 mainly for been a Google solution and appear in the Android Developers website. Is still a good option to use because it just works well and it´s fast.

On the other hand, the documentation was always poor (still nowadays) and they are not being actively developed anymore (and for a while).

Resources

Show me the Code