<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>润物无声 &#187; Android</title>
	<atom:link href="http://blog.zhourunsheng.com/tag/android/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.zhourunsheng.com</link>
	<description>天空一朵雨做的云</description>
	<lastBuildDate>Sat, 08 May 2021 05:17:21 +0000</lastBuildDate>
	<language>zh-CN</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.41</generator>
	<item>
		<title>手机安装账户同步服务</title>
		<link>http://blog.zhourunsheng.com/2014/07/%e6%89%8b%e6%9c%ba%e5%ae%89%e8%a3%85%e8%b4%a6%e6%88%b7%e5%90%8c%e6%ad%a5%e6%9c%8d%e5%8a%a1/</link>
		<comments>http://blog.zhourunsheng.com/2014/07/%e6%89%8b%e6%9c%ba%e5%ae%89%e8%a3%85%e8%b4%a6%e6%88%b7%e5%90%8c%e6%ad%a5%e6%9c%8d%e5%8a%a1/#comments</comments>
		<pubDate>Mon, 21 Jul 2014 01:14:14 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[程序设计]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[Sony]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1905</guid>
		<description><![CDATA[<p>【前提】 手机需要root过，不然没有权限拷贝apk到 /system/app 目录下面。 【步骤】 通过手机 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2014/07/%e6%89%8b%e6%9c%ba%e5%ae%89%e8%a3%85%e8%b4%a6%e6%88%b7%e5%90%8c%e6%ad%a5%e6%9c%8d%e5%8a%a1/">手机安装账户同步服务</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<h3>【前提】</h3>
<p>手机需要root过，不然没有权限拷贝apk到 /system/app 目录下面。</p>
<h3>【步骤】</h3>
<p>通过手机的【设置】--【关于手机】 查看当前系统的Android版本，例如我的手机是4.2.2。<br />
在 <a href="http://wiki.rootzwiki.com/Google_Apps">http://wiki.rootzwiki.com/Google_Apps</a> 中下载对应的服务包。<br />
<img src="http://blog.zhourunsheng.com/wp-content/uploads/2014/07/recommended_package_android.png" alt="" width="434" height="314" /></p>
<p>里面包含了很多的服务(system\app)app，我们只需要安装服务框架，登录服务，日历同步，通讯录同步，邮件同步即可。</p>
<p><img src="http://blog.zhourunsheng.com/wp-content/uploads/2014/07/install_apks.png" alt="" /><br />
拷贝到 \system\app 系统目录中。<br />
<img src="http://blog.zhourunsheng.com/wp-content/uploads/2014/07/copyapks.png" alt="" /></p>
<p><b>【账户同步】</b><br />
<img src="http://blog.zhourunsheng.com/wp-content/uploads/2014/07/google_sync.png" alt="" /></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2014/07/%e6%89%8b%e6%9c%ba%e5%ae%89%e8%a3%85%e8%b4%a6%e6%88%b7%e5%90%8c%e6%ad%a5%e6%9c%8d%e5%8a%a1/">手机安装账户同步服务</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2014/07/%e6%89%8b%e6%9c%ba%e5%ae%89%e8%a3%85%e8%b4%a6%e6%88%b7%e5%90%8c%e6%ad%a5%e6%9c%8d%e5%8a%a1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android云推送之GCM</title>
		<link>http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/</link>
		<comments>http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/#comments</comments>
		<pubDate>Sat, 28 Sep 2013 01:39:37 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[GCM]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1860</guid>
		<description><![CDATA[<p>本文展示了怎样来利用谷歌的云推送系统Google Cloud Messaging (GCM)来推送消息到And [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/">Android云推送之GCM</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>本文展示了怎样来利用谷歌的云推送系统Google Cloud Messaging (GCM)来推送消息到Android手机，目前支持两大类消息，一类是“send-to-sync”，即发送一个触发指令，格式为一个自定义的字符串，例如 "syncTime"表示同步时间，那么Android客户端收到此消息的话，需要启动一个后台线程，同设定的Server进行交互，获取最新的时间；还有一类是“message with payload”，即发送一条消息，消息中可包含正文内容，比如message.addData("new_time", curTime)，则可以通过键值"new_time"获取到具体的时间。<span id="more-1860"></span></p>
<p>Pushing data has a number of benefits such as increased battery life and decreased server load. GCM is free to use and has no quotas, so it is a perfect candidate to handle the heavy lifting required for data updates.</p>
<p>There are two types of messages you can push with GCM: Send-to-sync and message with payload. A send-to-sync is basically a tickle, telling your app to sync with the server while a message with payload lets you send data in the push message (up to 4kb). Here’s a simple, full stack example that will help highlight how to get each of these types of push messages integrated with your app.</p>
<p>The app will take either a push message with a payload that updates the time that is saved on the device or a send to sync message that triggers the app to sync the time with the server. This was created using the “Generate App Engine Backend” feature of Android Studio. The Android developers blog has a <a href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html">tutorial</a> on this. If you already have a backend setup, the GCM setup instructions for an existing server can be found <a href="http://developer.android.com/google/gcm/server.html">here</a>.</p>
<p>The source for this example is up on <a href="https://github.com/doubleencore/PushDontPoll" target="_blank">GitHub</a>.</p>
<h2>Create a new endpoint</h2>
<p>Wizard generated files <em>Message.java</em> and <em>MessageEndpoint.java</em> in PushDontPoll-AppEngine were deleted, along with the <em>messageendpoint</em> package in PushDontPoll-endpoints. In lieu of this, <em>DateEndpoint.java</em> was created in PushDontPoll-AppEngine. This class makes an endpoint at <em>/_ah/api/dateendpoint/v1/currentdate</em> that provides the current time in JSON:</p>
<pre>    @JsonAutoDetect
    public static class CurrentDate {

        @JsonProperty
        private long date = new Date().getTime();

        public long getDate() {
            return date;
        }
    }

    @ApiMethod(name = "currentDate")
    public CurrentDate getDate() {
        return new CurrentDate();
    }</pre>
<p>It also provides methods to send a message with payload and send to sync message via GCM. This was ripped out the deleted <em>MessageEndpoint.java</em> , so it only sends messages to a maximum of 10 devices. They are both built using GCM’s <a href="http://developer.android.com/reference/com/google/android/gcm/server/Message.Builder.html">Message.Builder()</a>.</p>
<p>The message with payload adds the time to the data parameter. Key/value pairs added here will be automatically added as extras in the the intent bundle when parsing the message.</p>
<pre>    String newTime = String.valueOf(new Date().getTime());
    Message message = new Message.Builder()
            .addData("new_time", newTime)
            .build();</pre>
<p>The send-to-sync message uses a collapse key so that only one message will be delivered if multiple tickles are queued up. You should not use more than four distinct keys since that is the maximum number that the GCM server can store. You can read more about this parameter <a href="http://developer.android.com/google/gcm/adv.html#msg-lifetime">here</a>.</p>
<pre>    Message message = new Message.Builder()
            .collapseKey("syncTime")
            .build();</pre>
<h2>Add send-to-sync and payload message buttons</h2>
<p>The <em>index.html</em> in the PushDontPoll-AppEngine project was modified to add buttons that allow us to send the two different types of push messages. Most of the wizard-generated code was removed for simplicity. The code to show the buttons is:</p>
<pre>  &lt;div&gt;
      &lt;button id="sendTickleButton"&gt;Send Tickle&lt;/button&gt;
  &lt;/div&gt;

  &lt;div&gt;
      &lt;button id="sendUpdateButton"&gt;Send Update&lt;/button&gt;
  &lt;/div&gt;</pre>
<p>The functions that they call are:</p>
<pre>  &lt;script type="text/javascript"&gt;
    $("#sendTickleButton").click(sendTickle);
    $("#successArea").hide();
  &lt;/script&gt;
  &lt;script type="text/javascript"&gt;
    $("#sendUpdateButton").click(sendUpdate);
    $("#successArea").hide();
  &lt;/script&gt;

  function sendTickle() {
    gapi.client.dateendpoint
    .sendTickle()
    .execute(handleMessageResponse);
  }

  function sendUpdate() {
    gapi.client.dateendpoint
    .sendUpdate()
    .execute(handleMessageResponse);
  }</pre>
<p>These call the <em>sendTickle()</em> and <em>sendUpdate()</em> methods in <em>DateEndpoint.java</em>. Make sure you re-generate the client libraries after changing the PushDontPoll-AppEngine code (Tools -&gt; Google Cloud Endpoints -&gt; Generate Client Libraries).</p>
<h2>Add code to parse GCM message</h2>
<p>The wizard-generated files <em>RegisterActivity.java</em> and <em>GCMIntentService.java</em> were moved to the main project so they could use the <em>Constants</em> class. The added application code to show the saved time was added to the <em>MainActivity</em>class in order to keep things separated and easy to understand. The <em>RegisterActivity</em> is accessible via an action item.</p>
<p>Parsing the GCM message is done in the <em>onMessage()</em> method in the <em>GCMIntentService</em> class. This class is already fleshed out for you thanks to the wizard.</p>
<h2>Parse send-to-sync</h2>
<p>The send-to-sync message will have a <em>collapse_key</em>, so we can request a sync if that matches the sync type we’re expecting:</p>
<pre>    if ("syncTime".equals(intent.getStringExtra("collapse_key"))) {
        Account[] accounts = AccountManager.get(this).getAccountsByType(Constants.ACCOUNT_TYPE);
        if (accounts.length &gt; 0) {
            Bundle bundle = new Bundle(2);
            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
            bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
            ContentResolver.requestSync(accounts[0], Constants.AUTHORITY, bundle);
        }
        [snip]</pre>
<p>This uses a sync adapter with a stubbed authenticator and content provider – see <a href="http://developer.android.com/training/sync-adapters/index.html">here</a> for more info. This can easily be modified to send a network request to sync using your method of choice (Volley, Retrofit, etc) if a sync adapter is not desirable. All of the syncing code is in the <em>com.doubleencore.android.pushdontpoll.sync</em> package, along with the necessary xml files in <em>res/xml/</em> and in the <em>AndroidManifest.xml</em>.</p>
<p>In the <em>onPerformSync()</em> of the <em>SyncAdapter</em>, we can make a network request to the new endpoint that was created to get the current time:</p>
<pre>    Dateendpoint.Builder endpointBuilder = new Dateendpoint.Builder(
            AndroidHttp.newCompatibleTransport(),
            new JacksonFactory(),
            new HttpRequestInitializer() {
                @Override
                public void initialize(HttpRequest httpRequest) throws IOException {
                }
            });

    Dateendpoint endpoint = CloudEndpointUtils.updateBuilder(endpointBuilder).build();
    try {
        CurrentDate date = endpoint.currentDate().execute();
        long time = date.getDate();
        [snip]</pre>
<h2>Parse message with payload</h2>
<p>The message with payload will have <em>new_time</em> as a key in the intent extras. This key coincides with the one we added when building the message. You can read more about the data parameter <a href="http://developer.android.com/google/gcm/server.html#send-msg">here</a>.</p>
<pre>    if (intent.hasExtra("new_time")) {
        // Extract the data from the message
        long time = Long.parseLong(intent.getStringExtra("new_time"));
        [snip]</pre>
<p>In both cases, the time is then saved to shared preferences, and a local broadcast is sent to update the UI. Again, this can be done using your preferred method (event bus, observable cursor, etc).</p>
<pre>    SharedPreferences.Editor editor = getSharedPreferences(Constants.PREFS_NAME, 0).edit();
    editor.putLong(Constants.PREFS_KEY_TIME, time).apply();

    Intent updateIntent = new Intent(Constants.UPDATE_UI_INTENT_ACTION);
    LocalBroadcastManager.getInstance(this).sendBroadcast(updateIntent);</pre>
<h2>Running the server</h2>
<p>In order to use GCM, you must <a href="http://developer.android.com/google/gcm/gs.html">have an API key</a>. You can enter this when running the wizard or by replacing it in <em>DateEndpoint.java</em> in the PushDontPoll-AppEngine project. The server can either be deployed as an App Engine app or run locally using the dev server.</p>
<h2>Local instance</h2>
<p>To run it as a local instance, you need to change:</p>
<pre>    //PushDontPoll-endpoints Project - CloudEndpointUtils.java
    protected static final boolean LOCAL_ANDROID_RUN = true;</pre>
<p>Then you can just run the <em>appengine:devserver</em> maven goal, and it will be running on localhost. The server will now work with a Google API emulator.</p>
<p>If you want to access this local instance on a device (it must be on the same network as the local instance,) you’ll also need to change:</p>
<pre>    //PushDontPoll-endpoints Project - CloudEndpointUtils.java
    protected static final String LOCAL_APP_ENGINE_SERVER_URL_FOR_ANDROID = "http://[local_ip_address]:8080";</pre>
<p>…and download the App Engine SDK from <a href="https://developers.google.com/appengine/downloads">here</a>, and run:</p>
<pre>    appengine-java-sdk/bin/dev_appserver.sh --address 0.0.0.0 [project_directory]/PushDontPoll-Appegine/target/PushDontPoll-AppEngine-1.0</pre>
<h2>App Engine app</h2>
<p>If you didn’t follow along in the blog and are just trying to get the sample code running, you need to create a Google Cloud Platform project and obtain the Project Number and Project ID. You can do this by following the Preliminary Setup section of <a href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html">this blog post</a>.</p>
<p>Set the <em>PROJECT_NUMBER</em> variable in <em>GCMIntentService.java</em> to your project number and the <em>&lt;application&gt;</em> tag in <em>appengine-web.xml</em> to your project ID, and regenerate the client libraries.</p>
<p>You may also want to change the packages (<em>com.doubleencore.android</em>) and domains (<em>doubleencore.com</em>) to your domain.</p>
<p>Deploy the backend server by running the <em>appengine:update</em> maven goal. You can then access your server at<em>http://&lt;project-id&gt;.appspot.com</em>.</p>
<h2>Running the Android app</h2>
<p>You can now deploy the PushDontPoll app to your device or emulator. Once deployed, click on the GCM Register action item to register your device with the server. Once the success message appears, press back and navigate to your server on your computer. Pressing the “Send Tickle” button will push a message that signals the device to sync with the server. The device will hit the date endpoint for the current time, and the time should refresh on the device. Pressing the “Send Update” button will push a message that has the new time in the payload data. This should instantly update the time shown on the device without needing to make an additional trip to the server.</p>
<p>Hopefully, this simple example app will assist you in getting a push framework up and running for your app. I highly recommend reading through the <a href="http://developer.android.com/google/gcm/index.html">GCM documentation</a> to get a thorough understanding, as there are many features not discussed in this article. You will need to handle things, such as request spikes with send-to-sync messages, more gracefully than this sample does as well. Good luck and happy pushing!</p>
<p>文章节选：<a href="http://www.doubleencore.com/2013/09/push-dont-poll-how-to-use-gcm-to-update-app-data/" target="_blank">http://www.doubleencore.com/2013/09/push-dont-poll-how-to-use-gcm-to-update-app-data/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/">Android云推送之GCM</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/09/android%e4%ba%91%e6%8e%a8%e9%80%81%e4%b9%8bgcm/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android后台服务之IntentService</title>
		<link>http://blog.zhourunsheng.com/2013/09/android%e5%90%8e%e5%8f%b0%e6%9c%8d%e5%8a%a1%e4%b9%8bintentservice/</link>
		<comments>http://blog.zhourunsheng.com/2013/09/android%e5%90%8e%e5%8f%b0%e6%9c%8d%e5%8a%a1%e4%b9%8bintentservice/#comments</comments>
		<pubDate>Sat, 07 Sep 2013 01:07:10 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[IntentService]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1841</guid>
		<description><![CDATA[<p>在Android的开发过程中，经常需要用到后台服务来完成非UI的工作，比如文件的下载，那么本文提到的Inten [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/android%e5%90%8e%e5%8f%b0%e6%9c%8d%e5%8a%a1%e4%b9%8bintentservice/">Android后台服务之IntentService</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在Android的开发过程中，经常需要用到后台服务来完成非UI的工作，比如文件的下载，那么本文提到的IntentService便是一个很好的选择，可以利用它的队列机制，放入一系列的任务，然后系统会依次启动单独的Service来执行任务，免去了自己维护队列的麻烦。</p>
<p>In the new category <b>Read the code</b> I’m going to show the internals of the Android framework. Reading the code of the framework can give you a good impression about what’s going on under the hood. In addition to that knowing how the framework developers solved common problems can help you to find the best solutions when facing problems in your own app code.<span id="more-1841"></span></p>
<p><b>What is the IntentService class good for?</b><br />
This article is about the <a href="https://developer.android.com/reference/android/app/IntentService.html">IntentService</a> class of Android. Extending the IntentService class is the best solution for implementing a background service that is going to process something in a queue-like fashion. You can pass data via <a href="https://developer.android.com/reference/android/content/Intent.html">Intents</a> to the IntentService and it will take care of queuing and processing the Intents on a worker thread one at a time. When writing your IntentService implementation you are required to override the<a href="https://developer.android.com/reference/android/app/IntentService.html#onHandleIntent(android.content.Intent)">onHandleIntent()</a> method to process the data of the supplied Intents.</p>
<p>Let’s take a look at a simple example: This DownloadService class receives Uris to download data from. It will download only one thing at a time with the other requests waiting in a queue.</p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm8.staticflickr.com/7393/9690898500_60727af5f0_o.png" width="500" height="360" border="0" /></td>
</tr>
<tr>
<td><a href="https://github.com/pocmo/Android-Zeitgeist-Samples/blob/master/IntentService/DownloadService.java">DownloadService</a></td>
</tr>
</tbody>
</table>
<p><b>The components</b><br />
Before we dip into the source code of the IntentService class, let's first take a look at the different components that we need to know in order to understand the source code.</p>
<p><b>Handler</b> (<a href="https://developer.android.com/reference/android/os/Handler.html">documentation</a>) (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/os/Handler.java">source code</a>)<br />
You may already have used Handler objects. When a Handler is created on the UI thread, messages can be posted to it and these messages will be processed on the UI thread.</p>
<p><b>ServiceHandler</b> (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#58">source code</a>)<br />
The ServiceHandler inner-class is a helper class extending the Handler class to delegate the Intent wrapped inside a Message object to the IntentService for processing.</p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm4.staticflickr.com/3736/9690898370_6ed2c8cdbf_o.png" width="388" height="181" border="0" /></td>
</tr>
<tr>
<td><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#58">ServiceHandler inner class of android.app.IntentService</a></td>
</tr>
</tbody>
</table>
<p><b>Looper</b> (<a href="https://developer.android.com/reference/android/os/Looper.html">documentation</a>) (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/os/Looper.java#Looper">source code</a>)<br />
The Looper class has a MessageQueue object attached to it and blocks the current thread until a Message is received. This message will be passed to the assigned Handler. After that the Looper processes the next message in the queue or blocks again until a message is received.</p>
<p><b>HandlerThread</b> (<a href="https://developer.android.com/reference/android/os/HandlerThread.html">documentation</a>) (<a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/os/HandlerThread.java#HandlerThread">source code</a>)<br />
A HandlerThread is a Thread implementation that does all the Looper setup for you. By creating and starting a HandlerThread instance you will have a running thread with a Looper attached to it waiting for messages to process.</p>
<p><b>Read the code!</b></p>
<p>Now we know enough about all the components to understand the <a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java">IntentService code</a>.</p>
<p><b>onCreate()</b></p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm8.staticflickr.com/7406/9687656993_78b59cdb59_o.png" width="560" height="150" border="0" /></td>
</tr>
<tr>
<td><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#101">IntentService.onCreate()</a></td>
</tr>
</tbody>
</table>
<p>At first a HandlerThread is created and started. We now have a background thread running that already has a Looper assigned. This Looper is waiting on the background thread for messages to process.</p>
<p>Next a ServiceHandler is created for this Looper. The Handler’s <a href="https://developer.android.com/reference/android/os/Handler.html#handleMessage(android.os.Message)">handleMessage</a>() method will be called for every message received by the Looper. The ServiceHandler obtains the Intent object from the Message and passes it to the onHandleIntent() method of the IntentService.</p>
<p><b>onStart()</b></p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm3.staticflickr.com/2864/9690898434_40462575aa_o.png" width="372" height="120" border="0" /></td>
</tr>
<tr>
<td><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#115">IntentService.onStart()</a></td>
</tr>
</tbody>
</table>
<p>The onStart() method is called every time <a href="https://developer.android.com/reference/android/content/Context.html#startService(android.content.Intent)">startService()</a> is called. We wrap the Intent in a Message object and post it to the Handler. The Handler will enqueue it in the message queue of the Looper. The onStart() method is deprecated since API level 5 (Android 2.0). Instead onStartCommand() should be implemented.</p>
<p><b>onStartCommand()</b></p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm4.staticflickr.com/3833/9687656925_11bee6184c_o.png" width="490" height="90" border="0" /></td>
</tr>
<tr>
<td><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#129">IntentService.onStartCommand()</a></td>
</tr>
</tbody>
</table>
<p>In onStartCommand() we call onStart() to enqueue the Intent. We return <a href="https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT">START_REDELIVER_INTENT</a> or<a href="https://developer.android.com/reference/android/app/Service.html#START_NOT_STICKY">START_NOT_STICK</a> depending on what the child class has set via <a href="https://developer.android.com/reference/android/app/IntentService.html#setIntentRedelivery(boolean)">setIntentRedelivery()</a>. Depending on this setting an Intent will be redelivered to the service if the process dies before onHandleIntent() returns or the Intent will die as well.</p>
<p><b>onDestroy()</b></p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm4.staticflickr.com/3695/9687656985_3b939dc0f1_o.png" width="200" height="75" border="0" /></td>
</tr>
<tr>
<td><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#134">IntentService.onDestroy()</a></td>
</tr>
</tbody>
</table>
<p>In onDestroy() we just need to stop the Looper.</p>
<p><b>Conclusion</b></p>
<p>The IntentService code is quite short and simple, yet a powerful pattern. With the <a href="https://developer.android.com/reference/android/os/Handler.html">Handler</a>, <a href="https://developer.android.com/reference/android/os/Looper.html">Looper</a> and <a href="https://developer.android.com/reference/java/lang/Thread.html">Thread</a>class you can easily build your own simple processing queues.</p>
<p>Oh, and if you are looking for an exercise. The code of the onCreate() method contains a TODO comment that I omitted above:</p>
<table cellspacing="0" cellpadding="0" align="center">
<tbody>
<tr>
<td><img style="border: 0px;" alt="" src="http://farm4.staticflickr.com/3681/9690898386_5c17f7cefe_o.png" width="550" height="90" border="0" /></td>
</tr>
<tr>
<td><a href="http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/IntentService.java#101">TODO in onCreate()</a></td>
</tr>
</tbody>
</table>
<p>引用：<a href="http://www.androidzeitgeist.com/2013/08/read-code-intentservice.html" target="_blank">http://www.androidzeitgeist.com/2013/08/read-code-intentservice.html</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/09/android%e5%90%8e%e5%8f%b0%e6%9c%8d%e5%8a%a1%e4%b9%8bintentservice/">Android后台服务之IntentService</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/09/android%e5%90%8e%e5%8f%b0%e6%9c%8d%e5%8a%a1%e4%b9%8bintentservice/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android动画设计之ViewOverlay</title>
		<link>http://blog.zhourunsheng.com/2013/08/android%e5%8a%a8%e7%94%bb%e8%ae%be%e8%ae%a1%e4%b9%8bviewoverlay/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/android%e5%8a%a8%e7%94%bb%e8%ae%be%e8%ae%a1%e4%b9%8bviewoverlay/#comments</comments>
		<pubDate>Thu, 22 Aug 2013 02:38:06 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[ViewOverlay]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1813</guid>
		<description><![CDATA[<p>什么是ViewOverlay？ ViewOverlay is a class that we can find [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/android%e5%8a%a8%e7%94%bb%e8%ae%be%e8%ae%a1%e4%b9%8bviewoverlay/">Android动画设计之ViewOverlay</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<h2>什么是ViewOverlay？</h2>
<p><a href="http://developer.android.com/reference/android/view/ViewOverlay.html">ViewOverlay</a> is a class that we can find in Android since its version <a href="http://developer.android.com/about/versions/android-4.3.html">4.3</a> (API version 18) that provides a transparent layer on top of a <a href="http://developer.android.com/reference/android/view/View.html">View</a>, to which you can add visual content and that does not affect the layout hierarchy.</p>
<p>It always has the same size and position as its host view, allowing you to add content over that view.<span id="more-1813"></span></p>
<h2>运行效果</h2>
<p><img class="alignnone" alt="" src="http://blog.zhourunsheng.com/wp-content/uploads/2013/08/vo-all.gif" width="475" height="686" /></p>
<p>红色部分：使用了 按钮的 parent-parent layout (the main, first-level, layout) ViewGroupOverlay，所以可以穿越整个画面。</p>
<p>橙色部分：没有使用ViewOverlay技术，所以按钮的动画只限定于它的父窗口部分。</p>
<p>绿色部分：结合使用了两种技术，首先是渐变消失，alpha animation from 1f to 0f，然后放在橙色 non-parent ViewOverlay (the orange layout)中，后段动画仅限制与橙色部分。</p>
<h2>使用方法</h2>
<p>You just have to call the <a href="http://developer.android.com/reference/android/view/View.html#getOverlay()">getOverlay()</a> method from any View of your app to get its<a href="http://developer.android.com/reference/android/view/ViewOverlay.html">ViewOverlay</a>, or a <a href="http://developer.android.com/reference/android/view/ViewGroupOverlay.html#add(android.view.View)">ViewGroupOverlay</a> if you are calling this method from some<a href="http://developer.android.com/reference/android/view/ViewGroup.html">ViewGroup</a> object, but both of them uses the same concept.</p>
<p>Once you got it, you can add any View or Drawable that you want to show in this overlay calling <a href="http://developer.android.com/reference/android/view/ViewOverlay.html#add(android.graphics.drawable.Drawable)">add(Drawable drawable)</a> method on <a href="http://developer.android.com/reference/android/view/ViewOverlay.html">ViewOverlay</a>, or <a href="http://developer.android.com/reference/android/view/ViewGroupOverlay.html#add(android.view.View)">add(View view)</a>on <a href="http://developer.android.com/reference/android/view/ViewGroupOverlay.html#add(android.view.View)">ViewGroupOverlay</a>.</p>
<p>ViewOverlay API is so simple, aside from <a href="http://developer.android.com/reference/android/view/ViewOverlay.html#add(android.graphics.drawable.Drawable)">add(Drawable drawable)</a>, we can also find<a href="http://developer.android.com/reference/android/view/ViewOverlay.html#clear()">clear()</a> and <a href="http://developer.android.com/reference/android/view/ViewOverlay.html#remove(android.graphics.drawable.Drawable)">remove(Drawable drawable)</a>. These are the only methods that we have to use to handle the views that we move to our ViewOverlays.</p>
<h2>优势</h2>
<p>Well, for now everything that I came up to my mind to do with this new API can be done using RelativeLayout and  a bit of <b>tricky &amp; ugly code</b>. But this lets us to do that things in a friendly way.</p>
<p>Essentially, <b>this component is visual-only</b>, so views attached to a ViewOverlay will not respond to any touch or tap event. ViewOverlay mechanism was conceived to be used combined with stuff like <b>animations</b>.</p>
<p><b>Using ViewOverlays we can animate views through other layouts in view hierarchy, even if they are not any of its parents.</b></p>
<p>So when some of these animations ends, we should have to call <a href="http://developer.android.com/reference/android/view/ViewOverlay.html#clear()">clear()</a> or<a href="http://developer.android.com/reference/android/view/ViewOverlay.html#remove(android.graphics.drawable.Drawable)">remove(Drawable drawable)</a> methods, to remove the view from our ViewOverlay to keep it clean and avoid memory leaks.</p>
<h2>使用限制</h2>
<p>Yeah, but…</p>
<ul>
<li>This is<b> only for API 18+</b>, although we hope it will be backported at some support library in the near future.</li>
<li>Right now, there is not a single example.</li>
</ul>
<p>We can not do anything to solve the first point, but today we have been playing with this new stuff to try to understand how it works. So…</p>
<p>程序源码<br />
1.  界面布局 activity_main.xml</p>
<pre>&lt;LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity" &gt;

&lt;FrameLayout
android:id="@+id/redContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/holo_red_light" &gt;

&lt;Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="ViewOverlay" /&gt;
&lt;/FrameLayout&gt;

&lt;FrameLayout
android:id="@+id/greenContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/holo_orange_light" &gt;

&lt;Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Normal animator" /&gt;
&lt;/FrameLayout&gt;

&lt;FrameLayout
android:id="@+id/orangeContainer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/holo_green_light" &gt;

&lt;Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="ViewOverlay on other parent" /&gt;
&lt;/FrameLayout&gt;

&lt;/LinearLayout&gt;</pre>
<p>2. MainActivity.java</p>
<pre>package cat.lafosca.viewoverlaytest;

import android.animation.Animator;
import android.animation.Animator.AnimatorListener;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.Button;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		viewsSetup();
	}

	private void viewsSetup() {
		setContentView(R.layout.activity_main);

		final Button button = (Button) findViewById(R.id.button);

		button.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// Our button is added to the parent of its parent, the most top-level layout
				final ViewGroup container = (ViewGroup) button.getParent().getParent();
				container.getOverlay().add(button);

				ObjectAnimator anim = ObjectAnimator.ofFloat(button, "translationY", container.getHeight());
				ObjectAnimator rotate = ObjectAnimator.ofFloat(button, "rotation", 0, 360);
				rotate.setRepeatCount(Animation.INFINITE);
				rotate.setRepeatMode(Animation.REVERSE);
				rotate.setDuration(350);

				/*
				 * Button needs to be removed after animation ending
				 * When we have added the view to the ViewOverlay, 
				 * it was removed from its original parent.
				 */
				anim.addListener(new AnimatorListener() {

					@Override
					public void onAnimationStart(Animator arg0) {
					}

					@Override
					public void onAnimationRepeat(Animator arg0) {
					}

					@Override
					public void onAnimationEnd(Animator arg0) {
						container.getOverlay().remove(button);
					}

					@Override
					public void onAnimationCancel(Animator arg0) {
						container.getOverlay().remove(button);
					}
				});

				anim.setDuration(2000);

				AnimatorSet set = new AnimatorSet();
				set.playTogether(anim, rotate);
				set.start();
			}
		});

		final Button button2 = (Button) findViewById(R.id.button2);
		button2.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				// Normal animation, we only see it when is animating in its original layout container.
				final ViewGroup container = (ViewGroup) button2.getParent().getParent();
				ObjectAnimator anim = ObjectAnimator.ofFloat(button2, "translationY", -container.getHeight());
				anim.setDuration(2000);
				anim.start();
			}
		});

		final Button button3 = (Button) findViewById(R.id.button3);
		button3.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View arg0) {
				ObjectAnimator fadeOut = ObjectAnimator.ofFloat(button3, "alpha", 1f, 0f);
				fadeOut.setDuration(500);

				/* 
				 * Here we add our button to center layout's ViewGroupOverlay
				 * when first fade-out animation ends.
				 */
				final ViewGroup container = (ViewGroup) button2.getParent();
				final ObjectAnimator anim = ObjectAnimator.ofFloat(button3, "translationY", -container.getHeight() * 2);
				anim.setDuration(2000);

				anim.addListener(new AnimatorListener() {

					@Override
					public void onAnimationStart(Animator animation) { }

					@Override
					public void onAnimationRepeat(Animator animation) { }

					@Override
					public void onAnimationEnd(Animator animation) {
						container.getOverlay().remove(button3);
					}

					@Override
					public void onAnimationCancel(Animator animation) {
						container.getOverlay().remove(button3);
					}
				});

				fadeOut.addListener(new AnimatorListener() {

					@Override
					public void onAnimationStart(Animator arg0) {
					}

					@Override
					public void onAnimationRepeat(Animator arg0) {
					}

					@Override
					public void onAnimationEnd(Animator arg0) {
						container.getOverlay().add(button3);
						button3.setAlpha(1f);
						anim.start();
					}

					@Override
					public void onAnimationCancel(Animator arg0) {
						container.getOverlay().add(button3);
						button3.setAlpha(1f);
						anim.start();
					}
				});

				fadeOut.start();
			}
		});
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		if (item.getItemId() == R.id.action_reset) { 
			viewsSetup();
		}
		return super.onOptionsItemSelected(item);
	}

}</pre>
<p>完整代码下载：You can download and check our project from <a href="http://www.lafosca.cat/wp-content/uploads/2013/08/ViewOverlayTest.zip">right here</a>.</p>
<p>参照文章：<a href="http://www.lafosca.cat/viewoverlay-in-android/">http://www.lafosca.cat/viewoverlay-in-android/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/android%e5%8a%a8%e7%94%bb%e8%ae%be%e8%ae%a1%e4%b9%8bviewoverlay/">Android动画设计之ViewOverlay</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/android%e5%8a%a8%e7%94%bb%e8%ae%be%e8%ae%a1%e4%b9%8bviewoverlay/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android网络编程之SPDY</title>
		<link>http://blog.zhourunsheng.com/2013/08/android%e7%bd%91%e7%bb%9c%e7%bc%96%e7%a8%8b%e4%b9%8bspdy/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/android%e7%bd%91%e7%bb%9c%e7%bc%96%e7%a8%8b%e4%b9%8bspdy/#comments</comments>
		<pubDate>Thu, 22 Aug 2013 01:33:06 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[SPDY]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1807</guid>
		<description><![CDATA[<p>Android的网络编程大家基本都熟悉，Http的Get，Post请求与其他语言的开发大同小异，可以采用And [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/android%e7%bd%91%e7%bb%9c%e7%bc%96%e7%a8%8b%e4%b9%8bspdy/">Android网络编程之SPDY</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>Android的网络编程大家基本都熟悉，Http的Get，Post请求与其他语言的开发大同小异，可以采用Android自带的网络开发包，也可以采用Apache的开源网络编程包。本文介绍一个支持SPDY的开发包<a href="http://square.github.io/okhttp/" target="_blank">OkHttp</a>，关于SPDY的知识，大家可以从<a href="http://zh.wikipedia.org/wiki/SPDY" target="_blank">维基百科</a>和<a href="http://baike.baidu.com/view/2984528.htm" target="_blank">百度百科</a>上面找到，基本就是增强版的Http。</p>
<p>OkHttp is an HTTP client that’s efficient by default:</p>
<ul>
<li>SPDY support allows all requests to the same host to share a socket.</li>
<li>Connection pooling reduces request latency (if SPDY isn’t available).</li>
<li>Transparent GZIP shrinks download sizes.</li>
<li>Response caching avoids the network completely for repeat requests.</li>
</ul>
<p><span id="more-1807"></span></p>
<h3>示例代码</h3>
<h4>GET A URL</h4>
<p>This program downloads a URL and print its contents as a string. <a href="https://raw.github.com/square/okhttp/master/samples/guide/src/main/java/com/squareup/okhttp/guide/GetExample.java">Full source</a>.</p>
<pre>    OkHttpClient client = new OkHttpClient();

    String get(URL url) throws IOException {
      HttpURLConnection connection = client.open(url);
      InputStream in = null;
      try {
        // Read the response.
        in = connection.getInputStream();
        byte[] response = readFully(in);
        return new String(response, "UTF-8");
      } finally {
        if (in != null) in.close();
      }
    }</pre>
<h4>POST TO A SERVER</h4>
<p>This program posts data to a service. <a href="https://raw.github.com/square/okhttp/master/samples/guide/src/main/java/com/squareup/okhttp/guide/PostExample.java">Full source</a>.</p>
<pre>    OkHttpClient client = new OkHttpClient();

    String post(URL url, byte[] body) throws IOException {
      HttpURLConnection connection = client.open(url);
      OutputStream out = null;
      InputStream in = null;
      try {
        // Write the request.
        connection.setRequestMethod("POST");
        out = connection.getOutputStream();
        out.write(body);
        out.close();

        // Read the response.
        if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
          throw new IOException("Unexpected HTTP response: "
              + connection.getResponseCode() + " " + connection.getResponseMessage());
        }
        in = connection.getInputStream();
        return readFirstLine(in);
      } finally {
        // Clean up.
        if (out != null) out.close();
        if (in != null) in.close();
      }
    }</pre>
<h4>MAVEN 依赖库配置</h4>
<pre>&lt;dependency&gt;
  &lt;groupId&gt;com.squareup.okhttp&lt;/groupId&gt;
  &lt;artifactId&gt;okhttp&lt;/artifactId&gt;
  &lt;version&gt;1.2.0&lt;/version&gt;
&lt;/dependency&gt;</pre>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/android%e7%bd%91%e7%bb%9c%e7%bc%96%e7%a8%8b%e4%b9%8bspdy/">Android网络编程之SPDY</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/android%e7%bd%91%e7%bb%9c%e7%bc%96%e7%a8%8b%e4%b9%8bspdy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>深度解读Android关机过程</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e6%b7%b1%e5%ba%a6%e8%a7%a3%e8%af%bbandroid%e5%85%b3%e6%9c%ba%e8%bf%87%e7%a8%8b/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e6%b7%b1%e5%ba%a6%e8%a7%a3%e8%af%bbandroid%e5%85%b3%e6%9c%ba%e8%bf%87%e7%a8%8b/#comments</comments>
		<pubDate>Wed, 21 Aug 2013 03:59:03 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1802</guid>
		<description><![CDATA[<p>想知道Android系统的关系过程么？？？ 本文将会展示一个完整的关机处理流程。 What happened  [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e6%b7%b1%e5%ba%a6%e8%a7%a3%e8%af%bbandroid%e5%85%b3%e6%9c%ba%e8%bf%87%e7%a8%8b/">深度解读Android关机过程</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>想知道Android系统的关系过程么？？？ 本文将会展示一个完整的关机处理流程。</p>
<p>What happened when I long press power button ?<br />
What is shutdown sequence ?<br />
How is it different from desktop linux shutdown sequence?<br />
How to change shutdown menu ?<span id="more-1802"></span></p>
<p>Many questions pop-up in mind when we think about Android shutdown sequence. Before you read about shutdown sequence I suggest you to read about<a href="http://translate.googleusercontent.com/translate_c?anno=2&amp;depth=1&amp;hl=zh-CN&amp;rurl=translate.google.com.hk&amp;sl=en&amp;tl=zh-TW&amp;u=http://www.kpbird.com/2012/11/in-depth-android-boot-sequence-process.html&amp;usg=ALkJrhifyp8G5ZVI1TjXstaxFyR3vHMgwQ" target="_blank"> boot sequence article</a> .<br />
Android is linux based open source operating system, x86 (x86 is a series of computer microprocessor instruction set architectures based on the Intel 8086 CPU.) is most likely system where linux kernel is deployed however all Android devices are running on ARM process (ARM (formerly Advanced RISC Machine, which was formerly Acorn RISC Machine)) except Intel's Xolo device ( <a href="http://translate.googleusercontent.com/translate_c?anno=2&amp;depth=1&amp;hl=zh-CN&amp;rurl=translate.google.com.hk&amp;sl=en&amp;tl=zh-TW&amp;u=http://xolo.in/xolo-x900-features&amp;usg=ALkJrhiv1UP76S3Ei8cX0zflB_Ol0DRVKg" target="_blank">http://xolo.in/xolo-x900-features</a> ). Xolo comes with Atom 1.6 GHz x86 processor. Android shutdown sequence is different from desktop linux like ubuntu, fedora, etc.<br />
In this article I am going to explain shutdown sequence for Android only. Please refer "<a href="http://translate.googleusercontent.com/translate_c?anno=2&amp;depth=1&amp;hl=zh-CN&amp;rurl=translate.google.com.hk&amp;sl=en&amp;tl=zh-TW&amp;u=http://www.techrepublic.com/article/customizing-the-linux-bootup-and-shutdown-processes/&amp;usg=ALkJrhjE2zLFgcq0jIaKPaiXoDy7aqaskg" target="_blank">Linux Boot and Shutdown Process</a>" for details of desktop linux shutdown process.</p>
<p>Following diagram illustrate shutdown sequence in detail.</p>
<p><img alt="" src="http://farm3.staticflickr.com/2854/9560949580_cf4085ef14.jpg" width="477" height="500" /></p>
<p>Step 1: Long Press Power Button for 500ms.<br />
Step 2: PhoneWindowManager.java identify Power Button long press and call method named "interceptKeyBeforeQueueing".<br />
Following code display power key snippet from the function.</p>
<pre> /** {@inheritDoc} */
 @Override
 public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags, boolean isScreenOn) {
 ....
 ....
 ....
 case KeyEvent.KEYCODE_POWER: {
      result &amp;= ~ACTION_PASS_TO_USER;
        if (down) {
          if (isScreenOn &amp;&amp; !mPowerKeyTriggered
                &amp;&amp; (event.getFlags() &amp; KeyEvent.FLAG_FALLBACK) == 0) {
                    mPowerKeyTriggered = true;
                    mPowerKeyTime = event.getDownTime();
                    interceptScreenshotChord();
             }
                ITelephony telephonyService = getTelephonyService();
                 boolean hungUp = false;
                if (telephonyService != null) {
                    try {
                        if (telephonyService.isRinging()) {
                            // Pressing Power while there's a ringing incoming
                            // call should silence the ringer.
                             telephonyService.silenceRinger();
                        } else if ((mIncallPowerBehavior
                                 &amp; Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR_HANGUP) != 0
                                &amp;&amp; telephonyService.isOffhook()) {
                             // Otherwise, if "Power button ends call" is enabled,
                            // the Power button will hang up any current active call.
                             hungUp = telephonyService.endCall();
                        }
                    } catch (RemoteException ex) {
                         Log.w(TAG, "ITelephony threw RemoteException", ex);
                    }
                }
                interceptPowerKeyDown(!isScreenOn || hungUp
                        || mVolumeDownKeyTriggered || mVolumeUpKeyTriggered);
            } else {
                mPowerKeyTriggered = false;
                cancelPendingScreenshotChordAction();
                if (interceptPowerKeyUp(canceled || mPendingPowerKeyUpCanceled)) {
                    result = (result &amp; ~ACTION_WAKE_UP) | ACTION_GO_TO_SLEEP;
                }
                mPendingPowerKeyUpCanceled = false;
            }
           break;
       }
 ....
 ....
 ....
 }</pre>
<p>Above code handle multiple options like silence ringtone, take screenshots and power off. It will identify appropriate option based on time duration and other key's status. It will call "interceptPowerKeyDown" option by eliminate other options.</p>
<p>Following code display interceptPowerKeyDown function. It will wait for 500 millisecond (ViewConfiguration#getGlobalActionKeyTimeout()) then call mPowerLongPress Thread.</p>
<pre> private void interceptPowerKeyDown(boolean handled) {
   mPowerKeyHandled = handled;
   if (!handled) {
        mHandler.postDelayed(mPowerLongPress, ViewConfiguration.getGlobalActionKeyTimeout());
   }
 }
Following code represent mPowerLongPress thread 
view sourceprint?
 private final Runnable mPowerLongPress = new Runnable() {
        @Override
        public void run() {
            // The context isn't read
            if (mLongPressOnPowerBehavior &lt; 0) {
                mLongPressOnPowerBehavior = mContext.getResources().getInteger(
                        com.android.internal.R.integer.config_longPressOnPowerBehavior);
            }
            int resolvedBehavior = mLongPressOnPowerBehavior;
            if (FactoryTest.isLongPressOnPowerOffEnabled()) {
                resolvedBehavior = LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM;
            }

            switch (resolvedBehavior) {
            case LONG_PRESS_POWER_NOTHING:
                break;
            case LONG_PRESS_POWER_GLOBAL_ACTIONS:
                mPowerKeyHandled = true;
                if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
                    performAuditoryFeedbackForAccessibilityIfNeed();
                }
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                showGlobalActionsDialog();
                break;
            case LONG_PRESS_POWER_SHUT_OFF:
            case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
                mPowerKeyHandled = true;
                performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
                mWindowManagerFuncs.shutdown(resolvedBehavior == LONG_PRESS_POWER_SHUT_OFF);
                break;
            }
        }
    };</pre>
<p>Step 3: Controls goes to GlobalActions.java which is responsible to display dialogbox with various options like (Power Off, Airplan mode, Take screenshot and few toggle buttons), This dialog box has different options are per your OEM provider, model and Android OS version. GlobalAction class has method named showdialog() which is responsible to create object of Dialogbox with options.</p>
<pre> void showGlobalActionsDialog() {
     if (mGlobalActions == null) {
         mGlobalActions = new GlobalActions(mContext, mWindowManagerFuncs);
     }
     final boolean keyguardShowing = keyguardIsShowingTq();
     mGlobalActions.showDialog(keyguardShowing, isDeviceProvisioned());
     if (keyguardShowing) {
          // since it took two seconds of long press to bring this up,
         // poke the wake lock so they have some time to see the dialog.
         mKeyguardMediator.userActivity();
     }
 }</pre>
<p><img src="http://farm3.staticflickr.com/2814/9558194995_d6a2c1bd5d.jpg" width="225" height="400" class="alignnone" /><br />
Step 4: If user select "Power Off" option from the dialogbox then control again goes back to PhoneWindowManager, It will start shutdown process.<br />
Step 5: Shutdown process initiate from ShutdownThread.java file's shoutdowninner() function, It wil display confirmation dialog with ok / cancel button, If user select ok option then actual shutdown process starts.<br />
<img src="http://farm3.staticflickr.com/2869/9558210759_059e2cd15b.jpg" width="225" height="400" class="alignnone" /><br />
Step 6: beginShutdownSequence() function called when user select OK option from the dialog.</p>
<pre>private static void beginShutdownSequence(Context context) {
        synchronized (sIsStartedGuard) {
            if (sIsStarted) {
                Log.d(TAG, "Shutdown sequence already running, returning.");
                return;
            }
            sIsStarted = true;
        }

        // throw up an indeterminate system dialog to indicate radio is
        // shutting down.
        ProgressDialog pd = new ProgressDialog(context);
        pd.setTitle(context.getText(com.android.internal.R.string.power_off));
        pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
        pd.setIndeterminate(true);
        pd.setCancelable(false);
        pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);

        pd.show();

        sInstance.mContext = context;
        sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);

        // make sure we never fall asleep again
        sInstance.mCpuWakeLock = null;
        try {
            sInstance.mCpuWakeLock = sInstance.mPowerManager.newWakeLock(
                    PowerManager.PARTIAL_WAKE_LOCK, TAG + "-cpu");
            sInstance.mCpuWakeLock.setReferenceCounted(false);
            sInstance.mCpuWakeLock.acquire();
        } catch (SecurityException e) {
            Log.w(TAG, "No permission to acquire wake lock", e);
            sInstance.mCpuWakeLock = null;
        }

        // also make sure the screen stays on for better user experience
        sInstance.mScreenWakeLock = null;
        if (sInstance.mPowerManager.isScreenOn()) {
            try {
                sInstance.mScreenWakeLock = sInstance.mPowerManager.newWakeLock(
                        PowerManager.FULL_WAKE_LOCK, TAG + "-screen");
                sInstance.mScreenWakeLock.setReferenceCounted(false);
                sInstance.mScreenWakeLock.acquire();
            } catch (SecurityException e) {
                Log.w(TAG, "No permission to acquire wake lock", e);
                sInstance.mScreenWakeLock = null;
            }
        }

        // start the thread that initiates shutdown
        sInstance.mHandler = new Handler() {
        };
        sInstance.start();
    }</pre>
<p>Run method, start actual shutdown process</p>
<pre> 
public void run() {
	BroadcastReceiver br = new BroadcastReceiver() {
		@Override
		public void onReceive(Context context, Intent intent) {
			// We don't allow apps to cancel this, so ignore the result.
			actionDone();
		}
	};
	/* * Write a system property in case the system_server reboots before we * get to the actual hardware restart. If that happens, we'll retry at * the beginning of the SystemServer startup. */
	{
		String reason = (mReboot ? "1" : "0") + (mRebootReason != null ? mRebootReason : "");
		SystemProperties.set(SHUTDOWN_ACTION_PROPERTY, reason);
	}
	/* * If we are rebooting into safe mode, write a system property * indicating so. */
	if (mRebootSafeMode) {
		SystemProperties.set(REBOOT_SAFEMODE_PROPERTY, "1");
	}
	Log.i(TAG, "Sending shutdown broadcast...");
// First send the high-level shut down broadcast.
	mActionDone = false;
	Intent intent = new Intent(Intent.ACTION_SHUTDOWN);
	intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
	mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, null, br, mHandler, 0, null, null);
	final long endTime = SystemClock.elapsedRealtime() + MAX_BROADCAST_TIME;
	synchronized (mActionDoneSync) {
		while (!mActionDone) {
			long delay = endTime - SystemClock.elapsedRealtime();    // Shutdown radios.
			if (delay &lt;= 0) {
				Log.w(TAG, "Shutdown broadcast timed out");
				break;
			}
			try {
				mActionDoneSync.wait(delay);
			} catch (InterruptedException e) { } } }
	Log.i(TAG, "Shutting down activity manager...");
	final IActivityManager am = ActivityManagerNative.asInterface(ServiceManager.checkService("activity"));
	if (am != null) {
		try {
			am.shutdown(MAX_BROADCAST_TIME);
		} catch (RemoteException e) { }
	}
	shutdownRadios(MAX_RADIO_WAIT_TIME);
// Shutdown MountService to ensure media is in a safe state
	IMountShutdownObserver observer = new IMountShutdownObserver.Stub() {
		public void onShutDownComplete(int statusCode) throws RemoteException {
			Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown");
			actionDone();
		}
	};
	Log.i(TAG, "Shutting down MountService");
// Set initial variables and time out time.
	mActionDone = false;
	final long endShutTime = SystemClock.elapsedRealtime() + MAX_SHUTDOWN_WAIT_TIME;
	synchronized (mActionDoneSync) {
		try {
			final IMountService mount = IMountService.Stub.asInterface( ServiceManager.checkService("mount"));
			if (mount != null) {
				mount.shutdown(observer);
			} else {
				Log.w(TAG, "MountService unavailable for shutdown");
			}
		} catch (Exception e) {
			Log.e(TAG, "Exception during MountService shutdown", e);
		}
		while (!mActionDone) {
			long delay = endShutTime - SystemClock.elapsedRealtime();
			if (delay &lt;= 0) {
				Log.w(TAG, "Shutdown wait timed out");
				break;
			}
			try {
				mActionDoneSync.wait(delay);
			} catch (InterruptedException e) { }
		}
	}
	rebootOrShutdown(mReboot, mRebootReason);
}</pre>
<p>Step 7: With rebootOrShutdown() method controls transfer to the native function of com_android_server_power_PowerManagerService.cpp file, and finally control goes to android_reboot.c file which is final step of shutdown sequence.</p>
<pre> static void nativeShutdown(JNIEnv *env, jclass clazz) {
     android_reboot(ANDROID_RB_POWEROFF, 0, 0);
 }</pre>
<p><b>References</b></p>
<ol>
<li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java">PhoneWindowManager.java</a></li>
<li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/policy/src/com/android/internal/policy/impl/GlobalActions.java">GlobalActions.java</a></li>
<li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/WindowManagerPolicy.java">WindowManagerPolicy.java</a></li>
<li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/power/ShutdownThread.java">ShutdownThread.java</a></li>
<li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/java/com/android/server/power/PowerManagerService.java">PowerManagerService.java</a></li>
<li><a href="https://android.googlesource.com/platform/frameworks/base/+/master/services/jni/com_android_server_power_PowerManagerService.cpp">com_android_server_power_PowerManagerService.cpp</a></li>
<li><a href="https://android.googlesource.com/platform/system/core/+/master/libcutils/android_reboot.c">android_reboot.c</a></li>
<li><a href="http://translate.googleusercontent.com/translate_c?anno=2&amp;depth=1&amp;hl=zh-CN&amp;rurl=translate.google.com.hk&amp;sl=en&amp;tl=zh-TW&amp;u=http://www.techrepublic.com/article/customizing-the-linux-bootup-and-shutdown-processes/&amp;usg=ALkJrhjE2zLFgcq0jIaKPaiXoDy7aqaskg">Linux Boot and Shutdown Process</a></li>
</ol>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e6%b7%b1%e5%ba%a6%e8%a7%a3%e8%af%bbandroid%e5%85%b3%e6%9c%ba%e8%bf%87%e7%a8%8b/">深度解读Android关机过程</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e6%b7%b1%e5%ba%a6%e8%a7%a3%e8%af%bbandroid%e5%85%b3%e6%9c%ba%e8%bf%87%e7%a8%8b/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于Gradle生成Android apklibs</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8egradle%e7%94%9f%e6%88%90android-apklibs/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8egradle%e7%94%9f%e6%88%90android-apklibs/#comments</comments>
		<pubDate>Wed, 21 Aug 2013 02:45:58 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Gradle]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1800</guid>
		<description><![CDATA[<p>So, you've decided to switch your Android library proje [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8egradle%e7%94%9f%e6%88%90android-apklibs/">基于Gradle生成Android apklibs</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>So, you've decided to switch your Android library project to the new hotness (Gradle, Android's new build system)? About to release a new Android library? Read on.</p>
<p>1. <b><i>What is apklib?</i></b><br />
apklib is a way to bundle an Android library project.</p>
<p>Many Android developers thought the existing Android build system, based on Ant, was insufficient. Maven is arguably a better build system, so the <b>android-maven-plugin</b> was created, to let Maven users build their Android projects.</p>
<p>Maven downloads all of the dependencies for your project - for most Java projects, these are jars. The apklib format was invented as a way to share Android code+resources. It’s essentially a zipped up Android library project, which was already the status quo for code+resource sharing.</p>
<p>2. <b><i>Why doesn’t Gradle support it?</i></b><br />
The Android team introduced the <b>aar</b> format at Google I/O 2013. From what I gather, it differs from <b>apklib</b> in one major way: classes are compiled and included in a classes.jar in the root of the <b>aar</b>, whereas <b>apklib</b> cannot contain compiled class files or jars (they are ignored).</p>
<p>3. <b><i>Why doesn’t the android-maven-plugin support aar?</i></b><br />
I suppose they haven’t got around to it yet. Plus, apklib came first! Ask +<a href="https://plus.google.com/105460375065140382634">Manfred Moser</a> :-)</p>
<p>4. <b><i>Show me the code</i></b><br />
(you can see a complete example here: <a href="https://github.com/googlemaps/android-maps-utils/blob/master/library/build.gradle" rel="nofollow">https://github.com/googlemaps/android-maps-utils/blob/master/library/build.gradle</a>)</p>
<p>So, if you want to ship an apklib to keep your Maven users happy, you’ll need to generate it and upload from your Gradle build script. I suppose someone will one day release a Gradle plugin, but for now the build rules are simple:</p>
<p>task apklib(type: Zip) {<br />
appendix = extension = 'apklib'</p>
<p>from 'AndroidManifest.xml'<br />
into('res') {<br />
from 'res'<br />
}<br />
into('src') {<br />
from 'src'<br />
}<br />
}</p>
<p>You may need to tweak this a bit (different paths for source code, resources, etc). You will then want to declare your <b>apklib</b> as an artifact of your project:</p>
<p>artifacts {<br />
archives apklib<br />
}</p>
<p>5. <b><i>Uploading to Maven</i></b><br />
Deploying multiple artifacts to Maven is a bit tricky. If you’re using the <b>maven</b>plugin from Gradle, you’ll need to modify your <b>mavenDeployer</b> entry:</p>
<p>addFilter('aar') { artifact, file -&gt;<br />
<a href="http://artifact.name/" rel="nofollow">artifact.name</a> == archivesBaseName<br />
}<br />
addFilter('apklib') { artifact, file -&gt;<br />
<a href="http://artifact.name/" rel="nofollow">artifact.name</a> == archivesBaseName + '-apklib'<br />
}</p>
<p>You’ll end up with two artifacts, <b>yourproject</b> and <b>yourproject-apklib</b>. You may want to inverse these (yourproject and yourproject-aar), if you already have people depending on the apklib packaging with the existing name.</p>
<p>文章选自：<a href="https://plus.google.com/+ChristopherBroadfoot/posts/7uyipf8DTau">https://plus.google.com/+ChristopherBroadfoot/posts/7uyipf8DTau</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8egradle%e7%94%9f%e6%88%90android-apklibs/">基于Gradle生成Android apklibs</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8egradle%e7%94%9f%e6%88%90android-apklibs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>创建自定义的SQLite Loader</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/#comments</comments>
		<pubDate>Wed, 21 Aug 2013 02:30:52 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[SQL]]></category>
		<category><![CDATA[SQLite]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1794</guid>
		<description><![CDATA[<p>在开发Android的过程中，我们经常会用到SQLite进行资料的存储，本文给出了一种自定义Loader的方式 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/">创建自定义的SQLite Loader</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>在开发Android的过程中，我们经常会用到SQLite进行资料的存储，本文给出了一种自定义Loader的方式，直接访问SQLite的API进行交互。</p>
<p>Today we’ll discuss about custom loaders in Android. Loaders have been introduced from Android 3.0 but we can also use loaders in prior versions of Android starting from Android 1.6 with the help of Android compatibility library.</p>
<p>In this tutorial we won’t discuss the basics of loader which is vastly available in the Internet. A very good tutorial on loaders in Android can be found here . Here we’ll discuss about how to create a custom loader in Android which can read data from SQLite Database and return the read data as a collection of POJO.</p>
<p>Android provides a ‘CursorLoader’ class which can read data from content providers but to read data directly from the SQLite database we need our own custom loader. A very nice loader library is written for this which reads data from cursor and can be found here and I’ve taken the idea of the custom loader from these mentioned links. But our custom loader is capable to read data from the SQLite database without using any content provider and returns the data as collection of objects.<span id="more-1794"></span></p>
<p>Create a new Android project and remember to use the library ‘android-support-v4.jar’ and implement the custom loader as follows :</p>
<p>First we’ll create a generic data source class file ‘DataSource.java’ with common methods to perform CRUD operations on data. The code is as follows :</p>
<pre>package com.example.customloaderexample.db;
import java.util.List;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
public abstract class DataSource {
	protected SQLiteDatabase mDatabase;
	public DataSource(SQLiteDatabase database) {
		mDatabase = database;
	}

	public abstract boolean insert(T entity);
	public abstract boolean delete(T entity);
	public abstract boolean update(T entity);
	public abstract List read();
	public abstract List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy);
}</pre>
<p>Next we’ll create our model class file ‘Test.java’ with the following code. This class represents the data that we want to store and retrieve from the database.</p>
<pre>package com.example.customloaderexample.model;
public class Test {
	private int id;
	private String name;
	public Test() {} 
	public Test(String name) {
		this.name = name;
	} 
	public int getId() {
		return id;
	} 
	public void setId(int id) {
		this.id = id;
	} 
	public String getName() {
		return name;
	} 
	public void setName(String name) {
		this.name = name;
	} 
	@Override 
	public String toString() {
		return name;
	}
}</pre>
<p>Next we’ll create a subclass of ‘TestDataSource.java’ to specifically perform CRUD operations on ‘Test’ objects.</p>
<pre>package com.example.customloaderexample.db;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import com.example.customloaderexample.model.Test;
public class TestDataSource extends DataSource {
	public static final String TABLE_NAME = "test";
	public static final String COLUMN_ID = "_id";
	public static final String COLUMN_NAME = "name";
	// Database creation sql statement
	public static final String CREATE_COMMAND = "create table " + TABLE_NAME + "(" + COLUMN_ID + " integer primary key autoincrement, " + COLUMN_NAME + " text not null);";
	public TestDataSource(SQLiteDatabase database) {
		super(database);
		// TODO Auto-generated constructor stub
	}
	@Override
	public boolean insert(Test entity) {
		if (entity == null) {
			return false;
		}
		long result = mDatabase.insert(TABLE_NAME, null, generateContentValuesFromObject(entity));
		return result != -1;
	}
	@Override
	public boolean delete(Test entity) {
		if (entity == null) {
			return false;
		}
		int result = mDatabase.delete(TABLE_NAME, COLUMN_ID + " = " + entity.getId(), null);
		return result != 0;
	}
	@Override
	public boolean update(Test entity) {
		if (entity == null) {
			return false;
		}
		int result = mDatabase.update(TABLE_NAME, generateContentValuesFromObject(entity), COLUMN_ID + " = " + entity.getId(), null);
		return result != 0;
	}
	@Override
	public List read() {
		Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), null, null, null, null, null);
		List tests = new ArrayList();
		if (cursor != null &amp;&amp; cursor.moveToFirst()) {
			while (!cursor.isAfterLast()) {
				tests.add(generateObjectFromCursor(cursor));
				cursor.moveToNext();
			}
			cursor.close();
		}
		return tests;
	}
	@Override
	public List read(String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
		Cursor cursor = mDatabase.query(TABLE_NAME, getAllColumns(), selection, selectionArgs, groupBy, having, orderBy);
		List tests = new ArrayList();
		if (cursor != null &amp;&amp; cursor.moveToFirst()) {
			while (!cursor.isAfterLast()) {
				tests.add(generateObjectFromCursor(cursor));
				cursor.moveToNext();
			}
			cursor.close();
		}
		return tests;
	}
	public String[] getAllColumns() {
		return new String[] { COLUMN_ID, COLUMN_NAME };
	}
	public Test generateObjectFromCursor(Cursor cursor) {
		if (cursor == null) {
			return null;
		}
		Test test = new Test();
		test.setId(cursor.getInt(cursor.getColumnIndex(COLUMN_ID)));
		test.setName(cursor.getString(cursor.getColumnIndex(COLUMN_NAME)));
		return test;
	}
	public ContentValues generateContentValuesFromObject(Test entity) {
		if (entity == null) {
			return null;
		}
		ContentValues values = new ContentValues();
		values.put(COLUMN_NAME, entity.getName());
		return values;
	}
}</pre>
<p>Now we’ll create our SQLite database open helper ‘DbHelper.java’. This class extends the SQLiteOpenHelper which helps us to create the initial database and tables.</p>
<pre>package com.example.customloaderexample.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DbHelper extends SQLiteOpenHelper {
	private static final String DATABASE_NAME = "test.db";
	private static final int DATABASE_VERSION = 1;
	public DbHelper(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}
	@Override
	public void onCreate(SQLiteDatabase database) {
		database.execSQL(TestDataSource.CREATE_COMMAND);
	}
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("DROP TABLE IF EXISTS " + TestDataSource.TABLE_NAME);
		onCreate(db);
	}
}</pre>
<p>Next we’ll create a generic AsyncTask subclass ‘ContentChangingTask.java’ to perform content changing in background. This class is the base class for changing contents. We’ll create subclasses of this base class to perform CRUD operation on data in background.</p>
<pre>package com.example.customloaderexample.loader;
import android.os.AsyncTask;
import android.support.v4.content.Loader;
public abstract class ContentChangingTask extends AsyncTask {
	private Loader loader=null;
	ContentChangingTask(Loader loader) {
		this.loader=loader;
	}
	@Override
	protected void onPostExecute(T3 param) {
		loader.onContentChanged();
	}
}</pre>
<p>Now we’ll create a generic base class for our custom loader ‘AbstractDataLoader.java’. This class is the base class of our custom loader.</p>
<pre>package com.example.customloaderexample.loader;
import java.util.List;
import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;
public abstract class AbstractDataLoader&gt; extends AsyncTaskLoader {
	protected E mLastDataList = null;
	protected abstract E buildList();
	public AbstractDataLoader(Context context) {
		super(context);
	}
	/** * Runs on a worker thread, loading in our data. Delegates the real work to * concrete subclass' buildCursor() method. */
	@Override
	public E loadInBackground() {
		return buildList();
	}
	/** * Runs on the UI thread, routing the results from the background thread to * whatever is using the dataList. */ @Override
	public void deliverResult(E dataList) {
		if (isReset()) {
			// An async query came in while the loader is stopped
			emptyDataList(dataList);
			return;
		}
		E oldDataList = mLastDataList;
		mLastDataList = dataList;
		if (isStarted()) {
			super.deliverResult(dataList);
		}
		if (oldDataList != null &amp;&amp; oldDataList != dataList &amp;&amp; oldDataList.size() &gt; 0) {
			emptyDataList(oldDataList);
		}
	}
	/** * Starts an asynchronous load of the list data. When the result is ready * the callbacks will be called on the UI thread. If a previous load has * been completed and is still valid the result may be passed to the * callbacks immediately. * * Must be called from the UI thread. */
	@Override
	protected void onStartLoading() {
		if (mLastDataList != null) {
			deliverResult(mLastDataList);
		}
		if (takeContentChanged() || mLastDataList == null || mLastDataList.size() == 0) {
			forceLoad();
		}
	}
	/** * Must be called from the UI thread, triggered by a call to stopLoading(). */
	@Override
	protected void onStopLoading() {
		// Attempt to cancel the current load task if possible.
		cancelLoad();
	}
	/** * Must be called from the UI thread, triggered by a call to cancel(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */
	@Override
	public void onCanceled(E dataList) {
		if (dataList != null &amp;&amp; dataList.size() &gt; 0) {
			emptyDataList(dataList);
		}
	}
	/** * Must be called from the UI thread, triggered by a call to reset(). Here, * we make sure our Cursor is closed, if it still exists and is not already * closed. */
	@Override
	protected void onReset() {
		super.onReset();
		// Ensure the loader is stopped
		onStopLoading();
		if (mLastDataList != null &amp;&amp; mLastDataList.size() &gt; 0) {
			emptyDataList(mLastDataList);
		}
		mLastDataList = null;
	}
	protected void emptyDataList(E dataList) {
		if (dataList != null &amp;&amp; dataList.size() &gt; 0) {
			for (int i = 0; i &lt; dataList.size(); i++) {
				dataList.remove(i);
			}
		}
	}
}</pre>
<p>Now we’ll create the specific loader ‘SQLiteTestDataLoader.java’ for ‘Test’ object which is a subclass of the previous generic loader. This class is the custom loader for ‘Test’ objects. We’ll instantiate this class as our loader.</p>
<pre>package com.example.customloaderexample.loader;
import java.util.List;
import android.content.Context;
import com.example.customloaderexample.db.DataSource;
import com.example.customloaderexample.model.Test;
public class SQLiteTestDataLoader extends AbstractDataLoader {
	private DataSource mDataSource;
	private String mSelection;
	private String[] mSelectionArgs;
	private String mGroupBy;
	private String mHaving;
	private String mOrderBy;
	public SQLiteTestDataLoader(Context context, DataSource dataSource, String selection, String[] selectionArgs, String groupBy, String having, String orderBy) {
		super(context);
		mDataSource = dataSource;
		mSelection = selection;
		mSelectionArgs = selectionArgs;
		mGroupBy = groupBy;
		mHaving = having;
		mOrderBy = orderBy;
	}
	@Override
	protected List buildList() {
		List testList = mDataSource.read(mSelection, mSelectionArgs, mGroupBy, mHaving,	mOrderBy);
		return testList;
	}
	public void insert(Test entity) {
		new InsertTask(this).execute(entity);
	}
	public void update(Test entity) {
		new UpdateTask(this).execute(entity);
	}
	public void delete(Test entity) {
		new DeleteTask(this).execute(entity);
	}
	private class InsertTask extends ContentChangingTask {
		InsertTask(SQLiteTestDataLoader loader) {
			super(loader);
		}
		@Override
		protected Void doInBackground(Test... params) {
			mDataSource.insert(params[0]);
			return (null);
		}
	}
	private class UpdateTask extends ContentChangingTask {
		UpdateTask(SQLiteTestDataLoader loader) {
			super(loader);
		}
		@Override
		protected Void doInBackground(Test... params) {
			mDataSource.update(params[0]);
			return (null);
		}
	}
	private class DeleteTask extends ContentChangingTask {
		DeleteTask(SQLiteTestDataLoader loader) {
			super(loader);
		}
		@Override
		protected Void doInBackground(Test... params) {
			mDataSource.delete(params[0]);
			return (null);
		}
	}
}</pre>
<p>Next we’ll create the initial view xml ‘activity_main.xml’ for our launcher activity into the res/layout folder.</p>
<pre>&lt;FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent"&gt;
&lt;fragmentclass="com.example.customloaderexample.CustomLoaderExampleListFragment" android:id="@+id/titles" android:layout_width="match_parent"android:layout_height="match_parent"/&gt;</pre>
<p>Now we’ll create the launcher activity to launch our project. This is a FragmentActivity which holds our ListFragment and performs initial record insertion into database.</p>
<pre>package com.example.customloaderexample;
import java.util.List;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import com.example.customloaderexample.db.DbHelper;
import com.example.customloaderexample.db.TestDataSource;
import com.example.customloaderexample.model.Test;
public class MainFragmentActivity extends FragmentActivity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		DbHelper helper = new DbHelper(this);
		SQLiteDatabase database = helper.getWritableDatabase();
		TestDataSource dataSource = new TestDataSource(database);
		List list = dataSource.read();
		if(list == null || list.size() == 0) {
			dataSource.insert(new Test("Samik"));
			dataSource.insert(new Test("Piyas"));
			dataSource.insert(new Test("Sujal"));
		}
		helper.close();
		database.close();
	}
}</pre>
<p>Don’t forget to maintain the entry of the launcher activity into the ‘AndroidManifest.xml’ along with proper intent filter (to make it the launcher activity).</p>
<p>Now we’ll create the ListFragment to retrieve the list of Test objects from the database and show using our custom loader.</p>
<pre>package com.example.customloaderexample;
import java.util.List;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.ArrayAdapter;
import com.example.customloaderexample.db.DbHelper;
import com.example.customloaderexample.db.TestDataSource;
import com.example.customloaderexample.loader.SQLiteTestDataLoader;
import com.example.customloaderexample.model.Test;
public class CustomLoaderExampleListFragment extends ListFragment implements LoaderManager.LoaderCallbacks {
	private ArrayAdapter mAdapter;
	// The Loader's id (this id is specific to the ListFragment's LoaderManager)
	private static final int LOADER_ID = 1;
	private static final boolean DEBUG = true;
	private static final String TAG = "CustomLoaderExampleListFragment";
	private SQLiteDatabase mDatabase;
	private TestDataSource mDataSource;
	private DbHelper mDbHelper;
	@Override
	public void onActivityCreated(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onActivityCreated(savedInstanceState);
		//setHasOptionsMenu(true);
		mDbHelper = new DbHelper(getActivity());
		mDatabase = mDbHelper.getWritableDatabase();
		mDataSource = new TestDataSource(mDatabase);
		mAdapter = new ArrayAdapter(getActivity(), android.R.layout.simple_list_item_1);
		setEmptyText("No data, please add from menu.");
		setListAdapter(mAdapter);
		setListShown(false);
		if (DEBUG) {
			Log.i(TAG, "+++ Calling initLoader()! +++");
			if (getLoaderManager().getLoader(LOADER_ID) == null) {
				Log.i(TAG, "+++ Initializing the new Loader... +++");
			} else {
				Log.i(TAG, "+++ Reconnecting with existing Loader (id '1')... +++");
			}
		}
		// Initialize a Loader with id '1'. If the Loader with this id already // exists, then the LoaderManager will reuse the existing Loader.
		getLoaderManager().initLoader(LOADER_ID, null, this);
	}
	@Override
	public Loader onCreateLoader(int id, Bundle args) {
		SQLiteTestDataLoader loader = new SQLiteTestDataLoader(getActivity(), mDataSource, null, null, null, null, null);
		return loader;
	}
	@Override
	public void onLoadFinished(Loader loader, List data) {
		if (DEBUG) {
			Log.i(TAG, "+++ onLoadFinished() called! +++");
		}
		mAdapter.clear();
		for(Test test : data) {
			mAdapter.add(test);
		}
		if (isResumed()) {
			setListShown(true);
		} else {
			setListShownNoAnimation(true);
		}
	}
	@Override
	public void onLoaderReset(Loader arg0) {
		mAdapter.clear();
	}
	@Override
	public void onDestroy() {
		super.onDestroy();
		mDbHelper.close();
		mDatabase.close();
		mDataSource = null;
		mDbHelper = null;
		mDatabase = null;
	}
}</pre>
<p>Now run the project as Android application and you’ll get the following screen :<br />
<img alt="android custom loader" src="http://farm6.staticflickr.com/5449/9698741410_dfb33b0d91_o.png" width="180" height="300" /></p>
<h1>Acknowledgement :</h1>
<p><a href="http://www.vogella.com/articles/AndroidSQLite/article.html" target="_blank" rel="nofollow">http://www.vogella.com/articles/AndroidSQLite/article.html</a></p>
<p><a href="https://github.com/commonsguy/cwac-loaderex" target="_blank" rel="nofollow">https://github.com/commonsguy/cwac-loaderex</a></p>
<p><a href="http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html" target="_blank" rel="nofollow">http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html</a></p>
<p>文章节选：<a href="http://www.phloxblog.in/android-custom-loader-load-data-sqlite-database-android-version-1-6/?utm_source=Android+Weekly&amp;utm_campaign=aa8a9379f2-Android_Weekly_66&amp;utm_medium=email&amp;utm_term=0_4eb677ad19-aa8a9379f2-337263089#.UhQV5mX53ws">http://www.phloxblog.in/android-custom-loader-load-data-sqlite-database-android-version-1-6/</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/">创建自定义的SQLite Loader</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e5%88%9b%e5%bb%ba%e8%87%aa%e5%ae%9a%e4%b9%89%e7%9a%84sqlite-loader/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android构建编译系统之Gradle Build</title>
		<link>http://blog.zhourunsheng.com/2013/08/android%e6%9e%84%e5%bb%ba%e7%bc%96%e8%af%91%e7%b3%bb%e7%bb%9f%e4%b9%8bgradle-build/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/android%e6%9e%84%e5%bb%ba%e7%bc%96%e8%af%91%e7%b3%bb%e7%bb%9f%e4%b9%8bgradle-build/#comments</comments>
		<pubDate>Tue, 13 Aug 2013 03:15:51 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[Gradle]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1756</guid>
		<description><![CDATA[<p>Google I/O 2013 大会推出了一款新的Android开发工具Android Studio IDE， [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/android%e6%9e%84%e5%bb%ba%e7%bc%96%e8%af%91%e7%b3%bb%e7%bb%9f%e4%b9%8bgradle-build/">Android构建编译系统之Gradle Build</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>Google I/O 2013 大会推出了一款新的Android开发工具Android Studio IDE，与Eclipse相差不多，实际的开发选择哪一款随个人喜好，像我平时的开发都是以Eclipse为主，ant辅助进行构建，也支持了一种新的构建工具Gradle，Android Studio IDE内置的默认构建工具就是Gradle，关于Gradle的详细资料可以参照<a href="http://www.gradle.org/" target="_blank">官方网站</a>。本系列文章介绍了Android开发过程中的Gradle Build，共有7篇文章，由浅入深。</p>
<p>At Google I/O 2013 Google announced some new developer tools including the new Android Studio IDE, and a new build system based around Gradle. In this series we’ll have a look at the Gradle build system, and look at how to convert existing projects to use the new build system.<span id="more-1756"></span></p>
<p>Previously on Styling Android we’ve looked at <a title="Maven and Android – Part 1" href="https://blog.stylingandroid.com/archives/1067">using Maven for automating your builds</a>, and we saw how Maven’s dependency management system can make life much easier. When using Maven, it can be advantageous in many cases to have your own maven repository management system (such as <a title="Sonatype Nexus" href="http://www.sonatype.org/nexus" target="_blank">Nexus</a>) to which you can deploy build artifacts so that they can be used across multiple projects. Gradle supports Maven repositories for retrieving dependencies (as well as other mechanisms such as <a href="http://ant.apache.org/ivy/" target="_blank">Ivy</a>) which allows much easier migration from other build systems.</p>
<p>Similarly to Maven and Ant, the Gradle build system requires of some additional config files which provide the build definition. It is entirely possible to have any combination of Ant, Maven, and Gradle builds configured for a project without interfering with each other, so it is possible to gently transition over to the new build system. So you are able to use both in parallel until you have the Gradle build working as well as or better than your existing build.</p>
<p>We will cover how to convert existing projects to use Gradle later in this series, but we’ll start with creating a new project, and analysing what gets created in order to understand a little about how the new build system works. Android Studio is fully integrated with the Gradle build system, so let’s use that to create a new project. Select “File|New Project…”, then we’ll set up a basic project named “GradleTest” with a package name of<code>com.stylingandroid.gradle</code> with a minSDK of API7 (2.1 Eclair), a targetSDK of API16 (4.1 Jelly Bean), and compiled with API17 (4.2 Jelly Bean). The rest of the details are unimportant because we’re interested in the build configuration and directory structure, and not the actual code.</p>
<p>This creates a new directory tree with a root folder named “GradleTestProject” this top level directory is actually a project parent, which allows us to create a multi-module project containing a number of distinct components such as library projects and APK projects. The actual APK project that we just created resides within this and is named “GradleTest”.</p>
<p>To begin with, let’s look at the parent project. As well as the “GradleTest” sub-project that it contains, there are some other things here. First there is a “.idea’ folder and a files named “GradleTestProject.iml” which are both files specific to Android Studio. We’re not covering Android Studio here, so we’ll ignore those.</p>
<p>There is a file named “local.properties” which is essentially the same as “project.properties” in the Eclipse based build.</p>
<p>There are a couple of files named “gradlew” and “gradlew.bat”, and a folder named “gradle”. These are the components of a Gradle wrapper which gets included within our project. The purpose of the wrapper is to provide a Gradle runtime which is included within the project and enables anyone to check out the code and perform a build without having to first install Gradle. This also ensures that the correct version of Gradle is used to perform the build and using the wrapper to perform the build can often save a lot of problems if an incompatible version of Gradle is installed on the computer performing the build. The “gradlew” and “gradlew.bat” files are *nix and Windows (respectively) scripts to launch the wrapper.</p>
<p>Finally we have two files named “build.gradle” and “settings.gradle” which are the actual build configuration and we’ll look at these in greater detail later in the series.</p>
<p>For now let’s look at how we can perform a build from the command line. To do this we need to invoke the gradle wrapper by calling the appropriate wrapper script file: “gradlew” for Linux / MacOS; “gradlew.bat” for Windows. We need to supply the names of the tasks that we wish to perform. In this case we cant to do a “clean” (to remove any build artifacts from previous builds) followed by “assemble” which will build our project and produce an APK:</p>
<pre>GradleTestProject &gt;./gradlew clean assemble
The TaskContainer.add() method has been deprecated and is scheduled to be removed in Gradle 2.0. Please use the create() method instead.
:GradleTest:clean
:GradleTest:prepareDebugDependencies
:GradleTest:compileDebugAidl
:GradleTest:generateDebugBuildConfig
:GradleTest:mergeDebugAssets
:GradleTest:compileDebugRenderscript
:GradleTest:mergeDebugResources
:GradleTest:processDebugManifest
:GradleTest:processDebugResources
:GradleTest:compileDebug
:GradleTest:dexDebug
:GradleTest:processDebugJavaRes UP-TO-DATE
:GradleTest:validateDebugSigning
:GradleTest:packageDebug
:GradleTest:assembleDebug
:GradleTest:prepareReleaseDependencies
:GradleTest:compileReleaseAidl
:GradleTest:generateReleaseBuildConfig
:GradleTest:mergeReleaseAssets
:GradleTest:compileReleaseRenderscript
:GradleTest:mergeReleaseResources
:GradleTest:processReleaseManifest
:GradleTest:processReleaseResources
:GradleTest:compileRelease
:GradleTest:dexRelease
:GradleTest:processReleaseJavaRes UP-TO-DATE
:GradleTest:packageRelease
:GradleTest:assembleRelease
:GradleTest:assemble

BUILD SUCCESSFUL

Total time: 12.42 secs
GradleTestProject &gt;</pre>
<p>At a first glance, 12 seconds seems rather slow for a build of a really simple project with very little code in it. Fortunately there are a couple of little things that we can do to speed this up. The first is to make use of Gradle’s incremental build which, will perform a checksum upon each file to determine whether that file needs to be re-built, or whether we can reuse a build product from a previous build. When we do a “clean” we remove all previous build products, so just running “assemble” on it’s own will result in a much faster build the second time:</p>
<pre>GradleTestProject &gt;./gradlew assemble
The TaskContainer.add() method has been deprecated and is scheduled to be removed in Gradle 2.0. Please use the create() method instead.
:GradleTest:prepareDebugDependencies
:GradleTest:compileDebugAidl UP-TO-DATE
:GradleTest:generateDebugBuildConfig UP-TO-DATE
:GradleTest:mergeDebugAssets UP-TO-DATE
:GradleTest:compileDebugRenderscript UP-TO-DATE
:GradleTest:mergeDebugResources UP-TO-DATE
:GradleTest:processDebugManifest UP-TO-DATE
:GradleTest:processDebugResources UP-TO-DATE
:GradleTest:compileDebug UP-TO-DATE
:GradleTest:dexDebug UP-TO-DATE
:GradleTest:processDebugJavaRes UP-TO-DATE
:GradleTest:validateDebugSigning
:GradleTest:packageDebug UP-TO-DATE
:GradleTest:assembleDebug UP-TO-DATE
:GradleTest:prepareReleaseDependencies
:GradleTest:compileReleaseAidl UP-TO-DATE
:GradleTest:generateReleaseBuildConfig UP-TO-DATE
:GradleTest:mergeReleaseAssets UP-TO-DATE
:GradleTest:compileReleaseRenderscript UP-TO-DATE
:GradleTest:mergeReleaseResources UP-TO-DATE
:GradleTest:processReleaseManifest UP-TO-DATE
:GradleTest:processReleaseResources UP-TO-DATE
:GradleTest:compileRelease UP-TO-DATE
:GradleTest:dexRelease UP-TO-DATE
:GradleTest:processReleaseJavaRes UP-TO-DATE
:GradleTest:packageRelease UP-TO-DATE
:GradleTest:assembleRelease UP-TO-DATE
:GradleTest:assemble UP-TO-DATE

BUILD SUCCESSFUL

Total time: 4.059 secs
GradleTestProject &gt;</pre>
<p>Notice how many of the steps are followed by “UP-TO-DATE” which means that Gradle did not need to re-compile, or rebuild these components. The main overhead that’s left is that each time we run Gradle, it is having to initialise itself. We can actually tell Gradle to keep itself in memory, and each invocation from the command line will re-use this in-memory version of Gradle. We do this by specifying the “–daemon” flag on the command line. This makes little difference the first time we run it because Gradle still needs to be loaded to memory, but on the second invocation with this flag we get another performance improvement:</p>
<pre>GradleTestProject &gt;./gradlew assemble --daemon
The TaskContainer.add() method has been deprecated and is scheduled to be removed in Gradle 2.0. Please use the create() method instead.
:GradleTest:prepareDebugDependencies
:GradleTest:compileDebugAidl UP-TO-DATE
:GradleTest:generateDebugBuildConfig UP-TO-DATE
:GradleTest:mergeDebugAssets UP-TO-DATE
:GradleTest:compileDebugRenderscript UP-TO-DATE
:GradleTest:mergeDebugResources UP-TO-DATE
:GradleTest:processDebugManifest UP-TO-DATE
:GradleTest:processDebugResources UP-TO-DATE
:GradleTest:compileDebug UP-TO-DATE
:GradleTest:dexDebug UP-TO-DATE
:GradleTest:processDebugJavaRes UP-TO-DATE
:GradleTest:validateDebugSigning
:GradleTest:packageDebug UP-TO-DATE
:GradleTest:assembleDebug UP-TO-DATE
:GradleTest:prepareReleaseDependencies
:GradleTest:compileReleaseAidl UP-TO-DATE
:GradleTest:generateReleaseBuildConfig UP-TO-DATE
:GradleTest:mergeReleaseAssets UP-TO-DATE
:GradleTest:compileReleaseRenderscript UP-TO-DATE
:GradleTest:mergeReleaseResources UP-TO-DATE
:GradleTest:processReleaseManifest UP-TO-DATE
:GradleTest:processReleaseResources UP-TO-DATE
:GradleTest:compileRelease UP-TO-DATE
:GradleTest:dexRelease UP-TO-DATE
:GradleTest:processReleaseJavaRes UP-TO-DATE
:GradleTest:packageRelease UP-TO-DATE
:GradleTest:assembleRelease UP-TO-DATE
:GradleTest:assemble UP-TO-DATE

BUILD SUCCESSFUL

Total time: 1.852 secs
GradleTestProject &gt;</pre>
<p>So, we have a basic Gradle build defined and working from the command line, and have seen a couple of tricks for improving performance. In the next article we’ll start looking a little deeper in to the build to see what’s happening.</p>
<p>The source code for this article is available <a href="https://bitbucket.org/StylingAndroid/gradle/src/efb5ff194f9d42a71cf1d4b879a71afc85755761/GradleTest/libs/?at=Part1" target="_blank">here</a>.</p>
<p><strong>文章节选</strong>：</p>
<ul>
<li><a href="https://blog.stylingandroid.com/archives/1872" target="_blank">Gradle Build – Part 1</a></li>
<li><a href="https://blog.stylingandroid.com/archives/1880" target="_blank">Gradle Build – Part 2</a></li>
<li><a href="https://blog.stylingandroid.com/archives/1890" target="_blank">Gradle Build – Part 3</a></li>
<li><a href="https://blog.stylingandroid.com/archives/1904" target="_blank">Gradle Build – Part 4</a></li>
<li><a href="https://blog.stylingandroid.com/archives/1920" target="_blank">Gradle Build – Part 5</a></li>
<li><a href="https://blog.stylingandroid.com/archives/1948" target="_blank">Gradle Build – Part 6</a></li>
<li><a href="https://blog.stylingandroid.com/archives/1972" target="_blank">Gradle Build – Part 7</a></li>
</ul>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/android%e6%9e%84%e5%bb%ba%e7%bc%96%e8%af%91%e7%b3%bb%e7%bb%9f%e4%b9%8bgradle-build/">Android构建编译系统之Gradle Build</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/android%e6%9e%84%e5%bb%ba%e7%bc%96%e8%af%91%e7%b3%bb%e7%bb%9f%e4%b9%8bgradle-build/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于PHP后台的Android云推送方案</title>
		<link>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/</link>
		<comments>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/#comments</comments>
		<pubDate>Thu, 08 Aug 2013 14:19:16 +0000</pubDate>
		<dc:creator><![CDATA[润物无声]]></dc:creator>
				<category><![CDATA[移动开发]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://blog.zhourunsheng.com/?p=1738</guid>
		<description><![CDATA[<p>开发过移动APP的同学都知道云推送，比如Google的后台服务，联系人同步，邮件同步，APK的自动升级等都是这 [&#8230;]</p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/">基于PHP后台的Android云推送方案</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></description>
				<content:encoded><![CDATA[<p>开发过移动APP的同学都知道云推送，比如Google的后台服务，联系人同步，邮件同步，APK的自动升级等都是这个机制，其实利用云推送机制还可以实现消息的发送，本文介绍了一种基于PHP后台的推送机制，实现原理还是通过Google的云端服务(Google Cloud Messaging for Android service)，android客户端采用的是phoneGap的PushPlugin。</p>
<p>Last days I’ve been working within a <a href="http://phonegap.com/">Phonegap</a> project for Android devices using Push Notifications. The idea is simple. We need to use the Push Notification <a href="https://github.com/phonegap-build/PushPlugin">Plugin</a> for Android. First we need to register the Google Cloud Messaging for Android service at Google’s <a href="https://code.google.com/apis/console/">console</a>, and then we can send Push notifications to our Android device.</p>
<p>The Push Notification plugin provides a simple example to send notifications using <a href="https://github.com/phonegap-build/PushPlugin/blob/master/Example/server/pushGCM.rb">Ruby</a>. Normally my backend is built with PHP (and sometimes Python) so instead of using the ruby script we are going to build a simple PHP script to send Push Notifications.<span id="more-1738"></span></p>
<p>The script is very simple</p>
<div>
<pre>&lt;?php
$apiKey = "myApiKey";
$regId = "device reg ID";

$pusher = new AndroidPusher\Pusher($apiKey);
$pusher-&gt;notify($regId, "Hola");

print_r($pusher-&gt;getOutputAsArray());
$app-&gt;run();</pre>
</div>
<p>And the whole library you can see here:</p>
<div>
<pre>&lt;?php
namespace AndroidPusher;

class Pusher
{
    const GOOGLE_GCM_URL = '&lt;a href="https://android.googleapis.com/gcm/send"&gt;https://android.googleapis.com/gcm/send&lt;/a&gt;';

    private $apiKey;
    private $proxy;
    private $output;

    public function __construct($apiKey, $proxy = null)
    {
        $this-&gt;apiKey = $apiKey;
        $this-&gt;proxy  = $proxy;
    }

    /**
     * @param string|array $regIds
     * @param string $data
     * @throws \Exception
     */
    public function notify($regIds, $data)
    {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, self::GOOGLE_GCM_URL);
        if (!is_null($this-&gt;proxy)) {
            curl_setopt($ch, CURLOPT_PROXY, $this-&gt;proxy);
        }
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $this-&gt;getHeaders());
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $this-&gt;getPostFields($regIds, $data));

        $result = curl_exec($ch);
        if ($result === false) {
            throw new \Exception(curl_error($ch));
        }

        curl_close($ch);

        $this-&gt;output = $result;
    }

    /**
     * @return array
     */
    public function getOutputAsArray()
    {
        return json_decode($this-&gt;output, true);
    }

    /**
     * @return object
     */
    public function getOutputAsObject()
    {
        return json_decode($this-&gt;output);
    }

    private function getHeaders()
    {
        return [
            'Authorization: key=' . $this-&gt;apiKey,
            'Content-Type: application/json'
        ];
    }

    private function getPostFields($regIds, $data)
    {
        $fields = [
            'registration_ids' =&gt; is_string($regIds) ? [$regIds] : $regIds,
            'data'             =&gt; is_string($data) ? ['message' =&gt; $data] : $data,
        ];

        return json_encode($fields, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE);
    }
}</pre>
</div>
<p>Maybe we could improve the library with a parser of google’s ouptuput, basically because we need to handle this output to notice if the user has uninstalled the app (and we need the remove his reg-id from our database), but at least now it cover all my needs.</p>
<p>源码下载：You can see the code at <a href="https://github.com/gonzalo123/androidpusher">github</a></p>
<p>参考文章：<a href="http://gonzalo123.com/2013/08/05/sending-android-push-notifications-from-php-to-phonegap-applications/?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+GonzaloAyuso+%28Gonzalo+Ayuso+%7C+Web+Architect%29">sending-android-push-notifications-from-php-to-phonegap-application</a></p>
<p><a rel="nofollow" href="http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/">基于PHP后台的Android云推送方案</a>，首发于<a rel="nofollow" href="http://blog.zhourunsheng.com">润物无声</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.zhourunsheng.com/2013/08/%e5%9f%ba%e4%ba%8ephp%e5%90%8e%e5%8f%b0%e7%9a%84android%e4%ba%91%e6%8e%a8%e9%80%81%e6%96%b9%e6%a1%88/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
